Mercurial > pidgin
comparison libpurple/protocols/jabber/jabber.c @ 24056:1de1494a13e5
propagate from branch 'im.pidgin.pidgin' (head e685599ddcc769d157547685b5498df0662de8a2)
to branch 'im.pidgin.xmpp.custom_smiley' (head 110555eba89887adcf842166213ffc82770c0ee4)
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Thu, 04 Sep 2008 21:27:33 +0000 |
parents | c45d05bd58ed 3c3032be12de |
children | 305fac6af8f9 |
comparison
equal
deleted
inserted
replaced
23781:c45d05bd58ed | 24056:1de1494a13e5 |
---|---|
63 | 63 |
64 static PurplePlugin *my_protocol = NULL; | 64 static PurplePlugin *my_protocol = NULL; |
65 GList *jabber_features = NULL; | 65 GList *jabber_features = NULL; |
66 | 66 |
67 static void jabber_unregister_account_cb(JabberStream *js); | 67 static void jabber_unregister_account_cb(JabberStream *js); |
68 static void try_srv_connect(JabberStream *js); | |
68 | 69 |
69 static void jabber_stream_init(JabberStream *js) | 70 static void jabber_stream_init(JabberStream *js) |
70 { | 71 { |
71 char *open_stream; | 72 char *open_stream; |
72 | 73 |
273 } | 274 } |
274 | 275 |
275 purple_circ_buffer_mark_read(js->write_buffer, ret); | 276 purple_circ_buffer_mark_read(js->write_buffer, ret); |
276 } | 277 } |
277 | 278 |
278 void jabber_send_raw(JabberStream *js, const char *data, int len) | 279 static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len) |
279 { | 280 { |
280 int ret; | 281 int ret; |
281 | 282 gboolean success = TRUE; |
282 /* because printing a tab to debug every minute gets old */ | |
283 if(strcmp(data, "\t")) | |
284 purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n", | |
285 js->gsc ? " (ssl)" : "", data); | |
286 | |
287 /* If we've got a security layer, we need to encode the data, | |
288 * splitting it on the maximum buffer length negotiated */ | |
289 | |
290 purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data); | |
291 if (data == NULL) | |
292 return; | |
293 | |
294 #ifdef HAVE_CYRUS_SASL | |
295 if (js->sasl_maxbuf>0) { | |
296 int pos; | |
297 | |
298 if (!js->gsc && js->fd<0) | |
299 return; | |
300 pos = 0; | |
301 if (len == -1) | |
302 len = strlen(data); | |
303 while (pos < len) { | |
304 int towrite; | |
305 const char *out; | |
306 unsigned olen; | |
307 | |
308 if ((len - pos) < js->sasl_maxbuf) | |
309 towrite = len - pos; | |
310 else | |
311 towrite = js->sasl_maxbuf; | |
312 | |
313 sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); | |
314 pos += towrite; | |
315 | |
316 if (js->writeh == 0) | |
317 ret = jabber_do_send(js, out, olen); | |
318 else { | |
319 ret = -1; | |
320 errno = EAGAIN; | |
321 } | |
322 | |
323 if (ret < 0 && errno != EAGAIN) | |
324 purple_connection_error_reason (js->gc, | |
325 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | |
326 _("Write error")); | |
327 else if (ret < olen) { | |
328 if (ret < 0) | |
329 ret = 0; | |
330 if (js->writeh == 0) | |
331 js->writeh = purple_input_add( | |
332 js->gsc ? js->gsc->fd : js->fd, | |
333 PURPLE_INPUT_WRITE, | |
334 jabber_send_cb, js); | |
335 purple_circ_buffer_append(js->write_buffer, | |
336 out + ret, olen - ret); | |
337 } | |
338 } | |
339 return; | |
340 } | |
341 #endif | |
342 | 283 |
343 if (len == -1) | 284 if (len == -1) |
344 len = strlen(data); | 285 len = strlen(data); |
345 | 286 |
346 if (js->writeh == 0) | 287 if (js->writeh == 0) |
348 else { | 289 else { |
349 ret = -1; | 290 ret = -1; |
350 errno = EAGAIN; | 291 errno = EAGAIN; |
351 } | 292 } |
352 | 293 |
353 if (ret < 0 && errno != EAGAIN) | 294 if (ret < 0 && errno != EAGAIN) { |
354 purple_connection_error_reason (js->gc, | 295 purple_connection_error_reason (js->gc, |
355 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 296 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
356 _("Write error")); | 297 _("Write error")); |
357 else if (ret < len) { | 298 success = FALSE; |
299 } else if (ret < len) { | |
358 if (ret < 0) | 300 if (ret < 0) |
359 ret = 0; | 301 ret = 0; |
360 if (js->writeh == 0) | 302 if (js->writeh == 0) |
361 js->writeh = purple_input_add( | 303 js->writeh = purple_input_add( |
362 js->gsc ? js->gsc->fd : js->fd, | 304 js->gsc ? js->gsc->fd : js->fd, |
363 PURPLE_INPUT_WRITE, jabber_send_cb, js); | 305 PURPLE_INPUT_WRITE, jabber_send_cb, js); |
364 purple_circ_buffer_append(js->write_buffer, | 306 purple_circ_buffer_append(js->write_buffer, |
365 data + ret, len - ret); | 307 data + ret, len - ret); |
366 } | 308 } |
367 return; | 309 |
310 return success; | |
311 } | |
312 | |
313 void jabber_send_raw(JabberStream *js, const char *data, int len) | |
314 { | |
315 | |
316 /* because printing a tab to debug every minute gets old */ | |
317 if(strcmp(data, "\t")) | |
318 purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n", | |
319 js->gsc ? " (ssl)" : "", data); | |
320 | |
321 /* If we've got a security layer, we need to encode the data, | |
322 * splitting it on the maximum buffer length negotiated */ | |
323 | |
324 purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data); | |
325 if (data == NULL) | |
326 return; | |
327 | |
328 #ifdef HAVE_CYRUS_SASL | |
329 if (js->sasl_maxbuf>0) { | |
330 int pos = 0; | |
331 | |
332 if (!js->gsc && js->fd<0) | |
333 return; | |
334 | |
335 if (len == -1) | |
336 len = strlen(data); | |
337 | |
338 while (pos < len) { | |
339 int towrite; | |
340 const char *out; | |
341 unsigned olen; | |
342 | |
343 towrite = MIN((len - pos), js->sasl_maxbuf); | |
344 | |
345 sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); | |
346 pos += towrite; | |
347 | |
348 if (!do_jabber_send_raw(js, out, olen)) | |
349 break; | |
350 } | |
351 return; | |
352 } | |
353 #endif | |
354 | |
355 do_jabber_send_raw(js, data, len); | |
368 } | 356 } |
369 | 357 |
370 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) | 358 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) |
371 { | 359 { |
372 JabberStream *js = (JabberStream*)gc->proto_data; | 360 JabberStream *js = (JabberStream*)gc->proto_data; |
388 txt = xmlnode_to_str(packet, &len); | 376 txt = xmlnode_to_str(packet, &len); |
389 jabber_send_raw(js, txt, len); | 377 jabber_send_raw(js, txt, len); |
390 g_free(txt); | 378 g_free(txt); |
391 } | 379 } |
392 | 380 |
393 static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer timeout) | 381 static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused) |
394 { | 382 { |
395 purple_timeout_remove(GPOINTER_TO_INT(timeout)); | 383 purple_timeout_remove(js->keepalive_timeout); |
396 js->keepalive_timeout = -1; | 384 js->keepalive_timeout = -1; |
397 } | 385 } |
398 | 386 |
399 static gboolean jabber_pong_timeout(PurpleConnection *gc) | 387 static gboolean jabber_pong_timeout(PurpleConnection *gc) |
400 { | 388 { |
414 | 402 |
415 xmlnode *ping = xmlnode_new_child(iq->node, "ping"); | 403 xmlnode *ping = xmlnode_new_child(iq->node, "ping"); |
416 xmlnode_set_namespace(ping, "urn:xmpp:ping"); | 404 xmlnode_set_namespace(ping, "urn:xmpp:ping"); |
417 | 405 |
418 js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc); | 406 js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc); |
419 jabber_iq_set_callback(iq, jabber_pong_cb, GINT_TO_POINTER(js->keepalive_timeout)); | 407 jabber_iq_set_callback(iq, jabber_pong_cb, NULL); |
420 jabber_iq_send(iq); | 408 jabber_iq_send(iq); |
421 } | 409 } |
422 } | 410 } |
423 | 411 |
424 static void | 412 static void |
443 jabber_parser_process(js, buf, len); | 431 jabber_parser_process(js, buf, len); |
444 if(js->reinit) | 432 if(js->reinit) |
445 jabber_stream_init(js); | 433 jabber_stream_init(js); |
446 } | 434 } |
447 | 435 |
448 if(errno == EAGAIN) | 436 if(len < 0 && errno == EAGAIN) |
449 return; | 437 return; |
450 else | 438 else { |
439 if (len == 0) | |
440 purple_debug_info("jabber", "Server closed the connection.\n"); | |
441 else | |
442 purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno)); | |
451 purple_connection_error_reason (js->gc, | 443 purple_connection_error_reason (js->gc, |
452 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 444 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
453 _("Read Error")); | 445 _("Read Error")); |
446 } | |
454 } | 447 } |
455 | 448 |
456 static void | 449 static void |
457 jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition) | 450 jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition) |
458 { | 451 { |
483 buf[len] = '\0'; | 476 buf[len] = '\0'; |
484 purple_debug(PURPLE_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf); | 477 purple_debug(PURPLE_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf); |
485 jabber_parser_process(js, buf, len); | 478 jabber_parser_process(js, buf, len); |
486 if(js->reinit) | 479 if(js->reinit) |
487 jabber_stream_init(js); | 480 jabber_stream_init(js); |
488 } else if(errno == EAGAIN) { | 481 } else if(len < 0 && errno == EAGAIN) { |
489 return; | 482 return; |
490 } else { | 483 } else { |
484 if (len == 0) | |
485 purple_debug_info("jabber", "Server closed the connection.\n"); | |
486 else | |
487 purple_debug_info("jabber", "Disconnected: %s\n", g_strerror(errno)); | |
491 purple_connection_error_reason (js->gc, | 488 purple_connection_error_reason (js->gc, |
492 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 489 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
493 _("Read Error")); | 490 _("Read Error")); |
494 } | 491 } |
495 } | 492 } |
524 { | 521 { |
525 PurpleConnection *gc = data; | 522 PurpleConnection *gc = data; |
526 JabberStream *js = gc->proto_data; | 523 JabberStream *js = gc->proto_data; |
527 | 524 |
528 if (source < 0) { | 525 if (source < 0) { |
529 gchar *tmp; | 526 if (js->srv_rec != NULL) { |
530 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), | 527 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); |
531 error); | 528 try_srv_connect(js); |
532 purple_connection_error_reason (gc, | 529 } else { |
533 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); | 530 gchar *tmp; |
534 g_free(tmp); | 531 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), |
532 error); | |
533 purple_connection_error_reason(gc, | |
534 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); | |
535 g_free(tmp); | |
536 } | |
535 return; | 537 return; |
536 } | 538 } |
539 | |
540 g_free(js->srv_rec); | |
541 js->srv_rec = NULL; | |
537 | 542 |
538 js->fd = source; | 543 js->fd = source; |
539 | 544 |
540 if(js->state == JABBER_STREAM_CONNECTING) | 545 if(js->state == JABBER_STREAM_CONNECTING) |
541 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); | 546 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
567 js->gc->inpa = 0; | 572 js->gc->inpa = 0; |
568 js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd, | 573 js->gsc = purple_ssl_connect_with_host_fd(js->gc->account, js->fd, |
569 jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc); | 574 jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc); |
570 } | 575 } |
571 | 576 |
572 static void jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port) | 577 static gboolean jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port, |
578 gboolean fatal_failure) | |
573 { | 579 { |
574 /* host should be used in preference to domain to | 580 /* host should be used in preference to domain to |
575 * allow SASL authentication to work with FQDN of the server, | 581 * allow SASL authentication to work with FQDN of the server, |
576 * but we use domain as fallback for when users enter IP address | 582 * but we use domain as fallback for when users enter IP address |
577 * in connect server */ | 583 * in connect server */ |
584 g_free(js->serverFQDN); | |
578 if (purple_ip_address_is_valid(host)) | 585 if (purple_ip_address_is_valid(host)) |
579 js->serverFQDN = g_strdup(domain); | 586 js->serverFQDN = g_strdup(domain); |
580 else | 587 else |
581 js->serverFQDN = g_strdup(host); | 588 js->serverFQDN = g_strdup(host); |
582 | 589 |
583 if (purple_proxy_connect(js->gc, js->gc->account, host, | 590 if (purple_proxy_connect(js->gc, js->gc->account, host, |
584 port, jabber_login_callback, js->gc) == NULL) | 591 port, jabber_login_callback, js->gc) == NULL) { |
585 purple_connection_error_reason (js->gc, | 592 if (fatal_failure) { |
586 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, | 593 purple_connection_error_reason (js->gc, |
587 _("Unable to create socket")); | 594 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
595 _("Unable to create socket")); | |
596 } | |
597 | |
598 return FALSE; | |
599 } | |
600 | |
601 return TRUE; | |
602 } | |
603 | |
604 static void try_srv_connect(JabberStream *js) | |
605 { | |
606 while (js->srv_rec != NULL && js->srv_rec_idx < js->max_srv_rec_idx) { | |
607 PurpleSrvResponse *tmp_resp = js->srv_rec + (js->srv_rec_idx++); | |
608 if (jabber_login_connect(js, tmp_resp->hostname, tmp_resp->hostname, tmp_resp->port, FALSE)) | |
609 return; | |
610 } | |
611 | |
612 g_free(js->srv_rec); | |
613 js->srv_rec = NULL; | |
614 | |
615 /* Fall back to the defaults (I'm not sure if we should actually do this) */ | |
616 jabber_login_connect(js, js->user->domain, js->user->domain, | |
617 purple_account_get_int(js->gc->account, "port", 5222), TRUE); | |
588 } | 618 } |
589 | 619 |
590 static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data) | 620 static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data) |
591 { | 621 { |
592 JabberStream *js; | 622 JabberStream *js = data; |
593 | |
594 js = data; | |
595 js->srv_query_data = NULL; | 623 js->srv_query_data = NULL; |
596 | 624 |
597 if(results) { | 625 if(results) { |
598 jabber_login_connect(js, resp->hostname, resp->hostname, resp->port); | 626 js->srv_rec = resp; |
599 g_free(resp); | 627 js->srv_rec_idx = 0; |
628 js->max_srv_rec_idx = results; | |
629 try_srv_connect(js); | |
600 } else { | 630 } else { |
601 jabber_login_connect(js, js->user->domain, js->user->domain, | 631 jabber_login_connect(js, js->user->domain, js->user->domain, |
602 purple_account_get_int(js->gc->account, "port", 5222)); | 632 purple_account_get_int(js->gc->account, "port", 5222), TRUE); |
603 } | 633 } |
604 } | 634 } |
605 | 635 |
606 void | 636 void |
607 jabber_login(PurpleAccount *account) | 637 jabber_login(PurpleAccount *account) |
680 | 710 |
681 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll | 711 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
682 * invoke the magic of SRV lookups, to figure out host and port */ | 712 * invoke the magic of SRV lookups, to figure out host and port */ |
683 if(!js->gsc) { | 713 if(!js->gsc) { |
684 if(connect_server[0]) { | 714 if(connect_server[0]) { |
685 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222)); | 715 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
686 } else { | 716 } else { |
687 js->srv_query_data = purple_srv_resolve("xmpp-client", | 717 js->srv_query_data = purple_srv_resolve("xmpp-client", |
688 "tcp", js->user->domain, srv_resolved_cb, js); | 718 "tcp", js->user->domain, srv_resolved_cb, js); |
689 } | 719 } |
690 } | 720 } |
1161 | 1191 |
1162 if(!js->gsc) { | 1192 if(!js->gsc) { |
1163 if (connect_server[0]) { | 1193 if (connect_server[0]) { |
1164 jabber_login_connect(js, js->user->domain, server, | 1194 jabber_login_connect(js, js->user->domain, server, |
1165 purple_account_get_int(account, | 1195 purple_account_get_int(account, |
1166 "port", 5222)); | 1196 "port", 5222), TRUE); |
1167 } else { | 1197 } else { |
1168 js->srv_query_data = purple_srv_resolve("xmpp-client", | 1198 js->srv_query_data = purple_srv_resolve("xmpp-client", |
1169 "tcp", | 1199 "tcp", |
1170 js->user->domain, | 1200 js->user->domain, |
1171 srv_resolved_cb, | 1201 srv_resolved_cb, |
1291 g_free(sh->jid); | 1321 g_free(sh->jid); |
1292 g_free(sh->host); | 1322 g_free(sh->host); |
1293 g_free(sh->zeroconf); | 1323 g_free(sh->zeroconf); |
1294 g_free(sh); | 1324 g_free(sh); |
1295 js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies); | 1325 js->bs_proxies = g_list_delete_link(js->bs_proxies, js->bs_proxies); |
1326 } | |
1327 | |
1328 while(js->url_datas) { | |
1329 purple_util_fetch_url_cancel(js->url_datas->data); | |
1330 js->url_datas = g_slist_delete_link(js->url_datas, js->url_datas); | |
1296 } | 1331 } |
1297 | 1332 |
1298 g_free(js->stream_id); | 1333 g_free(js->stream_id); |
1299 if(js->user) | 1334 if(js->user) |
1300 jabber_id_free(js->user); | 1335 jabber_id_free(js->user); |
1332 g_free(js->old_track); | 1367 g_free(js->old_track); |
1333 g_free(js->expected_rspauth); | 1368 g_free(js->expected_rspauth); |
1334 | 1369 |
1335 if (js->keepalive_timeout != -1) | 1370 if (js->keepalive_timeout != -1) |
1336 purple_timeout_remove(js->keepalive_timeout); | 1371 purple_timeout_remove(js->keepalive_timeout); |
1337 | 1372 |
1373 g_free(js->srv_rec); | |
1374 js->srv_rec = NULL; | |
1375 | |
1338 g_free(js); | 1376 g_free(js); |
1339 | 1377 |
1340 gc->proto_data = NULL; | 1378 gc->proto_data = NULL; |
1341 } | 1379 } |
1342 | 1380 |