3 Commits

Author SHA1 Message Date
Mickael Remond
1c4dd6c967 Remove debug print-out 2019-07-31 18:54:49 +02:00
Mickael Remond
1b3dec3902 Clean-up: remove test/debug code 2019-07-31 18:51:16 +02:00
Mickael Remond
3f48672946 Add initial support for stream management
For now it support enabling SM, replying to ack requests from server,
and trying resuming the session with existing Stream Management state.
2019-07-31 18:47:30 +02:00
21 changed files with 287 additions and 297 deletions

View File

@@ -189,10 +189,7 @@ func (c *Client) Resume(state SMState) error {
func (c *Client) Disconnect() { func (c *Client) Disconnect() {
_ = c.SendRaw("</stream:stream>") _ = c.SendRaw("</stream:stream>")
// TODO: Add a way to wait for stream close acknowledgement from the server for clean disconnect // TODO: Add a way to wait for stream close acknowledgement from the server for clean disconnect
conn := c.conn _ = c.conn.Close()
if conn != nil {
_ = conn.Close()
}
} }
func (c *Client) SetHandler(handler EventHandler) { func (c *Client) SetHandler(handler EventHandler) {

View File

@@ -1,198 +0,0 @@
# fluuxmpp
fluuxIO's XMPP command-line tool
## Installation
To install `fluuxmpp` in your Go path:
```
$ go get -u gosrc.io/xmpp/cmd/fluuxmpp
```
## Usage
```
$ fluuxmpp --help
fluuxIO's xmpp comandline tool
Usage:
fluuxmpp [command]
Available Commands:
check is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires
help Help about any command
send is a command-line tool to send to send XMPP messages to users
Flags:
-h, --help help for fluuxmpp
Use "fluuxmpp [command] --help" for more information about a command.
```
### check tls
```
$ fluuxmpp check --help
is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires
Usage:
fluuxmpp check <host[:port]> [flags]
Examples:
fluuxmpp check chat.sum7.eu:5222 --domain meckerspace.de
Flags:
-d, --domain string domain if host handle multiple domains
-h, --help help for check
```
### sending messages
```
$ fluuxmpp send --help
is a command-line tool to send to send XMPP messages to users
Usage:
fluuxmpp send <recipient,> [message] [flags]
Examples:
fluuxmpp send to@chat.sum7.eu "Hello World!"
Flags:
--addr string host[:port]
--config string config file (default is ~/.config/fluuxmpp.yml)
-h, --help help for send
--jid string using jid (required)
-m, --muc recipient is a muc (join it before sending messages)
--password string using password for your jid (required)
```
## Examples
### check tls
If you server is on standard port and XMPP domains matches the hostname you can simply use:
```
$ fluuxmpp check chat.sum7.eu
info All checks passed
⇢ address="chat.sum7.eu" domain=""
⇢ main.go:43 main.runCheck
⇢ 2019-07-16T22:01:39.765+02:00
```
You can also pass the port and the XMPP domain if different from the server hostname:
```
$ fluuxmpp check chat.sum7.eu:5222 --domain meckerspace.de
info All checks passed
⇢ address="chat.sum7.eu:5222" domain="meckerspace.de"
⇢ main.go:43 main.runCheck
⇢ 2019-07-16T22:01:33.270+02:00
```
Error code will be non-zero in case of error. You can thus use it directly with your usual
monitoring scripts.
### sending messages
Message from arguments:
```bash
$ fluuxmpp send to@example.org "Hello World!"
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:42:43.310+02:00
info send message
muc=false text="Hello World!" to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:42:43.310+02:00
```
Message from STDIN:
```bash
$ journalctl -f | fluuxmpp send to@example.org -
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:40:03.177+02:00
info send message
muc=false text="-- Logs begin at Mon 2019-07-08 22:16:54 CEST. --" to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:40:03.178+02:00
info send message
muc=false text="Jul 17 23:36:46 RECHNERNAME systemd[755]: Started Fetch mails." to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:40:03.178+02:00
^C
```
Multiple recipients:
```bash
$ fluuxmpp send to1@example.org,to2@example.org "Multiple recipient"
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:47:57.650+02:00
info send message
muc=false text="Multiple recipient" to="to1@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:47:57.651+02:00
info send message
muc=false text="Multiple recipient" to="to2@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:47:57.652+02:00
```
Send to MUC:
```bash
journalctl -f | fluuxmpp send testit@conference.chat.sum7.eu - --muc
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:52:56.269+02:00
info send message
muc=true text="-- Logs begin at Mon 2019-07-08 22:16:54 CEST. --" to="testit@conference.chat.sum7.eu"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:52:56.270+02:00
info send message
muc=true text="Jul 17 23:48:58 RECHNERNAME systemd[755]: mail.service: Succeeded." to="testit@conference.chat.sum7.eu"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:52:56.277+02:00
^C
```
## Authentification
### Configuration file
In `/etc/`, `~/.config` and `.` (here).
You could create the file name `fluuxmpp` with you favorite file extension (e.g. `toml`, `yml`).
e.g. ~/.config/fluuxmpp.toml
```toml
jid = "bot@example.org"
password = "secret"
addr = "example.com:5222"
```
### Environment variables
```bash
export FLUXXMPP_JID='bot@example.org';
export FLUXXMPP_PASSWORD='secret';
export FLUXXMPP_ADDR='example.com:5222';
fluuxmpp send to@example.org "Hello Welt";
```
### Parameters
Warning: This should not be used for production systems, as all users on the system
can read the running processes, and their parameters (and thus the password).
```bash
fluuxmpp send to@example.org "Hello World!" --jid bot@example.org --password secret --addr example.com:5222;
```

View File

@@ -1,5 +0,0 @@
/*
fluuxmpp: fluuxIO's xmpp comandline tool
*/
package main

View File

@@ -1,19 +0,0 @@
package main
import (
"github.com/bdlm/log"
"github.com/spf13/cobra"
)
// cmdRoot represents the base command when called without any subcommands
var cmdRoot = &cobra.Command{
Use: "fluuxmpp",
Short: "fluuxIO's xmpp comandline tool",
}
func main() {
log.AddHook(&hook{})
if err := cmdRoot.Execute(); err != nil {
log.Fatal(err)
}
}

131
cmd/sendxmpp/README.md Normal file
View File

@@ -0,0 +1,131 @@
# sendXMPP
sendxmpp is a tool to send messages from command-line.
## Installation
To install `sendxmpp` in your Go path:
```
$ go get -u gosrc.io/xmpp/cmd/sendxmpp
```
## Usage
```
$ sendxmpp --help
Usage:
sendxmpp <recipient,> [message] [flags]
Examples:
sendxmpp to@chat.sum7.eu "Hello World!"
Flags:
--addr string host[:port]
--config string config file (default is ~/.config/fluxxmpp.yml)
-h, --help help for sendxmpp
--jid string using jid (required)
-m, --muc recipient is a muc (join it before sending messages)
--password string using password for your jid (required)
```
## Examples
Message from arguments:
```bash
$ sendxmpp to@example.org "Hello World!"
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:42:43.310+02:00
info send message
muc=false text="Hello World!" to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:42:43.310+02:00
```
Message from STDIN:
```bash
$ journalctl -f | sendxmpp to@example.org -
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:40:03.177+02:00
info send message
muc=false text="-- Logs begin at Mon 2019-07-08 22:16:54 CEST. --" to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:40:03.178+02:00
info send message
muc=false text="Jul 17 23:36:46 RECHNERNAME systemd[755]: Started Fetch mails." to="to@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:40:03.178+02:00
^C
```
Multiple recipients:
```bash
$ sendxmpp to1@example.org,to2@example.org "Multiple recipient"
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:47:57.650+02:00
info send message
muc=false text="Multiple recipient" to="to1@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:47:57.651+02:00
info send message
muc=false text="Multiple recipient" to="to2@example.org"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:47:57.652+02:00
```
Send to MUC:
```bash
journalctl -f | sendxmpp testit@conference.chat.sum7.eu - --muc
info client connected
⇢ cmd.go:56 main.glob..func1.1
⇢ 2019-07-17T23:52:56.269+02:00
info send message
muc=true text="-- Logs begin at Mon 2019-07-08 22:16:54 CEST. --" to="testit@conference.chat.sum7.eu"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:52:56.270+02:00
info send message
muc=true text="Jul 17 23:48:58 RECHNERNAME systemd[755]: mail.service: Succeeded." to="testit@conference.chat.sum7.eu"
⇢ send.go:31 main.send
⇢ 2019-07-17T23:52:56.277+02:00
^C
```
### Authentification
#### Configuration file
In `/etc/`, `~/.config` and `.` (here).
You could create the file name `fluxxmpp` with you favorite file extenion (e.g. `toml`, `yml`).
e.g. ~/.config/fluxxmpp.toml
```toml
jid = "bot@example.org"
password = "secret"
addr = "example.com:5222"
```
#### Environment variables
```bash
export FLUXXMPP_JID='bot@example.org';
export FLUXXMPP_PASSWORD='secret';
export FLUXXMPP_ADDR='example.com:5222';
sendxmpp to@example.org "Hello Welt";
```
#### Parameters
Warning: This should not be used for production systems, as all users on the system
can read the running processes, and their parameters (and thus the password).
```bash
sendxmpp to@example.org "Hello World!" --jid bot@example.org --password secret --addr example.com:5222;
```

View File

@@ -1,18 +1,11 @@
# TODO # TODO
## check ## Issues
### Features
- Use a config file to define the checks to perform as client on an XMPP server.
## send
### Issues
- Remove global variable (like mucToleave) - Remove global variable (like mucToleave)
- Does not report error when trying to connect to a non open port (for example localhost with no server running). - Does not report error when trying to connect to a non open port (for example localhost with no server running).
### Features ## Features
- configuration - configuration
- allow unencrypted - allow unencrypted

View File

@@ -18,10 +18,9 @@ var configFile = ""
// FIXME: Remove global variables // FIXME: Remove global variables
var isMUCRecipient = false var isMUCRecipient = false
var cmdSend = &cobra.Command{ var cmd = &cobra.Command{
Use: "send <recipient,> [message]", Use: "sendxmpp <recipient,> [message]",
Short: "is a command-line tool to send to send XMPP messages to users", Example: `sendxmpp to@chat.sum7.eu "Hello World!"`,
Example: `fluuxmpp send to@chat.sum7.eu "Hello World!"`,
Args: cobra.ExactArgs(2), Args: cobra.ExactArgs(2),
Run: sendxmpp, Run: sendxmpp,
} }
@@ -96,30 +95,28 @@ func sendxmpp(cmd *cobra.Command, args []string) {
} }
func init() { func init() {
cmdRoot.AddCommand(cmdSend) cobra.OnInitialize(initConfig)
cmd.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is ~/.config/fluxxmpp.yml)")
cobra.OnInitialize(initConfigFile) cmd.Flags().StringP("jid", "", "", "using jid (required)")
cmdSend.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is ~/.config/fluuxmpp.yml)") viper.BindPFlag("jid", cmd.Flags().Lookup("jid"))
cmdSend.Flags().StringP("jid", "", "", "using jid (required)") cmd.Flags().StringP("password", "", "", "using password for your jid (required)")
viper.BindPFlag("jid", cmdSend.Flags().Lookup("jid")) viper.BindPFlag("password", cmd.Flags().Lookup("password"))
cmdSend.Flags().StringP("password", "", "", "using password for your jid (required)") cmd.Flags().StringP("addr", "", "", "host[:port]")
viper.BindPFlag("password", cmdSend.Flags().Lookup("password")) viper.BindPFlag("addr", cmd.Flags().Lookup("addr"))
cmdSend.Flags().StringP("addr", "", "", "host[:port]") cmd.Flags().BoolVarP(&isMUCRecipient, "muc", "m", false, "recipient is a muc (join it before sending messages)")
viper.BindPFlag("addr", cmdSend.Flags().Lookup("addr"))
cmdSend.Flags().BoolVarP(&isMUCRecipient, "muc", "m", false, "recipient is a muc (join it before sending messages)")
} }
// initConfig reads in config file and ENV variables if set. // initConfig reads in config file and ENV variables if set.
func initConfigFile() { func initConfig() {
if configFile != "" { if configFile != "" {
viper.SetConfigFile(configFile) viper.SetConfigFile(configFile)
} }
viper.SetConfigName("fluuxmpp") viper.SetConfigName("fluxxmpp")
viper.AddConfigPath("/etc/") viper.AddConfigPath("/etc/")
viper.AddConfigPath("$HOME/.config") viper.AddConfigPath("$HOME/.config")
viper.AddConfigPath(".") viper.AddConfigPath(".")

6
cmd/sendxmpp/doc.go Normal file
View File

@@ -0,0 +1,6 @@
/*
sendxmpp is a command-line tool to send to send XMPP messages to users
*/
package main

12
cmd/sendxmpp/main.go Normal file
View File

@@ -0,0 +1,12 @@
package main
import (
"github.com/bdlm/log"
)
func main() {
log.AddHook(&hook{})
if err := cmd.Execute(); err != nil {
log.Fatal(err)
}
}

49
cmd/xmpp-check/README.md Normal file
View File

@@ -0,0 +1,49 @@
# XMPP Check
XMPP check is a tool to check TLS certificate on a remote server.
## Installation
To install `xmpp-check` in your Go path:
```
$ go get -u gosrc.io/xmpp/cmd/xmpp-check
```
## Usage
```
$ xmpp-check --help
Usage:
xmpp-check <host[:port]> [flags]
Examples:
xmpp-check chat.sum7.eu:5222 --domain meckerspace.de
Flags:
-d, --domain string domain if host handle multiple domains
-h, --help help for xmpp-check
```
If you server is on standard port and XMPP domains matches the hostname you can simply use:
```
$ xmpp-check chat.sum7.eu
info All checks passed
⇢ address="chat.sum7.eu" domain=""
⇢ main.go:43 main.runCheck
⇢ 2019-07-16T22:01:39.765+02:00
```
You can also pass the port and the XMPP domain if different from the server hostname:
```
$ xmpp-check chat.sum7.eu:5222 --domain meckerspace.de
info All checks passed
⇢ address="chat.sum7.eu:5222" domain="meckerspace.de"
⇢ main.go:43 main.runCheck
⇢ 2019-07-16T22:01:33.270+02:00
```
Error code will be non-zero in case of error. You can thus use it directly with your usual
monitoring scripts.

3
cmd/xmpp-check/TODO.md Normal file
View File

@@ -0,0 +1,3 @@
# TODO
- Use a config file to define the checks to perform as client on an XMPP server.

6
cmd/xmpp-check/doc.go Normal file
View File

@@ -0,0 +1,6 @@
/*
xmpp-check is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires.
*/
package main

34
cmd/xmpp-check/log.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
"os"
"github.com/bdlm/log"
stdLogger "github.com/bdlm/std/logger"
)
type hook struct{}
func (h *hook) Fire(entry *log.Entry) error {
switch entry.Level {
case log.PanicLevel:
entry.Logger.Out = os.Stderr
case log.FatalLevel:
entry.Logger.Out = os.Stderr
case log.ErrorLevel:
entry.Logger.Out = os.Stderr
case log.WarnLevel:
entry.Logger.Out = os.Stdout
case log.InfoLevel:
entry.Logger.Out = os.Stdout
case log.DebugLevel:
entry.Logger.Out = os.Stdout
default:
}
return nil
}
func (h *hook) Levels() []stdLogger.Level {
return log.AllLevels
}

View File

@@ -6,11 +6,15 @@ import (
"gosrc.io/xmpp" "gosrc.io/xmpp"
) )
func main() {
log.AddHook(&hook{})
cmd.Execute()
}
var domain = "" var domain = ""
var cmdCheck = &cobra.Command{ var cmd = &cobra.Command{
Use: "check <host[:port]>", Use: "xmpp-check <host[:port]>",
Short: "is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires", Example: "xmpp-check chat.sum7.eu:5222 --domain meckerspace.de",
Example: "fluuxmpp check chat.sum7.eu:5222 --domain meckerspace.de",
Args: cobra.ExactArgs(1), Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
runCheck(args[0], domain) runCheck(args[0], domain)
@@ -18,8 +22,7 @@ var cmdCheck = &cobra.Command{
} }
func init() { func init() {
cmdRoot.AddCommand(cmdCheck) cmd.Flags().StringVarP(&domain, "domain", "d", "", "domain if host handle multiple domains")
cmdCheck.Flags().StringVarP(&domain, "domain", "d", "", "domain if host handle multiple domains")
} }
func runCheck(address, domain string) { func runCheck(address, domain string) {

View File

@@ -66,67 +66,56 @@ func NewComponent(opts ComponentOptions, r *Router) (*Component, error) {
// Connect triggers component connection to XMPP server component port. // Connect triggers component connection to XMPP server component port.
// TODO: Failed handshake should be a permanent error // TODO: Failed handshake should be a permanent error
func (c *Component) Connect() error { func (c *Component) Connect() error {
var state SMState
return c.Resume(state)
}
func (c *Component) Resume(sm SMState) error {
var conn net.Conn var conn net.Conn
var err error var err error
if conn, err = net.DialTimeout("tcp", c.Address, time.Duration(5)*time.Second); err != nil { if conn, err = net.DialTimeout("tcp", c.Address, time.Duration(5)*time.Second); err != nil {
return err return err
} }
c.conn = conn c.conn = conn
c.updateState(StateConnected)
// 1. Send stream open tag // 1. Send stream open tag
if _, err := fmt.Fprintf(conn, componentStreamOpen, c.Domain, stanza.NSComponent, stanza.NSStream); err != nil { if _, err := fmt.Fprintf(conn, componentStreamOpen, c.Domain, stanza.NSComponent, stanza.NSStream); err != nil {
c.updateState(StateStreamError) return errors.New("cannot send stream open " + err.Error())
return NewConnError(errors.New("cannot send stream open "+err.Error()), false)
} }
c.decoder = xml.NewDecoder(conn) c.decoder = xml.NewDecoder(conn)
// 2. Initialize xml decoder and extract streamID from reply // 2. Initialize xml decoder and extract streamID from reply
streamId, err := stanza.InitStream(c.decoder) streamId, err := stanza.InitStream(c.decoder)
if err != nil { if err != nil {
c.updateState(StateStreamError) return errors.New("cannot init decoder " + err.Error())
return NewConnError(errors.New("cannot init decoder "+err.Error()), false)
} }
// 3. Authentication // 3. Authentication
if _, err := fmt.Fprintf(conn, "<handshake>%s</handshake>", c.handshake(streamId)); err != nil { if _, err := fmt.Fprintf(conn, "<handshake>%s</handshake>", c.handshake(streamId)); err != nil {
c.updateState(StateStreamError) return errors.New("cannot send handshake " + err.Error())
return NewConnError(errors.New("cannot send handshake "+err.Error()), false)
} }
// 4. Check server response for authentication // 4. Check server response for authentication
val, err := stanza.NextPacket(c.decoder) val, err := stanza.NextPacket(c.decoder)
if err != nil { if err != nil {
c.updateState(StateDisconnected) return err
return NewConnError(err, true)
} }
switch v := val.(type) { switch v := val.(type) {
case stanza.StreamError: case stanza.StreamError:
c.streamError("conflict", "no auth loop") return errors.New("handshake failed " + v.Error.Local)
return NewConnError(errors.New("handshake failed "+v.Error.Local), true)
case stanza.Handshake: case stanza.Handshake:
// Start the receiver go routine // Start the receiver go routine
c.updateState(StateSessionEstablished)
go c.recv() go c.recv()
return nil return nil
default: default:
c.updateState(StateStreamError) return errors.New("expecting handshake result, got " + v.Name())
return NewConnError(errors.New("expecting handshake result, got "+v.Name()), true)
} }
} }
func (c *Component) Resume() error {
return errors.New("components do not support stream management")
}
func (c *Component) Disconnect() { func (c *Component) Disconnect() {
_ = c.SendRaw("</stream:stream>") _ = c.SendRaw("</stream:stream>")
// TODO: Add a way to wait for stream close acknowledgement from the server for clean disconnect // TODO: Add a way to wait for stream close acknowledgement from the server for clean disconnect
conn := c.conn _ = c.conn.Close()
if conn != nil {
_ = conn.Close()
}
} }
func (c *Component) SetHandler(handler EventHandler) { func (c *Component) SetHandler(handler EventHandler) {

View File

@@ -23,10 +23,3 @@ func TestHandshake(t *testing.T) {
func TestGenerateHandshake(t *testing.T) { func TestGenerateHandshake(t *testing.T) {
// TODO // TODO
} }
// Test that NewStreamManager can accept a Component.
//
// This validates that Component conforms to StreamClient interface.
func TestStreamManager(t *testing.T) {
NewStreamManager(&Component{}, nil)
}

View File

@@ -98,7 +98,7 @@ type Handler interface {
type Route struct { type Route struct {
handler Handler handler Handler
// Matchers are used to "specialize" routes and focus on specific packet features // Matchers are used to "specialize" routes and focus on specific packet features
matchers []Matcher matchers []matcher
} }
func (r *Route) Handler(handler Handler) *Route { func (r *Route) Handler(handler Handler) *Route {
@@ -122,8 +122,8 @@ func (r *Route) HandlerFunc(f HandlerFunc) *Route {
return r.Handler(f) return r.Handler(f)
} }
// AddMatcher adds a matcher to the route // addMatcher adds a matcher to the route
func (r *Route) AddMatcher(m Matcher) *Route { func (r *Route) addMatcher(m matcher) *Route {
r.matchers = append(r.matchers, m) r.matchers = append(r.matchers, m)
return r return r
} }
@@ -170,7 +170,7 @@ func (n nameMatcher) Match(p stanza.Packet, match *RouteMatch) bool {
// It matches on the Local part of the xml.Name // It matches on the Local part of the xml.Name
func (r *Route) Packet(name string) *Route { func (r *Route) Packet(name string) *Route {
name = strings.ToLower(name) name = strings.ToLower(name)
return r.AddMatcher(nameMatcher(name)) return r.addMatcher(nameMatcher(name))
} }
// ------------------------- // -------------------------
@@ -204,7 +204,7 @@ func (r *Route) StanzaType(types ...string) *Route {
for k, v := range types { for k, v := range types {
types[k] = strings.ToLower(v) types[k] = strings.ToLower(v)
} }
return r.AddMatcher(nsTypeMatcher(types)) return r.addMatcher(nsTypeMatcher(types))
} }
// ------------------------- // -------------------------
@@ -229,15 +229,14 @@ func (r *Route) IQNamespaces(namespaces ...string) *Route {
for k, v := range namespaces { for k, v := range namespaces {
namespaces[k] = strings.ToLower(v) namespaces[k] = strings.ToLower(v)
} }
return r.AddMatcher(nsIQMatcher(namespaces)) return r.addMatcher(nsIQMatcher(namespaces))
} }
// ============================================================================ // ============================================================================
// Matchers // Matchers
// Matchers are used to "specialize" routes and focus on specific packet features. // Matchers are used to "specialize" routes and focus on specific packet features
// You can register attach them to a route via the AddMatcher method. type matcher interface {
type Matcher interface {
Match(stanza.Packet, *RouteMatch) bool Match(stanza.Packet, *RouteMatch) bool
} }

View File

@@ -152,7 +152,7 @@ type Metrics struct {
ConnectTime time.Duration ConnectTime time.Duration
// LoginTime returns the between client initiation of the TCP/IP // LoginTime returns the between client initiation of the TCP/IP
// connection to the server and the return of the login result. // connection to the server and the return of the login result.
// This includes ConnectTime, but also XMPP level protocol negotiation // This includes ConnectTime, but also XMPP level protocol negociation
// like starttls. // like starttls.
LoginTime time.Duration LoginTime time.Duration
} }