25
|
1 /*
|
|
2 * This program is free software; you can redistribute it and/or modify
|
|
3 * it under the terms of the GNU General Public License as published by
|
|
4 * the Free Software Foundation; either version 2 of the License, or
|
|
5 * (at your option) any later version.
|
|
6 *
|
|
7 * This program is distributed in the hope that it will be useful,
|
|
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10 * GNU General Public License for more details.
|
|
11 *
|
|
12 * You should have received a copy of the GNU General Public License
|
|
13 * along with this program; if not, write to the Free Software
|
|
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
15 *
|
|
16 * Jabber
|
|
17 * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
|
|
18 */
|
|
19
|
|
20 #include "jabber.h"
|
|
21 #include "connwrap.h"
|
|
22
|
176
|
23 #include "../src/utils.h"
|
|
24
|
25
|
25 /* local macros for launching event handlers */
|
|
26 #define STATE_EVT(arg) if(j->on_state) { (j->on_state)(j, (arg) ); }
|
|
27
|
|
28 /* prototypes of the local functions */
|
|
29 static void startElement(void *userdata, const char *name, const char **attribs);
|
|
30 static void endElement(void *userdata, const char *name);
|
|
31 static void charData(void *userdata, const char *s, int slen);
|
|
32
|
|
33 /*
|
|
34 * jab_new -- initialize a new jabber connection
|
|
35 *
|
|
36 * parameters
|
|
37 * user -- jabber id of the user
|
|
38 * pass -- password of the user
|
|
39 *
|
|
40 * results
|
|
41 * a pointer to the connection structure
|
|
42 * or NULL if allocations failed
|
|
43 */
|
|
44 jconn jab_new(char *user, char *pass, int port, int ssl)
|
|
45 {
|
|
46 pool p;
|
|
47 jconn j;
|
|
48
|
|
49 if(!user) return(NULL);
|
|
50
|
|
51 p = pool_new();
|
|
52 if(!p) return(NULL);
|
|
53 j = pmalloc_x(p, sizeof(jconn_struct), 0);
|
|
54 if(!j) return(NULL);
|
|
55 j->p = p;
|
|
56
|
|
57 j->user = jid_new(p, user);
|
|
58 j->pass = pstrdup(p, pass);
|
|
59 j->port = port;
|
|
60
|
|
61 j->state = JCONN_STATE_OFF;
|
|
62 j->cw_state = 0;
|
|
63 j->id = 1;
|
|
64 j->fd = -1;
|
|
65 j->ssl = ssl;
|
|
66
|
|
67 return j;
|
|
68 }
|
|
69
|
|
70 /*
|
|
71 * jab_delete -- free a jabber connection
|
|
72 *
|
|
73 * parameters
|
|
74 * j -- connection
|
|
75 *
|
|
76 */
|
|
77 void jab_delete(jconn j)
|
|
78 {
|
|
79 if(!j) return;
|
|
80
|
|
81 jab_stop(j);
|
|
82 pool_free(j->p);
|
|
83 }
|
|
84
|
|
85 /*
|
|
86 * jab_state_handler -- set callback handler for state change
|
|
87 *
|
|
88 * parameters
|
|
89 * j -- connection
|
|
90 * h -- name of the handler function
|
|
91 */
|
|
92 void jab_state_handler(jconn j, jconn_state_h h)
|
|
93 {
|
|
94 if(!j) return;
|
|
95
|
|
96 j->on_state = h;
|
|
97 }
|
|
98
|
|
99 /*
|
|
100 * jab_packet_handler -- set callback handler for incoming packets
|
|
101 *
|
|
102 * parameters
|
|
103 * j -- connection
|
|
104 * h -- name of the handler function
|
|
105 */
|
|
106 void jab_packet_handler(jconn j, jconn_packet_h h)
|
|
107 {
|
|
108 if(!j) return;
|
|
109
|
|
110 j->on_packet = h;
|
|
111 }
|
|
112
|
|
113 void jab_logger(jconn j, jconn_logger h)
|
|
114 {
|
|
115 if(!j) return;
|
|
116
|
|
117 j->logger = h;
|
|
118 }
|
|
119
|
|
120
|
|
121 /*
|
|
122 * jab_start -- start connection
|
|
123 *
|
|
124 * parameters
|
|
125 * j -- connection
|
|
126 *
|
|
127 */
|
|
128 void jab_start(jconn j)
|
|
129 {
|
|
130 xmlnode x;
|
|
131 char *t,*t2;
|
|
132
|
|
133 if(!j || (j->state != JCONN_STATE_OFF && j->state != JCONN_STATE_CONNECTING) ) return;
|
|
134
|
|
135 if (!(j->cw_state & CW_CONNECT_WANT_SOMETHING)) { /* same as state != JCONN_STATE_CONNECTING */
|
|
136 j->parser = XML_ParserCreate(NULL);
|
|
137 XML_SetUserData(j->parser, (void *)j);
|
|
138 XML_SetElementHandler(j->parser, startElement, endElement);
|
|
139 XML_SetCharacterDataHandler(j->parser, charData);
|
|
140
|
|
141 if (j->cw_state & CW_CONNECT_BLOCKING)
|
|
142 j->fd = make_netsocket(j->port, j->user->server, NETSOCKET_CLIENT, j->ssl);
|
|
143 else
|
|
144 j->fd = make_nb_netsocket(j->port, j->user->server, NETSOCKET_CLIENT, j->ssl, &j->cw_state);
|
|
145
|
|
146 if(j->fd < 0) {
|
|
147 STATE_EVT(JCONN_STATE_OFF);
|
|
148 return;
|
|
149 }
|
|
150 }
|
|
151 else { /* subsequent calls to cw_nb_connect until it finishes negociation */
|
|
152 if (cw_nb_connect(j->fd, 0, 0, j->ssl, &j->cw_state)) {
|
|
153 STATE_EVT(JCONN_STATE_OFF);
|
|
154 return;
|
|
155 }
|
|
156 }
|
|
157 if (j->cw_state & CW_CONNECT_WANT_SOMETHING){ /* check if it finished negociation */
|
|
158 j->state = JCONN_STATE_CONNECTING;
|
|
159 STATE_EVT(JCONN_STATE_CONNECTING);
|
|
160 return;
|
|
161 }
|
|
162 change_socket_to_blocking(j->fd);
|
|
163
|
|
164 j->state = JCONN_STATE_CONNECTED;
|
|
165 STATE_EVT(JCONN_STATE_CONNECTED)
|
|
166
|
|
167 /* start stream */
|
|
168 x = jutil_header(NS_CLIENT, j->user->server);
|
|
169 t = xmlnode2str(x);
|
|
170 /* this is ugly, we can create the string here instead of jutil_header */
|
|
171 /* what do you think about it? -madcat */
|
|
172 t2 = strstr(t,"/>");
|
|
173 *t2++ = '>';
|
|
174 *t2 = '\0';
|
|
175 jab_send_raw(j,"<?xml version='1.0'?>");
|
|
176 jab_send_raw(j,t);
|
|
177 xmlnode_free(x);
|
|
178
|
|
179 j->state = JCONN_STATE_ON;
|
|
180 STATE_EVT(JCONN_STATE_ON)
|
|
181
|
|
182 }
|
|
183
|
|
184 /*
|
|
185 * jab_stop -- stop connection
|
|
186 *
|
|
187 * parameters
|
|
188 * j -- connection
|
|
189 */
|
|
190 void jab_stop(jconn j)
|
|
191 {
|
|
192 if(!j || j->state == JCONN_STATE_OFF) return;
|
|
193
|
|
194 j->state = JCONN_STATE_OFF;
|
|
195 cw_close(j->fd);
|
|
196 j->fd = -1;
|
|
197 XML_ParserFree(j->parser);
|
|
198 }
|
|
199
|
|
200 /*
|
|
201 * jab_getfd -- get file descriptor of connection socket
|
|
202 *
|
|
203 * parameters
|
|
204 * j -- connection
|
|
205 *
|
|
206 * returns
|
|
207 * fd of the socket or -1 if socket was not connected
|
|
208 */
|
|
209 int jab_getfd(jconn j)
|
|
210 {
|
|
211 if(j)
|
|
212 return j->fd;
|
|
213 else
|
|
214 return -1;
|
|
215 }
|
|
216
|
|
217 /*
|
|
218 * jab_getjid -- get jid structure of user
|
|
219 *
|
|
220 * parameters
|
|
221 * j -- connection
|
|
222 */
|
|
223 jid jab_getjid(jconn j)
|
|
224 {
|
|
225 if(j)
|
|
226 return(j->user);
|
|
227 else
|
|
228 return NULL;
|
|
229 }
|
|
230
|
|
231 /* jab_getsid -- get stream id
|
|
232 * This is the id of server's <stream:stream> tag and used for
|
|
233 * digest authorization.
|
|
234 *
|
|
235 * parameters
|
|
236 * j -- connection
|
|
237 */
|
|
238 char *jab_getsid(jconn j)
|
|
239 {
|
|
240 if(j)
|
|
241 return(j->sid);
|
|
242 else
|
|
243 return NULL;
|
|
244 }
|
|
245
|
|
246 /*
|
|
247 * jab_getid -- get a unique id
|
|
248 *
|
|
249 * parameters
|
|
250 * j -- connection
|
|
251 */
|
|
252 char *jab_getid(jconn j)
|
|
253 {
|
|
254 snprintf(j->idbuf, 8, "%d", j->id++);
|
|
255 return &j->idbuf[0];
|
|
256 }
|
|
257
|
|
258 /*
|
|
259 * jab_send -- send xml data
|
|
260 *
|
|
261 * parameters
|
|
262 * j -- connection
|
|
263 * x -- xmlnode structure
|
|
264 */
|
|
265 void jab_send(jconn j, xmlnode x)
|
|
266 {
|
|
267 if (j && j->state != JCONN_STATE_OFF)
|
|
268 {
|
|
269 char *buf = xmlnode2str(x);
|
|
270 if (buf) {
|
|
271 cw_write(j->fd, buf, strlen(buf), j->ssl);
|
|
272 if (j->logger)
|
|
273 (j->logger)(j, 0, buf);
|
|
274 }
|
|
275
|
|
276 #ifdef JDEBUG
|
|
277 printf ("out: %s\n", buf);
|
|
278 #endif
|
|
279 }
|
|
280 }
|
|
281
|
|
282 /*
|
|
283 * jab_send_raw -- send a string
|
|
284 *
|
|
285 * parameters
|
|
286 * j -- connection
|
|
287 * str -- xml string
|
|
288 */
|
|
289 void jab_send_raw(jconn j, const char *str)
|
|
290 {
|
|
291 if (j && j->state != JCONN_STATE_OFF) {
|
|
292 cw_write(j->fd, str, strlen(str), j->ssl);
|
|
293
|
|
294 if (j->logger)
|
|
295 (j->logger)(j, 0, str);
|
|
296 }
|
|
297
|
|
298 #ifdef JDEBUG
|
|
299 printf ("out: %s\n", str);
|
|
300 #endif
|
|
301 }
|
|
302
|
|
303 /*
|
|
304 * jab_recv -- read and parse incoming data
|
|
305 *
|
|
306 * parameters
|
|
307 * j -- connection
|
|
308 */
|
|
309 void jab_recv(jconn j)
|
|
310 {
|
|
311 static char buf[32768];
|
|
312 int len;
|
|
313
|
|
314 if(!j || j->state == JCONN_STATE_OFF)
|
|
315 return;
|
|
316
|
|
317 len = cw_read(j->fd, buf, sizeof(buf)-1, j->ssl);
|
|
318 if(len>0)
|
|
319 {
|
|
320 buf[len] = '\0';
|
|
321
|
|
322 if (j->logger)
|
|
323 (j->logger)(j, 1, buf);
|
|
324
|
|
325 #ifdef JDEBUG
|
|
326 printf (" in: %s\n", buf);
|
|
327 #endif
|
|
328 XML_Parse(j->parser, buf, len, 0);
|
|
329 }
|
|
330 else if(len<=0)
|
|
331 {
|
|
332 STATE_EVT(JCONN_STATE_OFF);
|
|
333 jab_stop(j);
|
|
334 }
|
|
335 }
|
|
336
|
|
337 /*
|
|
338 * jab_poll -- check socket for incoming data
|
|
339 *
|
|
340 * parameters
|
|
341 * j -- connection
|
|
342 * timeout -- poll timeout
|
|
343 */
|
|
344 void jab_poll(jconn j, int timeout)
|
|
345 {
|
|
346 fd_set fds;
|
|
347 struct timeval tv;
|
|
348 int r;
|
|
349
|
|
350 if (!j || j->state == JCONN_STATE_OFF)
|
|
351 return;
|
|
352
|
|
353 FD_ZERO(&fds);
|
|
354 FD_SET(j->fd, &fds);
|
|
355
|
|
356 if(timeout <= 0) {
|
|
357 r = select(j->fd + 1, &fds, NULL, NULL, NULL);
|
|
358
|
|
359 } else {
|
|
360 tv.tv_sec = 0;
|
|
361 tv.tv_usec = timeout;
|
|
362 r = select(j->fd + 1, &fds, NULL, NULL, &tv);
|
|
363
|
|
364 }
|
|
365
|
|
366 if(r > 0) {
|
|
367 jab_recv(j);
|
|
368
|
|
369 } else if(r) {
|
172
|
370 /* Don't disconnect for interrupted system call */
|
|
371 if(errno == EINTR) return;
|
171
|
372
|
176
|
373 ut_WriteLog("jab_poll: select returned errno=%d\n", errno);
|
25
|
374 STATE_EVT(JCONN_STATE_OFF);
|
|
375 jab_stop(j);
|
|
376
|
|
377 }
|
|
378 }
|
|
379
|
|
380 /*
|
|
381 * jab_auth -- authorize user
|
|
382 *
|
|
383 * parameters
|
|
384 * j -- connection
|
|
385 *
|
|
386 * returns
|
|
387 * id of the iq packet
|
|
388 */
|
|
389 char *jab_auth(jconn j)
|
|
390 {
|
|
391 xmlnode x,y,z;
|
|
392 char *hash, *user, *id;
|
|
393
|
|
394 if(!j) return(NULL);
|
|
395
|
|
396 x = jutil_iqnew(JPACKET__SET, NS_AUTH);
|
|
397 id = jab_getid(j);
|
|
398 xmlnode_put_attrib(x, "id", id);
|
|
399 y = xmlnode_get_tag(x,"query");
|
|
400
|
|
401 user = j->user->user;
|
|
402
|
|
403 if (user)
|
|
404 {
|
|
405 z = xmlnode_insert_tag(y, "username");
|
|
406 xmlnode_insert_cdata(z, user, -1);
|
|
407 }
|
|
408
|
|
409 z = xmlnode_insert_tag(y, "resource");
|
|
410 xmlnode_insert_cdata(z, j->user->resource, -1);
|
|
411
|
|
412 if (j->sid)
|
|
413 {
|
|
414 z = xmlnode_insert_tag(y, "digest");
|
|
415 hash = pmalloc(x->p, strlen(j->sid)+strlen(j->pass)+1);
|
|
416 strcpy(hash, j->sid);
|
|
417 strcat(hash, j->pass);
|
|
418 hash = shahash(hash);
|
|
419 xmlnode_insert_cdata(z, hash, 40);
|
|
420 }
|
|
421 else
|
|
422 {
|
|
423 z = xmlnode_insert_tag(y, "password");
|
|
424 xmlnode_insert_cdata(z, j->pass, -1);
|
|
425 }
|
|
426
|
|
427 jab_send(j, x);
|
|
428 xmlnode_free(x);
|
|
429 return id;
|
|
430 }
|
|
431
|
|
432 /*
|
|
433 * jab_reg -- register user
|
|
434 *
|
|
435 * parameters
|
|
436 * j -- connection
|
|
437 *
|
|
438 * returns
|
|
439 * id of the iq packet
|
|
440 */
|
|
441 char *jab_reg(jconn j)
|
|
442 {
|
|
443 xmlnode x,y,z;
|
176
|
444 char *user, *id;
|
25
|
445
|
|
446 if (!j) return(NULL);
|
|
447
|
|
448 x = jutil_iqnew(JPACKET__SET, NS_REGISTER);
|
|
449 id = jab_getid(j);
|
|
450 xmlnode_put_attrib(x, "id", id);
|
|
451 y = xmlnode_get_tag(x,"query");
|
|
452
|
|
453 user = j->user->user;
|
|
454
|
|
455 if (user)
|
|
456 {
|
|
457 z = xmlnode_insert_tag(y, "username");
|
|
458 xmlnode_insert_cdata(z, user, -1);
|
|
459 }
|
|
460
|
|
461 z = xmlnode_insert_tag(y, "resource");
|
|
462 xmlnode_insert_cdata(z, j->user->resource, -1);
|
|
463
|
|
464 if (j->pass)
|
|
465 {
|
|
466 z = xmlnode_insert_tag(y, "password");
|
|
467 xmlnode_insert_cdata(z, j->pass, -1);
|
|
468 }
|
|
469
|
|
470 jab_send(j, x);
|
|
471 xmlnode_free(x);
|
|
472 j->state = JCONN_STATE_ON;
|
|
473 STATE_EVT(JCONN_STATE_ON)
|
|
474 return id;
|
|
475 }
|
|
476
|
|
477
|
|
478 /* local functions */
|
|
479
|
|
480 static void startElement(void *userdata, const char *name, const char **attribs)
|
|
481 {
|
|
482 xmlnode x;
|
|
483 jconn j = (jconn)userdata;
|
|
484
|
|
485 if(j->current)
|
|
486 {
|
|
487 /* Append the node to the current one */
|
|
488 x = xmlnode_insert_tag(j->current, name);
|
|
489 xmlnode_put_expat_attribs(x, attribs);
|
|
490
|
|
491 j->current = x;
|
|
492 }
|
|
493 else
|
|
494 {
|
|
495 x = xmlnode_new_tag(name);
|
|
496 xmlnode_put_expat_attribs(x, attribs);
|
|
497 if(strcmp(name, "stream:stream") == 0) {
|
|
498 /* special case: name == stream:stream */
|
|
499 /* id attrib of stream is stored for digest auth */
|
|
500 j->sid = xmlnode_get_attrib(x, "id");
|
|
501 /* STATE_EVT(JCONN_STATE_AUTH) */
|
|
502 } else {
|
|
503 j->current = x;
|
|
504 }
|
|
505 }
|
|
506 }
|
|
507
|
|
508 static void endElement(void *userdata, const char *name)
|
|
509 {
|
|
510 jconn j = (jconn)userdata;
|
|
511 xmlnode x;
|
|
512 jpacket p;
|
|
513
|
|
514 if(j->current == NULL) {
|
|
515 /* we got </stream:stream> */
|
|
516 STATE_EVT(JCONN_STATE_OFF)
|
|
517 return;
|
|
518 }
|
|
519
|
|
520 x = xmlnode_get_parent(j->current);
|
|
521
|
|
522 if(x == NULL)
|
|
523 {
|
|
524 /* it is time to fire the event */
|
|
525 p = jpacket_new(j->current);
|
|
526
|
|
527 if(j->on_packet)
|
|
528 (j->on_packet)(j, p);
|
|
529 else
|
|
530 xmlnode_free(j->current);
|
|
531 }
|
|
532
|
|
533 j->current = x;
|
|
534 }
|
|
535
|
|
536 static void charData(void *userdata, const char *s, int slen)
|
|
537 {
|
|
538 jconn j = (jconn)userdata;
|
|
539
|
|
540 if (j->current)
|
|
541 xmlnode_insert_cdata(j->current, s, slen);
|
|
542 }
|