package xmpp import ( "crypto/sha1" "encoding/base64" "encoding/hex" "encoding/xml" "errors" "strconv" ) const ( XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data" XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata" ) type clientAvatarData struct { XMLName xml.Name `xml:"data"` Data []byte `xml:",innerxml"` } type clientAvatarInfo struct { XMLName xml.Name `xml:"info"` Bytes string `xml:"bytes,attr"` Width string `xml:"width,attr"` Height string `xml:"height,attr"` ID string `xml:"id,attr"` Type string `xml:"type,attr"` URL string `xml:"url,attr"` } type clientAvatarMetadata struct { XMLName xml.Name `xml:"metadata"` XMLNS string `xml:"xmlns,attr"` Info clientAvatarInfo `xml:"info"` } type AvatarData struct { Data []byte From string } type AvatarMetadata struct { From string Bytes int Width int Height int ID string Type string URL string } func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) { var data clientAvatarData err := xml.Unmarshal(itemsBody, &data) if err != nil { return AvatarData{}, err } // Base64-decode the avatar data to check its SHA1 hash dataRaw, err := base64.StdEncoding.DecodeString( string(data.Data)) if err != nil { return AvatarData{}, err } hash := sha1.Sum(dataRaw) hashStr := hex.EncodeToString(hash[:]) if hashStr != id { return AvatarData{}, errors.New("SHA1 hashes do not match") } return AvatarData{ Data: dataRaw, From: from, }, nil } func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) { var meta clientAvatarMetadata err := xml.Unmarshal(body, &meta) if err != nil { return AvatarMetadata{}, err } return AvatarMetadata{ From: from, Bytes: atoiw(meta.Info.Bytes), Width: atoiw(meta.Info.Width), Height: atoiw(meta.Info.Height), ID: meta.Info.ID, Type: meta.Info.Type, URL: meta.Info.URL, }, nil } // A wrapper for atoi which just returns -1 if an error occurs func atoiw(str string) int { i, err := strconv.Atoi(str) if err != nil { return -1 } return i } func (c *Client) AvatarSubscribeMetadata(jid string) { c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid) } func (c *Client) AvatarUnsubscribeMetadata(jid string) { c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid) } func (c *Client) AvatarRequestData(jid string) { c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid) } func (c *Client) AvatarRequestDataByID(jid, id string) { c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id) } func (c *Client) AvatarRequestMetadata(jid string) { c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid) }