implement client and start tester daemon
This commit is contained in:
parent
79459ff864
commit
a67d26d5a2
|
@ -15,3 +15,5 @@
|
||||||
.glide/
|
.glide/
|
||||||
|
|
||||||
tmp
|
tmp
|
||||||
|
yaja*.conf
|
||||||
|
!yaja*example.conf
|
||||||
|
|
|
@ -0,0 +1,275 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
|
"dev.sum7.eu/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client holds XMPP connection opitons
|
||||||
|
type Client struct {
|
||||||
|
conn net.Conn // connection to server
|
||||||
|
Out *xml.Encoder
|
||||||
|
In *xml.Decoder
|
||||||
|
|
||||||
|
JID *model.JID
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(jid model.JID, password string) (*Client, error) {
|
||||||
|
conn, err := net.Dial("tcp", jid.Domain+":5222")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := &Client{
|
||||||
|
conn: conn,
|
||||||
|
In: xml.NewDecoder(conn),
|
||||||
|
Out: xml.NewEncoder(conn),
|
||||||
|
|
||||||
|
JID: &jid,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = client.connect(password); err != nil {
|
||||||
|
client.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the XMPP connection
|
||||||
|
func (c *Client) Close() error {
|
||||||
|
if c.conn != (*tls.Conn)(nil) {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) Read() (*xml.StartElement, error) {
|
||||||
|
for {
|
||||||
|
nextToken, err := client.In.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch nextToken.(type) {
|
||||||
|
case xml.StartElement:
|
||||||
|
element := nextToken.(xml.StartElement)
|
||||||
|
return &element, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (client *Client) ReadElement(p interface{}) error {
|
||||||
|
element, err := client.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.In.DecodeElement(p, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) init() error {
|
||||||
|
// XMPP-Connection
|
||||||
|
_, err := fmt.Fprintf(client.conn, "<?xml version='1.0'?>\n"+
|
||||||
|
"<stream:stream to='%s' xmlns='%s'\n"+
|
||||||
|
" xmlns:stream='%s' version='1.0'>\n",
|
||||||
|
model.XMLEscape(client.JID.Domain), messages.NSClient, messages.NSStream)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
element, err := client.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if element.Name.Space != messages.NSStream || element.Name.Local != "stream" {
|
||||||
|
return errors.New("is not stream")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (client *Client) connect(password string) error {
|
||||||
|
if err := client.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var f messages.StreamFeatures
|
||||||
|
if err := client.ReadElement(&f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := client.Out.Encode(&messages.TLSStartTLS{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var p messages.TLSProceed
|
||||||
|
if err := client.ReadElement(&p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Change tcp to tls
|
||||||
|
tlsconn := tls.Client(client.conn, &tls.Config{
|
||||||
|
ServerName: client.JID.Domain,
|
||||||
|
})
|
||||||
|
client.conn = tlsconn
|
||||||
|
client.In = xml.NewDecoder(client.conn)
|
||||||
|
client.Out = xml.NewEncoder(client.conn)
|
||||||
|
|
||||||
|
if err := tlsconn.Handshake(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tlsconn.VerifyHostname(client.JID.Domain); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := client.init(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//auth:
|
||||||
|
if err := client.ReadElement(&f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mechanism := ""
|
||||||
|
for _, m := range f.Mechanisms.Mechanism {
|
||||||
|
if m == "PLAIN" {
|
||||||
|
mechanism = m
|
||||||
|
// Plain authentication: send base64-encoded \x00 user \x00 password.
|
||||||
|
raw := "\x00" + client.JID.Local + "\x00" + password
|
||||||
|
enc := make([]byte, base64.StdEncoding.EncodedLen(len(raw)))
|
||||||
|
base64.StdEncoding.Encode(enc, []byte(raw))
|
||||||
|
fmt.Fprintf(client.conn, "<auth xmlns='%s' mechanism='PLAIN'>%s</auth>\n", messages.NSSASL, enc)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if m == "DIGEST-MD5" {
|
||||||
|
mechanism = m
|
||||||
|
// Digest-MD5 authentication
|
||||||
|
fmt.Fprintf(client.conn, "<auth xmlns='%s' mechanism='DIGEST-MD5'/>\n", messages.NSSASL)
|
||||||
|
var ch string
|
||||||
|
if err := client.ReadElement(&ch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b, err := base64.StdEncoding.DecodeString(string(ch))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tokens := map[string]string{}
|
||||||
|
for _, token := range strings.Split(string(b), ",") {
|
||||||
|
kv := strings.SplitN(strings.TrimSpace(token), "=", 2)
|
||||||
|
if len(kv) == 2 {
|
||||||
|
if kv[1][0] == '"' && kv[1][len(kv[1])-1] == '"' {
|
||||||
|
kv[1] = kv[1][1 : len(kv[1])-1]
|
||||||
|
}
|
||||||
|
tokens[kv[0]] = kv[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
realm, _ := tokens["realm"]
|
||||||
|
nonce, _ := tokens["nonce"]
|
||||||
|
qop, _ := tokens["qop"]
|
||||||
|
charset, _ := tokens["charset"]
|
||||||
|
cnonceStr := cnonce()
|
||||||
|
digestURI := "xmpp/" + client.JID.Domain
|
||||||
|
nonceCount := fmt.Sprintf("%08x", 1)
|
||||||
|
digest := saslDigestResponse(client.JID.Local, realm, password, nonce, cnonceStr, "AUTHENTICATE", digestURI, nonceCount)
|
||||||
|
message := "username=\"" + client.JID.Local + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", cnonce=\"" + cnonceStr +
|
||||||
|
"\", nc=" + nonceCount + ", qop=" + qop + ", digest-uri=\"" + digestURI + "\", response=" + digest + ", charset=" + charset
|
||||||
|
|
||||||
|
fmt.Fprintf(client.conn, "<response xmlns='%s'>%s</response>\n", messages.NSSASL, base64.StdEncoding.EncodeToString([]byte(message)))
|
||||||
|
|
||||||
|
err = client.ReadElement(&ch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = base64.StdEncoding.DecodeString(ch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Fprintf(client.conn, "<response xmlns='%s'/>\n", messages.NSSASL)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mechanism == "" {
|
||||||
|
return fmt.Errorf("PLAIN authentication is not an option: %v", f.Mechanisms.Mechanism)
|
||||||
|
}
|
||||||
|
element, err := client.Read()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if element.Name.Local != "success" {
|
||||||
|
return errors.New("auth failed: " + element.Name.Local)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := client.ReadElement(&f); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// bind to resource
|
||||||
|
var msg string
|
||||||
|
if client.JID.Resource == "" {
|
||||||
|
msg = fmt.Sprintf("<bind xmlns='%s'></bind>", messages.NSBind)
|
||||||
|
} else {
|
||||||
|
msg = fmt.Sprintf(
|
||||||
|
`<bind xmlns='%s'>
|
||||||
|
<resource>%s</resource>
|
||||||
|
</bind>`,
|
||||||
|
messages.NSBind, client.JID.Resource)
|
||||||
|
}
|
||||||
|
client.Out.Encode(&messages.IQClient{
|
||||||
|
Type: messages.IQTypeSet,
|
||||||
|
To: client.JID.Domain,
|
||||||
|
From: client.JID.Full(),
|
||||||
|
ID: utils.CreateCookieString(),
|
||||||
|
Body: []byte(msg),
|
||||||
|
})
|
||||||
|
|
||||||
|
var iq messages.IQClient
|
||||||
|
if err := client.ReadElement(&iq); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if &iq.Bind == nil {
|
||||||
|
return errors.New("<iq> result missing <bind>")
|
||||||
|
}
|
||||||
|
if iq.Bind.JID != nil {
|
||||||
|
client.JID.Local = iq.Bind.JID.Local
|
||||||
|
client.JID.Domain = iq.Bind.JID.Domain
|
||||||
|
client.JID.Resource = iq.Bind.JID.Resource
|
||||||
|
} else {
|
||||||
|
return errors.New(string(iq.Body))
|
||||||
|
}
|
||||||
|
// set status
|
||||||
|
client.Out.Encode(&messages.PresenceClient{Show: "online", Status: "yaja client"})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saslDigestResponse(username, realm, passwd, nonce, cnonceStr, authenticate, digestURI, nonceCountStr string) string {
|
||||||
|
h := func(text string) []byte {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write([]byte(text))
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
hex := func(bytes []byte) string {
|
||||||
|
return fmt.Sprintf("%x", bytes)
|
||||||
|
}
|
||||||
|
kd := func(secret, data string) []byte {
|
||||||
|
return h(secret + ":" + data)
|
||||||
|
}
|
||||||
|
|
||||||
|
a1 := string(h(username+":"+realm+":"+passwd)) + ":" + nonce + ":" + cnonceStr
|
||||||
|
a2 := authenticate + ":" + digestURI
|
||||||
|
response := hex(kd(hex(h(a1)), nonce+":"+nonceCountStr+":"+cnonceStr+":auth:"+hex(h(a2))))
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
func cnonce() string {
|
||||||
|
randSize := big.NewInt(0)
|
||||||
|
randSize.Lsh(big.NewInt(1), 64)
|
||||||
|
cn, err := rand.Int(rand.Reader, randSize)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%016x", cn)
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dev.sum7.eu/genofire/yaja/daemon"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DaemonCMD represents the serve command
|
||||||
|
var DaemonCMD = &cobra.Command{
|
||||||
|
Use: "daemon",
|
||||||
|
Short: "daemon of yaja",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DaemonCMD.AddCommand(daemon.ServerCMD)
|
||||||
|
DaemonCMD.AddCommand(daemon.TesterCMD)
|
||||||
|
RootCMD.AddCommand(DaemonCMD)
|
||||||
|
}
|
|
@ -11,7 +11,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// RootCmd represents the base command when called without any subcommands
|
// RootCmd represents the base command when called without any subcommands
|
||||||
var RootCmd = &cobra.Command{
|
var RootCMD = &cobra.Command{
|
||||||
Use: "yaja",
|
Use: "yaja",
|
||||||
Short: "Yet another jabber server",
|
Short: "Yet another jabber server",
|
||||||
Long: `A small standalone jabber server, for easy deployment`,
|
Long: `A small standalone jabber server, for easy deployment`,
|
||||||
|
@ -20,7 +20,7 @@ var RootCmd = &cobra.Command{
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
if err := RootCmd.Execute(); err != nil {
|
if err := RootCMD.Execute(); err != nil {
|
||||||
log.Panicln(err)
|
log.Panicln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
var configPath string
|
|
@ -1,4 +1,4 @@
|
||||||
package cmd
|
package daemon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/acme/autocert"
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
|
serverDaemon "dev.sum7.eu/genofire/yaja/daemon/server"
|
||||||
"dev.sum7.eu/genofire/yaja/database"
|
"dev.sum7.eu/genofire/yaja/database"
|
||||||
"dev.sum7.eu/genofire/yaja/model/config"
|
|
||||||
"dev.sum7.eu/genofire/yaja/server/extension"
|
"dev.sum7.eu/genofire/yaja/server/extension"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/file"
|
"dev.sum7.eu/genofire/golang-lib/file"
|
||||||
|
@ -22,10 +22,8 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configPath string
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
configData = &config.Config{}
|
serverConfig = &serverDaemon.Config{}
|
||||||
db = &database.State{}
|
db = &database.State{}
|
||||||
statesaveWorker *worker.Worker
|
statesaveWorker *worker.Worker
|
||||||
srv *server.Server
|
srv *server.Server
|
||||||
|
@ -34,35 +32,35 @@ var (
|
||||||
extensionsServer extension.Extensions
|
extensionsServer extension.Extensions
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverCmd represents the serve command
|
// ServerCMD represents the serve command
|
||||||
var serverCmd = &cobra.Command{
|
var ServerCMD = &cobra.Command{
|
||||||
Use: "server",
|
Use: "server",
|
||||||
Short: "Runs the yaja server",
|
Short: "runs xmpp server",
|
||||||
Example: "yaja serve -c /etc/yaja.conf",
|
Example: "yaja daemon server -c /etc/yaja.conf",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
if err := file.ReadTOML(configPath, configData); err != nil {
|
if err := file.ReadTOML(configPath, serverConfig); err != nil {
|
||||||
log.Fatal("unable to load config file:", err)
|
log.Fatal("unable to load config file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.SetLevel(configData.Logging.Level)
|
log.SetLevel(serverConfig.Logging.Level)
|
||||||
|
|
||||||
if err := file.ReadJSON(configData.StatePath, db); err != nil {
|
if err := file.ReadJSON(serverConfig.StatePath, db); err != nil {
|
||||||
log.Warn("unable to load state file:", err)
|
log.Warn("unable to load state file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statesaveWorker = worker.NewWorker(time.Minute, func() {
|
statesaveWorker = worker.NewWorker(time.Minute, func() {
|
||||||
file.SaveJSON(configData.StatePath, db)
|
file.SaveJSON(serverConfig.StatePath, db)
|
||||||
log.Info("save state to:", configData.StatePath)
|
log.Info("save state to:", serverConfig.StatePath)
|
||||||
})
|
})
|
||||||
|
|
||||||
m := autocert.Manager{
|
m := autocert.Manager{
|
||||||
Cache: autocert.DirCache(configData.TLSDir),
|
Cache: autocert.DirCache(serverConfig.TLSDir),
|
||||||
Prompt: autocert.AcceptTOS,
|
Prompt: autocert.AcceptTOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
// https server to handle acme (by letsencrypt)
|
// https server to handle acme (by letsencrypt)
|
||||||
for _, addr := range configData.Address.Webserver {
|
for _, addr := range serverConfig.Address.Webserver {
|
||||||
hs := &http.Server{
|
hs := &http.Server{
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
TLSConfig: &tls.Config{GetCertificate: m.GetCertificate},
|
||||||
|
@ -77,12 +75,12 @@ var serverCmd = &cobra.Command{
|
||||||
srv = &server.Server{
|
srv = &server.Server{
|
||||||
TLSManager: &m,
|
TLSManager: &m,
|
||||||
Database: db,
|
Database: db,
|
||||||
ClientAddr: configData.Address.Client,
|
ClientAddr: serverConfig.Address.Client,
|
||||||
ServerAddr: configData.Address.Server,
|
ServerAddr: serverConfig.Address.Server,
|
||||||
LoggingClient: configData.Logging.LevelClient,
|
LoggingClient: serverConfig.Logging.LevelClient,
|
||||||
LoggingServer: configData.Logging.LevelServer,
|
LoggingServer: serverConfig.Logging.LevelServer,
|
||||||
RegisterEnable: configData.Register.Enable,
|
RegisterEnable: serverConfig.Register.Enable,
|
||||||
RegisterDomains: configData.Register.Domains,
|
RegisterDomains: serverConfig.Register.Domains,
|
||||||
ExtensionsServer: extensionsServer,
|
ExtensionsServer: extensionsServer,
|
||||||
ExtensionsClient: extensionsClient,
|
ExtensionsClient: extensionsClient,
|
||||||
}
|
}
|
||||||
|
@ -117,12 +115,12 @@ func quit() {
|
||||||
srv.Close()
|
srv.Close()
|
||||||
statesaveWorker.Close()
|
statesaveWorker.Close()
|
||||||
|
|
||||||
file.SaveJSON(configData.StatePath, db)
|
file.SaveJSON(serverConfig.StatePath, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reload() {
|
func reload() {
|
||||||
log.Info("start reloading...")
|
log.Info("start reloading...")
|
||||||
var configNewData *config.Config
|
var configNewData *serverDaemon.Config
|
||||||
|
|
||||||
if err := file.ReadTOML(configPath, configNewData); err != nil {
|
if err := file.ReadTOML(configPath, configNewData); err != nil {
|
||||||
log.Warn("unable to load config file:", err)
|
log.Warn("unable to load config file:", err)
|
||||||
|
@ -136,7 +134,7 @@ func reload() {
|
||||||
|
|
||||||
//TODO fetch changing address (to set restart)
|
//TODO fetch changing address (to set restart)
|
||||||
|
|
||||||
if configNewData.StatePath != configData.StatePath {
|
if configNewData.StatePath != serverConfig.StatePath {
|
||||||
statesaveWorker.Close()
|
statesaveWorker.Close()
|
||||||
statesaveWorker := worker.NewWorker(time.Minute, func() {
|
statesaveWorker := worker.NewWorker(time.Minute, func() {
|
||||||
file.SaveJSON(configNewData.StatePath, db)
|
file.SaveJSON(configNewData.StatePath, db)
|
||||||
|
@ -147,10 +145,10 @@ func reload() {
|
||||||
|
|
||||||
restartServer := false
|
restartServer := false
|
||||||
|
|
||||||
if configNewData.TLSDir != configData.TLSDir {
|
if configNewData.TLSDir != serverConfig.TLSDir {
|
||||||
|
|
||||||
m := autocert.Manager{
|
m := autocert.Manager{
|
||||||
Cache: autocert.DirCache(configData.TLSDir),
|
Cache: autocert.DirCache(serverConfig.TLSDir),
|
||||||
Prompt: autocert.AcceptTOS,
|
Prompt: autocert.AcceptTOS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +174,7 @@ func reload() {
|
||||||
srv = newServer
|
srv = newServer
|
||||||
}
|
}
|
||||||
|
|
||||||
configData = configNewData
|
serverConfig = configNewData
|
||||||
log.Info("reloaded")
|
log.Info("reloaded")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +198,6 @@ func init() {
|
||||||
&extension.IQPing{},
|
&extension.IQPing{},
|
||||||
})
|
})
|
||||||
|
|
||||||
RootCmd.AddCommand(serverCmd)
|
ServerCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-server.conf", "Path to configuration file")
|
||||||
serverCmd.Flags().StringVarP(&configPath, "config", "c", "yaja.conf", "Path to configuration file")
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package config
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
|
@ -0,0 +1,117 @@
|
||||||
|
package daemon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/acme/autocert"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/file"
|
||||||
|
"dev.sum7.eu/genofire/golang-lib/worker"
|
||||||
|
"dev.sum7.eu/genofire/yaja/client"
|
||||||
|
"dev.sum7.eu/genofire/yaja/daemon/tester"
|
||||||
|
"dev.sum7.eu/genofire/yaja/messages"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var configTester = &tester.Config{}
|
||||||
|
|
||||||
|
// TesterCMD represents the serve command
|
||||||
|
var TesterCMD = &cobra.Command{
|
||||||
|
Use: "tester",
|
||||||
|
Short: "runs xmpp tester server",
|
||||||
|
Example: "yaja daemon tester -c /etc/yaja.conf",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
|
if err := file.ReadTOML(configPath, configTester); err != nil {
|
||||||
|
log.Fatal("unable to load config file:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.SetLevel(configTester.Logging)
|
||||||
|
|
||||||
|
if err := file.ReadJSON(configTester.StatePath, db); err != nil {
|
||||||
|
log.Warn("unable to load state file:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
statesaveWorker = worker.NewWorker(time.Minute, func() {
|
||||||
|
file.SaveJSON(configTester.StatePath, db)
|
||||||
|
log.Info("save state to:", configTester.StatePath)
|
||||||
|
})
|
||||||
|
|
||||||
|
// https server to handle acme (by letsencrypt)
|
||||||
|
hs := &http.Server{
|
||||||
|
Addr: configTester.Webserver,
|
||||||
|
}
|
||||||
|
if configTester.TLSDir != "" {
|
||||||
|
m := autocert.Manager{
|
||||||
|
Cache: autocert.DirCache(configTester.TLSDir),
|
||||||
|
Prompt: autocert.AcceptTOS,
|
||||||
|
}
|
||||||
|
hs.TLSConfig = &tls.Config{GetCertificate: m.GetCertificate}
|
||||||
|
go func(hs *http.Server) {
|
||||||
|
if err := hs.ListenAndServeTLS("", ""); err != http.ErrServerClosed {
|
||||||
|
log.Errorf("webserver with addr %s: %s", hs.Addr, err)
|
||||||
|
}
|
||||||
|
}(hs)
|
||||||
|
} else {
|
||||||
|
go func(hs *http.Server) {
|
||||||
|
if err := hs.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
log.Errorf("webserver with addr %s: %s", hs.Addr, err)
|
||||||
|
}
|
||||||
|
}(hs)
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClient, err := client.NewClient(configTester.Client.JID, configTester.Client.Password)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("unable to connect with main jabber client: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, admin := range configTester.Admins {
|
||||||
|
mainClient.Out.Encode(&messages.MessageClient{
|
||||||
|
From: mainClient.JID.Full(),
|
||||||
|
To: admin.Full(),
|
||||||
|
Type: "chat",
|
||||||
|
Body: "yaja tester starts",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
go statesaveWorker.Start()
|
||||||
|
|
||||||
|
log.Infoln("yaja tester started ")
|
||||||
|
|
||||||
|
// Wait for INT/TERM
|
||||||
|
sigs := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||||
|
for sig := range sigs {
|
||||||
|
log.Infoln("received", sig)
|
||||||
|
switch sig {
|
||||||
|
case syscall.SIGTERM:
|
||||||
|
log.Panic("terminated")
|
||||||
|
os.Exit(0)
|
||||||
|
case syscall.SIGQUIT:
|
||||||
|
quitTester()
|
||||||
|
case syscall.SIGHUP:
|
||||||
|
quitTester()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func quitTester() {
|
||||||
|
srv.Close()
|
||||||
|
statesaveWorker.Close()
|
||||||
|
|
||||||
|
file.SaveJSON(configTester.StatePath, db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TesterCMD.Flags().StringVarP(&configPath, "config", "c", "yaja-tester.conf", "Path to configuration file")
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package tester
|
||||||
|
|
||||||
|
import (
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
TLSDir string `toml:"tlsdir"`
|
||||||
|
StatePath string `toml:"state_path"`
|
||||||
|
Logging log.Level `toml:"logging"`
|
||||||
|
Webserver string `toml:"webserver"`
|
||||||
|
Admins []model.JID `toml:"admins"`
|
||||||
|
Client struct {
|
||||||
|
JID model.JID `toml:"jid"`
|
||||||
|
Password string `toml:"password"`
|
||||||
|
} `toml:"client"`
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package messages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"dev.sum7.eu/genofire/yaja/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RFC 3920 C.1 Streams name space
|
||||||
|
type StreamFeatures struct {
|
||||||
|
XMLName xml.Name `xml:"http://etherx.jabber.org/streams features"`
|
||||||
|
StartTLS *TLSStartTLS
|
||||||
|
Mechanisms SASLMechanisms
|
||||||
|
Bind Bind
|
||||||
|
Session bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamError struct {
|
||||||
|
XMLName xml.Name `xml:"http://etherx.jabber.org/streams error"`
|
||||||
|
Any xml.Name
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 3920 C.3 TLS name space
|
||||||
|
type TLSStartTLS struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls starttls"`
|
||||||
|
Required *string `xml:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSFailure struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls failure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSProceed struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 3920 C.5 Resource binding name space
|
||||||
|
type Bind struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
|
||||||
|
Resource string `xml:"resource"`
|
||||||
|
JID *model.JID `xml:"jid"`
|
||||||
|
}
|
|
@ -2,8 +2,8 @@ package messages
|
||||||
|
|
||||||
import "encoding/xml"
|
import "encoding/xml"
|
||||||
|
|
||||||
// Error element
|
// ErrorClient element
|
||||||
type Error struct {
|
type ErrorClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client error"`
|
XMLName xml.Name `xml:"jabber:client error"`
|
||||||
Code string `xml:"code,attr"`
|
Code string `xml:"code,attr"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
|
|
|
@ -12,14 +12,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// IQ element - info/query
|
// IQ element - info/query
|
||||||
type IQ struct {
|
type IQClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client iq"`
|
XMLName xml.Name `xml:"jabber:client iq"`
|
||||||
From string `xml:"from,attr"`
|
From string `xml:"from,attr"`
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
To string `xml:"to,attr"`
|
To string `xml:"to,attr"`
|
||||||
Type IQType `xml:"type,attr"`
|
Type IQType `xml:"type,attr"`
|
||||||
Error *Error `xml:"error"`
|
Error *ErrorClient `xml:"error"`
|
||||||
//Bind bindBind `xml:"bind"`
|
Bind Bind `xml:"bind"`
|
||||||
Body []byte `xml:",innerxml"`
|
Body []byte `xml:",innerxml"`
|
||||||
// RosterRequest - better detection of iq's
|
// RosterRequest - better detection of iq's
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ const (
|
||||||
PresenceTypeError PresenceType = "error"
|
PresenceTypeError PresenceType = "error"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Presence element
|
// PresenceClient element
|
||||||
type Presence struct {
|
type PresenceClient struct {
|
||||||
XMLName xml.Name `xml:"jabber:client presence"`
|
XMLName xml.Name `xml:"jabber:client presence"`
|
||||||
From string `xml:"from,attr,omitempty"`
|
From string `xml:"from,attr,omitempty"`
|
||||||
ID string `xml:"id,attr,omitempty"`
|
ID string `xml:"id,attr,omitempty"`
|
||||||
|
@ -27,6 +27,19 @@ type Presence struct {
|
||||||
Status string `xml:"status,omitempty"` // sb []clientText
|
Status string `xml:"status,omitempty"` // sb []clientText
|
||||||
Priority string `xml:"priority,omitempty"`
|
Priority string `xml:"priority,omitempty"`
|
||||||
// Caps *ClientCaps `xml:"c"`
|
// Caps *ClientCaps `xml:"c"`
|
||||||
Error *Error `xml:"error"`
|
Error *ErrorClient `xml:"error"`
|
||||||
// Delay Delay `xml:"delay"`
|
// Delay Delay `xml:"delay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageClient element
|
||||||
|
type MessageClient struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:client message"`
|
||||||
|
From string `xml:"from,attr,omitempty"`
|
||||||
|
ID string `xml:"id,attr,omitempty"`
|
||||||
|
To string `xml:"to,attr,omitempty"`
|
||||||
|
Type string `xml:"type,attr,omitempty"`
|
||||||
|
Lang string `xml:"lang,attr,omitempty"`
|
||||||
|
Subject string `xml:"subject"`
|
||||||
|
Body string `xml:"body"`
|
||||||
|
Thread string `xml:"thread"`
|
||||||
|
}
|
||||||
|
|
|
@ -8,3 +8,9 @@ type SASLAuth struct {
|
||||||
Mechanism string `xml:"mechanism,attr"`
|
Mechanism string `xml:"mechanism,attr"`
|
||||||
Body string `xml:",chardata"`
|
Body string `xml:",chardata"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC 3920 C.4 SASL name space
|
||||||
|
type SASLMechanisms struct {
|
||||||
|
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-sasl mechanisms"`
|
||||||
|
Mechanism []string `xml:"mechanism"`
|
||||||
|
}
|
||||||
|
|
|
@ -52,13 +52,13 @@ func (jid *JID) Full() string {
|
||||||
return jid.Bare()
|
return jid.Bare()
|
||||||
}
|
}
|
||||||
|
|
||||||
//MarshalTOML to bytearray
|
//MarshalText to bytearray
|
||||||
func (jid JID) MarshalTOML() ([]byte, error) {
|
func (jid JID) MarshalText() ([]byte, error) {
|
||||||
return []byte(jid.Full()), nil
|
return []byte(jid.Full()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalTOML from bytearray
|
// UnmarshalText from bytearray
|
||||||
func (jid *JID) UnmarshalTOML(data []byte) (err error) {
|
func (jid *JID) UnmarshalText(data []byte) (err error) {
|
||||||
newJID := NewJID(string(data))
|
newJID := NewJID(string(data))
|
||||||
if newJID == nil {
|
if newJID == nil {
|
||||||
return errors.New("not a valid jid")
|
return errors.New("not a valid jid")
|
||||||
|
|
|
@ -137,14 +137,14 @@ func TestMarshal(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
jid := &JID{}
|
jid := &JID{}
|
||||||
err := jid.UnmarshalTOML([]byte("juliet@example.com/foo"))
|
err := jid.UnmarshalText([]byte("juliet@example.com/foo"))
|
||||||
|
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(jid.Local, "juliet")
|
assert.Equal(jid.Local, "juliet")
|
||||||
assert.Equal(jid.Domain, "example.com")
|
assert.Equal(jid.Domain, "example.com")
|
||||||
assert.Equal(jid.Resource, "foo")
|
assert.Equal(jid.Resource, "foo")
|
||||||
|
|
||||||
err = jid.UnmarshalTOML([]byte("juliet@example.com/ foo"))
|
err = jid.UnmarshalText([]byte("juliet@example.com/ foo"))
|
||||||
|
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ func TestMarshal(t *testing.T) {
|
||||||
Domain: "example.com",
|
Domain: "example.com",
|
||||||
Resource: "bar",
|
Resource: "bar",
|
||||||
}
|
}
|
||||||
jidString, err := jid.MarshalTOML()
|
jidString, err := jid.MarshalText()
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal("romeo@example.com/bar", string(jidString))
|
assert.Equal("romeo@example.com/bar", string(jidString))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func XMLEscape(s string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
xml.Escape(&b, []byte(s))
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
|
@ -11,8 +11,8 @@ type IQExtensions []IQExtension
|
||||||
|
|
||||||
type IQExtension interface {
|
type IQExtension interface {
|
||||||
Extension
|
Extension
|
||||||
Get(*messages.IQ, *utils.Client) bool
|
Get(*messages.IQClient, *utils.Client) bool
|
||||||
Set(*messages.IQ, *utils.Client) bool
|
Set(*messages.IQClient, *utils.Client) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iex IQExtensions) Spaces() (result []string) {
|
func (iex IQExtensions) Spaces() (result []string) {
|
||||||
|
@ -27,7 +27,7 @@ func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client)
|
||||||
log := client.Log.WithField("extension", "iq")
|
log := client.Log.WithField("extension", "iq")
|
||||||
|
|
||||||
// iq encode
|
// iq encode
|
||||||
var msg messages.IQ
|
var msg messages.IQClient
|
||||||
if err := client.In.DecodeElement(&msg, element); err != nil {
|
if err := client.In.DecodeElement(&msg, element); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ type IQDisco struct {
|
||||||
|
|
||||||
func (ex *IQDisco) Spaces() []string { return []string{"http://jabber.org/protocol/disco#items"} }
|
func (ex *IQDisco) Spaces() []string { return []string{"http://jabber.org/protocol/disco#items"} }
|
||||||
|
|
||||||
func (ex *IQDisco) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQDisco) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "disco-item").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "disco-item").WithField("id", msg.ID)
|
||||||
|
|
||||||
// query encode
|
// query encode
|
||||||
|
@ -57,7 +57,7 @@ func (ex *IQDisco) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -14,7 +14,7 @@ type IQExtensionDiscovery struct {
|
||||||
|
|
||||||
func (ex *IQExtensionDiscovery) Spaces() []string { return []string{} }
|
func (ex *IQExtensionDiscovery) Spaces() []string { return []string{} }
|
||||||
|
|
||||||
func (ex *IQExtensionDiscovery) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQExtensionDiscovery) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
||||||
|
|
||||||
// query encode
|
// query encode
|
||||||
|
@ -57,7 +57,7 @@ func (ex *IQExtensionDiscovery) Get(msg *messages.IQ, client *utils.Client) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// replay
|
// replay
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -15,7 +15,7 @@ type IQLast struct {
|
||||||
|
|
||||||
func (ex *IQLast) Spaces() []string { return []string{"jabber:iq:last"} }
|
func (ex *IQLast) Spaces() []string { return []string{"jabber:iq:last"} }
|
||||||
|
|
||||||
func (ex *IQLast) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQLast) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "last").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "last").WithField("id", msg.ID)
|
||||||
|
|
||||||
// query encode
|
// query encode
|
||||||
|
@ -45,7 +45,7 @@ func (ex *IQLast) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -13,7 +13,7 @@ type IQPing struct {
|
||||||
|
|
||||||
func (ex *IQPing) Spaces() []string { return []string{"urn:xmpp:ping"} }
|
func (ex *IQPing) Spaces() []string { return []string{"urn:xmpp:ping"} }
|
||||||
|
|
||||||
func (ex *IQPing) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQPing) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
|
||||||
|
|
||||||
// ping encode
|
// ping encode
|
||||||
|
@ -26,7 +26,7 @@ func (ex *IQPing) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -17,12 +17,12 @@ type iqPrivateQuery struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type iqPrivateExtension interface {
|
type iqPrivateExtension interface {
|
||||||
Handle(*messages.IQ, *iqPrivateQuery, *utils.Client) bool
|
Handle(*messages.IQClient, *iqPrivateQuery, *utils.Client) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ex *IQPrivate) Spaces() []string { return []string{"jabber:iq:private"} }
|
func (ex *IQPrivate) Spaces() []string { return []string{"jabber:iq:private"} }
|
||||||
|
|
||||||
func (ex *IQPrivate) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQPrivate) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
// query encode
|
// query encode
|
||||||
|
|
|
@ -11,7 +11,7 @@ type IQPrivateBookmark struct {
|
||||||
iqPrivateExtension
|
iqPrivateExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ex *IQPrivateBookmark) Handle(msg *messages.IQ, q *iqPrivateQuery, client *utils.Client) bool {
|
func (ex *IQPrivateBookmark) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
// storage encode
|
// storage encode
|
||||||
|
@ -35,7 +35,7 @@ func (ex *IQPrivateBookmark) Handle(msg *messages.IQ, q *iqPrivateQuery, client
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -11,7 +11,7 @@ type IQPrivateMetacontact struct {
|
||||||
iqPrivateExtension
|
iqPrivateExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ex *IQPrivateMetacontact) Handle(msg *messages.IQ, q *iqPrivateQuery, client *utils.Client) bool {
|
func (ex *IQPrivateMetacontact) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID)
|
||||||
|
|
||||||
// storage encode
|
// storage encode
|
||||||
|
@ -36,7 +36,7 @@ func (ex *IQPrivateMetacontact) Handle(msg *messages.IQ, q *iqPrivateQuery, clie
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -11,7 +11,7 @@ type IQPrivateRoster struct {
|
||||||
iqPrivateExtension
|
iqPrivateExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ex *IQPrivateRoster) Handle(msg *messages.IQ, q *iqPrivateQuery, client *utils.Client) bool {
|
func (ex *IQPrivateRoster) Handle(msg *messages.IQClient, q *iqPrivateQuery, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
// roster encode
|
// roster encode
|
||||||
|
@ -40,7 +40,7 @@ func (ex *IQPrivateRoster) Handle(msg *messages.IQ, q *iqPrivateQuery, client *u
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -15,7 +15,7 @@ type IQRoster struct {
|
||||||
|
|
||||||
func (ex *IQRoster) Spaces() []string { return []string{"jabber:iq:roster"} }
|
func (ex *IQRoster) Spaces() []string { return []string{"jabber:iq:roster"} }
|
||||||
|
|
||||||
func (ex *IQRoster) Get(msg *messages.IQ, client *utils.Client) bool {
|
func (ex *IQRoster) Get(msg *messages.IQClient, client *utils.Client) bool {
|
||||||
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
||||||
|
|
||||||
// query encode
|
// query encode
|
||||||
|
@ -59,7 +59,7 @@ func (ex *IQRoster) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reply
|
// reply
|
||||||
client.Messages <- &messages.IQ{
|
client.Messages <- &messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: client.JID.String(),
|
To: client.JID.String(),
|
||||||
From: client.JID.Domain,
|
From: client.JID.Domain,
|
||||||
|
|
|
@ -19,11 +19,11 @@ func (p *Presence) Process(element *xml.StartElement, client *utils.Client) bool
|
||||||
log := client.Log.WithField("extension", "presence")
|
log := client.Log.WithField("extension", "presence")
|
||||||
|
|
||||||
// iq encode
|
// iq encode
|
||||||
var msg messages.Presence
|
var msg messages.PresenceClient
|
||||||
if err := client.In.DecodeElement(&msg, element); err != nil {
|
if err := client.In.DecodeElement(&msg, element); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
client.Messages <- &messages.Presence{
|
client.Messages <- &messages.PresenceClient{
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
}
|
}
|
||||||
log.Debug("send")
|
log.Debug("send")
|
||||||
|
|
|
@ -199,7 +199,7 @@ func (state *AuthedStream) Process() state.State {
|
||||||
state.Client.Log.Warn("unable to read: ", err)
|
state.Client.Log.Warn("unable to read: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var msg messages.IQ
|
var msg messages.IQClient
|
||||||
if err = state.Client.In.DecodeElement(&msg, element); err != nil {
|
if err = state.Client.In.DecodeElement(&msg, element); err != nil {
|
||||||
state.Client.Log.Warn("is no iq: ", err)
|
state.Client.Log.Warn("is no iq: ", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -212,11 +212,8 @@ func (state *AuthedStream) Process() state.State {
|
||||||
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
|
state.Client.Log.Warn("iq with error: ", msg.Error.Code)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
type query struct {
|
|
||||||
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-bind bind"`
|
var q messages.Bind
|
||||||
Resource string `xml:"resource"`
|
|
||||||
}
|
|
||||||
q := &query{}
|
|
||||||
err = xml.Unmarshal(msg.Body, q)
|
err = xml.Unmarshal(msg.Body, q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Client.Log.Warn("is no iq bind: ", err)
|
state.Client.Log.Warn("is no iq bind: ", err)
|
||||||
|
@ -228,7 +225,7 @@ func (state *AuthedStream) Process() state.State {
|
||||||
state.Client.JID.Resource = q.Resource
|
state.Client.JID.Resource = q.Resource
|
||||||
}
|
}
|
||||||
state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full())
|
state.Client.Log = state.Client.Log.WithField("jid", state.Client.JID.Full())
|
||||||
state.Client.Out.Encode(&messages.IQ{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID.String(),
|
||||||
From: state.Client.JID.Domain,
|
From: state.Client.JID.Domain,
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (state *RegisterFormRequest) Process() state.State {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg messages.IQ
|
var msg messages.IQClient
|
||||||
if err := state.Client.In.DecodeElement(&msg, state.element); err != nil {
|
if err := state.Client.In.DecodeElement(&msg, state.element); err != nil {
|
||||||
state.Client.Log.Warn("is no iq: ", err)
|
state.Client.Log.Warn("is no iq: ", err)
|
||||||
return state
|
return state
|
||||||
|
@ -52,7 +52,7 @@ func (state *RegisterFormRequest) Process() state.State {
|
||||||
state.Client.Log.Warn("is no iq register: ", err)
|
state.Client.Log.Warn("is no iq register: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
state.Client.Out.Encode(&messages.IQ{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID.String(),
|
||||||
From: state.Client.JID.Domain,
|
From: state.Client.JID.Domain,
|
||||||
|
@ -90,7 +90,7 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
state.Client.Log.Warn("unable to read: ", err)
|
state.Client.Log.Warn("unable to read: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var msg messages.IQ
|
var msg messages.IQClient
|
||||||
if err = state.Client.In.DecodeElement(&msg, element); err != nil {
|
if err = state.Client.In.DecodeElement(&msg, element); err != nil {
|
||||||
state.Client.Log.Warn("is no iq: ", err)
|
state.Client.Log.Warn("is no iq: ", err)
|
||||||
return state
|
return state
|
||||||
|
@ -120,7 +120,7 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
account := model.NewAccount(state.Client.JID, q.Password)
|
account := model.NewAccount(state.Client.JID, q.Password)
|
||||||
err = state.database.AddAccount(account)
|
err = state.database.AddAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Client.Out.Encode(&messages.IQ{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID.String(),
|
||||||
From: state.Client.JID.Domain,
|
From: state.Client.JID.Domain,
|
||||||
|
@ -129,7 +129,7 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
<username>%s</username>
|
<username>%s</username>
|
||||||
<password>%s</password>
|
<password>%s</password>
|
||||||
</query>`, messages.NSIQRegister, q.Username, q.Password)),
|
</query>`, messages.NSIQRegister, q.Username, q.Password)),
|
||||||
Error: &messages.Error{
|
Error: &messages.ErrorClient{
|
||||||
Code: "409",
|
Code: "409",
|
||||||
Type: "cancel",
|
Type: "cancel",
|
||||||
Any: xml.Name{
|
Any: xml.Name{
|
||||||
|
@ -141,7 +141,7 @@ func (state *RegisterRequest) Process() state.State {
|
||||||
state.Client.Log.Warn("database error: ", err)
|
state.Client.Log.Warn("database error: ", err)
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
state.Client.Out.Encode(&messages.IQ{
|
state.Client.Out.Encode(&messages.IQClient{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
To: state.Client.JID.String(),
|
To: state.Client.JID.String(),
|
||||||
From: state.Client.JID.Domain,
|
From: state.Client.JID.Domain,
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
tlsdir = "tmp/ssl"
|
||||||
|
state_path = "tmp/yaja-tester.json"
|
||||||
|
logging = 5
|
||||||
|
|
||||||
|
webserver = ":https"
|
||||||
|
|
||||||
|
admins = ["a.admin@chat.sum7.eu"]
|
||||||
|
|
||||||
|
[client]
|
||||||
|
jid = "bot@chat.sum7.eu"
|
||||||
|
password = "test"
|
Reference in New Issue