// Copyright Martin Dosch. // Use of this source code is governed by the BSD-2-clause // license that can be found in the LICENSE file. package main import ( "context" "fmt" "log" "github.com/beevik/etree" // BSD-2-clause "github.com/mattn/go-xmpp" // BSD-3-Clause ) func sendIQ(client *xmpp.Client, iqc chan xmpp.IQ, target string, iQtype string, content string) (xmpp.IQ, error) { var iq xmpp.IQ id := getID() c := make(chan xmpp.IQ) go getIQ(id, c, iqc) _, err := client.RawInformation(client.JID(), target, id, iQtype, content) if err != nil { return iq, fmt.Errorf("sendIQ: failed to send iq: %w", err) } iq = <-c return iq, nil } func getIQ(id string, c chan xmpp.IQ, iqc chan xmpp.IQ) { for { iq := <-iqc if iq.ID == id { c <- iq return } } } func rcvStanzas(client *xmpp.Client, iqc chan xmpp.IQ, msgc chan xmpp.Chat, ctx context.Context) { for { received, err := client.Recv() // Don't print errors if the program is getting shut down, // as the errors might be triggered from trying to read from // a closed connection. select { case <-ctx.Done(): return default: if err != nil { log.Println(err) } } switch v := received.(type) { case xmpp.Chat: msgc <- v case xmpp.IQ: switch v.Type { case "get": var xmlns *etree.Attr iq := etree.NewDocument() err = iq.ReadFromBytes(v.Query) if err != nil { log.Println("Couldn't parse IQ:", string(v.Query), err) } query := iq.SelectElement("query") if query != nil { xmlns = query.SelectAttr("xmlns") } switch xmlns.Value { case nsDiscoInfo: root := etree.NewDocument() root.WriteSettings.AttrSingleQuote = true reply := root.CreateElement("iq") reply.CreateAttr("type", "result") reply.CreateAttr("from", client.JID()) reply.CreateAttr("to", v.From) reply.CreateAttr("id", v.ID) replyQuery := reply.CreateElement("query") replyQuery.CreateAttr("xmlns", nsDiscoInfo) identity := replyQuery.CreateElement("identity") identity.CreateAttr("category", "client") identity.CreateAttr("type", "bot") identity.CreateAttr("name", "go-sendxmpp") feat := replyQuery.CreateElement("feature") feat.CreateAttr("var", nsDiscoInfo) xmlString, err := root.WriteToString() if err == nil { _, err = client.SendOrg(xmlString) if err != nil { log.Println(err) } } default: root := etree.NewDocument() root.WriteSettings.AttrSingleQuote = true reply := root.CreateElement("iq") reply.CreateAttr("type", strError) reply.CreateAttr("from", client.JID()) reply.CreateAttr("to", v.From) reply.CreateAttr("id", v.ID) errorReply := reply.CreateElement(strError) errorReply.CreateAttr("type", "cancel") su := errorReply.CreateElement("service-unavailable") su.CreateAttr("xmlns", nsXMPPStanzas) xmlString, err := root.WriteToString() if err == nil { _, err = client.SendOrg(xmlString) if err != nil { log.Println(err) } } } case "set": root := etree.NewDocument() root.WriteSettings.AttrSingleQuote = true reply := root.CreateElement("iq") reply.CreateAttr("type", strError) reply.CreateAttr("from", client.JID()) reply.CreateAttr("to", v.From) reply.CreateAttr("id", v.ID) errorReply := reply.CreateElement(strError) errorReply.CreateAttr("type", "cancel") su := errorReply.CreateElement("service-unavailable") su.CreateAttr("xmlns", nsXMPPStanzas) xmlString, err := root.WriteToString() if err == nil { _, err = client.SendOrg(xmlString) if err != nil { log.Println(err) } } case "reply", strError: iqc <- v default: iqc <- v } } } }