package main import ( "io" "os" "crypto/tls" "crypto/x509" "encoding/xml" "log" ) 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 } multiw := io.MultiWriter(conn, os.Stdout) enc := xml.NewEncoder(multiw) receiver := make(chan xml.Token) termination := make(chan bool) go func() { quit := false tee := io.TeeReader(conn, os.Stdout) dec := xml.NewDecoder(tee) for { select { case <-termination: quit = true default: t, _ := dec.Token() if t != nil { receiver <- t } } if quit { break } } log.Println("Done!") }() end := sendStreamStart(enc, 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 <-receiver: // ignoring incoming XML tokens for now ... } if closing { break } } sendStreamEnd(enc, end) conn.Close() l.XmppEvents <- XmppDisconnect } func sendStreamStart(enc *xml.Encoder, 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"}, }, } err := enc.EncodeToken(start) if err != nil { log.Fatal("Could not encode stream start!") } err = enc.Flush() if err != nil { log.Fatal("Could not flush XML encoder!") } return start.End() } func sendStreamEnd(enc *xml.Encoder, end xml.EndElement) { err := enc.EncodeToken(end) if err != nil { log.Fatal("Could not encode stream end!") } err = enc.Flush() if err != nil { log.Fatal("Could not flush XML encoder!") } } // 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) }