Merge pull request #7 from chteufleur/master
This commit is contained in:
commit
29a325f2ae
|
|
@ -0,0 +1,69 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NodeAdHocCommand = "http://jabber.org/protocol/commands"
|
||||||
|
|
||||||
|
ActionAdHocExecute = "execute"
|
||||||
|
ActionAdHocNext = "next"
|
||||||
|
ActionAdHocCancel = "cancel"
|
||||||
|
|
||||||
|
StatusAdHocExecute = "executing"
|
||||||
|
StatusAdHocCompleted = "completed"
|
||||||
|
StatusAdHocCanceled = "canceled"
|
||||||
|
|
||||||
|
TypeAdHocForm = "form"
|
||||||
|
TypeAdHocResult = "result"
|
||||||
|
TypeAdHocSubmit = "submit"
|
||||||
|
|
||||||
|
TypeAdHocListSingle = "list-single"
|
||||||
|
TypeAdHocListMulti = "list-multi"
|
||||||
|
|
||||||
|
TypeAdHocNoteInfo = "info"
|
||||||
|
TypeAdHocNoteWarning = "warn"
|
||||||
|
TypeAdHocNoteError = "error"
|
||||||
|
|
||||||
|
TypeAdHocFieldListMulti = "list-multi"
|
||||||
|
TypeAdHocFieldListSingle = "list-single"
|
||||||
|
TypeAdHocFieldTextSingle = "text-single"
|
||||||
|
TypeAdHocFieldJidSingle = "jid-single"
|
||||||
|
TypeAdHocFieldTextPrivate = "text-private"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdHocCommand struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/commands command"`
|
||||||
|
Node string `xml:"node,attr"`
|
||||||
|
Action string `xml:"action,attr"`
|
||||||
|
SessionID string `xml:"sessionid,attr"`
|
||||||
|
Status string `xml:"status,attr"`
|
||||||
|
XForm AdHocXForm `xml:"x"`
|
||||||
|
Note AdHocNote `xml:"note,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdHocXForm struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:x:data x"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Title string `xml:"title"`
|
||||||
|
Instructions string `xml:"instructions"`
|
||||||
|
Fields []AdHocField `xml:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdHocField struct {
|
||||||
|
Var string `xml:"var,attr"`
|
||||||
|
Label string `xml:"label,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Options []AdHocFieldOption `xml:"option"`
|
||||||
|
Value string `xml:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdHocFieldOption struct {
|
||||||
|
Value string `xml:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdHocNote struct {
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Value string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSChatStatesNotification = "http://jabber.org/protocol/chatstates"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0085: Chat States Notification
|
||||||
|
|
||||||
|
type Active struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates active"`
|
||||||
|
}
|
||||||
|
type Composing struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates composing"`
|
||||||
|
}
|
||||||
|
type Paused struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates paused"`
|
||||||
|
}
|
||||||
|
type Inactive struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates inactive"`
|
||||||
|
}
|
||||||
|
type Gone struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/chatstates gone"`
|
||||||
|
}
|
||||||
|
|
@ -115,7 +115,7 @@ func startTLS(stream *Stream, config *ClientConfig) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify}
|
tlsConfig := tls.Config{InsecureSkipVerify: config.InsecureSkipVerify, ServerName: stream.config.ConnectionDomain}
|
||||||
return stream.UpgradeTLS(&tlsConfig)
|
return stream.UpgradeTLS(&tlsConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nsDiscoInfo = "http://jabber.org/protocol/disco#info"
|
NSDiscoInfo = "http://jabber.org/protocol/disco#info"
|
||||||
nsDiscoItems = "http://jabber.org/protocol/disco#items"
|
NSDiscoItems = "http://jabber.org/protocol/disco#items"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service Discovery (XEP-0030) protocol. "Wraps" XMPP instance to provide a
|
// Service Discovery (XEP-0030) protocol. "Wraps" XMPP instance to provide a
|
||||||
|
|
@ -19,6 +19,7 @@ type Disco struct {
|
||||||
// Iq get/result payload for "info" requests.
|
// Iq get/result payload for "info" requests.
|
||||||
type DiscoInfo struct {
|
type DiscoInfo struct {
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#info query"`
|
||||||
|
Node string `xml:"node,attr"`
|
||||||
Identity []DiscoIdentity `xml:"identity"`
|
Identity []DiscoIdentity `xml:"identity"`
|
||||||
Feature []DiscoFeature `xml:"feature"`
|
Feature []DiscoFeature `xml:"feature"`
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +39,7 @@ type DiscoFeature struct {
|
||||||
// Iq get/result payload for "items" requests.
|
// Iq get/result payload for "items" requests.
|
||||||
type DiscoItems struct {
|
type DiscoItems struct {
|
||||||
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#items query"`
|
XMLName xml.Name `xml:"http://jabber.org/protocol/disco#items query"`
|
||||||
|
Node string `xml:"node,attr"`
|
||||||
Item []DiscoItem `xml:"item"`
|
Item []DiscoItem `xml:"item"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,13 +51,13 @@ type DiscoItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request information about the service identified by 'to'.
|
// Request information about the service identified by 'to'.
|
||||||
func (disco *Disco) Info(to string, from string) (*DiscoInfo, error) {
|
func (disco *Disco) Info(to, from string) (*DiscoInfo, error) {
|
||||||
|
|
||||||
if from == "" {
|
if from == "" {
|
||||||
from = disco.XMPP.JID.Full()
|
from = disco.XMPP.JID.Full()
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &Iq{Id: UUID4(), Type: "get", To: to, From: from}
|
req := &Iq{Id: UUID4(), Type: IQTypeGet, To: to, From: from}
|
||||||
req.PayloadEncode(&DiscoInfo{})
|
req.PayloadEncode(&DiscoInfo{})
|
||||||
|
|
||||||
resp, err := disco.XMPP.SendRecv(req)
|
resp, err := disco.XMPP.SendRecv(req)
|
||||||
|
|
@ -72,14 +74,14 @@ func (disco *Disco) Info(to string, from string) (*DiscoInfo, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request items in the service identified by 'to'.
|
// Request items in the service identified by 'to'.
|
||||||
func (disco *Disco) Items(to string, from string) (*DiscoItems, error) {
|
func (disco *Disco) Items(to, from, node string) (*DiscoItems, error) {
|
||||||
|
|
||||||
if from == "" {
|
if from == "" {
|
||||||
from = disco.XMPP.JID.Full()
|
from = disco.XMPP.JID.Full()
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &Iq{Id: UUID4(), Type: "get", To: to, From: from}
|
req := &Iq{Id: UUID4(), Type: IQTypeGet, To: to, From: from}
|
||||||
req.PayloadEncode(&DiscoItems{})
|
req.PayloadEncode(&DiscoItems{Node: node})
|
||||||
|
|
||||||
resp, err := disco.XMPP.SendRecv(req)
|
resp, err := disco.XMPP.SendRecv(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -94,7 +96,7 @@ func (disco *Disco) Items(to string, from string) (*DiscoItems, error) {
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var discoNamespacePrefix = strings.Split(nsDiscoInfo, "#")[0]
|
var discoNamespacePrefix = strings.Split(NSDiscoInfo, "#")[0]
|
||||||
|
|
||||||
// Matcher instance to match <iq/> stanzas with a disco payload.
|
// Matcher instance to match <iq/> stanzas with a disco payload.
|
||||||
var DiscoPayloadMatcher = MatcherFunc(
|
var DiscoPayloadMatcher = MatcherFunc(
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package xmpp
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -27,7 +28,8 @@ func HomeServerAddrs(jid JID) (addr []string, err error) {
|
||||||
|
|
||||||
// Build list of "host:port" strings.
|
// Build list of "host:port" strings.
|
||||||
for _, a := range addrs {
|
for _, a := range addrs {
|
||||||
addr = append(addr, fmt.Sprintf("%s:%d", a.Target, a.Port))
|
target := strings.TrimRight(a.Target, ".")
|
||||||
|
addr = append(addr, fmt.Sprintf("%s:%d", target, a.Port))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSHTTPAuth = "http://jabber.org/protocol/http-auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0070: Verifying HTTP Requests via XMPP
|
||||||
|
type Confirm struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/http-auth confirm"`
|
||||||
|
Id string `xml:"id,attr"`
|
||||||
|
Method string `xml:"method,attr"`
|
||||||
|
URL string `xml:"url,attr"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSPing = "urn:xmpp:ping"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Ping struct {
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:ping ping"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSRegister = "jabber:iq:register"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0077: In-Band Registration
|
||||||
|
|
||||||
|
type RegisterQuery struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:iq:register query"`
|
||||||
|
Instructions string `xml:"instructions"`
|
||||||
|
Username string `xml:"username"`
|
||||||
|
Password string `xml:"password"`
|
||||||
|
XForm AdHocXForm `xml:"x"`
|
||||||
|
Registered *RegisterRegistered `xmp:"registered"`
|
||||||
|
Remove *RegisterRemove `xmp:"remove"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterRegistered struct {
|
||||||
|
XMLName xml.Name `xml:"registered"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterRemove struct {
|
||||||
|
XMLName xml.Name `xml:"remove"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSRemoteRosterManager = "urn:xmpp:tmp:roster-management:0"
|
||||||
|
|
||||||
|
RemoteRosterManagerTypeRequest = "request"
|
||||||
|
RemoteRosterManagerTypeAllowed = "allowed"
|
||||||
|
RemoteRosterManagerTypeRejected = "rejected"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0321: Remote Roster Manager
|
||||||
|
|
||||||
|
type RemoteRosterManagerQuery struct {
|
||||||
|
XMLName xml.Name `xml:"urn:xmpp:tmp:roster-management:0 query"`
|
||||||
|
Reason string `xml:"reason,attr,omitempty"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSRoster = "jabber:iq:roster"
|
||||||
|
|
||||||
|
RosterSubscriptionBoth = "both"
|
||||||
|
RosterSubscriptionFrom = "from"
|
||||||
|
RosterSubscriptionTo = "to"
|
||||||
|
RosterSubscriptionRemove = "remove"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RosterQuery struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:iq:roster query"`
|
||||||
|
Items []RosterItem `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RosterItem struct {
|
||||||
|
JID string `xml:"jid,attr"`
|
||||||
|
Name string `xml:"name,attr,omitempty"`
|
||||||
|
Subscription string `xml:"subscription,attr"`
|
||||||
|
Groupes []string `xml:"group"`
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSJabberClient = "jabber:iq:version"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0092 Software Version
|
||||||
|
type SoftwareVersion struct {
|
||||||
|
XMLName xml.Name `xml:"jabber:iq:version query"`
|
||||||
|
Name string `xml:"name,omitempty"`
|
||||||
|
Version string `xml:"version,omitempty"`
|
||||||
|
OS string `xml:"os,omitempty"`
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,17 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IQTypeGet = "get"
|
||||||
|
IQTypeSet = "set"
|
||||||
|
IQTypeResult = "result"
|
||||||
|
IQTypeError = "error"
|
||||||
|
|
||||||
|
MessageTypeNormal = "normal"
|
||||||
|
MessageTypeChat = "chat"
|
||||||
|
MessageTypeError = "error"
|
||||||
|
)
|
||||||
|
|
||||||
// XMPP <iq/> stanza.
|
// XMPP <iq/> stanza.
|
||||||
type Iq struct {
|
type Iq struct {
|
||||||
XMLName xml.Name `xml:"iq"`
|
XMLName xml.Name `xml:"iq"`
|
||||||
|
|
@ -56,13 +67,29 @@ func (iq *Iq) Response(type_ string) *Iq {
|
||||||
|
|
||||||
// XMPP <message/> stanza.
|
// XMPP <message/> stanza.
|
||||||
type Message struct {
|
type Message struct {
|
||||||
XMLName xml.Name `xml:"message"`
|
XMLName xml.Name `xml:"message"`
|
||||||
Id string `xml:"id,attr,omitempty"`
|
Id string `xml:"id,attr,omitempty"`
|
||||||
Type string `xml:"type,attr,omitempty"`
|
Type string `xml:"type,attr,omitempty"`
|
||||||
To string `xml:"to,attr,omitempty"`
|
To string `xml:"to,attr,omitempty"`
|
||||||
From string `xml:"from,attr,omitempty"`
|
From string `xml:"from,attr,omitempty"`
|
||||||
Subject string `xml:"subject,omitempty"`
|
Subject string `xml:"subject,omitempty"`
|
||||||
Body string `xml:"body,omitempty"`
|
Body []MessageBody `xml:"body,omitempty"`
|
||||||
|
Thread string `xml:"thread,omitempty"`
|
||||||
|
Error *Error `xml:"error"`
|
||||||
|
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||||
|
|
||||||
|
Confirm *Confirm `xml:"confirm"` // XEP-0070
|
||||||
|
|
||||||
|
Active *Active `xml:"active"` // XEP-0085
|
||||||
|
Composing *Composing `xml:"composing"` // XEP-0085
|
||||||
|
Paused *Paused `xml:"paused"` // XEP-0085
|
||||||
|
Inactive *Inactive `xml:"inactive"` // XEP-0085
|
||||||
|
Gone *Gone `xml:"gone"` // XEP-0085
|
||||||
|
}
|
||||||
|
|
||||||
|
type MessageBody struct {
|
||||||
|
Lang string `xml:"xml:lang,attr,omitempty"`
|
||||||
|
Value string `xml:",chardata"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// XMPP <presence/> stanza.
|
// XMPP <presence/> stanza.
|
||||||
|
|
@ -72,12 +99,17 @@ type Presence struct {
|
||||||
Type string `xml:"type,attr,omitempty"`
|
Type string `xml:"type,attr,omitempty"`
|
||||||
To string `xml:"to,attr,omitempty"`
|
To string `xml:"to,attr,omitempty"`
|
||||||
From string `xml:"from,attr,omitempty"`
|
From string `xml:"from,attr,omitempty"`
|
||||||
|
Show string `xml:"show"` // away, chat, dnd, xa
|
||||||
|
Status string `xml:"status"` // sb []clientText
|
||||||
|
Photo string `xml:"photo,omitempty"` // Avatar
|
||||||
|
Nick string `xml:"nick,omitempty"` // Nickname
|
||||||
}
|
}
|
||||||
|
|
||||||
// XMPP <error/>. May occur as a top-level stanza or embedded in another
|
// XMPP <error/>. May occur as a top-level stanza or embedded in another
|
||||||
// stanza, e.g. an <iq type="error"/>.
|
// stanza, e.g. an <iq type="error"/>.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
XMLName xml.Name `xml:"error"`
|
XMLName xml.Name `xml:"error"`
|
||||||
|
Code string `xml:"code,attr,omitempty"`
|
||||||
Type string `xml:"type,attr"`
|
Type string `xml:"type,attr"`
|
||||||
Payload string `xml:",innerxml"`
|
Payload string `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
@ -116,6 +148,12 @@ func NewError(errorType string, condition ErrorCondition, text string) *Error {
|
||||||
return &Error{Type: errorType, Payload: string(buf.Bytes())}
|
return &Error{Type: errorType, Payload: string(buf.Bytes())}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewErrorWithCode(code, errorType string, condition ErrorCondition, text string) *Error {
|
||||||
|
err := NewError(errorType, condition, text)
|
||||||
|
err.Code = code
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Return the error text from the payload, or "" if not present.
|
// Return the error text from the payload, or "" if not present.
|
||||||
func (e Error) Text() string {
|
func (e Error) Text() string {
|
||||||
dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
|
dec := xml.NewDecoder(bytes.NewBufferString(e.Payload))
|
||||||
|
|
@ -151,5 +189,11 @@ type ErrorCondition xml.Name
|
||||||
|
|
||||||
// Stanza errors.
|
// Stanza errors.
|
||||||
var (
|
var (
|
||||||
FeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"}
|
ErrorFeatureNotImplemented = ErrorCondition{nsErrorStanzas, "feature-not-implemented"}
|
||||||
|
ErrorRemoteServerNotFound = ErrorCondition{nsErrorStanzas, "remote-server-not-found"}
|
||||||
|
ErrorServiceUnavailable = ErrorCondition{nsErrorStanzas, "service-unavailable"}
|
||||||
|
ErrorNotAuthorized = ErrorCondition{nsErrorStanzas, "not-authorized"}
|
||||||
|
ErrorConflict = ErrorCondition{nsErrorStanzas, "conflict"}
|
||||||
|
ErrorNotAcceptable = ErrorCondition{nsErrorStanzas, "not-acceptable"}
|
||||||
|
ErrorForbidden = ErrorCondition{nsErrorStanzas, "forbidden"}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stream configuration.
|
// Stream configuration.
|
||||||
|
|
@ -16,6 +17,9 @@ type StreamConfig struct {
|
||||||
// are either sent to the server or delivered to the application. It also
|
// are either sent to the server or delivered to the application. It also
|
||||||
// causes incoming stanzas to be XML-parsed a second time.
|
// causes incoming stanzas to be XML-parsed a second time.
|
||||||
LogStanzas bool
|
LogStanzas bool
|
||||||
|
|
||||||
|
// The dommain connection for certificate validation.
|
||||||
|
ConnectionDomain string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
|
|
@ -42,6 +46,9 @@ func NewStream(addr string, config *StreamConfig) (*Stream, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stream := &Stream{conn: conn, dec: xml.NewDecoder(conn), config: config}
|
stream := &Stream{conn: conn, dec: xml.NewDecoder(conn), config: config}
|
||||||
|
if config.ConnectionDomain == "" {
|
||||||
|
config.ConnectionDomain = strings.SplitN(addr, ":", 2)[0]
|
||||||
|
}
|
||||||
|
|
||||||
if err := stream.send([]byte("<?xml version='1.0' encoding='utf-8'?>")); err != nil {
|
if err := stream.send([]byte("<?xml version='1.0' encoding='utf-8'?>")); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -163,6 +170,13 @@ func nextStartElement(dec *xml.Decoder) (*xml.StartElement, error) {
|
||||||
}
|
}
|
||||||
switch e := t.(type) {
|
switch e := t.(type) {
|
||||||
case xml.StartElement:
|
case xml.StartElement:
|
||||||
|
for i, _ := range e.Attr {
|
||||||
|
// Replace URL namespace to xml in order to avoid error on Unmarshal
|
||||||
|
// It's quite ugly, but working for now
|
||||||
|
if e.Attr[i].Name.Space == "http://www.w3.org/XML/1998/namespace" {
|
||||||
|
e.Attr[i].Name.Space = "xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
return &e, nil
|
return &e, nil
|
||||||
case xml.EndElement:
|
case xml.EndElement:
|
||||||
log.Printf("EOF due to %s\n", e.Name)
|
log.Printf("EOF due to %s\n", e.Name)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dictionary = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
)
|
||||||
|
|
||||||
// Generate a UUID4.
|
// Generate a UUID4.
|
||||||
func UUID4() string {
|
func UUID4() string {
|
||||||
uuid := make([]byte, 16)
|
uuid := make([]byte, 16)
|
||||||
|
|
@ -15,3 +19,14 @@ func UUID4() string {
|
||||||
uuid[8] = (uuid[8] &^ 0x40) | 0x80
|
uuid[8] = (uuid[8] &^ 0x40) | 0x80
|
||||||
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
|
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SessionID() string {
|
||||||
|
var bytes = make([]byte, 15)
|
||||||
|
if _, err := rand.Read(bytes); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for k, v := range bytes {
|
||||||
|
bytes[k] = dictionary[v%byte(len(dictionary))]
|
||||||
|
}
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NSVCardTemp = "vcard-temp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XEP-0054 vCard
|
||||||
|
type VCard struct {
|
||||||
|
XMLName xml.Name `xml:"vcard-temp vCard"`
|
||||||
|
// TODO Must complete truct
|
||||||
|
}
|
||||||
|
|
@ -169,12 +169,17 @@ func (x *XMPP) sender() {
|
||||||
|
|
||||||
// Close the stream. Note: relies on common element name for all types of
|
// Close the stream. Note: relies on common element name for all types of
|
||||||
// XMPP connection.
|
// XMPP connection.
|
||||||
x.stream.SendEnd(&xml.EndElement{xml.Name{"stream", "stream"}})
|
log.Println("Close XMPP stream")
|
||||||
|
x.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *XMPP) receiver() {
|
func (x *XMPP) receiver() {
|
||||||
|
|
||||||
defer close(x.In)
|
defer func() {
|
||||||
|
log.Println("Close XMPP receiver")
|
||||||
|
x.Close()
|
||||||
|
close(x.In)
|
||||||
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
start, err := x.stream.Next()
|
start, err := x.stream.Next()
|
||||||
|
|
@ -194,12 +199,12 @@ func (x *XMPP) receiver() {
|
||||||
case "presence":
|
case "presence":
|
||||||
v = &Presence{}
|
v = &Presence{}
|
||||||
default:
|
default:
|
||||||
log.Fatal("Unexected element: %T %v", start, start)
|
log.Println("Error. Unexected element: %T %v", start, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = x.stream.Decode(v, start)
|
err = x.stream.Decode(v, start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Println("Error. Failed to decode element. ", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered := false
|
filtered := false
|
||||||
|
|
@ -216,4 +221,9 @@ func (x *XMPP) receiver() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XMPP) Close() {
|
||||||
|
log.Println("Close XMPP")
|
||||||
|
x.stream.SendEnd(&xml.EndElement{xml.Name{"stream", "stream"}})
|
||||||
|
}
|
||||||
|
|
||||||
// BUG(matt): Filter channels are not closed when the stream is closed.
|
// BUG(matt): Filter channels are not closed when the stream is closed.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue