From 76ac66236612315860538a73334e6c7d10c7c4b4 Mon Sep 17 00:00:00 2001 From: Bohdan Horbeshko Date: Tue, 28 Jun 2022 19:34:14 -0400 Subject: [PATCH] Support vCard4 via PubSub --- xmpp/extensions/extensions.go | 2 +- xmpp/handlers.go | 139 +++++++++++++++++++++++++++++++--- 2 files changed, 129 insertions(+), 12 deletions(-) diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index 284dd91..878770a 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -119,7 +119,7 @@ func (c IqVcardTemp) Namespace() string { return c.XMLName.Space } -// Namespace is a namespace! +// GetSet getsets! func (c IqVcardTemp) GetSet() *stanza.ResultSet { return c.ResultSet } diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 870a292..06bc02b 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -3,6 +3,7 @@ package xmpp import ( "bytes" "encoding/base64" + "encoding/xml" "github.com/pkg/errors" "io" "strconv" @@ -17,6 +18,12 @@ import ( "gosrc.io/xmpp/stanza" ) +const ( + TypeVCardTemp byte = iota + TypeVCard4 +) +const NodeVCard4 string = "urn:xmpp:vcard4" + func logPacketType(p stanza.Packet) { log.Warnf("Ignoring packet: %T\n", p) } @@ -33,9 +40,16 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) { if iq.Type == "get" { _, ok := iq.Payload.(*extensions.IqVcardTemp) if ok { - go handleGetVcardTempIq(s, iq) + go handleGetVcardIq(s, iq, TypeVCardTemp) return } + pubsub, ok := iq.Payload.(*stanza.PubSubGeneric) + if ok { + if pubsub.Items != nil && pubsub.Items.Node == NodeVCard4 { + go handleGetVcardIq(s, iq, TypeVCard4) + return + } + } _, ok = iq.Payload.(*stanza.DiscoInfo) if ok { go handleGetDiscoInfo(s, iq) @@ -216,7 +230,7 @@ func handlePresence(s xmpp.Sender, p stanza.Presence) { } } -func handleGetVcardTempIq(s xmpp.Sender, iq *stanza.IQ) { +func handleGetVcardIq(s xmpp.Sender, iq *stanza.IQ, typ byte) { log.WithFields(log.Fields{ "from": iq.From, "to": iq.To, @@ -246,9 +260,9 @@ func handleGetVcardTempIq(s xmpp.Sender, iq *stanza.IQ) { return } - vcard := extensions.IqVcardTemp{} + var fn, photo, nickname, given, family, tel string if chat != nil { - vcard.Fn.Text = chat.Title + fn = chat.Title if chat.Photo != nil { file, path, err := session.OpenPhotoFile(chat.Photo.Small, 32) @@ -260,8 +274,7 @@ func handleGetVcardTempIq(s xmpp.Sender, iq *stanza.IQ) { _, err = io.Copy(binval, file) binval.Close() if err == nil { - vcard.Photo.Type.Text = "image/jpeg" - vcard.Photo.Binval.Text = buf.String() + photo = buf.String() } else { log.Errorf("Error calculating base64: %v", path) } @@ -273,10 +286,10 @@ func handleGetVcardTempIq(s xmpp.Sender, iq *stanza.IQ) { } } if user != nil { - vcard.Nickname.Text = user.Username - vcard.N.Given.Text = user.FirstName - vcard.N.Family.Text = user.LastName - vcard.Tel.Number.Text = user.PhoneNumber + nickname = user.Username + given = user.FirstName + family = user.LastName + tel = user.PhoneNumber } answer := stanza.IQ{ @@ -286,7 +299,7 @@ func handleGetVcardTempIq(s xmpp.Sender, iq *stanza.IQ) { Id: iq.Id, Type: "result", }, - Payload: vcard, + Payload: makeVCardPayload(typ, iq.To, fn, photo, nickname, given, family, tel), } log.Debugf("%#v", answer) @@ -357,3 +370,107 @@ func toToID(to string) (int64, bool) { } return toID, true } + +func makeVCardPayload(typ byte, id string, fn string, photo string, nickname string, given string, family string, tel string) stanza.IQPayload { + if typ == TypeVCardTemp { + vcard := &extensions.IqVcardTemp{} + + vcard.Fn.Text = fn + if photo != "" { + vcard.Photo.Type.Text = "image/jpeg" + vcard.Photo.Binval.Text = photo + } + vcard.Nickname.Text = nickname + vcard.N.Given.Text = given + vcard.N.Family.Text = family + vcard.Tel.Number.Text = tel + + return vcard + } else if typ == TypeVCard4 { + nodes := []stanza.Node{} + if fn != "" { + nodes = append(nodes, stanza.Node{ + XMLName: xml.Name{Local: "fn"}, + Nodes: []stanza.Node{ + stanza.Node{ + XMLName: xml.Name{Local: "text"}, + Content: fn, + }, + }, + }) + } + if photo != "" { + nodes = append(nodes, stanza.Node{ + XMLName: xml.Name{Local: "photo"}, + Nodes: []stanza.Node{ + stanza.Node{ + XMLName: xml.Name{Local: "uri"}, + Content: "data:image/jpeg;base64," + photo, + }, + }, + }) + } + if nickname != "" { + nodes = append(nodes, stanza.Node{ + XMLName: xml.Name{Local: "nickname"}, + Nodes: []stanza.Node{ + stanza.Node{ + XMLName: xml.Name{Local: "text"}, + Content: nickname, + }, + }, + }) + } + if family != "" || given != "" { + nodes = append(nodes, stanza.Node{ + XMLName: xml.Name{Local: "n"}, + Nodes: []stanza.Node{ + stanza.Node{ + XMLName: xml.Name{Local: "surname"}, + Content: family, + }, + stanza.Node{ + XMLName: xml.Name{Local: "given"}, + Content: given, + }, + }, + }) + } + if tel != "" { + nodes = append(nodes, stanza.Node{ + XMLName: xml.Name{Local: "tel"}, + Nodes: []stanza.Node{ + stanza.Node{ + XMLName: xml.Name{Local: "uri"}, + Content: "tel:" + tel, + }, + }, + }) + } + + pubsub := &stanza.PubSubGeneric{ + Items: &stanza.Items{ + Node: NodeVCard4, + List: []stanza.Item{ + stanza.Item{ + Id: id, + Any: &stanza.Node{ + XMLName: xml.Name{Local: "vcard"}, + Attrs: []xml.Attr{ + xml.Attr{ + Name: xml.Name{Local: "xmlns"}, + Value: "urn:ietf:params:xml:ns:vcard-4.0", + }, + }, + Nodes: nodes, + }, + }, + }, + }, + } + + return pubsub + } + + return nil +}