aboutsummaryrefslogblamecommitdiff
path: root/xmpp/handlers.go
blob: 971ac8614230676d27b11208804108d09b9f35e0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


            

                         
                               

            

                 
 
                                                         
                                                             
                                                          
 
                                        



                              
                                     
                                             









                                               













































































                                                                                                      

 
                                                   


                                                    
                                


                      



























                                                          
                                                                      

                                                                                              

                                      


                                                                     
                                                      

                                                                                     







                                                                                              
 










                                                     
         
                                         


























                                                           





                                            












                                              
                                                                                          
                
                      


                       
                                           
                                    


                                             

                                                                                

                                                                            
                                               



                                                                                       

         
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"
	"gosrc.io/xmpp"
	"gosrc.io/xmpp/stanza"
)

func logPacketType(p stanza.Packet) {
	log.Warnf("Ignoring packet: %T\n", p)
}

// HandleIq processes an incoming XMPP iq
func HandleIq(s xmpp.Sender, p stanza.Packet) {
	iq, ok := p.(stanza.IQ)
	if !ok {
		logPacketType(p)
		return
	}

	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
func HandleMessage(s xmpp.Sender, p stanza.Packet) {
	msg, ok := p.(stanza.Message)
	if !ok {
		logPacketType(p)
		return
	}

	component, ok := s.(*xmpp.Component)
	if !ok {
		log.Error("Not a component")
		return
	}

	if msg.Type != "error" && msg.Body != "" {
		log.WithFields(log.Fields{
			"from": msg.From,
			"to":   msg.To,
		}).Warn("Message")
		log.Debugf("%#v", msg)

		fromJid, err := xmpp.NewJid(msg.From)
		if err != nil {
			log.Error("Invalid from JID!")
			return
		}

		session, ok := sessions[fromJid.Bare()]
		if !ok {
			log.Error("Message from stranger")
			return
		}

		toParts := strings.Split(msg.To, "@")
		toID := toParts[0]
		if len(toParts) > 1 {
			toIDInt, err := strconv.ParseInt(toID, 10, 64)
			if err == nil {
				session.ProcessOutgoingMessage(toIDInt, msg.Body, 0, msg.From)
				return
			}
			log.WithFields(log.Fields{
				"toID": toID,
			}).Error(errors.Wrap(err, "Invalid to JID!"))
		} else if toID == gateway.Jid.Bare() {
			if strings.HasPrefix(msg.Body, "/") {
				response := session.ProcessTransportCommand(msg.Body)
				if response != "" {
					gateway.SendMessage(msg.From, "", response, component)
				}
				return
			}
		}
		log.Warn("Unknown purpose of the message, skipping")
	}
}

// HandlePresence processes an incoming XMPP presence
func HandlePresence(s xmpp.Sender, p stanza.Packet) {
	prs, ok := p.(stanza.Presence)
	if !ok {
		logPacketType(p)
		return
	}

	if prs.Type == "subscribe" {
		handleSubscription(s, prs)
	}
	if prs.To == gateway.Jid.Bare() {
		handlePresence(s, prs)
	}
}

func handleSubscription(s xmpp.Sender, p stanza.Presence) {
	log.WithFields(log.Fields{
		"from": p.From,
		"to":   p.To,
	}).Warn("Subscription request")
	log.Debugf("%#v", p)

	reply := stanza.Presence{Attrs: stanza.Attrs{
		From: p.To,
		To:   p.From,
		Id:   p.Id,
		Type: "subscribed",
	}}

	_ = s.Send(reply)
}

func handlePresence(s xmpp.Sender, p stanza.Presence) {
	presenceType := p.Type
	if presenceType == "" {
		presenceType = "online"
	}

	component, ok := s.(*xmpp.Component)
	if !ok {
		log.Error("Not a component")
		return
	}

	log.WithFields(log.Fields{
		"type": presenceType,
		"from": p.From,
		"to":   p.To,
	}).Warn("Presence")
	log.Debugf("%#v", p)

	fromJid, err := xmpp.NewJid(p.From)
	if err != nil {
		log.Error("Invalid from JID!")
		return
	}
	bareFromJid := fromJid.Bare()
	session, ok := getTelegramInstance(bareFromJid, &persistence.Session{}, component)
	if !ok {
		return
	}

	switch p.Type {
	case "unsubscribed", "unsubscribe":
		session.Disconnect()
		delete(sessions, bareFromJid)
	case "unavailable", "error":
		session.Disconnect()
	case "probe", "", "online":
		// due to the weird implementation of go-tdlib wrapper, it won't
		// return the client instance until successful authorization
		go func() {
			err = session.Connect()
			if err != nil {
				log.Error(errors.Wrap(err, "TDlib connection failure"))
			}
		}()
	}
}