| 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 { |
| 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 } |