58 str := fmt.Sprintf("id_%d", id) |
58 str := fmt.Sprintf("id_%d", id) |
59 ch <- str |
59 ch <- str |
60 id++ |
60 id++ |
61 } |
61 } |
62 }(idCh) |
62 }(idCh) |
|
63 } |
|
64 |
|
65 // Extensions can add stanza filters and/or new XML element types. |
|
66 type Extension struct { |
|
67 StanzaHandlers map[string] func(*xml.Name) interface{} |
|
68 Start func(*Client) |
63 } |
69 } |
64 |
70 |
65 // The client in a client-server XMPP connection. |
71 // The client in a client-server XMPP connection. |
66 type Client struct { |
72 type Client struct { |
67 // This client's unique ID. It's unique within the context of |
73 // This client's unique ID. It's unique within the context of |
94 filterOut chan<- <-chan Stanza |
100 filterOut chan<- <-chan Stanza |
95 filterIn <-chan <-chan Stanza |
101 filterIn <-chan <-chan Stanza |
96 } |
102 } |
97 var _ io.Closer = &Client{} |
103 var _ io.Closer = &Client{} |
98 |
104 |
99 // BUG(cjyar): Replace extStanza with a generalized extension interface |
|
100 // that handles starting filters, registering extended stanzes, and |
|
101 // anything else an extension has to do. |
|
102 |
|
103 // Connect to the appropriate server and authenticate as the given JID |
105 // Connect to the appropriate server and authenticate as the given JID |
104 // with the given password. This function will return as soon as a TCP |
106 // with the given password. This function will return as soon as a TCP |
105 // connection has been established, but before XMPP stream negotiation |
107 // connection has been established, but before XMPP stream negotiation |
106 // has completed. The negotiation will occur asynchronously, and any |
108 // has completed. The negotiation will occur asynchronously, and any |
107 // send operation to Client.Out will block until negotiation (resource |
109 // send operation to Client.Out will block until negotiation (resource |
108 // binding) is complete. |
110 // binding) is complete. |
109 func NewClient(jid *JID, password string, |
111 func NewClient(jid *JID, password string, exts []Extension) (*Client, |
110 extStanza map[string] func(*xml.Name) interface{}) (*Client, os.Error) { |
112 os.Error) { |
|
113 // Include the mandatory extensions. |
|
114 exts = append(exts, rosterExt) |
|
115 exts = append(exts, bindExt) |
|
116 |
111 // Resolve the domain in the JID. |
117 // Resolve the domain in the JID. |
112 _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain) |
118 _, srvs, err := net.LookupSRV(clientSrv, "tcp", jid.Domain) |
113 if err != nil { |
119 if err != nil { |
114 return nil, os.NewError("LookupSrv " + jid.Domain + |
120 return nil, os.NewError("LookupSrv " + jid.Domain + |
115 ": " + err.String()) |
121 ": " + err.String()) |
141 cl.Jid = *jid |
147 cl.Jid = *jid |
142 cl.socket = tcp |
148 cl.socket = tcp |
143 cl.handlers = make(chan *stanzaHandler, 100) |
149 cl.handlers = make(chan *stanzaHandler, 100) |
144 cl.inputControl = make(chan int) |
150 cl.inputControl = make(chan int) |
145 |
151 |
146 if extStanza == nil { |
152 extStanza := make(map[string] func(*xml.Name) interface{}) |
147 extStanza = make(map[string] func(*xml.Name) interface{}) |
153 for _, ext := range(exts) { |
148 } |
154 for k, v := range(ext.StanzaHandlers) { |
149 extStanza[NsRoster] = newRosterQuery |
155 extStanza[k] = v |
150 extStanza[NsBind] = newBind |
156 } |
|
157 } |
151 |
158 |
152 // Start the transport handler, initially unencrypted. |
159 // Start the transport handler, initially unencrypted. |
153 tlsr, tlsw := cl.startTransport() |
160 tlsr, tlsw := cl.startTransport() |
154 |
161 |
155 // Start the reader and writers that convert to and from XML. |
162 // Start the reader and writers that convert to and from XML. |
158 |
165 |
159 // Start the XMPP stream handler which filters stream-level |
166 // Start the XMPP stream handler which filters stream-level |
160 // events and responds to them. |
167 // events and responds to them. |
161 stIn := cl.startStreamReader(xmlIn, cl.xmlOut) |
168 stIn := cl.startStreamReader(xmlIn, cl.xmlOut) |
162 clOut := cl.startStreamWriter(cl.xmlOut) |
169 clOut := cl.startStreamWriter(cl.xmlOut) |
|
170 cl.Out = clOut |
163 |
171 |
164 // Start the manager for the filters that can modify what the |
172 // Start the manager for the filters that can modify what the |
165 // app sees. |
173 // app sees. |
166 clIn := cl.startFilter(stIn) |
174 clIn := cl.startFilter(stIn) |
167 startRosterFilter(cl) |
175 cl.In = clIn |
|
176 |
|
177 // Add filters for our extensions. |
|
178 for _, ext := range(exts) { |
|
179 ext.Start(cl) |
|
180 } |
168 |
181 |
169 // Initial handshake. |
182 // Initial handshake. |
170 hsOut := &stream{To: jid.Domain, Version: Version} |
183 hsOut := &stream{To: jid.Domain, Version: Version} |
171 cl.xmlOut <- hsOut |
184 cl.xmlOut <- hsOut |
172 |
|
173 cl.In = clIn |
|
174 cl.Out = clOut |
|
175 |
185 |
176 return cl, nil |
186 return cl, nil |
177 } |
187 } |
178 |
188 |
179 func (c *Client) Close() os.Error { |
189 func (c *Client) Close() os.Error { |