aboutsummaryrefslogblamecommitdiff
path: root/xmpp/handlers.go
blob: 2addd7bdea6b0ea16cb81e54e6b1c202137ef37e (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 {
			go handleGetVcardTempIq(s, iq)
		}
	}
}

// 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)

	// create session
	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 {
	// destroy session
	case "unsubscribed", "unsubscribe":
		session.Disconnect()
		delete(sessions, bareFromJid)
	// go offline
	case "unavailable", "error":
		session.Disconnect()
	// go online
	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"))
			}
		}()
	}
}

func handleGetVcardTempIq(s xmpp.Sender, iq stanza.IQ) {
	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)
}