# HG changeset patch # User Chris Jones # Date 1325043764 25200 # Node ID 122ab6208c3cbc3f2e6cffff079bd4084039ec06 # Parent 48be1ae93fd454ae8ee4f42b2c124592ee23b5e9 Added resource binding and structures for , , and . diff -r 48be1ae93fd4 -r 122ab6208c3c stream.go --- a/stream.go Tue Dec 27 15:36:07 2011 -0700 +++ b/stream.go Tue Dec 27 20:42:44 2011 -0700 @@ -114,6 +114,8 @@ case nsSASL + " challenge", nsSASL + " failure", nsSASL + " success": obj = &auth{} + case "jabber:client iq": + obj = &Iq{} default: obj = &Unrecognized{} log.Printf("Ignoring unrecognized: %s %s\n", @@ -208,6 +210,10 @@ cl.chooseSasl(fe) return } + + if fe.Bind != nil { + cl.bind(fe.Bind) + } } // readTransport() is running concurrently. We need to stop it, @@ -436,3 +442,16 @@ hex(h(a2)))) return response } + +func (cl *Client) bind(bind *Unrecognized) { + res := cl.Jid.Resource + msg := &Iq{Type: "set", Id: cl.NextId(), Any: + &Unrecognized{XMLName: xml.Name{Space: nsBind, Local: + "bind"}}} + if res != "" { + msg.Any.Any = &Unrecognized{XMLName: xml.Name{Local: + "resource"}, Chardata: res} + } + cl.xmlOut <- msg + // TODO Grab the iq result from the server and update cl.Jid. +} diff -r 48be1ae93fd4 -r 122ab6208c3c structs.go --- a/structs.go Tue Dec 27 15:36:07 2011 -0700 +++ b/structs.go Tue Dec 27 20:42:44 2011 -0700 @@ -21,9 +21,10 @@ // entities. It looks like node@domain/resource. Node and resource are // sometimes optional. type JID struct { + // TODO Make this not a pointer. Node *string Domain string - Resource *string + Resource string } var _ fmt.Stringer = &JID{} var _ flag.Value = &JID{} @@ -61,6 +62,7 @@ type Features struct { Starttls *starttls Mechanisms mechs + Bind *Unrecognized } type starttls struct { @@ -79,8 +81,64 @@ Any *Unrecognized } +type Stanza interface { + XName() string + XTo() string + XFrom() string + XId() string + XType() string + XLang() string + XError() *Error + XChild() *Unrecognized +} + +type Message struct { + To string `xml:"attr"` + From string `xml:"attr"` + Id string `xml:"attr"` + Type string `xml:"attr"` + Lang string `xml:"attr"` + Error *Error + Any *Unrecognized +} +var _ xml.Marshaler = &Message{} +var _ Stanza = &Message{} + +type Presence struct { + To string `xml:"attr"` + From string `xml:"attr"` + Id string `xml:"attr"` + Type string `xml:"attr"` + Lang string `xml:"attr"` + Error *Error + Any *Unrecognized +} +var _ xml.Marshaler = &Presence{} +var _ Stanza = &Presence{} + +type Iq struct { + To string `xml:"attr"` + From string `xml:"attr"` + Id string `xml:"attr"` + Type string `xml:"attr"` + Lang string `xml:"attr"` + Error *Error + Any *Unrecognized +} +var _ xml.Marshaler = &Iq{} +var _ Stanza = &Iq{} + +type Error struct { + Type string `xml:"attr"` + Any *Unrecognized +} +var _ xml.Marshaler = &Error{} + +// TODO Rename this to something like Generic. type Unrecognized struct { XMLName xml.Name + Any *Unrecognized + Chardata string `xml:"chardata"` } var _ fmt.Stringer = &Unrecognized{} @@ -89,8 +147,8 @@ if jid.Node != nil { result = *jid.Node + "@" + result } - if jid.Resource != nil { - result = result + "/" + *jid.Resource + if jid.Resource != "" { + result = result + "/" + jid.Resource } return result } @@ -107,11 +165,7 @@ jid.Node = &parts[2] } jid.Domain = parts[3] - if parts[5] == "" { - jid.Resource = nil - } else { - jid.Resource = &parts[5] - } + jid.Resource = parts[5] return true } @@ -187,6 +241,164 @@ } func (u *Unrecognized) String() string { - return fmt.Sprintf("unrecognized{%s %s}", u.XMLName.Space, + var sub string + if u.Any != nil { + sub = u.Any.String() + } + return fmt.Sprintf("<%s %s>%s%s", u.XMLName.Space, + u.XMLName.Local, sub, u.Chardata, u.XMLName.Space, u.XMLName.Local) } + +func marshalXML(st Stanza) ([]byte, os.Error) { + buf := bytes.NewBuffer(nil) + buf.WriteString("<") + buf.WriteString(st.XName()) + if st.XTo() != "" { + writeField(buf, "to", st.XTo()) + } + if st.XFrom() != "" { + writeField(buf, "from", st.XFrom()) + } + if st.XId() != "" { + writeField(buf, "id", st.XId()) + } + if st.XType() != "" { + writeField(buf, "type", st.XType()) + } + if st.XLang() != "" { + writeField(buf, "xml:lang", st.XLang()) + } + buf.WriteString(">") + if st.XError() != nil { + bytes, _ := st.XError().MarshalXML() + buf.WriteString(string(bytes)) + } + if st.XChild() != nil { + xml.Marshal(buf, st.XChild()) + } + buf.WriteString("") + return buf.Bytes(), nil +} + +func (er *Error) MarshalXML() ([]byte, os.Error) { + buf := bytes.NewBuffer(nil) + buf.WriteString("") + if er.Any != nil { + xml.Marshal(buf, er.Any) + } + buf.WriteString("") + return buf.Bytes(), nil +} + +func (m *Message) XName() string { + return "message" +} + +func (m *Message) XTo() string { + return m.To +} + +func (m *Message) XFrom() string { + return m.From +} + +func (m *Message) XId() string { + return m.Id +} + +func (m *Message) XType() string { + return m.Type + } + +func (m *Message) XLang() string { + return m.Lang +} + +func (m *Message) XError() *Error { + return m.Error +} + +func (m *Message) XChild() *Unrecognized { + return m.Any +} + +func (m *Message) MarshalXML() ([]byte, os.Error) { + return marshalXML(m) +} + +func (p *Presence) XName() string { + return "presence" +} + +func (p *Presence) XTo() string { + return p.To +} + +func (p *Presence) XFrom() string { + return p.From +} + +func (p *Presence) XId() string { + return p.Id +} + +func (p *Presence) XType() string { + return p.Type + } + +func (p *Presence) XLang() string { + return p.Lang +} + +func (p *Presence) XError() *Error { + return p.Error +} + +func (p *Presence) XChild() *Unrecognized { + return p.Any +} + +func (p *Presence) MarshalXML() ([]byte, os.Error) { + return marshalXML(p) +} + +func (iq *Iq) XName() string { + return "iq" +} + +func (iq *Iq) XTo() string { + return iq.To +} + +func (iq *Iq) XFrom() string { + return iq.From +} + +func (iq *Iq) XId() string { + return iq.Id +} + +func (iq *Iq) XType() string { + return iq.Type + } + +func (iq *Iq) XLang() string { + return iq.Lang +} + +func (iq *Iq) XError() *Error { + return iq.Error +} + +func (iq *Iq) XChild() *Unrecognized { + return iq.Any +} + +func (iq *Iq) MarshalXML() ([]byte, os.Error) { + return marshalXML(iq) +} diff -r 48be1ae93fd4 -r 122ab6208c3c structs_test.go --- a/structs_test.go Tue Dec 27 15:36:07 2011 -0700 +++ b/structs_test.go Tue Dec 27 20:42:44 2011 -0700 @@ -25,7 +25,7 @@ } assertEquals(t, "user", *jid.Node) assertEquals(t, "domain", jid.Domain) - assertEquals(t, "res", *jid.Resource) + assertEquals(t, "res", jid.Resource) assertEquals(t, str, jid.String()) str = "domain.tld" @@ -36,8 +36,8 @@ t.Errorf("Node: %v\n", *jid.Node) } assertEquals(t, "domain.tld", jid.Domain) - if jid.Resource != nil { - t.Errorf("Resource: %v\n", *jid.Resource) + if jid.Resource != "" { + t.Errorf("Resource: %v\n", jid.Resource) } assertEquals(t, str, jid.String()) } @@ -81,3 +81,11 @@ `" xml:lang="pt">things happen` assertMarshal(t, exp, e) } + +func TestIqMarshal(t *testing.T) { + iq := &Iq{Type: "set", Id: "3", Any: &Unrecognized{XMLName: + xml.Name{Space: nsBind, Local: "bind"}}} + exp := `` + assertMarshal(t, exp, iq) +} diff -r 48be1ae93fd4 -r 122ab6208c3c xmpp.go --- a/xmpp.go Tue Dec 27 15:36:07 2011 -0700 +++ b/xmpp.go Tue Dec 27 20:42:44 2011 -0700 @@ -24,6 +24,7 @@ nsStream = "http://etherx.jabber.org/streams" nsTLS = "urn:ietf:params:xml:ns:xmpp-tls" nsSASL = "urn:ietf:params:xml:ns:xmpp-sasl" + nsBind = "urn:ietf:params:xml:ns:xmpp-bind" // DNS SRV names serverSrv = "xmpp-server" @@ -39,6 +40,9 @@ socket net.Conn socketSync sync.WaitGroup saslExpected string + authDone bool + idMutex sync.Mutex + nextId int64 In <-chan interface{} Out chan<- interface{} xmlOut chan<- interface{} @@ -200,3 +204,11 @@ } } } + +func (cl *Client) NextId() string { + cl.idMutex.Lock() + defer cl.idMutex.Unlock() + id := cl.nextId + cl.nextId++ + return fmt.Sprintf("id_%d", id) +}