forked from genofire/unified-push-xmpp
154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/xml"
|
|
"io"
|
|
|
|
"github.com/bdlm/log"
|
|
"mellium.im/sasl"
|
|
"mellium.im/xmlstream"
|
|
"mellium.im/xmpp"
|
|
"mellium.im/xmpp/jid"
|
|
"mellium.im/xmpp/mux"
|
|
"mellium.im/xmpp/stanza"
|
|
|
|
"dev.sum7.eu/genofire/unified-push-xmpp/messages"
|
|
)
|
|
|
|
type XMPPService struct {
|
|
Login string
|
|
Password string
|
|
session *xmpp.Session
|
|
}
|
|
|
|
func (s *XMPPService) Run() error {
|
|
var err error
|
|
j := jid.MustParse(s.Login)
|
|
if s.session, err = xmpp.DialClientSession(
|
|
context.TODO(), j,
|
|
xmpp.BindCustom(func(i jid.JID,r string) (jid.JID, error) {
|
|
// Never run
|
|
log.Infof("try to bind: %v with ressource %s", i, r)
|
|
return j, nil
|
|
}),
|
|
xmpp.StartTLS(&tls.Config{
|
|
ServerName: j.Domain().String(),
|
|
}),
|
|
// sasl.ScramSha1Plus <- problem with (my) ejabberd
|
|
//xmpp.SASL("", xs.Password, sasl.ScramSha1Plus, sasl.ScramSha1, sasl.Plain),
|
|
xmpp.SASL("", s.Password, sasl.ScramSha1, sasl.Plain),
|
|
); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
log.Info("Closing session…")
|
|
if err := s.session.Close(); err != nil {
|
|
log.Errorf("Error closing session: %q", err)
|
|
}
|
|
log.Println("Closing conn…")
|
|
if err := s.session.Conn().Close(); err != nil {
|
|
log.Errorf("Error closing connection: %q", err)
|
|
}
|
|
}()
|
|
// Send initial presence to let the server know we want to receive messages.
|
|
err = s.session.Send(context.TODO(), stanza.Presence{Type: stanza.AvailablePresence}.Wrap(nil))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
log.Infof("connected with %s", s.session.LocalAddr())
|
|
s.session.Serve(mux.New(
|
|
// register - get + set
|
|
mux.IQFunc(stanza.SetIQ, xml.Name{Local: messages.LocalRegister, Space: messages.Space}, s.handleRegister),
|
|
mux.IQFunc(stanza.GetIQ, xml.Name{Local: messages.LocalRegister, Space: messages.Space}, s.handleRegister),
|
|
// unregister - get + set
|
|
mux.IQFunc(stanza.SetIQ, xml.Name{Local: messages.LocalUnregister, Space: messages.Space}, s.handleUnregister),
|
|
mux.IQFunc(stanza.GetIQ, xml.Name{Local: messages.LocalUnregister, Space: messages.Space}, s.handleUnregister),
|
|
// auto accept
|
|
mux.PresenceFunc(stanza.SubscribePresence, xml.Name{}, s.autoSubscribe),
|
|
))
|
|
return nil
|
|
}
|
|
// autoSubscribe to allow sending IQ
|
|
func (s *XMPPService) autoSubscribe(presHead stanza.Presence, t xmlstream.TokenReadEncoder) error {
|
|
log.WithField("p", presHead).Info("autoSubscribe")
|
|
// request eighter
|
|
t.Encode(stanza.Presence{
|
|
Type: stanza.SubscribePresence,
|
|
To: presHead.From,
|
|
})
|
|
// agree
|
|
t.Encode(stanza.Presence{
|
|
Type: stanza.SubscribedPresence,
|
|
To: presHead.From,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (s *XMPPService) handleRegister(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
|
|
reply := messages.RegisterIQ{
|
|
IQ: stanza.IQ{
|
|
ID: iq.ID,
|
|
Type: stanza.ErrorIQ,
|
|
To: iq.From,
|
|
},
|
|
}
|
|
defer func(){
|
|
if err := t.Encode(reply); err != nil {
|
|
log.Errorf("sending response: %v", err)
|
|
}
|
|
}()
|
|
log.Infof("recieved iq: %v", iq)
|
|
|
|
tokenData := messages.TokenData{}
|
|
err := xml.NewTokenDecoder(t).Decode(&tokenData)
|
|
if err != nil && err != io.EOF {
|
|
log.Errorf("Error decoding message: %q", err)
|
|
reply.Register.Error = &messages.ErrorData{ Body: "unable decode"}
|
|
return nil
|
|
}
|
|
token := tokenData.Body
|
|
if token == "" {
|
|
log.Errorf("no token found: %v", token)
|
|
reply.Register.Error = &messages.ErrorData{ Body: "no token"}
|
|
return nil
|
|
}
|
|
endpoint := "https://localhost/UP?token=" + token + "&to=" +iq.From.String()
|
|
reply.IQ.Type = stanza.ResultIQ
|
|
reply.Register.Endpoint = &messages.EndpointData{ Body: endpoint}
|
|
log.Infof("generate respone: %v", endpoint)
|
|
return nil
|
|
}
|
|
func (s *XMPPService) handleUnregister(iq stanza.IQ, t xmlstream.TokenReadEncoder, start *xml.StartElement) error {
|
|
reply := messages.UnregisterIQ{
|
|
IQ: stanza.IQ{
|
|
ID: iq.ID,
|
|
Type: stanza.ErrorIQ,
|
|
To: iq.From,
|
|
},
|
|
}
|
|
defer func(){
|
|
if err := t.Encode(reply); err != nil {
|
|
log.Errorf("sending response: %v", err)
|
|
}
|
|
}()
|
|
log.Infof("unhandled: %v", start)
|
|
|
|
reply.Unregister.Error = "not implemented"
|
|
return nil
|
|
}
|
|
|
|
// SendMessage of an UP Notification
|
|
func (s *XMPPService) SendMessage(to, token, content string) error {
|
|
return s.session.Encode(context.TODO(), messages.Message{
|
|
Message: stanza.Message{
|
|
To: jid.MustParse(to),
|
|
// Type: stanza.ChatMessage,
|
|
Type: stanza.NormalMessage,
|
|
},
|
|
Token: token,
|
|
Body: content,
|
|
})
|
|
}
|