Merge.
--- a/README Wed Nov 06 20:38:03 2013 -0700
+++ b/README Wed Nov 06 20:39:36 2013 -0700
@@ -4,4 +4,11 @@
The core of the protocol is handled by xmpp.go, structs.go, and
stream.go. Everything else is an extension, though some of the
-provided "extensions" are mandatory pieces of the protocol.
+provided "extensions" are mandatory pieces of the protocol. Many of
+the XEPs at http://xmpp.org/xmpp-protocols/xmpp-extensions/ can be
+supported by this library, though at present only base protocol
+support is here.
+
+An simple client using this library is in the example directory. A
+more interesting example can be found at
+https://cjones.org/hg/foosfiend.
--- a/TODO.txt Wed Nov 06 20:38:03 2013 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-Review all the *Client receiver methods in layer3.go. They should
-probably either all be receivers, or none.
-
-Add a Reconnect() function.
-
-Eliminate as many uses of Generic as possible.
--- a/xmpp/layer1.go Wed Nov 06 20:38:03 2013 -0700
+++ b/xmpp/layer1.go Wed Nov 06 20:39:36 2013 -0700
@@ -64,7 +64,7 @@
for {
select {
case stat := <-status:
- if stat.fatal() {
+ if stat.Fatal() {
return
}
--- a/xmpp/status.go Wed Nov 06 20:38:03 2013 -0700
+++ b/xmpp/status.go Wed Nov 06 20:39:36 2013 -0700
@@ -42,7 +42,9 @@
StatusError Status = statusError
)
-func (s Status) fatal() bool {
+// Does the status value indicate that the client is or has
+// disconnected?
+func (s Status) Fatal() bool {
switch s {
default:
return false
@@ -135,7 +137,7 @@
if current == waitFor {
return nil
}
- if current.fatal() {
+ if current.Fatal() {
break
}
if current > waitFor {
--- a/xmpp/structs.go Wed Nov 06 20:38:03 2013 -0700
+++ b/xmpp/structs.go Wed Nov 06 20:39:36 2013 -0700
@@ -98,9 +98,9 @@
type Message struct {
XMLName xml.Name `xml:"jabber:client message"`
Header
- Subject *Generic `xml:"jabber:client subject"`
- Body *Generic `xml:"jabber:client body"`
- Thread *Generic `xml:"jabber:client thread"`
+ Subject []Text `xml:"jabber:client subject"`
+ Body []Text `xml:"jabber:client body"`
+ Thread *Data `xml:"jabber:client thread"`
}
var _ Stanza = &Message{}
@@ -109,9 +109,9 @@
type Presence struct {
XMLName xml.Name `xml:"presence"`
Header
- Show *Generic `xml:"jabber:client show"`
- Status *Generic `xml:"jabber:client status"`
- Priority *Generic `xml:"jabber:client priority"`
+ Show *Data `xml:"jabber:client show"`
+ Status []Text `xml:"jabber:client status"`
+ Priority *Data `xml:"jabber:client priority"`
}
var _ Stanza = &Presence{}
@@ -142,6 +142,22 @@
Jid *string `xml:"jid"`
}
+// Holds human-readable text, with an optional language
+// specification. Generally multiple instances of these can be found
+// together, allowing the software to choose which language to present
+// to the user.
+type Text struct {
+ XMLName xml.Name
+ Lang string `xml:"http://www.w3.org/XML/1998/namespace lang,attr,omitempty"`
+ Chardata string `xml:",chardata"`
+}
+
+// Non-human-readable content of some sort, used by the protocol.
+type Data struct {
+ XMLName xml.Name
+ Chardata string `xml:",chardata"`
+}
+
// Holds an XML element not described by the more specific types.
type Generic struct {
XMLName xml.Name
@@ -162,8 +178,7 @@
return result
}
-// Set implements flag.Value. It returns true if it successfully
-// parses the string.
+// Set implements flag.Value.
func (jid *JID) Set(val string) error {
r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$")
parts := r.FindStringSubmatch(val)
--- a/xmpp/structs_test.go Wed Nov 06 20:38:03 2013 -0700
+++ b/xmpp/structs_test.go Wed Nov 06 20:39:36 2013 -0700
@@ -108,8 +108,8 @@
}
func TestMarshalEscaping(t *testing.T) {
- msg := &Message{Body: &Generic{XMLName: xml.Name{Local: "body"},
- Chardata: `&<!-- "`}}
+ msg := &Message{Body: []Text{Text{XMLName: xml.Name{Local: "body"},
+ Chardata: `&<!-- "`}}}
exp := `<message xmlns="jabber:client"><body>&<!-- "</body></message>`
assertMarshal(t, exp, msg)
}
@@ -123,8 +123,8 @@
obs := <-ch
exp := &Message{XMLName: xml.Name{Local: "message", Space: "jabber:client"},
Header: Header{To: "a@b.c", Innerxml: "<body>foo!</body>"},
- Body: &Generic{XMLName: xml.Name{Local: "body", Space: "jabber:client"},
- Chardata: "foo!"}}
+ Body: []Text{Text{XMLName: xml.Name{Local: "body", Space: "jabber:client"},
+ Chardata: "foo!"}}}
if !reflect.DeepEqual(obs, exp) {
t.Errorf("read %s\ngot: %#v\nwant: %#v\n", str, obs, exp)
}
--- a/xmpp/xmpp.go Wed Nov 06 20:38:03 2013 -0700
+++ b/xmpp/xmpp.go Wed Nov 06 20:39:36 2013 -0700
@@ -13,6 +13,7 @@
"io"
"net"
"reflect"
+ "sync"
)
const (
@@ -66,7 +67,8 @@
// set up the XMPP stream will not appear here.
Recv <-chan Stanza
// Outgoing XMPP stanzas to the server should be sent to this
- // channel.
+ // channel. The application should not close this channel;
+ // rather, call Close().
Send chan<- Stanza
sendRaw chan<- interface{}
statmgr *statmgr
@@ -80,6 +82,7 @@
tlsConfig tls.Config
layer1 *layer1
error chan error
+ shutdownOnce sync.Once
}
// Creates an XMPP client identified by the given JID, authenticating
@@ -256,8 +259,9 @@
func (cl *Client) Close() {
// Shuts down the receivers:
cl.setStatus(StatusShutdown)
+
// Shuts down the senders:
- close(cl.Send)
+ cl.shutdownOnce.Do(func() { close(cl.Send) })
}
// If there's a buffered error in the channel, return it. Otherwise,