forked from jshiffer/go-xmpp
merge commandline tools
This commit is contained in:
committed by
Mickaël Rémond
parent
80d2e0fa1e
commit
76f59be5ed
198
cmd/fluxxmpp/README.md
Normal file
198
cmd/fluxxmpp/README.md
Normal file
@@ -0,0 +1,198 @@
|
||||
# fluxxmpp
|
||||
|
||||
fluxxIO's xmpp comandline tool
|
||||
|
||||
## Installation
|
||||
|
||||
To install `fluxxmpp` in your Go path:
|
||||
|
||||
```
|
||||
$ go get -u gosrc.io/xmpp/cmd/fluxxmpp
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
$ fluxxmpp --help
|
||||
fluxxIO's xmpp comandline tool
|
||||
|
||||
Usage:
|
||||
fluxxmpp [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 fluxxmpp
|
||||
|
||||
Use "fluxxmpp [command] --help" for more information about a command.
|
||||
```
|
||||
|
||||
### check tls
|
||||
|
||||
```
|
||||
$ fluxxmpp check --help
|
||||
is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires
|
||||
|
||||
Usage:
|
||||
fluxxmpp check <host[:port]> [flags]
|
||||
|
||||
Examples:
|
||||
fluxxmpp 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
|
||||
|
||||
```
|
||||
$ fluxxmpp send --help
|
||||
is a command-line tool to send to send XMPP messages to users
|
||||
|
||||
Usage:
|
||||
fluxxmpp send <recipient,> [message] [flags]
|
||||
|
||||
Examples:
|
||||
fluxxmpp send to@chat.sum7.eu "Hello World!"
|
||||
|
||||
Flags:
|
||||
--addr string host[:port]
|
||||
--config string config file (default is ~/.config/fluxxmpp.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:
|
||||
|
||||
```
|
||||
$ fluxxmpp 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:
|
||||
|
||||
```
|
||||
$ fluxxmpp 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
|
||||
$ fluxxmpp 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 | fluxxmpp 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
|
||||
$ fluxxmpp 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 | fluxxmpp 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 `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';
|
||||
|
||||
fluxxmpp 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
|
||||
fluxxmpp send to@example.org "Hello World!" --jid bot@example.org --password secret --addr example.com:5222;
|
||||
```
|
||||
21
cmd/fluxxmpp/TODO.md
Normal file
21
cmd/fluxxmpp/TODO.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# TODO
|
||||
|
||||
## 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)
|
||||
- Does not report error when trying to connect to a non open port (for example localhost with no server running).
|
||||
|
||||
### Features
|
||||
|
||||
- configuration
|
||||
- allow unencrypted
|
||||
- skip tls verification
|
||||
- support muc and single user at same time
|
||||
- send html -> parse console colors to xhtml (is there a easy way or lib for it ?)
|
||||
41
cmd/fluxxmpp/check.go
Normal file
41
cmd/fluxxmpp/check.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bdlm/log"
|
||||
"github.com/spf13/cobra"
|
||||
"gosrc.io/xmpp"
|
||||
)
|
||||
|
||||
var domain = ""
|
||||
var cmdCheck = &cobra.Command{
|
||||
Use: "check <host[:port]>",
|
||||
Short: "is a command-line to check if you XMPP TLS certificate is valid and warn you before it expires",
|
||||
Example: "fluxxmpp check chat.sum7.eu:5222 --domain meckerspace.de",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
runCheck(args[0], domain)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdCheck)
|
||||
cmdCheck.Flags().StringVarP(&domain, "domain", "d", "", "domain if host handle multiple domains")
|
||||
}
|
||||
|
||||
func runCheck(address, domain string) {
|
||||
logger := log.WithFields(map[string]interface{}{
|
||||
"address": address,
|
||||
"domain": domain,
|
||||
})
|
||||
client, err := xmpp.NewChecker(address, domain)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Error: ", err)
|
||||
}
|
||||
|
||||
if err = client.Check(); err != nil {
|
||||
logger.Fatal("Failed connection check: ", err)
|
||||
}
|
||||
|
||||
logger.Println("All checks passed")
|
||||
}
|
||||
5
cmd/fluxxmpp/doc.go
Normal file
5
cmd/fluxxmpp/doc.go
Normal file
@@ -0,0 +1,5 @@
|
||||
/*
|
||||
|
||||
fluxxmpp: fluxxIO's xmpp comandline tool
|
||||
*/
|
||||
package main
|
||||
34
cmd/fluxxmpp/log.go
Normal file
34
cmd/fluxxmpp/log.go
Normal 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
|
||||
}
|
||||
19
cmd/fluxxmpp/main.go
Normal file
19
cmd/fluxxmpp/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: "fluxxmpp",
|
||||
Short: "fluxxIO's xmpp comandline tool",
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.AddHook(&hook{})
|
||||
if err := cmdRoot.Execute(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
134
cmd/fluxxmpp/send.go
Normal file
134
cmd/fluxxmpp/send.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/bdlm/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"gosrc.io/xmpp"
|
||||
)
|
||||
|
||||
var configFile = ""
|
||||
|
||||
// FIXME: Remove global variables
|
||||
var isMUCRecipient = false
|
||||
|
||||
var cmdSend = &cobra.Command{
|
||||
Use: "send <recipient,> [message]",
|
||||
Short: "is a command-line tool to send to send XMPP messages to users",
|
||||
Example: `fluxxmpp send to@chat.sum7.eu "Hello World!"`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: sendxmpp,
|
||||
}
|
||||
|
||||
func sendxmpp(cmd *cobra.Command, args []string) {
|
||||
receiver := strings.Split(args[0], ",")
|
||||
msgText := args[1]
|
||||
|
||||
var err error
|
||||
client, err := xmpp.NewClient(xmpp.Config{
|
||||
Jid: viper.GetString("jid"),
|
||||
Address: viper.GetString("addr"),
|
||||
Password: viper.GetString("password"),
|
||||
}, xmpp.NewRouter())
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("error when starting xmpp client: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
|
||||
// FIXME: Remove global variables
|
||||
var mucsToLeave []*xmpp.Jid
|
||||
|
||||
cm := xmpp.NewStreamManager(client, func(c xmpp.Sender) {
|
||||
defer wg.Done()
|
||||
|
||||
log.Info("client connected")
|
||||
|
||||
if isMUCRecipient {
|
||||
for _, muc := range receiver {
|
||||
jid, err := xmpp.NewJid(muc)
|
||||
if err != nil {
|
||||
log.WithField("muc", muc).Errorf("skipping invalid muc jid: %w", err)
|
||||
continue
|
||||
}
|
||||
jid.Resource = "sendxmpp"
|
||||
|
||||
if err := joinMUC(c, jid); err != nil {
|
||||
log.WithField("muc", muc).Errorf("error joining muc: %w", err)
|
||||
continue
|
||||
}
|
||||
mucsToLeave = append(mucsToLeave, jid)
|
||||
}
|
||||
}
|
||||
|
||||
if msgText != "-" {
|
||||
send(c, receiver, msgText)
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
for scanner.Scan() {
|
||||
send(c, receiver, scanner.Text())
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Errorf("error on reading stdin: %s", err)
|
||||
}
|
||||
})
|
||||
go func() {
|
||||
err := cm.Run()
|
||||
log.Panic("closed connection:", err)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
leaveMUCs(client, mucsToLeave)
|
||||
}
|
||||
|
||||
func init() {
|
||||
cmdRoot.AddCommand(cmdSend)
|
||||
|
||||
cobra.OnInitialize(initConfigFile)
|
||||
cmdSend.PersistentFlags().StringVar(&configFile, "config", "", "config file (default is ~/.config/fluxxmpp.yml)")
|
||||
|
||||
cmdSend.Flags().StringP("jid", "", "", "using jid (required)")
|
||||
viper.BindPFlag("jid", cmdSend.Flags().Lookup("jid"))
|
||||
|
||||
cmdSend.Flags().StringP("password", "", "", "using password for your jid (required)")
|
||||
viper.BindPFlag("password", cmdSend.Flags().Lookup("password"))
|
||||
|
||||
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.
|
||||
func initConfigFile() {
|
||||
if configFile != "" {
|
||||
viper.SetConfigFile(configFile)
|
||||
}
|
||||
|
||||
viper.SetConfigName("fluxxmpp")
|
||||
viper.AddConfigPath("/etc/")
|
||||
viper.AddConfigPath("$HOME/.config")
|
||||
viper.AddConfigPath(".")
|
||||
|
||||
viper.SetEnvPrefix("FLUXXMPP")
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// If a config file is found, read it in.
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
log.Warnf("no configuration found (somebody could read your password from process argument list): %s", err)
|
||||
}
|
||||
}
|
||||
28
cmd/fluxxmpp/xmppmuc.go
Normal file
28
cmd/fluxxmpp/xmppmuc.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"gosrc.io/xmpp"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
func joinMUC(c xmpp.Sender, toJID *xmpp.Jid) error {
|
||||
return c.Send(stanza.Presence{Attrs: stanza.Attrs{To: toJID.Full()},
|
||||
Extensions: []stanza.PresExtension{
|
||||
stanza.MucPresence{
|
||||
History: stanza.History{MaxStanzas: stanza.NewNullableInt(0)},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
func leaveMUCs(c xmpp.Sender, mucsToLeave []*xmpp.Jid) {
|
||||
for _, muc := range mucsToLeave {
|
||||
if err := c.Send(stanza.Presence{Attrs: stanza.Attrs{
|
||||
To: muc.Full(),
|
||||
Type: stanza.PresenceTypeUnavailable,
|
||||
}}); err != nil {
|
||||
log.WithField("muc", muc).Errorf("error on leaving muc: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
36
cmd/fluxxmpp/xmppsend.go
Normal file
36
cmd/fluxxmpp/xmppsend.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"gosrc.io/xmpp"
|
||||
"gosrc.io/xmpp/stanza"
|
||||
)
|
||||
|
||||
func send(c xmpp.Sender, recipient []string, msgText string) {
|
||||
msg := stanza.Message{
|
||||
Attrs: stanza.Attrs{Type: stanza.MessageTypeChat},
|
||||
Body: msgText,
|
||||
}
|
||||
|
||||
if isMUCRecipient {
|
||||
msg.Type = stanza.MessageTypeGroupchat
|
||||
}
|
||||
|
||||
for _, to := range recipient {
|
||||
msg.To = to
|
||||
if err := c.Send(msg); err != nil {
|
||||
log.WithFields(map[string]interface{}{
|
||||
"muc": isMUCRecipient,
|
||||
"to": to,
|
||||
"text": msgText,
|
||||
}).Errorf("error on send message: %s", err)
|
||||
} else {
|
||||
log.WithFields(map[string]interface{}{
|
||||
"muc": isMUCRecipient,
|
||||
"to": to,
|
||||
"text": msgText,
|
||||
}).Info("send message")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user