package main /* #cgo LDFLAGS: -L${SRCDIR} -latalk #include #include #include #include #include #include #include #include #include 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 }