Initial commit

This commit is contained in:
Josh Deprez 2016-08-30 16:04:35 +10:00 committed by GitHub
parent 6bcf340ff1
commit a75477f73d
2 changed files with 238 additions and 0 deletions

BIN
Mac Classic Twitter.img Normal file

Binary file not shown.

238
memo.go Normal file
View File

@ -0,0 +1,238 @@
package main
/*
#cgo LDFLAGS: -L${SRCDIR} -latalk
#include <stdlib.h>
#include <errno.h>
#include <netatalk/endian.h>
#include <netatalk/at.h>
#include <atalk/atp.h>
#include <atalk/netddp.h>
#include <atalk/nbp.h>
#include <atalk/util.h>
#include <atalk/unicode.h>
const charset_t kChMac = CH_MAC;
const charset_t kChUnix = CH_UNIX;
const size_t kSizeMax = SIZE_MAX;
const u_int8_t kATAddrAnyPort = ATADDR_ANYPORT;
// C can't have Go pointers to Go pointers...
struct sockaddr_at * make_sockaddr_at() {
return (struct sockaddr_at *)malloc(sizeof(struct sockaddr_at));
}
struct iovec * make_iovec() {
return (struct iovec *)malloc(sizeof(struct iovec));
}
// The ATP block contains a union which is painful to use in straight Go.
// Hence we need simple helpers.
void set_rreqdata(struct atp_block *atpb, void *buf, int len) {
atpb->atp_rreqdata = buf;
atpb->atp_rreqdlen = len;
}
int get_rreqdlen(struct atp_block *atpb) {
return atpb->atp_rreqdlen;
}
void set_sresdata(struct atp_block *atpb, struct iovec * iov, int iovcnt) {
atpb->atp_sresiov = iov;
atpb->atp_sresiovcnt = iovcnt;
}
*/
import "C"
import (
"encoding/json"
"flag"
"fmt"
"log"
"os"
"os/signal"
"strings"
"unsafe"
"github.com/ChimeraCoder/anaconda"
)
var (
api *anaconda.TwitterApi
nbpName = flag.String("nbp_name", "GophersInYourAppletalk:Twitter", "Name to register for NBP")
standardResponse = flag.String("standard_response", "Gophers in your HyperCard!", "Standard response to ATP requests")
enableTweeting = flag.Bool("enable_tweeting", false, "Turns on the Twitter functionality; input will be tweeted")
twitterParamsFile = flag.String("twitter_params", "twitter_params.json", "Twitter parameters in JSON file")
twitterTokenFile = flag.String("twitter_token", "twitter_token.json", "Twitter user token in JSON file")
)
type oauthToken struct {
Token string `json:"oauth_token"`
Secret string `json:"oauth_token_secret"`
}
func doOAuth() (*anaconda.TwitterApi, error) {
u, c, err := anaconda.AuthorizationURL("")
if err != nil {
return nil, err
}
fmt.Printf("Go to %s in your Twitter browser session and then enter the PIN: ", u)
var p string
if _, err := fmt.Scanf("%s", &p); err != nil {
return nil, err
}
_, v, err := anaconda.GetCredentials(c, p)
if err != nil {
return nil, err
}
t := oauthToken{
Token: v.Get("oauth_token"),
Secret: v.Get("oauth_token_secret"),
}
if err := saveToken(*twitterTokenFile, &t); err != nil {
return nil, err
}
return anaconda.NewTwitterApi(t.Token, t.Secret), nil
}
func saveToken(path string, t *oauthToken) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(t)
}
func twitterAPI() (*anaconda.TwitterApi, error) {
var t oauthToken
f, err := os.Open(*twitterTokenFile)
if err != nil {
log.Printf("Cannot open Twitter token file: %v", err)
return doOAuth()
}
defer f.Close()
if err := json.NewDecoder(f).Decode(&t); err != nil {
log.Printf("Cannot decode Twitter token file: %v", err)
f.Close()
return doOAuth()
}
return anaconda.NewTwitterApi(t.Token, t.Secret), nil
}
func main() {
flag.Parse()
if *enableTweeting {
log.Print("Loading Twitter consumer key & secret")
f, err := os.Open(*twitterParamsFile)
if err != nil {
log.Fatalf("Cannot load Twitter parameters: %v", err)
}
defer f.Close()
var p struct {
ConsumerKey string `json:"consumer_key"`
ConsumerSecret string `json:"consumer_secret"`
}
if err := json.NewDecoder(f).Decode(&p); err != nil {
log.Fatalf("Cannot load Twitter parameters: %v", err)
}
anaconda.SetConsumerKey(p.ConsumerKey)
anaconda.SetConsumerSecret(p.ConsumerSecret)
log.Printf("Loading user OAuth token & secret")
a, err := twitterAPI()
if err != nil {
log.Fatalf("Cannot obtain OAuth token: %v", err)
}
api = a
}
interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
// Make sure nbpName is a useful looking name, I think?
cn := C.CString(*nbpName)
defer C.free(unsafe.Pointer(cn))
var convname *C.char
if C.convert_string_allocate(C.kChUnix, C.kChMac, unsafe.Pointer(cn), C.kSizeMax, &convname) == C.kSizeMax {
convname = cn
}
var o, t, z *C.char
if s := C.nbp_name(convname, &o, &t, &z); s != 0 {
log.Fatalf("nbp_name: the name was wrong: %v", s)
}
// Open ATP on an AppleTalk (DDP) port.
sat := C.make_sockaddr_at()
defer C.free(unsafe.Pointer(sat))
atAddr := (*C.struct_at_addr)(unsafe.Pointer(&sat.sat_addr))
atp, err := C.atp_open(C.kATAddrAnyPort, atAddr)
if atp == nil || err != nil {
log.Fatalf("atp_open: %v", err)
}
defer C.atp_close(atp)
// Register the NBP name.
addr := &atp.atph_saddr
n := fmt.Sprintf("%s:%s@%s", C.GoString(o), C.GoString(t), C.GoString(z))
log.Printf("Registering NBP name %s", n)
if r := C.nbp_rgstr(addr, o, t, z); r < 0 {
log.Fatalf("Couldn't register %s\n: %d", n, r)
}
defer C.nbp_unrgstr(o, t, z, atAddr)
// Serving loop.
go func() {
for {
if err := handleOne(sat, atp); err != nil {
log.Printf("Error handling request: %v", err)
}
}
}()
<-interrupt
log.Println("SIGINT received, quitting...")
C.nbp_unrgstr(o, t, z, atAddr)
}
func handleOne(sat *C.struct_sockaddr_at, atp *C.struct_atp_handle) error {
log.Println("Awaiting next request")
const bufSize = 4624
buf := C.malloc(bufSize)
defer C.free(buf)
atpb := &C.struct_atp_block{
atp_saddr: sat,
}
C.set_rreqdata(atpb, buf, bufSize)
if s := C.atp_rreq(atp, atpb); s < 0 {
return fmt.Errorf("atp_rreq: %v", s)
}
bt := C.GoBytes(buf, C.get_rreqdlen(atpb))
log.Printf("Got request: %s", bt)
if api != nil {
twt := strings.TrimPrefix(string(bt), "REQS")
if _, err := api.PostTweet(twt, nil); err != nil {
log.Printf("Error posting tweet: %v", err)
}
}
cmsg := fmt.Sprintf("RESP%s", *standardResponse)
resp := unsafe.Pointer(C.CString(cmsg))
defer C.free(resp)
iov := C.make_iovec()
defer C.free(unsafe.Pointer(iov))
iov.iov_base = resp
iov.iov_len = C.size_t(len(cmsg) + 1)
C.set_sresdata(atpb, iov, 1)
if s := C.atp_sresp(atp, atpb); s < 0 {
return fmt.Errorf("atp_sresp: %v", s)
}
log.Printf("Responded with: %s", *standardResponse)
return nil
}