package main import ( "crypto/tls" "crypto/x509" "encoding/xml" "log" "reflect" ) type XmppEvent uint8 const ( XmppDisconnect XmppEvent = iota XmppConnect ) func (l *Limox) xmpp(jid string, pwd string) { log.Printf("JID: '%s' PWD: '%s'\n", jid, pwd) domain := domainpart(jid) log.Printf("Domain: '%s'\n", domain) roots, err := x509.SystemCertPool() if err != nil { l.XmppEvents <- err return } conn, err := tls.Dial("tcp", domain+":"+"5223", &tls.Config{RootCAs: roots}) if err != nil { l.XmppEvents <- err return } defer conn.Close() receiver := make(chan xml.Token) termination := make(chan bool) go func() { quit := false dec := xml.NewDecoder(conn) for { select { case <-termination: quit = true default: t, _ := dec.Token() if t != nil { receiver <- t } } if quit { break } } log.Println("Done!") }() end := sendStreamStart(conn, jid) l.XmppEvents <- XmppConnect var closing bool = false for { select { case ev := <-l.GuiEvents: switch ev { case Disconnect: termination <- true closing = true default: //log.Fatalf("Unknown GuiEvent '%d'!\n", ev) } case rx := <-receiver: logToken(rx, false) } if closing { break } } sendStreamEnd(conn, end) l.XmppEvents <- XmppDisconnect } func logToken(t xml.Token, isTx bool) { var prefix string if isTx { prefix = "C" } else { prefix = "S" } switch t.(type) { case xml.CharData: log.Printf("%s: [ %v ] %s\n", prefix, reflect.TypeOf(t), t) default: log.Printf("%s: [ %v ] %+v\n", prefix, reflect.TypeOf(t), t) } } func sendStreamStart(t *tls.Conn, jid string) (xml.EndElement) { start := xml.StartElement{ xml.Name{"jabber:client", "stream:stream"}, []xml.Attr{ xml.Attr{xml.Name{"", "from"}, jid}, xml.Attr{xml.Name{"", "to"}, domainpart(jid)}, xml.Attr{xml.Name{"", "version"}, "1.0"}, xml.Attr{xml.Name{"", "xml:lang"}, "en"}, xml.Attr{xml.Name{"", "xmlns:stream"}, "http://etherx.jabber.org/streams"}, }, } e := xml.NewEncoder(t) err := e.EncodeToken(start) if err != nil { log.Fatal("Could not encode stream start!") } e.Close() logToken(start, true) return start.End() } func sendStreamEnd(t *tls.Conn, end xml.EndElement) { e := xml.NewEncoder(t) err := e.EncodeToken(end) if err != nil { log.Fatal("Could not encode stream end!") } e.Close() logToken(end, true) } // domainpart extracts the domain name from a JID / XMPP address. See // https://datatracker.ietf.org/doc/html/rfc7622#section-3.2 for details. func domainpart(jid string) string { list := []rune(jid) for i, v := range list { if v == '/' { list = list[:i] break } } for i, v := range list { if v == '@' { list = list[i+1:] break } } return string(list) }