write some dirty extension (to be compatible with gajim
This commit is contained in:
parent
1aceea7133
commit
5d8c92a76a
|
@ -30,7 +30,7 @@ var (
|
||||||
statesaveWorker *worker.Worker
|
statesaveWorker *worker.Worker
|
||||||
srv *server.Server
|
srv *server.Server
|
||||||
certs *tls.Config
|
certs *tls.Config
|
||||||
extensions []extension.Extension
|
extensions extension.Extensions
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverCmd represents the serve command
|
// serverCmd represents the serve command
|
||||||
|
@ -178,8 +178,19 @@ func reload() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
extensions = append(extensions,
|
||||||
|
&extension.Message{},
|
||||||
|
extension.IQExtensions{
|
||||||
|
&extension.Private{},
|
||||||
|
&extension.Ping{},
|
||||||
|
&extension.Disco{Database: db},
|
||||||
|
&extension.Roster{Database: db},
|
||||||
|
&extension.ExtensionDiscovery{GetSpaces: func() []string {
|
||||||
|
return extensions.Spaces()
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
|
||||||
RootCmd.AddCommand(serverCmd)
|
RootCmd.AddCommand(serverCmd)
|
||||||
serverCmd.Flags().StringVarP(&configPath, "config", "c", "yaja.conf", "Path to configuration file")
|
serverCmd.Flags().StringVarP(&configPath, "config", "c", "yaja.conf", "Path to configuration file")
|
||||||
|
|
||||||
extensions = append(extensions, &extension.Message{}, &extension.Roster{Database: db})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,3 +66,18 @@ func (s *State) Authenticate(jid *model.JID, password string) (bool, error) {
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) GetAccount(jid *model.JID) *model.Account {
|
||||||
|
logger := log.WithField("database", "get")
|
||||||
|
|
||||||
|
if domain, ok := s.Domains[jid.Domain]; ok {
|
||||||
|
if acc, ok := domain.Accounts[jid.Local]; ok {
|
||||||
|
return acc
|
||||||
|
} else {
|
||||||
|
logger.Debug("account not found")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.Debug("domain not found")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -16,4 +16,6 @@ const (
|
||||||
NSIQRegister = "jabber:iq:register"
|
NSIQRegister = "jabber:iq:register"
|
||||||
|
|
||||||
NSFeaturesIQRegister = "http://jabber.org/features/iq-register"
|
NSFeaturesIQRegister = "http://jabber.org/features/iq-register"
|
||||||
|
|
||||||
|
NSDisco = "http://jabber.org/protocol/disco#info"
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,6 +33,7 @@ type Account struct {
|
||||||
Domain *Domain `json:"-"`
|
Domain *Domain `json:"-"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Roster map[string]*Buddy `json:"roster"`
|
Roster map[string]*Buddy `json:"roster"`
|
||||||
|
Bookmarks map[string]*Bookmark `json:"bookmarks"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAccount(jid *JID, password string) *Account {
|
func NewAccount(jid *JID, password string) *Account {
|
||||||
|
|
|
@ -15,3 +15,6 @@ type Buddy struct {
|
||||||
Subscription int `json:"subscription"`
|
Subscription int `json:"subscription"`
|
||||||
Ask int `json:"ask"`
|
Ask int `json:"ask"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Bookmark struct {
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IQExtensions []IQExtension
|
||||||
|
|
||||||
|
type IQExtension interface {
|
||||||
|
Extension
|
||||||
|
Get(*messages.IQ, *utils.Client) bool
|
||||||
|
Set(*messages.IQ, *utils.Client) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iex IQExtensions) Spaces() (result []string) {
|
||||||
|
for _, extension := range iex {
|
||||||
|
spaces := extension.Spaces()
|
||||||
|
result = append(result, spaces...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iex IQExtensions) Process(element *xml.StartElement, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "iq")
|
||||||
|
|
||||||
|
// iq encode
|
||||||
|
var msg messages.IQ
|
||||||
|
if err := client.In.DecodeElement(&msg, element); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// run every extensions
|
||||||
|
count := 0
|
||||||
|
for _, extension := range iex {
|
||||||
|
switch msg.Type {
|
||||||
|
case messages.IQTypeGet:
|
||||||
|
if extension.Get(&msg, client) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
case messages.IQTypeSet:
|
||||||
|
if extension.Set(&msg, client) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not extensions found
|
||||||
|
if count != 1 {
|
||||||
|
log.Debug(msg.XMLName.Space, " - ", msg.Type, ": ", string(msg.Body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/database"
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Disco struct {
|
||||||
|
IQExtension
|
||||||
|
Database *database.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Disco) Spaces() []string { return []string{} }
|
||||||
|
|
||||||
|
func (r *Disco) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "disco-item").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// query encode
|
||||||
|
type query struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#items query"`
|
||||||
|
Body []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
q := &query{}
|
||||||
|
err := xml.Unmarshal(msg.Body, q)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// answer query
|
||||||
|
q.Body = []byte{}
|
||||||
|
|
||||||
|
// build answer body
|
||||||
|
type item struct {
|
||||||
|
XMLName xml.Name `xml:"item"`
|
||||||
|
JID string `xml:"jid,attr"`
|
||||||
|
}
|
||||||
|
if acc := r.Database.GetAccount(client.JID); acc != nil {
|
||||||
|
for jid, _ := range acc.Bookmarks {
|
||||||
|
itemByte, err := xml.Marshal(&item{
|
||||||
|
JID: jid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q.Body = append(q.Body, itemByte...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode query
|
||||||
|
queryByte, err := xml.Marshal(q)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
Body: queryByte,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtensionDiscovery struct {
|
||||||
|
IQExtension
|
||||||
|
GetSpaces func() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex *ExtensionDiscovery) Spaces() []string { return []string{} }
|
||||||
|
|
||||||
|
func (ex *ExtensionDiscovery) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// query encode
|
||||||
|
type query struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
|
||||||
|
Body []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
q := &query{}
|
||||||
|
err := xml.Unmarshal(msg.Body, q)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// answer query
|
||||||
|
q.Body = []byte{}
|
||||||
|
|
||||||
|
// build answer body
|
||||||
|
type feature struct {
|
||||||
|
XMLName xml.Name `xml:"feature"`
|
||||||
|
Var string `xml:"var,attr"`
|
||||||
|
}
|
||||||
|
for _, namespace := range ex.GetSpaces() {
|
||||||
|
if namespace == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
itemByte, err := xml.Marshal(&feature{
|
||||||
|
Var: namespace,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q.Body = append(q.Body, itemByte...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode query
|
||||||
|
queryByte, err := xml.Marshal(q)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// replay
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
Body: queryByte,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ping struct {
|
||||||
|
IQExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Ping) Spaces() []string { return []string{"urn:xmpp:ping"} }
|
||||||
|
|
||||||
|
func (p *Ping) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "ping").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// ping encode
|
||||||
|
type ping struct {
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:ping ping"`
|
||||||
|
}
|
||||||
|
pq := &ping{}
|
||||||
|
err := xml.Unmarshal(msg.Body, pq)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Private struct {
|
||||||
|
IQExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
type privateQuery struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:iq:private query"`
|
||||||
|
Body []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ioPrivateExtension interface {
|
||||||
|
Handle(*messages.IQ, *privateQuery, *utils.Client) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Private) Spaces() []string { return []string{"jabber:iq:private"} }
|
||||||
|
|
||||||
|
func (p *Private) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// query encode
|
||||||
|
q := &privateQuery{}
|
||||||
|
err := xml.Unmarshal(msg.Body, q)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// run every extensions
|
||||||
|
count := 0
|
||||||
|
for _, e := range []ioPrivateExtension{
|
||||||
|
&PrivateMetacontact{},
|
||||||
|
&PrivateRoster{},
|
||||||
|
} {
|
||||||
|
if e.Handle(msg, q, client) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not extensions found
|
||||||
|
if count != 1 {
|
||||||
|
log.Debug(msg.XMLName.Space, " - ", msg.Type, ": ", string(q.Body))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrivateMetacontact struct {
|
||||||
|
ioPrivateExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrivateMetacontact) Handle(msg *messages.IQ, q *privateQuery, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "private-metacontact").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// storage encode
|
||||||
|
type storage struct {
|
||||||
|
XMLName xml.Name `xml:"storage:metacontacts storage"`
|
||||||
|
}
|
||||||
|
s := &storage{}
|
||||||
|
err := xml.Unmarshal(q.Body, s)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
TODO full implement XEP-0209
|
||||||
|
https://xmpp.org/extensions/xep-0209.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
queryByte, err := xml.Marshal(&privateQuery{
|
||||||
|
Body: q.Body,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
Body: queryByte,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrivateRoster struct {
|
||||||
|
ioPrivateExtension
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrivateRoster) Handle(msg *messages.IQ, q *privateQuery, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "private").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// roster encode
|
||||||
|
type roster struct {
|
||||||
|
XMLName xml.Name `xml:"roster:delimiter roster"`
|
||||||
|
Body []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
r := &roster{}
|
||||||
|
err := xml.Unmarshal(q.Body, r)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
rosterByte, err := xml.Marshal(&roster{
|
||||||
|
Body: []byte("::"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
queryByte, err := xml.Marshal(&privateQuery{
|
||||||
|
Body: rosterByte,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
Body: queryByte,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package extension
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
|
||||||
|
"github.com/genofire/yaja/database"
|
||||||
|
"github.com/genofire/yaja/messages"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Roster struct {
|
||||||
|
IQExtension
|
||||||
|
Database *database.State
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Roster) Spaces() []string { return []string{"jabber:iq:roster"} }
|
||||||
|
|
||||||
|
func (r *Roster) Get(msg *messages.IQ, client *utils.Client) bool {
|
||||||
|
log := client.Log.WithField("extension", "roster").WithField("id", msg.ID)
|
||||||
|
|
||||||
|
// query encode
|
||||||
|
type query struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:iq:roster query"`
|
||||||
|
Version string `xml:"ver,attr"`
|
||||||
|
Body []byte `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
q := &query{}
|
||||||
|
err := xml.Unmarshal(msg.Body, q)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// answer query
|
||||||
|
q.Body = []byte{}
|
||||||
|
q.Version = "1"
|
||||||
|
|
||||||
|
// build answer body
|
||||||
|
type item struct {
|
||||||
|
XMLName xml.Name `xml:"item"`
|
||||||
|
JID string `xml:"jid,attr"`
|
||||||
|
}
|
||||||
|
if acc := r.Database.GetAccount(client.JID); acc != nil {
|
||||||
|
for jid, _ := range acc.Roster {
|
||||||
|
itemByte, err := xml.Marshal(&item{
|
||||||
|
JID: jid,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
q.Body = append(q.Body, itemByte...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// decode query
|
||||||
|
queryByte, err := xml.Marshal(q)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// reply
|
||||||
|
client.Out.Encode(&messages.IQ{
|
||||||
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
|
ID: msg.ID,
|
||||||
|
Body: queryByte,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.Debug("send")
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -6,6 +6,33 @@ import (
|
||||||
"github.com/genofire/yaja/server/utils"
|
"github.com/genofire/yaja/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Extensions []Extension
|
||||||
|
|
||||||
type Extension interface {
|
type Extension interface {
|
||||||
Process(*xml.StartElement, *utils.Client) bool
|
Process(*xml.StartElement, *utils.Client) bool
|
||||||
|
Spaces() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex Extensions) Spaces() (result []string) {
|
||||||
|
for _, extension := range ex {
|
||||||
|
result = append(result, extension.Spaces()...)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ex Extensions) Process(element *xml.StartElement, client *utils.Client) {
|
||||||
|
log := client.Log.WithField("extension", "all")
|
||||||
|
|
||||||
|
// run every extensions
|
||||||
|
count := 0
|
||||||
|
for _, extension := range ex {
|
||||||
|
if extension.Process(element, client) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not extensions found
|
||||||
|
if count != 1 {
|
||||||
|
log.Debug(element)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ type Message struct {
|
||||||
Extension
|
Extension
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Message) Spaces() []string { return []string{} }
|
||||||
|
|
||||||
func (m *Message) Process(element *xml.StartElement, client *utils.Client) bool {
|
func (m *Message) Process(element *xml.StartElement, client *utils.Client) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
package extension
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/xml"
|
|
||||||
|
|
||||||
"github.com/genofire/yaja/database"
|
|
||||||
"github.com/genofire/yaja/messages"
|
|
||||||
"github.com/genofire/yaja/server/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Roster struct {
|
|
||||||
Extension
|
|
||||||
Database *database.State
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Roster) Process(element *xml.StartElement, client *utils.Client) bool {
|
|
||||||
var msg messages.IQ
|
|
||||||
if err := client.In.DecodeElement(&msg, element); err != nil {
|
|
||||||
client.Log.Warn("is no iq: ", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if msg.Type != messages.IQTypeGet {
|
|
||||||
client.Log.Warn("is no get iq")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ type Server struct {
|
||||||
LoggingClient log.Level
|
LoggingClient log.Level
|
||||||
RegisterEnable bool
|
RegisterEnable bool
|
||||||
RegisterDomains []string
|
RegisterDomains []string
|
||||||
Extensions []extension.Extension
|
Extensions extension.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) Start() {
|
func (srv *Server) Start() {
|
||||||
|
@ -74,7 +74,7 @@ func (srv *Server) handleServer(conn net.Conn) {
|
||||||
func (srv *Server) handleClient(conn net.Conn) {
|
func (srv *Server) handleClient(conn net.Conn) {
|
||||||
log.Info("new client connection:", conn.RemoteAddr())
|
log.Info("new client connection:", conn.RemoteAddr())
|
||||||
client := utils.NewClient(conn, srv.LoggingClient)
|
client := utils.NewClient(conn, srv.LoggingClient)
|
||||||
state := toclient.ConnectionStartup(srv.Database, srv.TLSConfig, srv.TLSManager, srv.DomainRegisterAllowed)
|
state := toclient.ConnectionStartup(srv.Database, srv.TLSConfig, srv.TLSManager, srv.DomainRegisterAllowed, srv.Extensions)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
state, client = state.Process(client)
|
state, client = state.Process(client)
|
||||||
|
|
|
@ -16,8 +16,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionStartup return steps through TCP TLS state
|
// ConnectionStartup return steps through TCP TLS state
|
||||||
func ConnectionStartup(db *database.State, tlsconfig *tls.Config, tlsmgmt *autocert.Manager, registerAllowed utils.DomainRegisterAllowed) state.State {
|
func ConnectionStartup(db *database.State, tlsconfig *tls.Config, tlsmgmt *autocert.Manager, registerAllowed utils.DomainRegisterAllowed, extensions []extension.Extension) state.State {
|
||||||
receiving := &ReceivingClient{}
|
receiving := &ReceivingClient{Extensions: extensions}
|
||||||
sending := &SendingClient{Next: receiving}
|
sending := &SendingClient{Next: receiving}
|
||||||
authedstream := &AuthedStream{Next: sending}
|
authedstream := &AuthedStream{Next: sending}
|
||||||
authedstart := &AuthedStart{Next: authedstream}
|
authedstart := &AuthedStart{Next: authedstream}
|
||||||
|
@ -213,6 +213,8 @@ func (state *AuthedStream) Process(client *utils.Client) (state.State, *utils.Cl
|
||||||
client.Log = client.Log.WithField("jid", client.JID.Full())
|
client.Log = client.Log.WithField("jid", client.JID.Full())
|
||||||
client.Out.Encode(&messages.IQ{
|
client.Out.Encode(&messages.IQ{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(
|
Body: []byte(fmt.Sprintf(
|
||||||
`<bind xmlns='%s'>
|
`<bind xmlns='%s'>
|
||||||
|
@ -223,50 +225,3 @@ func (state *AuthedStream) Process(client *utils.Client) (state.State, *utils.Cl
|
||||||
|
|
||||||
return state.Next, client
|
return state.Next, client
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendingClient state
|
|
||||||
type SendingClient struct {
|
|
||||||
Next state.State
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process messages
|
|
||||||
func (state *SendingClient) Process(client *utils.Client) (state.State, *utils.Client) {
|
|
||||||
client.Log = client.Log.WithField("state", "normal")
|
|
||||||
client.Log.Debug("sending")
|
|
||||||
// sending
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case msg := <-client.Messages:
|
|
||||||
err := client.Out.Encode(msg)
|
|
||||||
client.Log.Info(err)
|
|
||||||
case <-client.OnClose():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
client.Log.Debug("receiving")
|
|
||||||
return state.Next, client
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReceivingClient state
|
|
||||||
type ReceivingClient struct {
|
|
||||||
Extensions []extension.Extension
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process messages
|
|
||||||
func (state *ReceivingClient) Process(client *utils.Client) (state.State, *utils.Client) {
|
|
||||||
element, err := client.Read()
|
|
||||||
if err != nil {
|
|
||||||
client.Log.Warn("unable to read: ", err)
|
|
||||||
return nil, client
|
|
||||||
}
|
|
||||||
count := 0
|
|
||||||
for _, extension := range state.Extensions {
|
|
||||||
if extension.Process(element, client) {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if count != 1 {
|
|
||||||
client.Log.WithField("extension", count).Debug(element)
|
|
||||||
}
|
|
||||||
return state, client
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package toclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/genofire/yaja/server/extension"
|
||||||
|
"github.com/genofire/yaja/server/state"
|
||||||
|
"github.com/genofire/yaja/server/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SendingClient state
|
||||||
|
type SendingClient struct {
|
||||||
|
Next state.State
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process messages
|
||||||
|
func (state *SendingClient) Process(client *utils.Client) (state.State, *utils.Client) {
|
||||||
|
client.Log = client.Log.WithField("state", "normal")
|
||||||
|
client.Log.Debug("sending")
|
||||||
|
// sending
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case msg := <-client.Messages:
|
||||||
|
err := client.Out.Encode(msg)
|
||||||
|
client.Log.Info(err)
|
||||||
|
case <-client.OnClose():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
client.Log.Debug("receiving")
|
||||||
|
return state.Next, client
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReceivingClient state
|
||||||
|
type ReceivingClient struct {
|
||||||
|
Extensions extension.Extensions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process messages
|
||||||
|
func (state *ReceivingClient) Process(client *utils.Client) (state.State, *utils.Client) {
|
||||||
|
element, err := client.Read()
|
||||||
|
if err != nil {
|
||||||
|
client.Log.Warn("unable to read: ", err)
|
||||||
|
return nil, client
|
||||||
|
}
|
||||||
|
state.Extensions.Process(element, client)
|
||||||
|
return state, client
|
||||||
|
}
|
|
@ -53,6 +53,8 @@ func (state *RegisterFormRequest) Process(client *utils.Client) (state.State, *u
|
||||||
}
|
}
|
||||||
client.Out.Encode(&messages.IQ{
|
client.Out.Encode(&messages.IQ{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(`<query xmlns='%s'><instructions>
|
Body: []byte(fmt.Sprintf(`<query xmlns='%s'><instructions>
|
||||||
Choose a username and password for use with this service.
|
Choose a username and password for use with this service.
|
||||||
|
@ -118,6 +120,8 @@ func (state *RegisterRequest) Process(client *utils.Client) (state.State, *utils
|
||||||
if err != nil {
|
if err != nil {
|
||||||
client.Out.Encode(&messages.IQ{
|
client.Out.Encode(&messages.IQ{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
Body: []byte(fmt.Sprintf(`<query xmlns='%s'>
|
Body: []byte(fmt.Sprintf(`<query xmlns='%s'>
|
||||||
<username>%s</username>
|
<username>%s</username>
|
||||||
|
@ -137,6 +141,8 @@ func (state *RegisterRequest) Process(client *utils.Client) (state.State, *utils
|
||||||
}
|
}
|
||||||
client.Out.Encode(&messages.IQ{
|
client.Out.Encode(&messages.IQ{
|
||||||
Type: messages.IQTypeResult,
|
Type: messages.IQTypeResult,
|
||||||
|
To: client.JID.String(),
|
||||||
|
From: client.JID.Domain,
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Reference in New Issue