mirror of
https://github.com/FluuxIO/go-xmpp.git
synced 2025-11-23 01:53:44 -08:00
Compare commits
14 Commits
go-xmpp-47
...
v0.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b66e31888 | ||
|
|
005c8823d9 | ||
|
|
2cdda48467 | ||
|
|
8794ea6ed8 | ||
|
|
7e596fc33c | ||
|
|
6f9808fe16 | ||
|
|
7b1f83f6b7 | ||
|
|
6005a964ba | ||
|
|
d3b45b42a5 | ||
|
|
6e65ba2a0b | ||
|
|
76f59be5ed | ||
|
|
80d2e0fa1e | ||
|
|
2e864ff7f6 | ||
|
|
3de99e0e0e |
@@ -4,3 +4,4 @@ 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=
|
||||||
|
|||||||
14
client.go
14
client.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -189,7 +190,10 @@ 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
|
||||||
_ = c.conn.Close()
|
conn := c.conn
|
||||||
|
if conn != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) SetHandler(handler EventHandler) {
|
func (c *Client) SetHandler(handler EventHandler) {
|
||||||
@@ -208,7 +212,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.sendWithLogger(string(data))
|
return c.sendWithWriter(c.Session.streamLogger, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRaw sends an XMPP stanza as a string to the server.
|
// SendRaw sends an XMPP stanza as a string to the server.
|
||||||
@@ -221,12 +225,12 @@ func (c *Client) SendRaw(packet string) error {
|
|||||||
return errors.New("client is not connected")
|
return errors.New("client is not connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.sendWithLogger(packet)
|
return c.sendWithWriter(c.Session.streamLogger, []byte(packet))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) sendWithLogger(packet string) error {
|
func (c *Client) sendWithWriter(writer io.Writer, packet []byte) error {
|
||||||
var err error
|
var err error
|
||||||
_, err = fmt.Fprintf(c.Session.streamLogger, packet)
|
_, err = writer.Write(packet)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
client_internal_test.go
Normal file
19
client_internal_test.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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
cmd/fluuxmpp/README.md
Normal file
198
cmd/fluuxmpp/README.md
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# 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;
|
||||||
|
```
|
||||||
@@ -1,11 +1,18 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## Issues
|
## check
|
||||||
|
### 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
|
||||||
@@ -6,15 +6,11 @@ import (
|
|||||||
"gosrc.io/xmpp"
|
"gosrc.io/xmpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.AddHook(&hook{})
|
|
||||||
cmd.Execute()
|
|
||||||
}
|
|
||||||
|
|
||||||
var domain = ""
|
var domain = ""
|
||||||
var cmd = &cobra.Command{
|
var cmdCheck = &cobra.Command{
|
||||||
Use: "xmpp-check <host[:port]>",
|
Use: "check <host[:port]>",
|
||||||
Example: "xmpp-check chat.sum7.eu:5222 --domain meckerspace.de",
|
Short: "is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires",
|
||||||
|
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)
|
||||||
@@ -22,7 +18,8 @@ var cmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Flags().StringVarP(&domain, "domain", "d", "", "domain if host handle multiple domains")
|
cmdRoot.AddCommand(cmdCheck)
|
||||||
|
cmdCheck.Flags().StringVarP(&domain, "domain", "d", "", "domain if host handle multiple domains")
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCheck(address, domain string) {
|
func runCheck(address, domain string) {
|
||||||
5
cmd/fluuxmpp/doc.go
Normal file
5
cmd/fluuxmpp/doc.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
fluuxmpp: fluuxIO's xmpp comandline tool
|
||||||
|
*/
|
||||||
|
package main
|
||||||
19
cmd/fluuxmpp/main.go
Normal file
19
cmd/fluuxmpp/main.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,9 +18,10 @@ var configFile = ""
|
|||||||
// FIXME: Remove global variables
|
// FIXME: Remove global variables
|
||||||
var isMUCRecipient = false
|
var isMUCRecipient = false
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
var cmdSend = &cobra.Command{
|
||||||
Use: "sendxmpp <recipient,> [message]",
|
Use: "send <recipient,> [message]",
|
||||||
Example: `sendxmpp to@chat.sum7.eu "Hello World!"`,
|
Short: "is a command-line tool to send to send XMPP messages to users",
|
||||||
|
Example: `fluuxmpp send to@chat.sum7.eu "Hello World!"`,
|
||||||
Args: cobra.ExactArgs(2),
|
Args: cobra.ExactArgs(2),
|
||||||
Run: sendxmpp,
|
Run: sendxmpp,
|
||||||
}
|
}
|
||||||
@@ -95,28 +96,30 @@ func sendxmpp(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cobra.OnInitialize(initConfig)
|
cmdRoot.AddCommand(cmdSend)
|
||||||
cmd.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is ~/.config/fluxxmpp.yml)")
|
|
||||||
|
|
||||||
cmd.Flags().StringP("jid", "", "", "using jid (required)")
|
cobra.OnInitialize(initConfigFile)
|
||||||
viper.BindPFlag("jid", cmd.Flags().Lookup("jid"))
|
cmdSend.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is ~/.config/fluuxmpp.yml)")
|
||||||
|
|
||||||
cmd.Flags().StringP("password", "", "", "using password for your jid (required)")
|
cmdSend.Flags().StringP("jid", "", "", "using jid (required)")
|
||||||
viper.BindPFlag("password", cmd.Flags().Lookup("password"))
|
viper.BindPFlag("jid", cmdSend.Flags().Lookup("jid"))
|
||||||
|
|
||||||
cmd.Flags().StringP("addr", "", "", "host[:port]")
|
cmdSend.Flags().StringP("password", "", "", "using password for your jid (required)")
|
||||||
viper.BindPFlag("addr", cmd.Flags().Lookup("addr"))
|
viper.BindPFlag("password", cmdSend.Flags().Lookup("password"))
|
||||||
|
|
||||||
cmd.Flags().BoolVarP(&isMUCRecipient, "muc", "m", false, "recipient is a muc (join it before sending messages)")
|
cmdSend.Flags().StringP("addr", "", "", "host[:port]")
|
||||||
|
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 initConfig() {
|
func initConfigFile() {
|
||||||
if configFile != "" {
|
if configFile != "" {
|
||||||
viper.SetConfigFile(configFile)
|
viper.SetConfigFile(configFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.SetConfigName("fluxxmpp")
|
viper.SetConfigName("fluuxmpp")
|
||||||
viper.AddConfigPath("/etc/")
|
viper.AddConfigPath("/etc/")
|
||||||
viper.AddConfigPath("$HOME/.config")
|
viper.AddConfigPath("$HOME/.config")
|
||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
@@ -143,6 +143,7 @@ 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=
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
# 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,6 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
sendxmpp is a command-line tool to send to send XMPP messages to users
|
|
||||||
|
|
||||||
*/
|
|
||||||
package main
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/bdlm/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.AddHook(&hook{})
|
|
||||||
if err := cmd.Execute(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# TODO
|
|
||||||
|
|
||||||
- Use a config file to define the checks to perform as client on an XMPP server.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
xmpp-check is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires.
|
|
||||||
|
|
||||||
*/
|
|
||||||
package main
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
33
component.go
33
component.go
@@ -66,56 +66,67 @@ 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 {
|
||||||
return errors.New("cannot send stream open " + err.Error())
|
c.updateState(StateStreamError)
|
||||||
|
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 {
|
||||||
return errors.New("cannot init decoder " + err.Error())
|
c.updateState(StateStreamError)
|
||||||
|
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 {
|
||||||
return errors.New("cannot send handshake " + err.Error())
|
c.updateState(StateStreamError)
|
||||||
|
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 {
|
||||||
return err
|
c.updateState(StateDisconnected)
|
||||||
|
return NewConnError(err, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case stanza.StreamError:
|
case stanza.StreamError:
|
||||||
return errors.New("handshake failed " + v.Error.Local)
|
c.streamError("conflict", "no auth loop")
|
||||||
|
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:
|
||||||
return errors.New("expecting handshake result, got " + v.Name())
|
c.updateState(StateStreamError)
|
||||||
|
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
|
||||||
_ = c.conn.Close()
|
conn := c.conn
|
||||||
|
if conn != nil {
|
||||||
|
_ = conn.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Component) SetHandler(handler EventHandler) {
|
func (c *Component) SetHandler(handler EventHandler) {
|
||||||
|
|||||||
@@ -23,3 +23,10 @@ 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)
|
||||||
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -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-20190513163551-3ee3066db522
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -2,3 +2,5 @@ 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=
|
||||||
|
|||||||
17
router.go
17
router.go
@@ -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,14 +229,15 @@ 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.
|
||||||
type matcher interface {
|
// You can register attach them to a route via the AddMatcher method.
|
||||||
|
type Matcher interface {
|
||||||
Match(stanza.Packet, *RouteMatch) bool
|
Match(stanza.Packet, *RouteMatch) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
stanza/error_test.go
Normal file
31
stanza/error_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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("Paylod is not an iot control set: %v", cs)
|
t.Errorf("Payload is not an iot control set: %v", cs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 generique structure
|
// Any is used to decode unknown payload as a generic structure
|
||||||
Any *Node `xml:",any"`
|
Any *Node `xml:",any"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:",innerxml"`
|
Content string `xml:",cdata"`
|
||||||
Nodes []Node `xml:",any"`
|
Nodes []Node `xml:",any"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,5 +47,8 @@ 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
stanza/node_test.go
Normal file
30
stanza/node_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 negociation
|
// This includes ConnectTime, but also XMPP level protocol negotiation
|
||||||
// like starttls.
|
// like starttls.
|
||||||
LoginTime time.Duration
|
LoginTime time.Duration
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user