shlist

share and manage lists between multiple people
Log | Files | Refs

apnd.go (3949B)


      1 package main
      2 
      3 import (
      4 	"bytes"
      5 	"crypto/tls"
      6 	//"crypto/x509"
      7 	"encoding/json"
      8 	"flag"
      9 	"fmt"
     10 	"log"
     11 	"net/http"
     12 	"net"
     13 	"os"
     14 	"os/signal"
     15 	"syscall"
     16 
     17 	"golang.org/x/net/http2"
     18 )
     19 
     20 // Object that comes from the main server
     21 type NotifyRequest struct {
     22 	Devices [][]string `json:"devices"`
     23 	MsgType string `json:"msg_type"`
     24 	Payload interface{} `json:"payload"`
     25 }
     26 
     27 // Object that matches the format for badge changes
     28 type Badge struct {
     29 	Count int `json:"badge"`
     30 }
     31 
     32 // Object that we serialize and send as the POST payload to APN servers
     33 type APNRequest struct {
     34 	Aps interface{} `json:"aps"`
     35 	MsgType string `json:"msg_type"`
     36 	Payload interface{} `json:"payload"`
     37 }
     38 
     39 func process_client(c net.Conn, h http.Client) {
     40 	// Read data from connection
     41 	buf := make([]byte, 4096)
     42 	nr, err := c.Read(buf);
     43 	if err != nil {
     44 		return
     45 	}
     46 	c.Close()
     47 
     48 	data := buf[0:nr]
     49 
     50 	// Parse JSON
     51 	var notify_request NotifyRequest
     52 	err = json.Unmarshal(data, &notify_request)
     53 	if err != nil {
     54 		log.Printf("error parsing json:", err)
     55 		return
     56 	}
     57 
     58 	total_devices := len(notify_request.Devices)
     59 	log.Printf("sending message type '%s' to %d device(s)", notify_request.MsgType, total_devices)
     60 
     61 	if total_devices == 0 {
     62 		return
     63 	}
     64 
     65 	var badge Badge
     66 	badge.Count = 17
     67 
     68 	var apn_request APNRequest
     69 	apn_request.Aps = badge
     70 	apn_request.Payload = notify_request.Payload
     71 	apn_request.MsgType = notify_request.MsgType
     72 
     73 	// Create the POST request body, only needs to be done once because we
     74 	// send the same message to all devices
     75 	request_body, err := json.Marshal(apn_request)
     76 	if err != nil {
     77 		log.Printf("error re-marshaling payload:", err)
     78 		return
     79 	}
     80 
     81 	// APN documentation says this is where we request stuff from
     82 	base_url := "https://api.development.push.apple.com/3/device/"
     83 
     84 	// Loop over all devices, check if they are ios and send a message
     85 	for i, d := range notify_request.Devices {
     86 
     87 		// Order defined by SQL statement in main server
     88 		os, hex_token := d[0], d[1]
     89 		log_header := fmt.Sprintf("%3d %s", i + 1, hex_token)
     90 
     91 		// Filter out any non iOS devices
     92 		if os != "ios" {
     93 			log.Printf("%s: not an ios device", log_header)
     94 			continue
     95 		}
     96 
     97 		// Construct entire post URL by adding hexadecimal device token
     98 		// to base URL
     99 		post_url := base_url + hex_token
    100 
    101 		// Make new POST request
    102 		req, err := http.NewRequest("POST", post_url, bytes.NewBuffer(request_body))
    103 		if err != nil {
    104 			log.Printf("%s: new request error: %s", log_header, err)
    105 			continue
    106 		}
    107 
    108 		// This delivers messages to our iOS application only
    109 		req.Header.Set("apns-topic", "com.octopus.shlist")
    110 
    111 		// Make request over existing transport
    112 		resp, err := h.Do(req)
    113 		if err != nil {
    114 			log.Printf("%s: %s", log_header, err)
    115 			continue
    116 		}
    117 		log.Printf("%s: %s", log_header, resp.Status)
    118 	}
    119 }
    120 
    121 func main() {
    122 	var sock_path = flag.String("p", "../apnd.socket", "path to control socket")
    123 	flag.Parse()
    124 
    125 	// These keys are provided by Apple through their Developer program
    126 	cert, err := tls.LoadX509KeyPair("certs/aps.pem", "certs/aps.key")
    127 	if err != nil {
    128 		log.Fatalf("loadkeys: %s", err)
    129 	}
    130 	config := tls.Config{Certificates: []tls.Certificate{cert}, InsecureSkipVerify: true}
    131 
    132 	// Create new http client with http2 TLS transport underneath
    133 	client := http.Client {
    134 		Transport: &http2.Transport{TLSClientConfig: &config},
    135 	}
    136 
    137 	// Create socket that listens for connections from the main server
    138 	l, err := net.Listen("unix", *sock_path)
    139 	if err != nil {
    140 		log.Fatal("listen error:", err)
    141 	}
    142 
    143 	// Close (and unlink) listener socket on shutdown signals
    144 	sigc := make(chan os.Signal, 1)
    145 	signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
    146 	go func(c chan os.Signal) {
    147 		// Wait for signal
    148 		sig := <-c
    149 		log.Printf("caught signal %s: shutting down", sig)
    150 
    151 		l.Close()
    152 		os.Exit(0)
    153 	}(sigc)
    154 
    155 	// Main loop, service new main server connections
    156 	for {
    157 		fd, err := l.Accept()
    158 		if err != nil {
    159 			log.Fatal("accept error:", err)
    160 			continue
    161 		}
    162 
    163 		go process_client(fd, client)
    164 	}
    165 }