Repository: FluuxIO/go-xmpp
Pull request #71 (2019-06-24 10:20:56)

muc history of zero stanzes are not supported
if `int` is `0` it will not be decoded - because you set `omitempty`.
Solution is to set use pointer so that `omitempty` would handle the pointer and not the value.
On this value a value like `0` is allowed.

---

if you do not use it like here programmed, please create tests for it.


Patch:
diff --git a/_examples/go.mod b/_examples/go.mod
index 5554f9f..48a6241 100644
--- a/_examples/go.mod
+++ b/_examples/go.mod
@@ -6,5 +6,7 @@ require (
 	github.com/google/go-cmp v0.3.0 // indirect
 	github.com/processone/mpg123 v1.0.0
 	github.com/processone/soundcloud v1.0.0
-	gosrc.io/xmpp v0.1.1-0.20190622092414-e9c704eff56e
+	gosrc.io/xmpp v0.1.1
 )
+
+replace gosrc.io/xmpp => ./../
diff --git a/_examples/go.sum b/_examples/go.sum
index 5c643ff..26a290b 100644
--- a/_examples/go.sum
+++ b/_examples/go.sum
@@ -8,9 +8,3 @@ golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF
 golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gosrc.io/xmpp v0.1.1-0.20190619120342-a6cbc0c08f52 h1:H5BezaFYvDL9r72ng90ICneftomo1iXx6+BxxZ9jBtg=
-gosrc.io/xmpp v0.1.1-0.20190619120342-a6cbc0c08f52/go.mod h1:WvSgrZF7lMvjd1SH8nVGi7ZGr6gNU7oUuBdwpFTs9nY=
-gosrc.io/xmpp v0.1.1-0.20190619153249-b1dde2330764 h1:jlYtpqdRoBC3Gke7MacXsVpSZL0g5nIBG/b9JVxpAVY=
-gosrc.io/xmpp v0.1.1-0.20190619153249-b1dde2330764/go.mod h1:WvSgrZF7lMvjd1SH8nVGi7ZGr6gNU7oUuBdwpFTs9nY=
-gosrc.io/xmpp v0.1.1-0.20190622092414-e9c704eff56e h1:dgWkSZHo0u54X+kFI8CUPiFoeX0rhmuEgykKfyFeHKI=
-gosrc.io/xmpp v0.1.1-0.20190622092414-e9c704eff56e/go.mod h1:WvSgrZF7lMvjd1SH8nVGi7ZGr6gNU7oUuBdwpFTs9nY=
diff --git a/client.go b/client.go
index bb62ce8..1a63041 100644
--- a/client.go
+++ b/client.go
@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"net"
-	"strings"
 	"time"
 )
 
@@ -83,10 +82,10 @@ Setting up the client / Checking the parameters
 // NewClient generates a new XMPP client, based on Config passed as parameters.
 // If host is not specified, the DNS SRV should be used to find the host from the domainpart of the JID.
 // Default the port to 5222.
