diff --git a/component/config.go b/component/config.go index e40b43d..6ba0623 100644 --- a/component/config.go +++ b/component/config.go @@ -21,6 +21,13 @@ func (c *Config) Start() (err error) { if err != nil { return } + + router := xmpp.NewRouter() + router.NewRoute().IQNamespaces(xmpp.NSDiscoInfo).HandlerFunc(c.handleDiscoInfo) + router.NewRoute().IQNamespaces(xmpp.NSDiscoItems).HandlerFunc(c.handleDiscoItems) + router.HandleFunc("iq", c.handleIQ) + router.HandleFunc("message", c.handleMessage) + c.xmpp, err = xmpp.NewComponent(xmpp.ComponentOptions{ Domain: c.Host, Secret: c.Secret, @@ -28,18 +35,13 @@ func (c *Config) Start() (err error) { Name: c.Type, Category: "gateway", Type: "service", - }) + }, router) if err != nil { return } cm := xmpp.NewStreamManager(c.xmpp, nil) - err = cm.Start() - if err != nil { - return - } - + go cm.Run() go c.sender(out) - go c.receiver() return nil } diff --git a/component/receiver.go b/component/receiver.go index 0a5c7ff..29023cb 100644 --- a/component/receiver.go +++ b/component/receiver.go @@ -1,106 +1,112 @@ package component import ( + "encoding/xml" + "github.com/bdlm/log" "gosrc.io/xmpp" ) -func (c *Config) receiver() { - for packet := range c.xmpp.Recv() { - p, back := c.receiving(packet) - if p == nil { - continue - } - if back { - c.xmpp.Send(p) - } else { - c.comp.Send(p) +func (c *Config) handleDiscoInfo(s xmpp.Sender, p xmpp.Packet) { + iq, ok := p.(xmpp.IQ) + if !ok || iq.Type != "get" { + return + } + discoInfo, ok := iq.Payload.(*xmpp.DiscoInfo) + if !ok { + return + } + attrs := iq.PacketAttrs + iq = xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en") + + payload := xmpp.DiscoInfo{ + XMLName: xml.Name{ + Space: xmpp.NSDiscoInfo, + Local: "query", + }, + Features: []xmpp.Feature{ + {Var: xmpp.NSDiscoInfo}, + {Var: xmpp.NSDiscoItems}, + {Var: xmpp.NSMsgReceipts}, + {Var: xmpp.NSMsgChatMarkers}, + {Var: xmpp.NSMsgChatStateNotifications}, + }, + } + if discoInfo.Node == "" { + payload.Identity = xmpp.Identity{ + Name: c.Type, + Category: "gateway", + Type: "service", } } + iq.Payload = &payload + log.WithFields(map[string]interface{}{ + "type": c.Type, + "from": s, + "to": attrs.To, + }).Debug("disco info") + s.Send(iq) } -func (c *Config) receiving(packet interface{}) (xmpp.Packet, bool) { - logger := log.WithField("type", c.Type) - - switch p := packet.(type) { - case xmpp.IQ: - attrs := p.PacketAttrs - loggerIQ := logger.WithFields(map[string]interface{}{ - "from": attrs.From, - "to": attrs.To, - }) - - switch inner := p.Payload[0].(type) { - case *xmpp.DiscoInfo: - if p.Type == "get" { - iq := xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en") - var identity xmpp.Identity - if inner.Node == "" { - identity = xmpp.Identity{ - Name: c.Type, - Category: "gateway", - Type: "service", - } - } - - payload := xmpp.DiscoInfo{ - Identity: identity, - Features: []xmpp.Feature{ - {Var: xmpp.NSDiscoInfo}, - {Var: xmpp.NSDiscoItems}, - {Var: xmpp.NSMsgReceipts}, - {Var: xmpp.NSMsgChatMarkers}, - {Var: xmpp.NSMsgChatStateNotifications}, - }, - } - iq.AddPayload(&payload) - loggerIQ.Debug("disco info") - return iq, true - } - - case *xmpp.DiscoItems: - if p.Type == "get" { - iq := xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en") - - var payload xmpp.DiscoItems - if inner.Node == "" { - payload = xmpp.DiscoItems{ - Items: []xmpp.DiscoItem{ - {Name: c.Type, JID: c.Host, Node: "node1"}, - }, - } - } - iq.AddPayload(&payload) - loggerIQ.Debug("disco items") - return iq, true - } - default: - logger.Debug("ignoring iq packet", inner) - xError := xmpp.Err{ - Code: 501, - Reason: "feature-not-implemented", - Type: "cancel", - } - reply := p.MakeError(xError) - - return reply, true - } - - case xmpp.Message: - if c.XMPPDebug { - logger.WithFields(map[string]interface{}{ - "from": p.PacketAttrs.From, - "to": p.PacketAttrs.To, - "id": p.PacketAttrs.Id, - }).Debug(p.XMPPFormat()) - } - return p, false - - case xmpp.Presence: - logger.Debug("received presence:", p.Type) - - default: - logger.Debug("ignoring packet:", packet) +func (c *Config) handleDiscoItems(s xmpp.Sender, p xmpp.Packet) { + iq, ok := p.(xmpp.IQ) + if !ok || iq.Type != "get" { + return } - return nil, false + discoItems, ok := iq.Payload.(*xmpp.DiscoItems) + if !ok { + return + } + attrs := iq.PacketAttrs + iq = xmpp.NewIQ("result", attrs.To, attrs.From, attrs.Id, "en") + + payload := xmpp.DiscoItems{} + if discoItems.Node == "" { + payload.Items = []xmpp.DiscoItem{ + {Name: c.Type, JID: c.Host, Node: "node1"}, + } + } + iq.Payload = &payload + + log.WithFields(map[string]interface{}{ + "type": c.Type, + "from": s, + "to": attrs.To, + }).Debug("disco items") + s.Send(iq) +} +func (c *Config) handleIQ(s xmpp.Sender, p xmpp.Packet) { + iq, ok := p.(xmpp.IQ) + if !ok || iq.Type != "get" { + return + } + xError := xmpp.Err{ + Code: 501, + Reason: "feature-not-implemented", + Type: "cancel", + } + resp := iq.MakeError(xError) + attrs := iq.PacketAttrs + + log.WithFields(map[string]interface{}{ + "type": c.Type, + "from": s, + "to": attrs.To, + }).Debugf("ignore: %s", iq.Payload) + s.Send(resp) +} +func (c *Config) handleMessage(s xmpp.Sender, p xmpp.Packet) { + msg, ok := p.(xmpp.Message) + if !ok { + return + } + if c.XMPPDebug { + log.WithFields(map[string]interface{}{ + "type": c.Type, + "from": s, + "to": msg.PacketAttrs.To, + "id": msg.PacketAttrs.Id, + }).Debug(msg.XMPPFormat()) + } + c.comp.Send(p) } diff --git a/component/receiver_test.go b/component/receiver_test.go index 45b1e64..a59e492 100644 --- a/component/receiver_test.go +++ b/component/receiver_test.go @@ -7,59 +7,102 @@ import ( "gosrc.io/xmpp" ) +type dummyComp struct { + Component + LastPacket xmpp.Packet +} + +func (d *dummyComp) Connect() (chan xmpp.Packet, error) { + return nil, nil +} +func (d *dummyComp) Send(a xmpp.Packet) { + d.LastPacket = a +} + +type dummyXMPP struct { + xmpp.Sender + LastPacket xmpp.Packet +} + +func (d *dummyXMPP) Send(a xmpp.Packet) error { + d.LastPacket = a + return nil +} + func TestReceive(t *testing.T) { assert := assert.New(t) + s := &dummyXMPP{} - c := Config{Host: "example.org", Type: "monkeyservice", XMPPDebug: true} - - // ignoring packet - p, _ := c.receiving(xmpp.Handshake{}) - assert.Nil(p) - - // receive presence - p, _ = c.receiving(xmpp.Presence{}) - assert.Nil(p) + comp := &dummyComp{} + c := Config{ + Host: "example.org", + Type: "monkeyservice", + XMPPDebug: true, + comp: comp, + } // message - p, back := c.receiving(xmpp.Message{}) - assert.False(back) - assert.NotNil(p) + c.handleMessage(s, xmpp.IQ{}) + assert.Nil(comp.LastPacket) + + c.handleMessage(s, xmpp.Message{}) + _, ok := comp.LastPacket.(xmpp.Message) + assert.True(ok) // unsupported iq - p, back = c.receiving(xmpp.IQ{Payload: []xmpp.IQPayload{ - &xmpp.Err{}, - }}) - assert.True(back) - assert.NotNil(p) - iq := p.(xmpp.IQ) + c.handleIQ(s, xmpp.IQ{}) + assert.Nil(s.LastPacket) + + c.handleIQ(s, xmpp.IQ{ + PacketAttrs: xmpp.PacketAttrs{Type: "get"}, + }) + assert.NotNil(s.LastPacket) + iq := s.LastPacket.(xmpp.IQ) assert.Equal("error", iq.Type) assert.Equal("feature-not-implemented", iq.Error.Reason) + s.LastPacket = nil // iq disco info - p, back = c.receiving(xmpp.IQ{ - PacketAttrs: xmpp.PacketAttrs{Type: "get"}, - Payload: []xmpp.IQPayload{ - &xmpp.DiscoInfo{}, - }, + c.handleDiscoInfo(s, xmpp.IQ{ + Payload: &xmpp.DiscoInfo{}, }) - assert.True(back) - assert.NotNil(p) - iq = p.(xmpp.IQ) + assert.Nil(s.LastPacket) + + c.handleDiscoInfo(s, xmpp.IQ{ + PacketAttrs: xmpp.PacketAttrs{Type: "get"}, + }) + assert.Nil(s.LastPacket) + + c.handleDiscoInfo(s, xmpp.IQ{ + PacketAttrs: xmpp.PacketAttrs{Type: "get"}, + Payload: &xmpp.DiscoInfo{}, + }) + assert.NotNil(s.LastPacket) + iq = s.LastPacket.(xmpp.IQ) assert.Equal("result", iq.Type) - dinfo := iq.Payload[0].(*xmpp.DiscoInfo) + dinfo := iq.Payload.(*xmpp.DiscoInfo) assert.Equal("monkeyservice", dinfo.Identity.Name) + s.LastPacket = nil // iq disco items - p, back = c.receiving(xmpp.IQ{ - PacketAttrs: xmpp.PacketAttrs{Type: "get"}, - Payload: []xmpp.IQPayload{ - &xmpp.DiscoItems{}, - }, + c.handleDiscoItems(s, xmpp.IQ{ + Payload: &xmpp.DiscoItems{}, }) - assert.True(back) - assert.NotNil(p) - iq = p.(xmpp.IQ) + assert.Nil(s.LastPacket) + + c.handleDiscoItems(s, xmpp.IQ{ + PacketAttrs: xmpp.PacketAttrs{Type: "get"}, + }) + assert.Nil(s.LastPacket) + + c.handleDiscoItems(s, xmpp.IQ{ + PacketAttrs: xmpp.PacketAttrs{Type: "get"}, + Payload: &xmpp.DiscoItems{}, + }) + assert.NotNil(s.LastPacket) + iq = s.LastPacket.(xmpp.IQ) assert.Equal("result", iq.Type) - ditems := iq.Payload[0].(*xmpp.DiscoItems) + ditems := iq.Payload.(*xmpp.DiscoItems) assert.Equal("monkeyservice", ditems.Items[0].Name) + s.LastPacket = nil }