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
31 changed files with 295 additions and 393 deletions
-1
View File
@@ -4,4 +4,3 @@ github.com/processone/soundcloud v1.0.0/go.mod h1:kDLeWpkRtN3C8kIReQdxoiRi92P9xR
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+5 -9
View File
@@ -4,7 +4,6 @@ import (
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io"
"net" "net"
"time" "time"
@@ -190,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) {
@@ -212,7 +208,7 @@ func (c *Client) Send(packet stanza.Packet) error {
return errors.New("cannot marshal packet " + err.Error()) return errors.New("cannot marshal packet " + err.Error())
} }
return c.sendWithWriter(c.Session.streamLogger, data) return c.sendWithLogger(string(data))
} }
// SendRaw sends an XMPP stanza as a string to the server. // SendRaw sends an XMPP stanza as a string to the server.
@@ -225,12 +221,12 @@ func (c *Client) SendRaw(packet string) error {
return errors.New("client is not connected") return errors.New("client is not connected")
} }
return c.sendWithWriter(c.Session.streamLogger, []byte(packet)) return c.sendWithLogger(packet)
} }
func (c *Client) sendWithWriter(writer io.Writer, packet []byte) error { func (c *Client) sendWithLogger(packet string) error {
var err error var err error
_, err = writer.Write(packet) _, err = fmt.Fprintf(c.Session.streamLogger, packet)
return err return err
} }
-19
View File
@@ -1,19 +0,0 @@
package xmpp
import (
"bytes"
"testing"
)
func TestClient_Send(t *testing.T) {
buffer := bytes.NewBufferString("")
client := Client{}
data := []byte("https://da.wikipedia.org/wiki/J%C3%A6vnd%C3%B8gn")
if err := client.sendWithWriter(buffer, data); err != nil {
t.Errorf("Writing failed: %v", err)
}
if buffer.String() != string(data) {
t.Errorf("Incorrect value sent to buffer: '%s'", buffer.String())
}
}
-198
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;
```
-5
View File
@@ -1,5 +0,0 @@
/*
fluuxmpp: fluuxIO's xmpp comandline tool
*/
package main
-19
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)
}
}
-1
View File
@@ -143,7 +143,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4= 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= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+131
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;
```
@@ -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
+14 -17
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
View File
@@ -0,0 +1,6 @@
/*
sendxmpp is a command-line tool to send to send XMPP messages to users
*/
package main
+12
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
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
View File
@@ -0,0 +1,3 @@
# TODO
- Use a config file to define the checks to perform as client on an XMPP server.
+6
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
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
}
@@ -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) {
+11 -22
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) {
-7
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)
}
+1 -1
View File
@@ -4,5 +4,5 @@ go 1.12
require ( require (
github.com/google/go-cmp v0.3.0 github.com/google/go-cmp v0.3.0
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522
) )
-2
View File
@@ -2,5 +2,3 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522 h1:bhOzK9QyoD0ogCnFro1m2mz41+Ib0oOhfJnBp5MR4K4= 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= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+8 -9
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
} }
-31
View File
@@ -1,31 +0,0 @@
package stanza
import (
"encoding/xml"
"testing"
)
func TestErr_UnmarshalXML(t *testing.T) {
packet := `
<iq from='pubsub.example.com'
id='kj4vz31m'
to='romeo@example.net/foo'
type='error'>
<error type='wait'>
<resource-constraint
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>System overloaded, please retry</text>
</error>
</iq>`
parsedIQ := IQ{}
data := []byte(packet)
if err := xml.Unmarshal(data, &parsedIQ); err != nil {
t.Errorf("Unmarshal(%s) returned error", data)
}
xmppError := parsedIQ.Error
if xmppError.Text != "System overloaded, please retry" {
t.Errorf("Could not extract error text: '%s'", xmppError.Text)
}
}
+1 -1
View File
@@ -21,6 +21,6 @@ func TestControlSet(t *testing.T) {
} }
if cs, ok := parsedIQ.Payload.(*ControlSet); !ok { if cs, ok := parsedIQ.Payload.(*ControlSet); !ok {
t.Errorf("Payload is not an iot control set: %v", cs) t.Errorf("Paylod is not an iot control set: %v", cs)
} }
} }
+1 -1
View File
@@ -22,7 +22,7 @@ type IQ struct { // Info/Query
// request." // request."
Payload IQPayload `xml:",omitempty"` Payload IQPayload `xml:",omitempty"`
Error Err `xml:"error,omitempty"` Error Err `xml:"error,omitempty"`
// Any is used to decode unknown payload as a generic structure // Any is used to decode unknown payload as a generique structure
Any *Node `xml:",any"` Any *Node `xml:",any"`
} }
+1 -4
View File
@@ -10,7 +10,7 @@ import "encoding/xml"
type Node struct { type Node struct {
XMLName xml.Name XMLName xml.Name
Attrs []xml.Attr `xml:"-"` Attrs []xml.Attr `xml:"-"`
Content string `xml:",cdata"` Content string `xml:",innerxml"`
Nodes []Node `xml:",any"` Nodes []Node `xml:",any"`
} }
@@ -47,8 +47,5 @@ func (n Node) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
err = e.EncodeToken(start) err = e.EncodeToken(start)
e.EncodeElement(n.Nodes, xml.StartElement{Name: n.XMLName}) e.EncodeElement(n.Nodes, xml.StartElement{Name: n.XMLName})
if n.Content != "" {
e.EncodeToken(xml.CharData(n.Content))
}
return e.EncodeToken(xml.EndElement{Name: start.Name}) return e.EncodeToken(xml.EndElement{Name: start.Name})
} }
-30
View File
@@ -1,30 +0,0 @@
package stanza
import (
"encoding/xml"
"testing"
)
func TestNode_Marshal(t *testing.T) {
jsonData := []byte("{\"key\":\"value\"}")
iqResp := NewIQ(Attrs{Type: "result", From: "admin@localhost", To: "test@localhost", Id: "1"})
iqResp.Any = &Node{
XMLName: xml.Name{Space: "myNS", Local: "space"},
Content: string(jsonData),
}
bytes, err := xml.Marshal(iqResp)
if err != nil {
t.Errorf("Could not marshal XML: %v", err)
}
parsedIQ := IQ{}
if err := xml.Unmarshal(bytes, &parsedIQ); err != nil {
t.Errorf("Unmarshal returned error: %v", err)
}
if parsedIQ.Any.Content != string(jsonData) {
t.Errorf("Cannot find generic any payload in parsedIQ: '%s'", parsedIQ.Any.Content)
}
}
+1 -1
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
} }