summaryrefslogtreecommitdiffstats
path: root/vendor/google.golang.org/appengine/xmpp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/appengine/xmpp')
-rw-r--r--vendor/google.golang.org/appengine/xmpp/xmpp.go253
-rw-r--r--vendor/google.golang.org/appengine/xmpp/xmpp_test.go173
2 files changed, 426 insertions, 0 deletions
diff --git a/vendor/google.golang.org/appengine/xmpp/xmpp.go b/vendor/google.golang.org/appengine/xmpp/xmpp.go
new file mode 100644
index 000000000..3a561fd53
--- /dev/null
+++ b/vendor/google.golang.org/appengine/xmpp/xmpp.go
@@ -0,0 +1,253 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+/*
+Package xmpp provides the means to send and receive instant messages
+to and from users of XMPP-compatible services.
+
+To send a message,
+ m := &xmpp.Message{
+ To: []string{"kaylee@example.com"},
+ Body: `Hi! How's the carrot?`,
+ }
+ err := m.Send(c)
+
+To receive messages,
+ func init() {
+ xmpp.Handle(handleChat)
+ }
+
+ func handleChat(c context.Context, m *xmpp.Message) {
+ // ...
+ }
+*/
+package xmpp // import "google.golang.org/appengine/xmpp"
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+
+ "golang.org/x/net/context"
+
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/internal"
+ pb "google.golang.org/appengine/internal/xmpp"
+)
+
+// Message represents an incoming chat message.
+type Message struct {
+ // Sender is the JID of the sender.
+ // Optional for outgoing messages.
+ Sender string
+
+ // To is the intended recipients of the message.
+ // Incoming messages will have exactly one element.
+ To []string
+
+ // Body is the body of the message.
+ Body string
+
+ // Type is the message type, per RFC 3921.
+ // It defaults to "chat".
+ Type string
+
+ // RawXML is whether the body contains raw XML.
+ RawXML bool
+}
+
+// Presence represents an outgoing presence update.
+type Presence struct {
+ // Sender is the JID (optional).
+ Sender string
+
+ // The intended recipient of the presence update.
+ To string
+
+ // Type, per RFC 3921 (optional). Defaults to "available".
+ Type string
+
+ // State of presence (optional).
+ // Valid values: "away", "chat", "xa", "dnd" (RFC 3921).
+ State string
+
+ // Free text status message (optional).
+ Status string
+}
+
+var (
+ ErrPresenceUnavailable = errors.New("xmpp: presence unavailable")
+ ErrInvalidJID = errors.New("xmpp: invalid JID")
+)
+
+// Handle arranges for f to be called for incoming XMPP messages.
+// Only messages of type "chat" or "normal" will be handled.
+func Handle(f func(c context.Context, m *Message)) {
+ http.HandleFunc("/_ah/xmpp/message/chat/", func(_ http.ResponseWriter, r *http.Request) {
+ f(appengine.NewContext(r), &Message{
+ Sender: r.FormValue("from"),
+ To: []string{r.FormValue("to")},
+ Body: r.FormValue("body"),
+ })
+ })
+}
+
+// Send sends a message.
+// If any failures occur with specific recipients, the error will be an appengine.MultiError.
+func (m *Message) Send(c context.Context) error {
+ req := &pb.XmppMessageRequest{
+ Jid: m.To,
+ Body: &m.Body,
+ RawXml: &m.RawXML,
+ }
+ if m.Type != "" && m.Type != "chat" {
+ req.Type = &m.Type
+ }
+ if m.Sender != "" {
+ req.FromJid = &m.Sender
+ }
+ res := &pb.XmppMessageResponse{}
+ if err := internal.Call(c, "xmpp", "SendMessage", req, res); err != nil {
+ return err
+ }
+
+ if len(res.Status) != len(req.Jid) {
+ return fmt.Errorf("xmpp: sent message to %d JIDs, but only got %d statuses back", len(req.Jid), len(res.Status))
+ }
+ me, any := make(appengine.MultiError, len(req.Jid)), false
+ for i, st := range res.Status {
+ if st != pb.XmppMessageResponse_NO_ERROR {
+ me[i] = errors.New(st.String())
+ any = true
+ }
+ }
+ if any {
+ return me
+ }
+ return nil
+}
+
+// Invite sends an invitation. If the from address is an empty string
+// the default (yourapp@appspot.com/bot) will be used.
+func Invite(c context.Context, to, from string) error {
+ req := &pb.XmppInviteRequest{
+ Jid: &to,
+ }
+ if from != "" {
+ req.FromJid = &from
+ }
+ res := &pb.XmppInviteResponse{}
+ return internal.Call(c, "xmpp", "SendInvite", req, res)
+}
+
+// Send sends a presence update.
+func (p *Presence) Send(c context.Context) error {
+ req := &pb.XmppSendPresenceRequest{
+ Jid: &p.To,
+ }
+ if p.State != "" {
+ req.Show = &p.State
+ }
+ if p.Type != "" {
+ req.Type = &p.Type
+ }
+ if p.Sender != "" {
+ req.FromJid = &p.Sender
+ }
+ if p.Status != "" {
+ req.Status = &p.Status
+ }
+ res := &pb.XmppSendPresenceResponse{}
+ return internal.Call(c, "xmpp", "SendPresence", req, res)
+}
+
+var presenceMap = map[pb.PresenceResponse_SHOW]string{
+ pb.PresenceResponse_NORMAL: "",
+ pb.PresenceResponse_AWAY: "away",
+ pb.PresenceResponse_DO_NOT_DISTURB: "dnd",
+ pb.PresenceResponse_CHAT: "chat",
+ pb.PresenceResponse_EXTENDED_AWAY: "xa",
+}
+
+// GetPresence retrieves a user's presence.
+// If the from address is an empty string the default
+// (yourapp@appspot.com/bot) will be used.
+// Possible return values are "", "away", "dnd", "chat", "xa".
+// ErrPresenceUnavailable is returned if the presence is unavailable.
+func GetPresence(c context.Context, to string, from string) (string, error) {
+ req := &pb.PresenceRequest{
+ Jid: &to,
+ }
+ if from != "" {
+ req.FromJid = &from
+ }
+ res := &pb.PresenceResponse{}
+ if err := internal.Call(c, "xmpp", "GetPresence", req, res); err != nil {
+ return "", err
+ }
+ if !*res.IsAvailable || res.Presence == nil {
+ return "", ErrPresenceUnavailable
+ }
+ presence, ok := presenceMap[*res.Presence]
+ if ok {
+ return presence, nil
+ }
+ return "", fmt.Errorf("xmpp: unknown presence %v", *res.Presence)
+}
+
+// GetPresenceMulti retrieves multiple users' presence.
+// If the from address is an empty string the default
+// (yourapp@appspot.com/bot) will be used.
+// Possible return values are "", "away", "dnd", "chat", "xa".
+// If any presence is unavailable, an appengine.MultiError is returned
+func GetPresenceMulti(c context.Context, to []string, from string) ([]string, error) {
+ req := &pb.BulkPresenceRequest{
+ Jid: to,
+ }
+ if from != "" {
+ req.FromJid = &from
+ }
+ res := &pb.BulkPresenceResponse{}
+
+ if err := internal.Call(c, "xmpp", "BulkGetPresence", req, res); err != nil {
+ return nil, err
+ }
+
+ presences := make([]string, 0, len(res.PresenceResponse))
+ errs := appengine.MultiError{}
+
+ addResult := func(presence string, err error) {
+ presences = append(presences, presence)
+ errs = append(errs, err)
+ }
+
+ anyErr := false
+ for _, subres := range res.PresenceResponse {
+ if !subres.GetValid() {
+ anyErr = true
+ addResult("", ErrInvalidJID)
+ continue
+ }
+ if !*subres.IsAvailable || subres.Presence == nil {
+ anyErr = true
+ addResult("", ErrPresenceUnavailable)
+ continue
+ }
+ presence, ok := presenceMap[*subres.Presence]
+ if ok {
+ addResult(presence, nil)
+ } else {
+ anyErr = true
+ addResult("", fmt.Errorf("xmpp: unknown presence %q", *subres.Presence))
+ }
+ }
+ if anyErr {
+ return presences, errs
+ }
+ return presences, nil
+}
+
+func init() {
+ internal.RegisterErrorCodeMap("xmpp", pb.XmppServiceError_ErrorCode_name)
+}
diff --git a/vendor/google.golang.org/appengine/xmpp/xmpp_test.go b/vendor/google.golang.org/appengine/xmpp/xmpp_test.go
new file mode 100644
index 000000000..c3030d36d
--- /dev/null
+++ b/vendor/google.golang.org/appengine/xmpp/xmpp_test.go
@@ -0,0 +1,173 @@
+// Copyright 2011 Google Inc. All rights reserved.
+// Use of this source code is governed by the Apache 2.0
+// license that can be found in the LICENSE file.
+
+package xmpp
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/internal/aetesting"
+ pb "google.golang.org/appengine/internal/xmpp"
+)
+
+func newPresenceResponse(isAvailable bool, presence pb.PresenceResponse_SHOW, valid bool) *pb.PresenceResponse {
+ return &pb.PresenceResponse{
+ IsAvailable: proto.Bool(isAvailable),
+ Presence: presence.Enum(),
+ Valid: proto.Bool(valid),
+ }
+}
+
+func setPresenceResponse(m *pb.PresenceResponse, isAvailable bool, presence pb.PresenceResponse_SHOW, valid bool) {
+ m.IsAvailable = &isAvailable
+ m.Presence = presence.Enum()
+ m.Valid = &valid
+}
+
+func TestGetPresence(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "GetPresence", func(in *pb.PresenceRequest, out *pb.PresenceResponse) error {
+ if jid := in.GetJid(); jid != "user@example.com" {
+ return fmt.Errorf("bad jid %q", jid)
+ }
+ setPresenceResponse(out, true, pb.PresenceResponse_CHAT, true)
+ return nil
+ })
+
+ presence, err := GetPresence(c, "user@example.com", "")
+ if err != nil {
+ t.Fatalf("GetPresence: %v", err)
+ }
+
+ if presence != "chat" {
+ t.Errorf("GetPresence: got %#v, want %#v", presence, pb.PresenceResponse_CHAT)
+ }
+}
+
+func TestGetPresenceMultiSingleJID(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error {
+ if !reflect.DeepEqual(in.Jid, []string{"user@example.com"}) {
+ return fmt.Errorf("bad request jids %#v", in.Jid)
+ }
+ out.PresenceResponse = []*pb.PresenceResponse{
+ newPresenceResponse(true, pb.PresenceResponse_NORMAL, true),
+ }
+ return nil
+ })
+
+ presence, err := GetPresenceMulti(c, []string{"user@example.com"}, "")
+ if err != nil {
+ t.Fatalf("GetPresenceMulti: %v", err)
+ }
+ if !reflect.DeepEqual(presence, []string{""}) {
+ t.Errorf("GetPresenceMulti: got %s, want %s", presence, []string{""})
+ }
+}
+
+func TestGetPresenceMultiJID(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error {
+ if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) {
+ return fmt.Errorf("bad request jids %#v", in.Jid)
+ }
+ out.PresenceResponse = []*pb.PresenceResponse{
+ newPresenceResponse(true, pb.PresenceResponse_NORMAL, true),
+ newPresenceResponse(true, pb.PresenceResponse_AWAY, true),
+ }
+ return nil
+ })
+
+ jids := []string{"user@example.com", "user2@example.com"}
+ presence, err := GetPresenceMulti(c, jids, "")
+ if err != nil {
+ t.Fatalf("GetPresenceMulti: %v", err)
+ }
+ want := []string{"", "away"}
+ if !reflect.DeepEqual(presence, want) {
+ t.Errorf("GetPresenceMulti: got %v, want %v", presence, want)
+ }
+}
+
+func TestGetPresenceMultiFromJID(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error {
+ if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) {
+ return fmt.Errorf("bad request jids %#v", in.Jid)
+ }
+ if jid := in.GetFromJid(); jid != "bot@appspot.com" {
+ return fmt.Errorf("bad from jid %q", jid)
+ }
+ out.PresenceResponse = []*pb.PresenceResponse{
+ newPresenceResponse(true, pb.PresenceResponse_NORMAL, true),
+ newPresenceResponse(true, pb.PresenceResponse_CHAT, true),
+ }
+ return nil
+ })
+
+ jids := []string{"user@example.com", "user2@example.com"}
+ presence, err := GetPresenceMulti(c, jids, "bot@appspot.com")
+ if err != nil {
+ t.Fatalf("GetPresenceMulti: %v", err)
+ }
+ want := []string{"", "chat"}
+ if !reflect.DeepEqual(presence, want) {
+ t.Errorf("GetPresenceMulti: got %v, want %v", presence, want)
+ }
+}
+
+func TestGetPresenceMultiInvalid(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error {
+ if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) {
+ return fmt.Errorf("bad request jids %#v", in.Jid)
+ }
+ out.PresenceResponse = []*pb.PresenceResponse{
+ newPresenceResponse(true, pb.PresenceResponse_EXTENDED_AWAY, true),
+ newPresenceResponse(true, pb.PresenceResponse_CHAT, false),
+ }
+ return nil
+ })
+
+ jids := []string{"user@example.com", "user2@example.com"}
+ presence, err := GetPresenceMulti(c, jids, "")
+
+ wantErr := appengine.MultiError{nil, ErrInvalidJID}
+ if !reflect.DeepEqual(err, wantErr) {
+ t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr)
+ }
+
+ want := []string{"xa", ""}
+ if !reflect.DeepEqual(presence, want) {
+ t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want)
+ }
+}
+
+func TestGetPresenceMultiUnavailable(t *testing.T) {
+ c := aetesting.FakeSingleContext(t, "xmpp", "BulkGetPresence", func(in *pb.BulkPresenceRequest, out *pb.BulkPresenceResponse) error {
+ if !reflect.DeepEqual(in.Jid, []string{"user@example.com", "user2@example.com"}) {
+ return fmt.Errorf("bad request jids %#v", in.Jid)
+ }
+ out.PresenceResponse = []*pb.PresenceResponse{
+ newPresenceResponse(false, pb.PresenceResponse_AWAY, true),
+ newPresenceResponse(false, pb.PresenceResponse_DO_NOT_DISTURB, true),
+ }
+ return nil
+ })
+
+ jids := []string{"user@example.com", "user2@example.com"}
+ presence, err := GetPresenceMulti(c, jids, "")
+
+ wantErr := appengine.MultiError{
+ ErrPresenceUnavailable,
+ ErrPresenceUnavailable,
+ }
+ if !reflect.DeepEqual(err, wantErr) {
+ t.Fatalf("GetPresenceMulti: got %#v, want %#v", err, wantErr)
+ }
+ want := []string{"", ""}
+ if !reflect.DeepEqual(presence, want) {
+ t.Errorf("GetPresenceMulti: got %#v, want %#v", presence, want)
+ }
+}