-// TODO: better config checks
 func NewClient(config Config, r *Router) (c *Client, err error) {
-	// TODO: If option address is nil, use the Jid domain to compose the address
-	if config.Address, err = checkAddress(config.Address); err != nil {
+	// Parse JID
+	if config.parsedJid, err = NewJid(config.Jid); err != nil {
+		err = errors.New("missing jid")
 		return nil, NewConnError(err, true)
 	}
 
@@ -95,11 +94,11 @@ func NewClient(config Config, r *Router) (c *Client, err error) {
 		return nil, NewConnError(err, true)
 	}
 
-	// Parse JID
-	if config.parsedJid, err = NewJid(config.Jid); err != nil {
-		err = errors.New("missing jid")
-		return nil, NewConnError(err, true)
+	// fallback to jid domain
+	if config.Address == "" {
+		config.Address = config.parsedJid.Domain
 	}
+	config.Address = ensurePort(config.Address, 5222)
 
 	c = new(Client)
 	c.config = config
@@ -112,24 +111,6 @@ func NewClient(config Config, r *Router) (c *Client, err error) {
 	return
 }
 
-// TODO Pass JID to be able to add default address based on JID, if addr is empty
-func checkAddress(addr string) (string, error) {
-	var err error
-	hostport := strings.Split(addr, ":")
-	if len(hostport) > 2 {
-		err = errors.New("too many colons in xmpp server address")
-		return addr, err
-	}
-
-	// Address is composed of two parts, we are good
-	if len(hostport) == 2 && hostport[1] != "" {
-		return addr, err
-	}
-
-	// Port was not passed, we append XMPP default port:
-	return strings.Join([]string{hostport[0], "5222"}, ":"), err
-}
-
 // Connect triggers actual TCP connection, based on previously defined parameters.
 func (c *Client) Connect() error {
 	var err error
@@ -173,12 +154,17 @@ func (c *Client) SetHandler(handler EventHandler) {
 
 // Send marshals XMPP stanza and sends it to the server.
 func (c *Client) Send(packet Packet) error {
+	conn := c.conn
+	if conn == nil {
+		return errors.New("client is not connected")
+	}
+
 	data, err := xml.Marshal(packet)
 	if err != nil {
 		return errors.New("cannot marshal packet " + err.Error())
 	}
 
-	if _, err := fmt.Fprintf(c.conn, string(data)); err != nil {
+	if _, err := fmt.Fprintf(conn, string(data)); err != nil {
 		return errors.New("cannot send packet " + err.Error())
 	}
 	return nil
@@ -189,8 +175,13 @@ func (c *Client) Send(packet Packet) error {
 // disconnect the client. It is up to the user of this method to
 // carefully craft the XML content to produce valid XMPP.
 func (c *Client) SendRaw(packet string) error {
+	conn := c.conn
+	if conn == nil {
+		return errors.New("client is not connected")
+	}
+
 	var err error
-	_, err = fmt.Fprintf(c.Session.socketProxy, packet)
+	_, err = fmt.Fprintf(conn, packet)
 	return err
 }
 
@@ -210,13 +201,13 @@ func (c *Client) recv(keepaliveQuit chan<- struct{}) (err error) {
 		// Handle stream errors
 		switch packet := val.(type) {
 		case StreamError:
-			c.router.Route(c, val)
+			c.router.route(c, val)
 			close(keepaliveQuit)
 			c.streamError(packet.Error.Local, packet.Text)
 			return errors.New("stream error: " + packet.Error.Local)
 		}
 
-		c.router.Route(c, val)
+		c.router.route(c, val)
 	}
 }
 
diff --git a/component.go b/component.go
index 5fedc9a..d13822b 100644
--- a/component.go
+++ b/component.go
@@ -128,22 +128,27 @@ func (c *Component) recv() (err error) {
 		// Handle stream errors
 		switch p := val.(type) {
 		case StreamError:
-			c.router.Route(c, val)
+			c.router.route(c, val)
 			c.streamError(p.Error.Local, p.Text)
 			return errors.New("stream error: " + p.Error.Local)
 		}
-		c.router.Route(c, val)
+		c.router.route(c, val)
 	}
 }
 
 // Send marshalls XMPP stanza and sends it to the server.
 func (c *Component) Send(packet Packet) error {
+	conn := c.conn
+	if conn == nil {
+		return errors.New("component is not connected")
+	}
+
 	data, err := xml.Marshal(packet)
 	if err != nil {
 		return errors.New("cannot marshal packet " + err.Error())
 	}
 
-	if _, err := fmt.Fprintf(c.conn, string(data)); err != nil {
+	if _, err := fmt.Fprintf(conn, string(data)); err != nil {
 		return errors.New("cannot send packet " + err.Error())
 	}
 	return nil
@@ -154,6 +159,11 @@ func (c *Component) Send(packet Packet) error {
 // disconnect the component. It is up to the user of this method to
 // carefully craft the XML content to produce valid XMPP.
 func (c *Component) SendRaw(packet string) error {
+	conn := c.conn
+	if conn == nil {
+		return errors.New("component is not connected")
+	}
+
 	var err error
 	_, err = fmt.Fprintf(c.conn, packet)
 	return err
diff --git a/error.go b/error.go
index 2fe542f..a4b4f17 100644
--- a/error.go
+++ b/error.go
@@ -27,7 +27,7 @@ func (x *Err) Namespace() string {
 	return x.XMLName.Space
 }
 
-// UnmarshalXML implements custom parsing for IQs
+// UnmarshalXML implements custom parsing for XMPP errors
 func (x *Err) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
 	x.XMLName = start.Name
 
diff --git a/error_enum.go b/error_enum.go
index b89b925..314df4c 100644
--- a/error_enum.go
+++ b/error_enum.go
@@ -8,6 +8,6 @@ const (
 	ErrorTypeAuth     ErrorType = "auth"
 	ErrorTypeCancel   ErrorType = "cancel"
 	ErrorTypeContinue ErrorType = "continue"
-	ErrorTypeModify   ErrorType = "motify"
+	ErrorTypeModify   ErrorType = "modify"
 	ErrorTypeWait     ErrorType = "wait"
 )
diff --git a/iq.go b/iq.go
index 4597609..d04ab36 100644
--- a/iq.go
+++ b/iq.go
@@ -23,7 +23,8 @@ type IQ struct { // Info/Query
 	//    request."
 	Payload IQPayload `xml:",omitempty"`
 	Error   Err       `xml:"error,omitempty"`
-	RawXML  string    `xml:",innerxml"`
+	// Any is used to decode unknown payload as a generique structure
+	Any *Node `xml:",any"`
 }
 
 type IQPayload interface {
@@ -114,7 +115,12 @@ func (iq *IQ) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
 				continue
 			}
 			// TODO: If unknown decode as generic node
-			return fmt.Errorf("unexpected element in iq: %s %s", tt.Name.Space, tt.Name.Local)
+			node := new(Node)
+			err = d.DecodeElement(node, &tt)
+			if err != nil {
+				return err
+			}
+			iq.Any = node
 		case xml.EndElement:
 			if tt == start.End() {
 				return nil
diff --git a/network.go b/network.go
new file mode 100644
index 0000000..75a0a60
--- /dev/null
+++ b/network.go
@@ -0,0 +1,32 @@
+package xmpp
+
+import (
+	"strconv"
+	"strings"
+)
+
+// ensurePort adds a port to an address if none are provided.
+// It handles both IPV4 and IPV6 addresses.
+func ensurePort(addr string, port int) string {
+	// This is an IPV6 address literal
+	if strings.HasPrefix(addr, "[") {
+		// if address has no port (behind his ipv6 address) - add default port
+		if strings.LastIndex(addr, ":") <= strings.LastIndex(addr, "]") {
+			return addr + ":" + strconv.Itoa(port)
+		}
+		return addr
+	}
+
+	// This is either an IPV6 address without bracket or an IPV4 address
+	switch strings.Count(addr, ":") {
+	case 0:
+		// This is IPV4 without port
+		return addr + ":" + strconv.Itoa(port)
+	case 1:
+		// This is IPV$ with port
+		return addr
+	default:
+		// This is IPV6 without port, as you need to use bracket with port in IPV6
+		return "[" + addr + "]:" + strconv.Itoa(port)
+	}
+}
diff --git a/pres_muc.go b/pres_muc.go
index 10d947e..f7ae599 100644
--- a/pres_muc.go
+++ b/pres_muc.go
@@ -2,6 +2,7 @@ package xmpp
 
 import (
 	"encoding/xml"
+	"strconv"
 	"time"
 )
 
@@ -16,12 +17,130 @@ type MucPresence struct {
 	History  History  `xml:"history,omitempty"`
 }
 
+const timeLayout = "2006-01-02T15:04:05Z"
+
 // History implements XEP-0045: Multi-User Chat - 19.1
 type History struct {
-	MaxChars   int       `xml:"maxchars,attr,omitempty"`
-	MaxStanzas int       `xml:"maxstanzas,attr,omitempty"`
-	Seconds    int       `xml:"seconds,attr,omitempty"`
-	Since      time.Time `xml:"since,attr,omitempty"`
+	XMLName    xml.Name
+	MaxChars   NullableInt `xml:"maxchars,attr,omitempty"`
+	MaxStanzas NullableInt `xml:"maxstanzas,attr,omitempty"`
+	Seconds    NullableInt `xml:"seconds,attr,omitempty"`
+	Since      time.Time   `xml:"since,attr,omitempty"`
+}
+
+type NullableInt struct {
+	Value int
+	isSet bool
+}
+
+func NewNullableInt(val int) NullableInt {
+	return NullableInt{val, true}
+}
+
+func (n NullableInt) Get() (v int, ok bool) {
+	return n.Value, n.isSet
+}
+
+// UnmarshalXML implements custom parsing for history element
+func (h *History) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	h.XMLName = start.Name
+
+	// Extract attributes
+	for _, attr := range start.Attr {
+		switch attr.Name.Local {
+		case "maxchars":
+			v, err := strconv.Atoi(attr.Value)
+			if err != nil {
+				return err
+			}
+			h.MaxChars = NewNullableInt(v)
+		case "maxstanzas":
+			v, err := strconv.Atoi(attr.Value)
+			if err != nil {
+				return err
+			}
+			h.MaxStanzas = NewNullableInt(v)
+		case "seconds":
+			v, err := strconv.Atoi(attr.Value)
+			if err != nil {
+				return err
+			}
+			h.Seconds = NewNullableInt(v)
+		case "since":
+			t, err := time.Parse(timeLayout, attr.Value)
+			if err != nil {
+				return err
+			}
+			h.Since = t
+		}
+	}
+
+	// Consume remaining data until element end
+	for {
+		t, err := d.Token()
+		if err != nil {
+			return err
+		}
+
+		switch tt := t.(type) {
+		case xml.EndElement:
+			if tt == start.End() {
+				return nil
+			}
+		}
+	}
+}
+
+func (h History) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
+	mc, isMcSet := h.MaxChars.Get()
+	ms, isMsSet := h.MaxStanzas.Get()
+	s, isSSet := h.Seconds.Get()
+
+	// We do not have any value, ignore history element
+	if h.Since.IsZero() && !isMcSet && !isMsSet && !isSSet {
+		return nil
+	}
+
+	// Encode start element and attributes
+	start.Name = xml.Name{Local: "history"}
+
+	if isMcSet {
+		attr := xml.Attr{
+			Name:  xml.Name{Local: "maxchars"},
+			Value: strconv.Itoa(mc),
+		}
+		start.Attr = append(start.Attr, attr)
+	}
+
+	if isMsSet {
+		attr := xml.Attr{
+			Name:  xml.Name{Local: "maxstanzas"},
+			Value: strconv.Itoa(ms),
+		}
+		start.Attr = append(start.Attr, attr)
+	}
+
+	if isSSet {
+		attr := xml.Attr{
+			Name:  xml.Name{Local: "seconds"},
+			Value: strconv.Itoa(s),
+		}
+		start.Attr = append(start.Attr, attr)
+	}
+
+	if !h.Since.IsZero() {
+		attr := xml.Attr{
+			Name:  xml.Name{Local: "since"},
+			Value: h.Since.Format(timeLayout),
+		}
+		start.Attr = append(start.Attr, attr)
+	}
+	if err := e.EncodeToken(start); err != nil {
+		return err
+	}
+
+	return e.EncodeToken(xml.EndElement{Name: start.Name})
+
 }
 
 func init() {
diff --git a/router.go b/router.go
index 79837a6..b252db6 100644
--- a/router.go
+++ b/router.go
@@ -1,6 +1,7 @@
 package xmpp
 
 import (
+	"encoding/xml"
 	"strings"
 )
 
@@ -29,11 +30,33 @@ func NewRouter() *Router {
 	return &Router{}
 }
 
-func (r *Router) Route(s Sender, p Packet) {
+// route is called by the XMPP client to dispatch stanza received using the set up routes.
+// It is also used by test, but is not supposed to be used directly by users of the library.
+func (r *Router) route(s Sender, p Packet) {
+
 	var match RouteMatch
 	if r.Match(p, &match) {
+		// If we match, route the packet
 		match.Handler.HandlePacket(s, p)
+		return
+	}
+	// If there is no match and we receive an iq set or get, we need to send a reply
+	if iq, ok := p.(IQ); ok {
+		if iq.Type == IQTypeGet || iq.Type == IQTypeSet {
+			iqNotImplemented(s, iq)
+		}
+	}
+}
+
+func iqNotImplemented(s Sender, iq IQ) {
+	err := Err{
+		XMLName: xml.Name{Local: "error"},
+		Code:    501,
+		Type:    "cancel",
+		Reason:  "feature-not-implemented",
 	}
+	reply := iq.MakeError(err)
+	_ = s.Send(reply)
 }
 
 // NewRoute registers an empty routes


Test patch:
diff --git a/iq_test.go b/iq_test.go
index 0a8aae6..df9b360 100644
--- a/iq_test.go
+++ b/iq_test.go
@@ -153,3 +153,19 @@ func TestPayloadWithError(t *testing.T) {
 		t.Errorf("incorrect error value: '%s'", parsedIQ.Error.Reason)
 	}
 }
+
+func TestUnknownPayload(t *testing.T) {
+	iq := `<iq type="get" to="service.localhost" id="1" >
+ <query xmlns="unknown:ns"/>
+</iq>`
+	parsedIQ := xmpp.IQ{}
+	err := xml.Unmarshal([]byte(iq), &parsedIQ)
+	if err != nil {
+		t.Errorf("Unmarshal error: %#v (%s)", err, iq)
+		return
+	}
+
+	if parsedIQ.Any.XMLName.Space != "unknown:ns" {
+		t.Errorf("could not extract namespace: '%s'", parsedIQ.Any.XMLName.Space)
+	}
+}
diff --git a/network_test.go b/network_test.go
new file mode 100644
index 0000000..116ecef
--- /dev/null
+++ b/network_test.go
@@ -0,0 +1,35 @@
+package xmpp
+
+import (
+	"testing"
+)
+
+type params struct {
+}
+
+func TestParseAddr(t *testing.T) {
+	tests := []struct {
+		name  string
+		input string
+		want  string
+	}{
+		{name: "ipv4-no-port-1", input: "localhost", want: "localhost:5222"},
+		{name: "ipv4-with-port-1", input: "localhost:5555", want: "localhost:5555"},
+		{name: "ipv4-no-port-2", input: "127.0.0.1", want: "127.0.0.1:5222"},
+		{name: "ipv4-with-port-2", input: "127.0.0.1:5555", want: "127.0.0.1:5555"},
+		{name: "ipv6-no-port-1", input: "::1", want: "[::1]:5222"},
+		{name: "ipv6-no-port-2", input: "[::1]", want: "[::1]:5222"},
+		{name: "ipv6-no-port-3", input: "2001::7334", want: "[2001::7334]:5222"},
+		{name: "ipv6-no-port-4", input: "2001:db8:85a3:0:0:8a2e:370:7334", want: "[2001:db8:85a3:0:0:8a2e:370:7334]:5222"},
+		{name: "ipv6-with-port-1", input: "[::1]:5555", want: "[::1]:5555"},
+	}
+	for _, tc := range tests {
+		t.Run(tc.name, func(st *testing.T) {
+			addr := ensurePort(tc.input, 5222)
+
+			if addr != tc.want {
+				st.Errorf("incorrect Result: %v (!= %v)", addr, tc.want)
+			}
+		})
+	}
+}
diff --git a/pres_muc_test.go b/pres_muc_test.go
index b2120e3..5b8db83 100644
--- a/pres_muc_test.go
+++ b/pres_muc_test.go
@@ -46,15 +46,52 @@ func TestMucHistory(t *testing.T) {
 
 	var parsedPresence xmpp.Presence
 	if err := xml.Unmarshal([]byte(str), &parsedPresence); err != nil {
-		t.Errorf("Unmarshal(%s) returned error", str)
+		t.Errorf("Unmarshal(%s) returned error: %s", str, err)
+		return
 	}
 
 	var muc xmpp.MucPresence
 	if ok := parsedPresence.Get(&muc); !ok {
 		t.Error("muc presence extension was not found")
+		return
+	}
+
+	if v, ok := muc.History.MaxStanzas.Get(); !ok || v != 20 {
+		t.Errorf("incorrect MaxStanzas: '%#v'", muc.History.MaxStanzas)
+	}
+}
+
+// https://xmpp.org/extensions/xep-0045.html#example-37
+func TestMucNoHistory(t *testing.T) {
+	str := "<presence" +
+		" id=\"n13mt3l\"" +
+		" from=\"hag66@shakespeare.lit/pda\"" +
+		" to=\"coven@chat.shakespeare.lit/thirdwitch\">" +
+		"<x xmlns=\"http://jabber.org/protocol/muc\">" +
+		"<history maxstanzas=\"0\"></history>" +
+		"</x>" +
+		"</presence>"
+
+	maxstanzas := 0
+
+	pres := xmpp.Presence{Attrs: xmpp.Attrs{
+		From: "hag66@shakespeare.lit/pda",
+		Id:   "n13mt3l",
+		To:   "coven@chat.shakespeare.lit/thirdwitch",
+	},
+		Extensions: []xmpp.PresExtension{
+			xmpp.MucPresence{
+				History: xmpp.History{MaxStanzas: xmpp.NewNullableInt(maxstanzas)},
+			},
+		},
+	}
+	data, err := xml.Marshal(&pres)
+	if err != nil {
+		t.Error("error on encode:", err)
+		return
 	}
 
-	if muc.History.MaxStanzas != 20 {
-		t.Errorf("incorrect max stanza: '%d'", muc.History.MaxStanzas)
+	if string(data) != str {
+		t.Errorf("incorrect stanza: \n%s\n%s", str, data)
 	}
 }
diff --git a/router_test.go b/router_test.go
index 7da1218..c1049bf 100644
--- a/router_test.go
+++ b/router_test.go
@@ -1,91 +1,89 @@
-package xmpp_test
+package xmpp
 
 import (
 	"bytes"
 	"encoding/xml"
 	"testing"
-
-	"gosrc.io/xmpp"
 )
 
 // ============================================================================
 // Test route & matchers
 
 func TestNameMatcher(t *testing.T) {
-	router := xmpp.NewRouter()
-	router.HandleFunc("message", func(s xmpp.Sender, p xmpp.Packet) {
+	router := NewRouter()
+	router.HandleFunc("message", func(s Sender, p Packet) {
 		_ = s.SendRaw(successFlag)
 	})
 
 	// Check that a message packet is properly matched
 	conn := NewSenderMock()
-	msg := xmpp.NewMessage(xmpp.Attrs{Type: xmpp.MessageTypeChat, To: "test@localhost", Id: "1"})
+	msg := NewMessage(Attrs{Type: MessageTypeChat, To: "test@localhost", Id: "1"})
 	msg.Body = "Hello"
-	router.Route(conn, msg)
+	router.route(conn, msg)
 	if conn.String() != successFlag {
 		t.Error("Message was not matched and routed properly")
 	}
 
 	// Check that an IQ packet is not matched
 	conn = NewSenderMock()
-	iq := xmpp.NewIQ(xmpp.Attrs{Type: xmpp.IQTypeGet, To: "localhost", Id: "1"})
-	iq.Payload = &xmpp.DiscoInfo{}
-	router.Route(conn, iq)
+	iq := NewIQ(Attrs{Type: IQTypeGet, To: "localhost", Id: "1"})
+	iq.Payload = &DiscoInfo{}
+	router.route(conn, iq)
 	if conn.String() == successFlag {
 		t.Error("IQ should not have been matched and routed")
 	}
 }
 
 func TestIQNSMatcher(t *testing.T) {
-	router := xmpp.NewRouter()
+	router := NewRouter()
 	router.NewRoute().
-		IQNamespaces(xmpp.NSDiscoInfo, xmpp.NSDiscoItems).
-		HandlerFunc(func(s xmpp.Sender, p xmpp.Packet) {
+		IQNamespaces(NSDiscoInfo, NSDiscoItems).
+		HandlerFunc(func(s Sender, p Packet) {
 			_ = s.SendRaw(successFlag)
 		})
 
 	// Check that an IQ with proper namespace does match
 	conn := NewSenderMock()
-	iqDisco := xmpp.NewIQ(xmpp.Attrs{Type: xmpp.IQTypeGet, To: "localhost", Id: "1"})
+	iqDisco := NewIQ(Attrs{Type: IQTypeGet, To: "localhost", Id: "1"})
 	// TODO: Add a function to generate payload with proper namespace initialisation
-	iqDisco.Payload = &xmpp.DiscoInfo{
+	iqDisco.Payload = &DiscoInfo{
 		XMLName: xml.Name{
-			Space: xmpp.NSDiscoInfo,
+			Space: NSDiscoInfo,
 			Local: "query",
 		}}
-	router.Route(conn, iqDisco)
+	router.route(conn, iqDisco)
 	if conn.String() != successFlag {
 		t.Errorf("IQ should have been matched and routed: %v", iqDisco)
 	}
 
 	// Check that another namespace is not matched
 	conn = NewSenderMock()
-	iqVersion := xmpp.NewIQ(xmpp.Attrs{Type: xmpp.IQTypeGet, To: "localhost", Id: "1"})
+	iqVersion := NewIQ(Attrs{Type: IQTypeGet, To: "localhost", Id: "1"})
 	// TODO: Add a function to generate payload with proper namespace initialisation
-	iqVersion.Payload = &xmpp.DiscoInfo{
+	iqVersion.Payload = &DiscoInfo{
 		XMLName: xml.Name{
 			Space: "jabber:iq:version",
 			Local: "query",
 		}}
-	router.Route(conn, iqVersion)
+	router.route(conn, iqVersion)
 	if conn.String() == successFlag {
 		t.Errorf("IQ should not have been matched and routed: %v", iqVersion)
 	}
 }
 
 func TestTypeMatcher(t *testing.T) {
-	router := xmpp.NewRouter()
+	router := NewRouter()
 	router.NewRoute().
 		StanzaType("normal").
-		HandlerFunc(func(s xmpp.Sender, p xmpp.Packet) {
+		HandlerFunc(func(s Sender, p Packet) {
 			_ = s.SendRaw(successFlag)
 		})
 
 	// Check that a packet with the proper type matches
 	conn := NewSenderMock()
-	message := xmpp.NewMessage(xmpp.Attrs{Type: "normal", To: "test@localhost", Id: "1"})
+	message := NewMessage(Attrs{Type: "normal", To: "test@localhost", Id: "1"})
 	message.Body = "hello"
-	router.Route(conn, message)
+	router.route(conn, message)
 
 	if conn.String() != successFlag {
 		t.Errorf("'normal' message should have been matched and routed: %v", message)
@@ -93,9 +91,9 @@ func TestTypeMatcher(t *testing.T) {
 
 	// We should match on default type 'normal' for message without a type
 	conn = NewSenderMock()
-	message = xmpp.NewMessage(xmpp.Attrs{To: "test@localhost", Id: "1"})
+	message = NewMessage(Attrs{To: "test@localhost", Id: "1"})
 	message.Body = "hello"
-	router.Route(conn, message)
+	router.route(conn, message)
 
 	if conn.String() != successFlag {
 		t.Errorf("message should have been matched and routed: %v", message)
@@ -103,13 +101,13 @@ func TestTypeMatcher(t *testing.T) {
 
 	// We do not match on other types
 	conn = NewSenderMock()
-	iqVersion := xmpp.NewIQ(xmpp.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
-	iqVersion.Payload = &xmpp.DiscoInfo{
+	iqVersion := NewIQ(Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
+	iqVersion.Payload = &DiscoInfo{
 		XMLName: xml.Name{
 			Space: "jabber:iq:version",
 			Local: "query",
 		}}
-	router.Route(conn, iqVersion)
+	router.route(conn, iqVersion)
 
 	if conn.String() == successFlag {
 		t.Errorf("iq get should not have been matched and routed: %v", iqVersion)
@@ -117,42 +115,42 @@ func TestTypeMatcher(t *testing.T) {
 }
 
 func TestCompositeMatcher(t *testing.T) {
-	router := xmpp.NewRouter()
+	router := NewRouter()
 	router.NewRoute().
 		IQNamespaces("jabber:iq:version").
 		StanzaType("get").
-		HandlerFunc(func(s xmpp.Sender, p xmpp.Packet) {
+		HandlerFunc(func(s Sender, p Packet) {
 			_ = s.SendRaw(successFlag)
 		})
 
 	// Data set
-	getVersionIq := xmpp.NewIQ(xmpp.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
-	getVersionIq.Payload = &xmpp.Version{
+	getVersionIq := NewIQ(Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
+	getVersionIq.Payload = &Version{
 		XMLName: xml.Name{
 			Space: "jabber:iq:version",
 			Local: "query",
 		}}
 
-	setVersionIq := xmpp.NewIQ(xmpp.Attrs{Type: "set", From: "service.localhost", To: "test@localhost", Id: "1"})
-	setVersionIq.Payload = &xmpp.Version{
+	setVersionIq := NewIQ(Attrs{Type: "set", From: "service.localhost", To: "test@localhost", Id: "1"})
+	setVersionIq.Payload = &Version{
 		XMLName: xml.Name{
 			Space: "jabber:iq:version",
 			Local: "query",
 		}}
 
-	GetDiscoIq := xmpp.NewIQ(xmpp.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
-	GetDiscoIq.Payload = &xmpp.DiscoInfo{
+	GetDiscoIq := NewIQ(Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
+	GetDiscoIq.Payload = &DiscoInfo{
 		XMLName: xml.Name{
 			Space: "http://jabber.org/protocol/disco#info",
 			Local: "query",
 		}}
 
-	message := xmpp.NewMessage(xmpp.Attrs{Type: "normal", To: "test@localhost", Id: "1"})
+	message := NewMessage(Attrs{Type: "normal", To: "test@localhost", Id: "1"})
 	message.Body = "hello"
 
 	tests := []struct {
 		name  string
-		input xmpp.Packet
+		input Packet
 		want  bool
 	}{
 		{name: "match get version iq", input: getVersionIq, want: true},
@@ -165,7 +163,7 @@ func TestCompositeMatcher(t *testing.T) {
 	for _, tc := range tests {
 		t.Run(tc.name, func(st *testing.T) {
 			conn := NewSenderMock()
-			router.Route(conn, tc.input)
+			router.route(conn, tc.input)
 
 			res := conn.String() == successFlag
 			if tc.want != res {
@@ -178,30 +176,30 @@ func TestCompositeMatcher(t *testing.T) {
 // A blank route with empty matcher will always match
 // It can be use to receive all packets that do not match any of the previous route.
 func TestCatchallMatcher(t *testing.T) {
-	router := xmpp.NewRouter()
+	router := NewRouter()
 	router.NewRoute().
-		HandlerFunc(func(s xmpp.Sender, p xmpp.Packet) {
+		HandlerFunc(func(s Sender, p Packet) {
 			_ = s.SendRaw(successFlag)
 		})
 
 	// Check that we match on several packets
 	conn := NewSenderMock()
-	message := xmpp.NewMessage(xmpp.Attrs{Type: "chat", To: "test@localhost", Id: "1"})
+	message := NewMessage(Attrs{Type: "chat", To: "test@localhost", Id: "1"})
 	message.Body = "hello"
-	router.Route(conn, message)
+	router.route(conn, message)
 
 	if conn.String() != successFlag {
 		t.Errorf("chat message should have been matched and routed: %v", message)
 	}
 
 	conn = NewSenderMock()
-	iqVersion := xmpp.NewIQ(xmpp.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
-	iqVersion.Payload = &xmpp.DiscoInfo{
+	iqVersion := NewIQ(Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
+	iqVersion.Payload = &DiscoInfo{
 		XMLName: xml.Name{
 			Space: "jabber:iq:version",
 			Local: "query",
 		}}
-	router.Route(conn, iqVersion)
+	router.route(conn, iqVersion)
 
 	if conn.String() != successFlag {
 		t.Errorf("iq get should have been matched and routed: %v", iqVersion)
@@ -221,7 +219,7 @@ func NewSenderMock() SenderMock {
 	return SenderMock{buffer: new(bytes.Buffer)}
 }
 
-func (s SenderMock) Send(packet xmpp.Packet) error {
+func (s SenderMock) Send(packet Packet) error {
 	out, err := xml.Marshal(packet)
 	if err != nil {
 		return err
@@ -241,7 +239,7 @@ func (s SenderMock) String() string {
 
 func TestSenderMock(t *testing.T) {
 	conn := NewSenderMock()
-	msg := xmpp.NewMessage(xmpp.Attrs{To: "test@localhost", Id: "1"})
+	msg := NewMessage(Attrs{To: "test@localhost", Id: "1"})
 	msg.Body = "Hello"
 	if err := conn.Send(msg); err != nil {
 		t.Error("Could not send message")