6 // and 3921, plus the various XEPs at http://xmpp.org/protocols/. |
6 // and 3921, plus the various XEPs at http://xmpp.org/protocols/. |
7 package xmpp |
7 package xmpp |
8 |
8 |
9 import ( |
9 import ( |
10 "bytes" |
10 "bytes" |
|
11 "encoding/xml" |
|
12 "errors" |
11 "fmt" |
13 "fmt" |
12 "io" |
14 "io" |
|
15 "log/syslog" |
13 "net" |
16 "net" |
14 "os" |
|
15 "sync" |
17 "sync" |
16 "syslog" |
|
17 "xml" |
|
18 ) |
18 ) |
19 |
19 |
20 const ( |
20 const ( |
21 // Version of RFC 3920 that we implement. |
21 // Version of RFC 3920 that we implement. |
22 Version = "1.0" |
22 Version = "1.0" |
23 |
23 |
24 // Various XML namespaces. |
24 // Various XML namespaces. |
|
25 NsClient = "jabber:client" |
25 NsStreams = "urn:ietf:params:xml:ns:xmpp-streams" |
26 NsStreams = "urn:ietf:params:xml:ns:xmpp-streams" |
26 NsStream = "http://etherx.jabber.org/streams" |
27 NsStream = "http://etherx.jabber.org/streams" |
27 NsTLS = "urn:ietf:params:xml:ns:xmpp-tls" |
28 NsTLS = "urn:ietf:params:xml:ns:xmpp-tls" |
28 NsSASL = "urn:ietf:params:xml:ns:xmpp-sasl" |
29 NsSASL = "urn:ietf:params:xml:ns:xmpp-sasl" |
29 NsBind = "urn:ietf:params:xml:ns:xmpp-bind" |
30 NsBind = "urn:ietf:params:xml:ns:xmpp-bind" |
104 // with the given password. This function will return as soon as a TCP |
105 // with the given password. This function will return as soon as a TCP |
105 // connection has been established, but before XMPP stream negotiation |
106 // connection has been established, but before XMPP stream negotiation |
106 // has completed. The negotiation will occur asynchronously, and any |
107 // has completed. The negotiation will occur asynchronously, and any |
107 // send operation to Client.Out will block until negotiation (resource |
108 // send operation to Client.Out will block until negotiation (resource |
108 // binding) is complete. |
109 // binding) is complete. |
109 func NewClient(jid *JID, password string, exts []Extension) (*Client, |
110 func NewClient(jid *JID, password string, exts []Extension) (*Client, error) { |
110 os.Error) { |
|
111 // Include the mandatory extensions. |
111 // Include the mandatory extensions. |
112 exts = append(exts, rosterExt) |
112 exts = append(exts, rosterExt) |
113 exts = append(exts, bindExt) |
113 exts = append(exts, bindExt) |
114 |
114 |
115 // Resolve the domain in the JID. |
115 // Resolve the domain in the JID. |
116 _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain) |
116 _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain) |
117 if err != nil { |
117 if err != nil { |
118 return nil, os.NewError("LookupSrv " + jid.Domain + |
118 return nil, errors.New("LookupSrv " + jid.Domain + |
119 ": " + err.String()) |
119 ": " + err.Error()) |
120 } |
120 } |
121 |
121 |
122 var tcp *net.TCPConn |
122 var tcp *net.TCPConn |
123 for _, srv := range srvs { |
123 for _, srv := range srvs { |
124 addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port) |
124 addrStr := fmt.Sprintf("%s:%d", srv.Target, srv.Port) |
125 addr, err := net.ResolveTCPAddr("tcp", addrStr) |
125 addr, err := net.ResolveTCPAddr("tcp", addrStr) |
126 if err != nil { |
126 if err != nil { |
127 err = os.NewError(fmt.Sprintf("ResolveTCPAddr(%s): %s", |
127 err = fmt.Errorf("ResolveTCPAddr(%s): %s", |
128 addrStr, err.String())) |
128 addrStr, err.Error()) |
129 continue |
129 continue |
130 } |
130 } |
131 tcp, err = net.DialTCP("tcp", nil, addr) |
131 tcp, err = net.DialTCP("tcp", nil, addr) |
132 if err != nil { |
132 if err != nil { |
133 err = os.NewError(fmt.Sprintf("DialTCP(%s): %s", |
133 err = fmt.Errorf("DialTCP(%s): %s", |
134 addr, err.String())) |
134 addr, err) |
135 continue |
135 continue |
136 } |
136 } |
137 } |
137 } |
138 if tcp == nil { |
138 if tcp == nil { |
139 return nil, err |
139 return nil, err |
269 // Start an XMPP session. A typical XMPP client should call this |
269 // Start an XMPP session. A typical XMPP client should call this |
270 // immediately after creating the Client in order to start the |
270 // immediately after creating the Client in order to start the |
271 // session, retrieve the roster, and broadcast an initial |
271 // session, retrieve the roster, and broadcast an initial |
272 // presence. The presence can be as simple as a newly-initialized |
272 // presence. The presence can be as simple as a newly-initialized |
273 // Presence struct. See RFC 3921, Section 3. |
273 // Presence struct. See RFC 3921, Section 3. |
274 func (cl *Client) StartSession(getRoster bool, pr *Presence) os.Error { |
274 func (cl *Client) StartSession(getRoster bool, pr *Presence) error { |
275 id := <-Id |
275 id := <-Id |
276 iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}} |
276 iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Nested: []interface{}{Generic{XMLName: xml.Name{Space: NsSession, Local: "session"}}}} |
277 ch := make(chan os.Error) |
277 ch := make(chan error) |
278 f := func(st Stanza) bool { |
278 f := func(st Stanza) bool { |
279 if st.GetType() == "error" { |
279 iq, ok := st.(*Iq) |
|
280 if !ok { |
|
281 if Log != nil { |
|
282 Log.Err("iq reply not iq; can't start session") |
|
283 } |
|
284 ch <- errors.New("bad session start reply") |
|
285 return false |
|
286 } |
|
287 if iq.Type == "error" { |
280 if Log != nil { |
288 if Log != nil { |
281 Log.Err(fmt.Sprintf("Can't start session: %v", |
289 Log.Err(fmt.Sprintf("Can't start session: %v", |
282 st)) |
290 iq)) |
283 } |
291 } |
284 ch <- st.GetError() |
292 ch <- iq.Error |
285 return false |
293 return false |
286 } |
294 } |
287 ch <- nil |
295 ch <- nil |
288 return false |
296 return false |
289 } |
297 } |