Пример #1
0
func main() {
	var deviceToken, filename, password, environment string

	flag.StringVar(&deviceToken, "d", "", "Device token")
	flag.StringVar(&filename, "c", "", "Path to p12 certificate file")
	flag.StringVar(&password, "p", "", "Password for p12 file.")
	flag.StringVar(&environment, "e", "development", "Environment")
	flag.Parse()

	cert, key, err := certificate.Load(filename, password)
	if err != nil {
		log.Fatal(err)
	}

	service := push.Service{
		Client: push.NewClient(certificate.TLS(cert, key)),
		Host:   push.Development,
	}
	if environment == "production" {
		service.Host = push.Production
	}

	p := payload.APS{
		Alert: payload.Alert{Body: "Hello HTTP/2"},
		Badge: badge.New(42),
	}

	id, err := service.Push(deviceToken, &push.Headers{}, p)
	if err != nil {
		log.Fatal(err)
	}
	log.Println("apns-id:", id)
}
Пример #2
0
func main() {
	var filename, password string

	flag.StringVar(&filename, "c", "", "Path to p12 certificate file")
	flag.StringVar(&password, "p", "", "Password for p12 file.")
	flag.Parse()

	var err error
	cert, privateKey, err = certificate.Load(filename, password)
	if err != nil {
		log.Fatal(err)
	}

	service = push.Service{
		Client: push.NewClient(certificate.TLS(cert, privateKey)),
		Host:   push.Production,
	}

	r := mux.NewRouter()
	r.HandleFunc("/", indexHandler).Methods("GET")
	r.HandleFunc("/request", requestPermissionHandler)
	r.HandleFunc("/push", pushHandler)
	r.HandleFunc("/click", clickHandler).Methods("GET")

	// WebServiceURL endpoints
	r.HandleFunc("/v1/pushPackages/{websitePushID}", pushPackagesHandler).Methods("POST")
	r.HandleFunc("/v1/devices/{deviceToken}/registrations/{websitePushID}", registerDeviceHandler).Methods("POST")
	r.HandleFunc("/v1/devices/{deviceToken}/registrations/{websitePushID}", forgetDeviceHandler).Methods("DELETE")
	r.HandleFunc("/v1/log", logHandler).Methods("POST")

	http.ListenAndServe(":5000", r)
}
Пример #3
0
func TestValidCert(t *testing.T) {
	const name = "../fixtures/cert.p12"

	_, _, err := certificate.Load(name, "")
	if err != nil {
		t.Fatal(err)
	}
}
Пример #4
0
func TestExpiredCert(t *testing.T) {
	// TODO: figure out how to test certificate loading and validation in CI
	const name = "../cert-expired.p12"

	_, _, err := certificate.Load(name, "")
	if err != certificate.ErrExpired {
		t.Fatal("Expected expired cert error, got", err)
	}
}
Пример #5
0
func TestValidCert(t *testing.T) {
	// TODO: figure out how to test certificate loading and validation in CI
	const name = "../cert.p12"

	_, err := certificate.Load(name, "")
	if err != nil {
		t.Fatal(err)
	}
}
Пример #6
0
func TestNew(t *testing.T) {
	website := pushpackage.Website{
		Name:                "Bay Airlines",
		PushID:              "web.com.example.domain",
		AllowedDomains:      []string{"http://domain.example.com"},
		URLFormatString:     `http://domain.example.com/%@/?flight=%@`,
		AuthenticationToken: "19f8d7a6e9fb8a7f6d9330dabe",
		WebServiceURL:       "https://example.com/push",
	}

	cert, privateKey, err := certificate.Load("../fixtures/cert.p12", "")
	if err != nil {
		t.Fatal(err)
	}

	buf := new(bytes.Buffer)

	pkg := pushpackage.New(buf)
	pkg.EncodeJSON("website.json", website)
	pkg.File("icon.iconset/[email protected]", "../fixtures/gopher.png")
	if err := pkg.Sign(cert, privateKey, nil); err != nil {
		t.Fatal(err)
	}

	expected := map[string]string{
		"website.json":  `{"websiteName":"Bay Airlines","websitePushID":"web.com.example.domain","allowedDomains":["http://domain.example.com"],"urlFormatString":"http://domain.example.com/%@/?flight=%@","authenticationToken":"19f8d7a6e9fb8a7f6d9330dabe","webServiceURL":"https://example.com/push"}`,
		"manifest.json": `{"icon.iconset/[email protected]":"5d31b7d2ea66ec7087c3789b2c6ca2aad67e459c","website.json":"8225d6cdd71f00888ff576aaab8d7ec4a27553c7"}`,
	}

	z, err := zip.NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
	for _, f := range z.File {
		if exp, ok := expected[f.Name]; ok {
			b, err := zipReadFile(f)
			if err != nil {
				t.Fatal(err)
			}
			if string(b) != exp {
				t.Errorf("Unexpected content for %s: %s", f.Name, b)
			}
		} else {
			t.Log(f.Name)
		}
	}
}
Пример #7
0
func main() {
	var filename, password, intermediate string

	flag.StringVar(&filename, "c", "", "Path to p12 certificate file")
	flag.StringVar(&password, "p", "", "Password for p12 file.")
	flag.StringVar(&intermediate, "i", "", "Path to WWDR intermediate .cer file")
	flag.Parse()

	cert, privateKey, err := certificate.Load(filename, password)
	failIfError(err)

	wwdr, err := loadWWDR(intermediate)
	failIfError(err)

	f, err := os.Create("Event.pkpass")
	failIfError(err)
	defer f.Close()

	passFiles := []string{
		"pass.json",
		"background.png",
		"*****@*****.**",
		"icon.png",
		"*****@*****.**",
		"logo.png",
		"*****@*****.**",
		"thumbnail.png",
		"*****@*****.**",
	}

	pkg := pushpackage.New(f)
	for _, name := range passFiles {
		pkg.File(name, "./Event.pass/"+name)
	}

	err = pkg.Sign(cert, privateKey, wwdr)
	failIfError(err)
}
Пример #8
0
func main() {
	var filename string
	var alert string
	var badgeInt int
	var sound string
	var category string
	var environmentString string
	var passphrase string
	var contentAvailable bool
	var payloadString string
	var payloadFile string
	var tokensFile string

	app := cli.NewApp()
	app.Name = "thunderstorm"
	// app.EnableBashCompletion = true
	app.Usage = "push TOKEN [...]"
	app.Version = "0.2.0"
	app.Commands = []cli.Command{
		{
			Name:  "push",
			Usage: "Sends an Apple Push Notification to specified devices",
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:        "certificate, c",
					Value:       "",
					Usage:       "Path to certificate (.p12) file",
					Destination: &filename,
				},
				cli.StringFlag{
					Name:        "alert, m",
					Value:       "",
					Usage:       "Body of the alert to send in the push notification",
					Destination: &alert,
				},
				cli.IntFlag{
					Name:        "badge, b",
					Usage:       "Badge number to set with the push notification",
					Destination: &badgeInt,
				},
				cli.StringFlag{
					Name:        "sound, s",
					Usage:       "Sound to play with the notification",
					Destination: &sound,
				},
				cli.StringFlag{
					Name:        "category, y",
					Usage:       "Category of notification",
					Destination: &category,
				},
				cli.StringFlag{
					Name:        "environment, e",
					Usage:       "Environment to send push notification (production or development (default))",
					Destination: &environmentString,
				},
				cli.StringFlag{
					Name:        "passphrase, p",
					Usage:       "Provides the certificate passphrase",
					Destination: &passphrase,
				},
				cli.BoolFlag{
					Name:        "content-available, n",
					Usage:       "Indicates content available",
					Destination: &contentAvailable,
				},
				cli.StringFlag{
					Name:        "payload, P",
					Usage:       "JSON payload for notifications",
					Destination: &payloadString,
				},
				cli.StringFlag{
					Name:        "payload-file, f",
					Usage:       "Path of JSON file containing the payload to be sent",
					Destination: &payloadFile,
				},
				cli.StringFlag{
					Name:        "tokens-path, t",
					Usage:       "Path of JSON file containing the tokens",
					Destination: &tokensFile,
				},
			},

			// TODO: support low priority notifications
			// TODO: support expiration time
			// TODO: support id
			// TODO: support apns-collapse-id
			// TODO: support threads

			// TODO: support buford features https://github.com/RobotsAndPencils/buford

			// TODO: use a queue to send push notifications

			// TODO: support other parameters https://github.com/nomad/houston/blob/master/bin/apn

			Action: func(c *cli.Context) error {
				deviceToken := c.Args().First()

				cert, err := certificate.Load(filename, passphrase)
				if err != nil {
					// TODO detect if the error is
					// "pkcs12: expected exactly two safe bags in the PFX PDU"
					// and suggest to export the certificate from the keychain selecting
					// only one row
					log.Fatal(err)
					return err
				}
				// tls := certificate.TLS(cert, private)

				certificate.TopicFromCert(cert)

				commonName := cert.Leaf.Subject.CommonName
				bundle := strings.Replace(commonName, "Apple Push Services: ", "", 1)

				var environment = push.Development

				if environmentString == "production" {
					environment = push.Production
				}

				client, err := push.NewClient(cert)
				if err != nil {
					log.Fatal(err)
				}

				service := push.Service{
					Client: client,
					Host:   environment,
				}

				if payloadFile != "" {
					buffer, err := ioutil.ReadFile(payloadFile)
					if err == nil {
						payloadString = string(buffer)
					}
				}

				var p map[string]interface{}
				if payloadString != "" {
					error := json.Unmarshal([]byte(payloadString), &p)
					if error != nil {
						log.Fatal("Cannot parse payload")
						return err
					}
				}

				if p == nil {
					bUint := uint(badgeInt)

					// TODO: use Alert title, body and action or let it be with paylod
					// TODO: support mutable-content modifier
					pay := payload.APS{
						Alert:            payload.Alert{Body: alert},
						Badge:            badge.New(bUint),
						Sound:            sound,
						Category:         category,
						ContentAvailable: contentAvailable,
					}
					p = pay.Map()
				}

				headers := &push.Headers{
					Topic: bundle,
				}

				tokensString := ""

				if tokensFile != "" {
					buffer, err := ioutil.ReadFile(tokensFile)
					if err == nil {
						tokensString = string(buffer)
					}

					var tokens []string
					if tokensString != "" {
						error := json.Unmarshal([]byte(tokensString), &tokens)
						if error != nil {
							log.Fatal("Cannot parse tokens array", error)
							return error
						}
						for _, token := range tokens {
							send(token, headers, p, &service)
						}
					}
				} else {
					send(deviceToken, headers, p, &service)
				}
				return nil
			},
		},
	}

	app.Run(os.Args)
}
Пример #9
0
func TestMissingFile(t *testing.T) {
	_, _, err := certificate.Load("hide-and-seek.p12", "")
	if err == nil {
		t.Fatal("Expected file not found, got", err)
	}
}