forked from lug/matterbridge
		
	
		
			
				
	
	
		
			191 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package bxmpp
 | |
| 
 | |
| import (
 | |
| 	"crypto/tls"
 | |
| 	"github.com/42wim/matterbridge/bridge/config"
 | |
| 	log "github.com/Sirupsen/logrus"
 | |
| 	"github.com/jpillora/backoff"
 | |
| 	"github.com/mattn/go-xmpp"
 | |
| 
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| type Bxmpp struct {
 | |
| 	xc      *xmpp.Client
 | |
| 	xmppMap map[string]string
 | |
| 	*config.BridgeConfig
 | |
| }
 | |
| 
 | |
| var flog *log.Entry
 | |
| var protocol = "xmpp"
 | |
| 
 | |
| func init() {
 | |
| 	flog = log.WithFields(log.Fields{"module": protocol})
 | |
| }
 | |
| 
 | |
| func New(cfg *config.BridgeConfig) *Bxmpp {
 | |
| 	b := &Bxmpp{BridgeConfig: cfg}
 | |
| 	b.xmppMap = make(map[string]string)
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) Connect() error {
 | |
| 	var err error
 | |
| 	flog.Infof("Connecting %s", b.Config.Server)
 | |
| 	b.xc, err = b.createXMPP()
 | |
| 	if err != nil {
 | |
| 		flog.Debugf("%#v", err)
 | |
| 		return err
 | |
| 	}
 | |
| 	flog.Info("Connection succeeded")
 | |
| 	go func() {
 | |
| 		initial := true
 | |
| 		bf := &backoff.Backoff{
 | |
| 			Min:    time.Second,
 | |
| 			Max:    5 * time.Minute,
 | |
| 			Jitter: true,
 | |
| 		}
 | |
| 		for {
 | |
| 			if initial {
 | |
| 				b.handleXmpp()
 | |
| 				initial = false
 | |
| 			}
 | |
| 			d := bf.Duration()
 | |
| 			flog.Infof("Disconnected. Reconnecting in %s", d)
 | |
| 			time.Sleep(d)
 | |
| 			b.xc, err = b.createXMPP()
 | |
| 			if err == nil {
 | |
| 				b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EVENT_REJOIN_CHANNELS}
 | |
| 				b.handleXmpp()
 | |
| 				bf.Reset()
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) Disconnect() error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error {
 | |
| 	b.xc.JoinMUCNoHistory(channel.Name+"@"+b.Config.Muc, b.Config.Nick)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) Send(msg config.Message) (string, error) {
 | |
| 	// ignore delete messages
 | |
| 	if msg.Event == config.EVENT_MSG_DELETE {
 | |
| 		return "", nil
 | |
| 	}
 | |
| 	flog.Debugf("Receiving %#v", msg)
 | |
| 	if msg.Extra != nil {
 | |
| 		if len(msg.Extra["file"]) > 0 {
 | |
| 			for _, f := range msg.Extra["file"] {
 | |
| 				fi := f.(config.FileInfo)
 | |
| 				if fi.URL != "" {
 | |
| 					msg.Text = fi.URL
 | |
| 				}
 | |
| 				b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | |
| 			}
 | |
| 			return "", nil
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
 | |
| 	return "", nil
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
 | |
| 	tc := new(tls.Config)
 | |
| 	tc.InsecureSkipVerify = b.Config.SkipTLSVerify
 | |
| 	tc.ServerName = strings.Split(b.Config.Server, ":")[0]
 | |
| 	options := xmpp.Options{
 | |
| 		Host:      b.Config.Server,
 | |
| 		User:      b.Config.Jid,
 | |
| 		Password:  b.Config.Password,
 | |
| 		NoTLS:     true,
 | |
| 		StartTLS:  true,
 | |
| 		TLSConfig: tc,
 | |
| 
 | |
| 		//StartTLS:      false,
 | |
| 		Debug:                        true,
 | |
| 		Session:                      true,
 | |
| 		Status:                       "",
 | |
| 		StatusMessage:                "",
 | |
| 		Resource:                     "",
 | |
| 		InsecureAllowUnencryptedAuth: false,
 | |
| 		//InsecureAllowUnencryptedAuth: true,
 | |
| 	}
 | |
| 	var err error
 | |
| 	b.xc, err = options.NewClient()
 | |
| 	return b.xc, err
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) xmppKeepAlive() chan bool {
 | |
| 	done := make(chan bool)
 | |
| 	go func() {
 | |
| 		ticker := time.NewTicker(90 * time.Second)
 | |
| 		defer ticker.Stop()
 | |
| 		for {
 | |
| 			select {
 | |
| 			case <-ticker.C:
 | |
| 				flog.Debugf("PING")
 | |
| 				err := b.xc.PingC2S("", "")
 | |
| 				if err != nil {
 | |
| 					flog.Debugf("PING failed %#v", err)
 | |
| 				}
 | |
| 			case <-done:
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| 	return done
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) handleXmpp() error {
 | |
| 	var ok bool
 | |
| 	done := b.xmppKeepAlive()
 | |
| 	defer close(done)
 | |
| 	nodelay := time.Time{}
 | |
| 	for {
 | |
| 		m, err := b.xc.Recv()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		switch v := m.(type) {
 | |
| 		case xmpp.Chat:
 | |
| 			var channel, nick string
 | |
| 			if v.Type == "groupchat" {
 | |
| 				s := strings.Split(v.Remote, "@")
 | |
| 				if len(s) >= 2 {
 | |
| 					channel = s[0]
 | |
| 				}
 | |
| 				s = strings.Split(s[1], "/")
 | |
| 				if len(s) == 2 {
 | |
| 					nick = s[1]
 | |
| 				}
 | |
| 				if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" {
 | |
| 					rmsg := config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account, UserID: v.Remote}
 | |
| 					rmsg.Text, ok = b.replaceAction(rmsg.Text)
 | |
| 					if ok {
 | |
| 						rmsg.Event = config.EVENT_USER_ACTION
 | |
| 					}
 | |
| 					flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account)
 | |
| 					b.Remote <- rmsg
 | |
| 				}
 | |
| 			}
 | |
| 		case xmpp.Presence:
 | |
| 			// do nothing
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (b *Bxmpp) replaceAction(text string) (string, bool) {
 | |
| 	if strings.HasPrefix(text, "/me ") {
 | |
| 		return strings.Replace(text, "/me ", "", -1), true
 | |
| 	}
 | |
| 	return text, false
 | |
| }
 | 
