diff --git a/xmpp/extensions/extensions.go b/xmpp/extensions/extensions.go index ec94837..594140f 100644 --- a/xmpp/extensions/extensions.go +++ b/xmpp/extensions/extensions.go @@ -24,6 +24,85 @@ type PresenceXVCardUpdatePhoto struct { Text string `xml:",chardata"` } +// IqVcardTemp is from XEP-0054 +type IqVcardTemp struct { + XMLName xml.Name `xml:"vcard-temp vCard"` + Fn IqVcardFn + Nickname IqVcardNickname + N IqVcardN + Tel IqVcardTel + Photo IqVcardPhoto +} + +// IqVcardFn is vCard/FN +type IqVcardFn struct { + XMLName xml.Name `xml:"FN"` + Text string `xml:",chardata"` +} + +// IqVcardNickname is vCard/NICKNAME +type IqVcardNickname struct { + XMLName xml.Name `xml:"NICKNAME"` + Text string `xml:",chardata"` +} + +// IqVcardN is vCard/N +type IqVcardN struct { + XMLName xml.Name `xml:"N"` + Family IqVcardNFamily + Given IqVcardNGiven + Middle IqVcardNMiddle +} + +// IqVcardNFamily is vCard/N/FAMILY +type IqVcardNFamily struct { + XMLName xml.Name `xml:"FAMILY"` + Text string `xml:",chardata"` +} + +// IqVcardNGiven is vCard/N/GIVEN +type IqVcardNGiven struct { + XMLName xml.Name `xml:"GIVEN"` + Text string `xml:",chardata"` +} + +// IqVcardNMiddle is vCard/N/MIDDLE +type IqVcardNMiddle struct { + XMLName xml.Name `xml:"MIDDLE"` + Text string `xml:",chardata"` +} + +// IqVcardTel is vCard/TEL +type IqVcardTel struct { + XMLName xml.Name `xml:"TEL"` + Number IqVcardTelNumber +} + +// IqVcardTelNumber is vCard/TEL/NUMBER +type IqVcardTelNumber struct { + XMLName xml.Name `xml:"NUMBER"` + Text string `xml:",chardata"` +} + +// IqVcardPhoto is vCard/PHOTO +type IqVcardPhoto struct { + XMLName xml.Name `xml:"PHOTO"` + Type IqVcardPhotoType + Binval IqVcardPhotoBinval +} + +// IqVcardPhotoType is vCard/PHOTO/TYPE +type IqVcardPhotoType struct { + XMLName xml.Name `xml:"TYPE"` + Text string `xml:",chardata"` +} + +// IqVcardPhotoBinval is vCard/PHOTO/BINVAL +type IqVcardPhotoBinval struct { + XMLName xml.Name `xml:"BINVAL"` + Text string `xml:",chardata"` +} + // Namespace is a namespace! func (c PresenceNickExtension) Namespace() string { return c.XMLName.Space @@ -34,6 +113,11 @@ func (c PresenceXVCardUpdateExtension) Namespace() string { return c.XMLName.Space } +// Namespace is a namespace! +func (c IqVcardTemp) Namespace() string { + return c.XMLName.Space +} + func init() { // presence nick stanza.TypeRegistry.MapExtension(stanza.PKTPresence, xml.Name{ @@ -46,4 +130,10 @@ func init() { "vcard-temp:x:update", "x", }, PresenceXVCardUpdateExtension{}) + + // iq vcard request + stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{ + "vcard-temp", + "vCard", + }, IqVcardTemp{}) } diff --git a/xmpp/handlers.go b/xmpp/handlers.go index 5635f10..31c2b27 100644 --- a/xmpp/handlers.go +++ b/xmpp/handlers.go @@ -1,11 +1,16 @@ package xmpp import ( + "bytes" + "encoding/base64" "github.com/pkg/errors" + "io" + "os" "strconv" "strings" "dev.narayana.im/narayana/telegabber/persistence" + "dev.narayana.im/narayana/telegabber/xmpp/extensions" "dev.narayana.im/narayana/telegabber/xmpp/gateway" log "github.com/sirupsen/logrus" @@ -25,7 +30,84 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) { return } - log.Printf("Iq: %#v\n", iq) + log.Debugf("%#v", iq) + if iq.Type == "get" { + _, ok := iq.Payload.(*extensions.IqVcardTemp) + if ok { + log.WithFields(log.Fields{ + "from": iq.From, + "to": iq.To, + }).Warn("VCard request") + + fromJid, err := xmpp.NewJid(iq.From) + if err != nil { + log.Error("Invalid from JID!") + return + } + + session, ok := sessions[fromJid.Bare()] + if !ok { + log.Error("IQ from stranger") + return + } + + toParts := strings.Split(iq.To, "@") + toID, err := strconv.ParseInt(toParts[0], 10, 64) + if err != nil { + log.Error("Invalid IQ to") + return + } + chat, user, err := session.GetContactByID(toID, nil) + if err != nil { + log.Error(err) + return + } + + vcard := extensions.IqVcardTemp{} + if chat != nil { + vcard.Fn.Text = chat.Title + + if chat.Photo != nil { + path := chat.Photo.Small.Local.Path + file, err := os.Open(path) + if err == nil { + defer file.Close() + + buf := new(bytes.Buffer) + binval := base64.NewEncoder(base64.StdEncoding, buf) + _, err = io.Copy(binval, file) + if err == nil { + vcard.Photo.Type.Text = "image/jpeg" + vcard.Photo.Binval.Text = buf.String() + } else { + log.Errorf("Error calculating hash: %v", path) + } + } else if path != "" { + log.Errorf("Photo does not exist: %v", path) + } + } + } + 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 + } + + answer := stanza.IQ{ + Attrs: stanza.Attrs{ + From: iq.To, + To: iq.From, + Id: iq.Id, + Type: "result", + }, + Payload: vcard, + } + log.Debugf("%#v", answer) + + _ = s.Send(answer) + } + } } // HandleMessage processes an incoming XMPP message