# HG changeset patch # User Chris Jones # Date 1384024177 25200 # Node ID ccfebbd9f49b6a75a27f9ed9bf2ac5f51cc468e5 # Parent 63f33bb8fa334163de801978d4e9f70175ba6b41 Changed the JID type to be an alias of string, rather than a struct. This allows it to be used as a key in a map, among other benefits. diff -r 63f33bb8fa33 -r ccfebbd9f49b TODO.txt --- a/TODO.txt Wed Nov 06 20:40:50 2013 -0700 +++ b/TODO.txt Sat Nov 09 12:09:37 2013 -0700 @@ -1,5 +1,2 @@ -Make JID a string type, with receiver functions to get its -pieces. This way it can be used as a key in a map. - Don't force the client to understand the RFCs. Keep message types in a convenient set of constants, for example. diff -r 63f33bb8fa33 -r ccfebbd9f49b example/interact.go --- a/example/interact.go Wed Nov 06 20:40:50 2013 -0700 +++ b/example/interact.go Sat Nov 09 12:09:37 2013 -0700 @@ -18,11 +18,11 @@ // Demonstrate the API, and allow the user to interact with an XMPP // server via the terminal. func main() { - var jid xmpp.JID - flag.Var(&jid, "jid", "JID to log in as") - var pw *string = flag.String("pw", "", "password") + jidStr := flag.String("jid", "", "JID to log in as") + pw := flag.String("pw", "", "password") flag.Parse() - if jid.Domain == "" || *pw == "" { + jid := xmpp.JID(*jidStr) + if jid.Domain() == "" || *pw == "" { flag.Usage() os.Exit(2) } diff -r 63f33bb8fa33 -r ccfebbd9f49b xmpp/layer3.go --- a/xmpp/layer3.go Wed Nov 06 20:40:50 2013 -0700 +++ b/xmpp/layer3.go Sat Nov 09 12:09:37 2013 -0700 @@ -136,12 +136,12 @@ // Now re-send the initial handshake message to start the new // session. - cl.sendRaw <- &stream{To: cl.Jid.Domain, Version: XMPPVersion} + cl.sendRaw <- &stream{To: cl.Jid.Domain(), Version: XMPPVersion} } // Send a request to bind a resource. RFC 3920, section 7. func (cl *Client) bind() { - res := cl.Jid.Resource + res := cl.Jid.Resource() bindReq := &bindIq{} if res != "" { bindReq.Resource = &res @@ -170,19 +170,13 @@ cl.setError(fmt.Errorf("Bad bind reply: %#v", iq)) return } - jidStr := bindRepl.Jid - if jidStr == nil || *jidStr == "" { + jid := bindRepl.Jid + if jid == nil || *jid == "" { cl.setError(fmt.Errorf("empty resource in bind %#v", iq)) return } - jid := new(JID) - if err := jid.Set(*jidStr); err != nil { - cl.setError(fmt.Errorf("bind: an't parse JID %s: %v", - *jidStr, err)) - return - } - cl.Jid = *jid + cl.Jid = JID(*jid) cl.setStatus(StatusBound) } cl.SetCallback(msg.Id, f) diff -r 63f33bb8fa33 -r ccfebbd9f49b xmpp/sasl.go --- a/xmpp/sasl.go Wed Nov 06 20:40:50 2013 -0700 +++ b/xmpp/sasl.go Sat Nov 09 12:09:37 2013 -0700 @@ -54,7 +54,7 @@ case "success": cl.setStatus(StatusAuthenticated) cl.Features = nil - ss := &stream{To: cl.Jid.Domain, Version: XMPPVersion} + ss := &stream{To: cl.Jid.Domain(), Version: XMPPVersion} cl.sendRaw <- ss } } @@ -80,17 +80,17 @@ passwd := cl.password nonce := srvMap["nonce"] - digestUri := "xmpp/" + cl.Jid.Domain + digestUri := "xmpp/" + cl.Jid.Domain() nonceCount := int32(1) nonceCountStr := fmt.Sprintf("%08x", nonceCount) // Begin building the response. Username is // user@domain or just domain. var username string - if cl.Jid.Node == "" { - username = cl.Jid.Domain + if cl.Jid.Node() == "" { + username = cl.Jid.Domain() } else { - username = cl.Jid.Node + username = cl.Jid.Node() } // Generate our own nonce from random data. diff -r 63f33bb8fa33 -r ccfebbd9f49b xmpp/structs.go --- a/xmpp/structs.go Wed Nov 06 20:40:50 2013 -0700 +++ b/xmpp/structs.go Sat Nov 09 12:09:37 2013 -0700 @@ -5,11 +5,9 @@ import ( "bytes" "encoding/xml" - "flag" "fmt" "log" "reflect" - "regexp" "strings" ) @@ -19,14 +17,7 @@ // JID represents an entity that can communicate with other // entities. It looks like node@domain/resource. Node and resource are // sometimes optional. -type JID struct { - Node string - Domain string - Resource string -} - -var _ fmt.Stringer = &JID{} -var _ flag.Value = &JID{} +type JID string // XMPP's XML element type stream struct { @@ -98,8 +89,8 @@ type Message struct { XMLName xml.Name `xml:"jabber:client message"` Header - Subject []Text `xml:"jabber:client subject"` - Body []Text `xml:"jabber:client body"` + Subject []Text `xml:"jabber:client subject"` + Body []Text `xml:"jabber:client body"` Thread *Data `xml:"jabber:client thread"` } @@ -109,9 +100,9 @@ type Presence struct { XMLName xml.Name `xml:"presence"` Header - Show *Data `xml:"jabber:client show"` - Status []Text `xml:"jabber:client status"` - Priority *Data `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{} @@ -147,14 +138,14 @@ // together, allowing the software to choose which language to present // to the user. type Text struct { - XMLName xml.Name + 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 + XMLName xml.Name Chardata string `xml:",chardata"` } @@ -167,31 +158,29 @@ var _ fmt.Stringer = &Generic{} -func (jid *JID) String() string { - result := jid.Domain - if jid.Node != "" { - result = jid.Node + "@" + result +func (j JID) Node() string { + at := strings.Index(string(j), "@") + if at == -1 { + return "" } - if jid.Resource != "" { - result = result + "/" + jid.Resource - } - return result + return string(j[:at]) } -// Set implements flag.Value. -func (jid *JID) Set(val string) error { - r := regexp.MustCompile("^(([^@/]+)@)?([^@/]+)(/([^@/]+))?$") - parts := r.FindStringSubmatch(val) - if parts == nil { - return fmt.Errorf("%s doesn't match user@domain/resource", val) +func (j JID) Domain() string { + at := strings.Index(string(j), "@") + slash := strings.LastIndex(string(j), "/") + if slash == -1 { + slash = len(j) } - // jid.Node = stringprep.Nodeprep(parts[2]) - // jid.Domain = stringprep.Nodeprep(parts[3]) - // jid.Resource = stringprep.Resourceprep(parts[5]) - jid.Node = parts[2] - jid.Domain = parts[3] - jid.Resource = parts[5] - return nil + return string(j[at+1 : slash]) +} + +func (j JID) Resource() string { + slash := strings.LastIndex(string(j), "/") + if slash == -1 { + return "" + } + return string(j[slash+1:]) } func (s *stream) String() string { diff -r 63f33bb8fa33 -r ccfebbd9f49b xmpp/structs_test.go --- a/xmpp/structs_test.go Wed Nov 06 20:40:50 2013 -0700 +++ b/xmpp/structs_test.go Sat Nov 09 12:09:37 2013 -0700 @@ -23,28 +23,19 @@ } func TestJid(t *testing.T) { - str := "user@domain/res" - jid := &JID{} - if err := jid.Set(str); err != nil { - t.Errorf("Set(%s) failed: %s", str, err) - } - assertEquals(t, "user", jid.Node) - assertEquals(t, "domain", jid.Domain) - assertEquals(t, "res", jid.Resource) - assertEquals(t, str, jid.String()) + jid := JID("user@domain/res") + assertEquals(t, "user", jid.Node()) + assertEquals(t, "domain", jid.Domain()) + assertEquals(t, "res", jid.Resource()) - str = "domain.tld" - if err := jid.Set(str); err != nil { - t.Errorf("Set(%s) failed: %s", str, err) + jid = "domain.tld" + if jid.Node() != "" { + t.Errorf("Node: %v\n", jid.Node()) } - if jid.Node != "" { - t.Errorf("Node: %v\n", jid.Node) + assertEquals(t, "domain.tld", jid.Domain()) + if jid.Resource() != "" { + t.Errorf("Resource: %v\n", jid.Resource()) } - assertEquals(t, "domain.tld", jid.Domain) - if jid.Resource != "" { - t.Errorf("Resource: %v\n", jid.Resource) - } - assertEquals(t, str, jid.String()) } func assertMarshal(t *testing.T, expected string, marshal interface{}) { diff -r 63f33bb8fa33 -r ccfebbd9f49b xmpp/xmpp.go --- a/xmpp/xmpp.go Wed Nov 06 20:40:50 2013 -0700 +++ b/xmpp/xmpp.go Sat Nov 09 12:09:37 2013 -0700 @@ -93,7 +93,7 @@ pr Presence, status chan<- Status) (*Client, error) { // Resolve the domain in the JID. - _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain) + _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain()) if err != nil { return nil, fmt.Errorf("LookupSrv %s: %v", jid.Domain, err) } @@ -211,7 +211,7 @@ } // Initial handshake. - hsOut := &stream{To: jid.Domain, Version: XMPPVersion} + hsOut := &stream{To: jid.Domain(), Version: XMPPVersion} cl.sendRaw <- hsOut // Wait until resource binding is complete. @@ -224,7 +224,7 @@ // Initialize the session. id := NextId() - iq := &Iq{Header: Header{To: cl.Jid.Domain, Id: id, Type: "set", + iq := &Iq{Header: Header{To: cl.Jid.Domain(), Id: id, Type: "set", Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}}} ch := make(chan error) f := func(st Stanza) {