comparison libpurple/protocols/bonjour/jabber.c @ 21829:cc0809ec0c85

This is the second part of the fix to support having multiple presence records in Bonjour (and also supporting multiple presences using the same IP). Incoming conversations are no longer immediately attached to a buddy - we use an algorithm to match a conversation to a buddy based on the IP and, if possible the "from" attribute of the stream or the first tag in the stream. Thanks to Sjoerd Simons from Telepahy Salut for noticing and suggesting the algorithm. Fixes #4187. It'd be good if a few people test this.
author Daniel Atallah <daniel.atallah@gmail.com>
date Fri, 14 Dec 2007 21:44:34 +0000
parents 509871e4f61c
children 603d5325af4c
comparison
equal deleted inserted replaced
21828:85fcbd3b9d32 21829:cc0809ec0c85
76 76
77 static void 77 static void
78 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb); 78 xep_iq_parse(xmlnode *packet, PurpleConnection *connection, PurpleBuddy *pb);
79 79
80 static BonjourJabberConversation * 80 static BonjourJabberConversation *
81 bonjour_jabber_conv_new(PurpleBuddy *pb) { 81 bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
82 82
83 BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1); 83 BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
84 bconv->socket = -1; 84 bconv->socket = -1;
85 bconv->tx_buf = purple_circ_buffer_new(512); 85 bconv->tx_buf = purple_circ_buffer_new(512);
86 bconv->tx_handler = 0; 86 bconv->tx_handler = 0;
87 bconv->rx_handler = 0; 87 bconv->rx_handler = 0;
88 bconv->pb = pb; 88 bconv->pb = pb;
89 bconv->account = account;
90 bconv->ip = g_strdup(ip);
89 91
90 bonjour_parser_setup(bconv); 92 bonjour_parser_setup(bconv);
91 93
92 return bconv; 94 return bconv;
93 } 95 }
94
95 96
96 static const char * 97 static const char *
97 _font_size_ichat_to_purple(int size) 98 _font_size_ichat_to_purple(int size)
98 { 99 {
99 if (size > 24) { 100 if (size > 24) {
202 serv_got_im(gc, pb->name, body, 0, time(NULL)); 203 serv_got_im(gc, pb->name, body, 0, time(NULL));
203 204
204 g_free(body); 205 g_free(body);
205 } 206 }
206 207
207 struct _check_buddy_by_address_t { 208 struct _match_buddies_by_address_t {
208 const char *address; 209 const char *address;
209 PurpleBuddy **pb; 210 GSList *matched_buddies;
210 BonjourJabber *bj; 211 BonjourJabber *jdata;
211 }; 212 };
212 213
213 static void 214 static void
214 _check_buddy_by_address(gpointer key, gpointer value, gpointer data) 215 _match_buddies_by_address(gpointer key, gpointer value, gpointer data)
215 { 216 {
216 PurpleBuddy *pb = value; 217 PurpleBuddy *pb = value;
217 BonjourBuddy *bb; 218 struct _match_buddies_by_address_t *mbba = data;
218 struct _check_buddy_by_address_t *cbba = data;
219 219
220 /* 220 /*
221 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account 221 * If the current PurpleBuddy's data is not null and the PurpleBuddy's account
222 * is the same as the account requesting the check then continue to determine 222 * is the same as the account requesting the check then continue to determine
223 * whether one of the buddies IPs matches the target IP. 223 * whether one of the buddies IPs matches the target IP.
224 */ 224 */
225 if (cbba->bj->account == pb->account) 225 if (mbba->jdata->account == pb->account && pb->proto_data != NULL)
226 { 226 {
227 bb = pb->proto_data; 227 const char *ip;
228 if (bb != NULL) { 228 BonjourBuddy *bb = pb->proto_data;
229 const char *ip; 229 GSList *tmp = bb->ips;
230 GSList *tmp = bb->ips; 230
231 231 while(tmp) {
232 while(tmp) { 232 ip = tmp->data;
233 ip = tmp->data; 233 if (ip != NULL && g_ascii_strcasecmp(ip, mbba->address) == 0) {
234 if (ip != NULL && g_ascii_strcasecmp(ip, cbba->address) == 0) { 234 mbba->matched_buddies = g_slist_prepend(mbba->matched_buddies, pb);
235 *(cbba->pb) = pb; 235 break;
236 break;
237 }
238 tmp = tmp->next;
239 } 236 }
237 tmp = tmp->next;
240 } 238 }
241 } 239 }
242 } 240 }
243 241
244 static void 242 static void
246 { 244 {
247 PurpleBuddy *pb = data; 245 PurpleBuddy *pb = data;
248 BonjourBuddy *bb = pb->proto_data; 246 BonjourBuddy *bb = pb->proto_data;
249 BonjourJabberConversation *bconv = bb->conversation; 247 BonjourJabberConversation *bconv = bb->conversation;
250 int ret, writelen; 248 int ret, writelen;
251
252 /* TODO: Make sure that the stream has been established before sending */
253 249
254 writelen = purple_circ_buffer_get_max_read(bconv->tx_buf); 250 writelen = purple_circ_buffer_get_max_read(bconv->tx_buf);
255 251
256 if (writelen == 0) { 252 if (writelen == 0) {
257 purple_input_remove(bconv->tx_handler); 253 purple_input_remove(bconv->tx_handler);
336 } 332 }
337 333
338 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) { 334 void bonjour_jabber_process_packet(PurpleBuddy *pb, xmlnode *packet) {
339 335
340 g_return_if_fail(packet != NULL); 336 g_return_if_fail(packet != NULL);
337 g_return_if_fail(pb != NULL);
341 338
342 if (!strcmp(packet->name, "message")) 339 if (!strcmp(packet->name, "message"))
343 _jabber_parse_and_write_message_to_ui(packet, pb); 340 _jabber_parse_and_write_message_to_ui(packet, pb);
344 else if(!strcmp(packet->name, "iq")) 341 else if(!strcmp(packet->name, "iq"))
345 xep_iq_parse(packet, NULL, pb); 342 xep_iq_parse(packet, NULL, pb);
349 346
350 347
351 static void 348 static void
352 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition) 349 _client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
353 { 350 {
354 PurpleBuddy *pb = data; 351 BonjourJabberConversation *bconv = data;
355 gint len, message_length; 352 gint len, message_length;
356 static char message[4096]; 353 static char message[4096];
357
358 /*TODO: use a static buffer */
359 354
360 /* Read the data from the socket */ 355 /* Read the data from the socket */
361 if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) { 356 if ((len = recv(socket, message, sizeof(message) - 1, 0)) == -1) {
362 /* There have been an error reading from the socket */ 357 /* There have been an error reading from the socket */
363 if (errno != EAGAIN) { 358 if (errno != EAGAIN) {
364 BonjourBuddy *bb = pb->proto_data;
365 const char *err = g_strerror(errno); 359 const char *err = g_strerror(errno);
366 360
367 purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)"); 361 purple_debug_warning("bonjour", "receive error: %s\n", err ? err : "(null)");
368 362
369 bonjour_jabber_close_conversation(bb->conversation); 363 bonjour_jabber_close_conversation(bconv);
370 bb->conversation = NULL; 364 if (bconv->pb != NULL) {
365 BonjourBuddy *bb = bconv->pb->proto_data;
366 bb->conversation = NULL;
367 }
371 368
372 /* I guess we really don't need to notify the user. 369 /* I guess we really don't need to notify the user.
373 * If they try to send another message it'll reconnect */ 370 * If they try to send another message it'll reconnect */
374 } 371 }
375 return; 372 return;
376 } else if (len == 0) { /* The other end has closed the socket */ 373 } else if (len == 0) { /* The other end has closed the socket */
377 purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", pb->name ? pb->name : "(null)"); 374 purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (bconv->pb && bconv->pb->name) ? bconv->pb->name : "(unknown)");
378 bonjour_jabber_stream_ended(pb); 375 bonjour_jabber_stream_ended(bconv);
379 return; 376 return;
380 } else { 377 } else {
381 message_length = len; 378 message_length = len;
382 message[message_length] = '\0'; 379 message[message_length] = '\0';
383 380
387 } 384 }
388 } 385 }
389 386
390 purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len); 387 purple_debug_info("bonjour", "Receive: -%s- %d bytes\n", message, len);
391 388
392 bonjour_parser_process(pb, message, message_length); 389 bonjour_parser_process(bconv, message, message_length);
393 } 390 }
394 391
395 void bonjour_jabber_stream_ended(PurpleBuddy *pb) { 392 void bonjour_jabber_stream_ended(BonjourJabberConversation *bconv) {
396 BonjourBuddy *bb = pb->proto_data; 393
397 394 purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", bconv->pb ? bconv->pb->name : "(unknown)");
398 purple_debug_info("bonjour", "Recieved conversation close notification from %s.\n", pb->name);
399
400 g_return_if_fail(bb != NULL);
401 395
402 /* Inform the user that the conversation has been closed */ 396 /* Inform the user that the conversation has been closed */
403 if (bb->conversation != NULL) { 397 if (bconv != NULL) {
398 BonjourBuddy *bb = NULL;
399
400 if(bconv->pb != NULL)
401 bb = bconv->pb->proto_data;
404 #if 0 402 #if 0
405 PurpleConversation *conv; 403 if(bconv->pb != NULL) {
406 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, pb->name, pb->account); 404 PurpleConversation *conv;
407 if (conv != NULL) { 405 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bconv->pb->name, bconv->pb->account);
408 char *tmp = g_strdup_printf(_("%s has closed the conversation."), pb->name); 406 if (conv != NULL) {
409 purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); 407 char *tmp = g_strdup_printf(_("%s has closed the conversation."), bconv->pb->name);
410 g_free(tmp); 408 purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
409 g_free(tmp);
410 }
411 } 411 }
412 #endif 412 #endif
413 /* Close the socket, clear the watcher and free memory */ 413 /* Close the socket, clear the watcher and free memory */
414 bonjour_jabber_close_conversation(bb->conversation); 414 bonjour_jabber_close_conversation(bconv);
415 bb->conversation = NULL; 415 if(bb)
416 bb->conversation = NULL;
416 } 417 }
417 } 418 }
418 419
419 420
420 struct _stream_start_data { 421 struct _stream_start_data {
423 424
424 425
425 static void 426 static void
426 _start_stream(gpointer data, gint source, PurpleInputCondition condition) 427 _start_stream(gpointer data, gint source, PurpleInputCondition condition)
427 { 428 {
428 PurpleBuddy *pb = data; 429 BonjourJabberConversation *bconv = data;
429 BonjourBuddy *bb = pb->proto_data;
430 BonjourJabberConversation *bconv = bb->conversation;
431 struct _stream_start_data *ss = bconv->stream_data; 430 struct _stream_start_data *ss = bconv->stream_data;
432 int len, ret; 431 int len, ret;
433 432
434 len = strlen(ss->msg); 433 len = strlen(ss->msg);
435 434
439 if (ret == -1 && errno == EAGAIN) 438 if (ret == -1 && errno == EAGAIN)
440 return; 439 return;
441 else if (ret <= 0) { 440 else if (ret <= 0) {
442 const char *err = g_strerror(errno); 441 const char *err = g_strerror(errno);
443 PurpleConversation *conv; 442 PurpleConversation *conv;
444 const char *ip = NULL; 443 const char *bname = bconv->buddy_name;
445 444 BonjourBuddy *bb = NULL;
446 /* For better or worse, use the first IP*/ 445
447 if (bb->ips) 446 if(bconv->pb) {
448 ip = bb->ips->data; 447 bb = bconv->pb->proto_data;
449 448 bname = purple_buddy_get_name(bconv->pb);
450 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", 449 }
451 purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)"); 450
451 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
452 bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
453
454 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
455 if (conv != NULL)
456 purple_conversation_write(conv, NULL,
457 _("Unable to send the message, the conversation couldn't be started."),
458 PURPLE_MESSAGE_SYSTEM, time(NULL));
459
460 bonjour_jabber_close_conversation(bconv);
461 if(bb != NULL)
462 bb->conversation = NULL;
463
464 return;
465 }
466
467 /* This is EXTREMELY unlikely to happen */
468 if (ret < len) {
469 char *tmp = g_strdup(ss->msg + ret);
470 g_free(ss->msg);
471 ss->msg = tmp;
472 return;
473 }
474
475 g_free(ss->msg);
476 g_free(ss);
477 bconv->stream_data = NULL;
478
479 /* Stream started; process the send buffer if there is one */
480 purple_input_remove(bconv->tx_handler);
481 bconv->tx_handler = 0;
482 bconv->sent_stream_start = FULLY_SENT;
483
484 bonjour_jabber_stream_started(bconv);
485 }
486
487 static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv, int client_socket)
488 {
489 int ret, len;
490 char *stream_start;
491 const char *bname = bconv->buddy_name;
492
493 if (bconv->pb != NULL)
494 bname = purple_buddy_get_name(bconv->pb);
495
496 /* If we have no idea who "to" is, use an empty string.
497 * If we don't know now, it is because the other side isn't playing nice, so they can't complain. */
498 if (bname == NULL)
499 bname = "";
500
501 stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(bconv->account), bname);
502 len = strlen(stream_start);
503
504 bconv->sent_stream_start = PARTIALLY_SENT;
505
506 /* Start the stream */
507 ret = send(client_socket, stream_start, len, 0);
508
509 if (ret == -1 && errno == EAGAIN)
510 ret = 0;
511 else if (ret <= 0) {
512 const char *err = g_strerror(errno);
513
514 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
515 (*bname) ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
516
517 if (bconv->pb) {
518 PurpleConversation *conv;
519 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
520 if (conv != NULL)
521 purple_conversation_write(conv, NULL,
522 _("Unable to send the message, the conversation couldn't be started."),
523 PURPLE_MESSAGE_SYSTEM, time(NULL));
524 }
525
526 close(client_socket);
527 g_free(stream_start);
528
529 return FALSE;
530 }
531
532 /* This is unlikely to happen */
533 if (ret < len) {
534 struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
535 ss->msg = g_strdup(stream_start + ret);
536 bconv->stream_data = ss;
537 /* Finish sending the stream start */
538 bconv->tx_handler = purple_input_add(client_socket,
539 PURPLE_INPUT_WRITE, _start_stream, bconv);
540 } else
541 bconv->sent_stream_start = FULLY_SENT;
542
543 g_free(stream_start);
544
545 return TRUE;
546 }
547
548 /* This gets called when we've successfully sent our <stream:stream />
549 * AND when we've recieved a <stream:stream /> */
550 void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
551
552 if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
553 const char *err = g_strerror(errno);
554 const char *bname = bconv->buddy_name;
555
556 if (bconv->pb)
557 bname = purple_buddy_get_name(bconv->pb);
558
559 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
560 bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
561
562 if (bconv->pb) {
563 PurpleConversation *conv;
564 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bname, bconv->account);
565 if (conv != NULL)
566 purple_conversation_write(conv, NULL,
567 _("Unable to send the message, the conversation couldn't be started."),
568 PURPLE_MESSAGE_SYSTEM, time(NULL));
569 }
570
571 /* We don't want to recieve anything else */
572 close(bconv->socket);
573 bconv->socket = -1;
574
575 /* This must be asynchronous because it destroys the parser and we
576 * may be in the middle of parsing.
577 */
578 async_bonjour_jabber_close_conversation(bconv);
579 return;
580 }
581
582 /* If the stream has been completely started and we know who we're talking to, we can start doing stuff. */
583 /* I don't think the circ_buffer can actually contain anything without a buddy being associated, but lets be explicit. */
584 if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
585 && bconv->pb && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) {
586 /* Watch for when we can write the buffered messages */
587 bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
588 _send_data_write_cb, bconv->pb);
589 /* We can probably write the data right now. */
590 _send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE);
591 }
592
593 }
594
595 static void
596 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
597 {
598 BonjourJabber *jdata = data;
599 struct sockaddr_in their_addr; /* connector's address information */
600 socklen_t sin_size = sizeof(struct sockaddr);
601 int client_socket;
602 int flags;
603 char *address_text = NULL;
604 struct _match_buddies_by_address_t *mbba;
605 BonjourJabberConversation *bconv;
606
607 /* Check that it is a read condition */
608 if (condition != PURPLE_INPUT_READ)
609 return;
610
611 if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1)
612 return;
613
614 flags = fcntl(client_socket, F_GETFL);
615 fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
616
617 /* Look for the buddy that has opened the conversation and fill information */
618 address_text = inet_ntoa(their_addr.sin_addr);
619 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
620 mbba = g_new0(struct _match_buddies_by_address_t, 1);
621 mbba->address = address_text;
622 mbba->jdata = jdata;
623 g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
624
625 if (mbba->matched_buddies == NULL) {
626 purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
627 g_slist_free(mbba->matched_buddies);
628 g_free(mbba);
629 close(client_socket);
630 return;
631 }
632
633 g_slist_free(mbba->matched_buddies);
634 g_free(mbba);
635
636 /* We've established that this *could* be from one of our buddies.
637 * Wait for the stream open to see if that matches too before assigning it.
638 */
639 bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
640
641 /* We wait for the stream start before doing anything else */
642 bconv->socket = client_socket;
643 bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv);
644
645 }
646
647 gint
648 bonjour_jabber_start(BonjourJabber *jdata)
649 {
650 struct sockaddr_in my_addr;
651 int yes = 1;
652 int i;
653 gboolean bind_successful;
654
655 /* Open a listening socket for incoming conversations */
656 if ((jdata->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
657 {
658 purple_debug_error("bonjour", "Cannot open socket: %s\n", g_strerror(errno));
659 purple_connection_error_reason (jdata->account->gc,
660 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
661 _("Cannot open socket"));
662 return -1;
663 }
664
665 /* Make the socket reusable */
666 if (setsockopt(jdata->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
667 {
668 purple_debug_error("bonjour", "Error setting socket options: %s\n", g_strerror(errno));
669 purple_connection_error_reason (jdata->account->gc,
670 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
671 _("Error setting socket options"));
672 return -1;
673 }
674
675 memset(&my_addr, 0, sizeof(struct sockaddr_in));
676 my_addr.sin_family = AF_INET;
677
678 /* Attempt to find a free port */
679 bind_successful = FALSE;
680 for (i = 0; i < 10; i++)
681 {
682 my_addr.sin_port = htons(jdata->port);
683 if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
684 {
685 bind_successful = TRUE;
686 break;
687 }
688 jdata->port++;
689 }
690
691 /* On no! We tried 10 ports and could not bind to ANY of them */
692 if (!bind_successful)
693 {
694 purple_debug_error("bonjour", "Cannot bind socket: %s\n", g_strerror(errno));
695 purple_connection_error_reason (jdata->account->gc,
696 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
697 _("Could not bind socket to port"));
698 return -1;
699 }
700
701 /* Attempt to listen on the bound socket */
702 if (listen(jdata->socket, 10) != 0)
703 {
704 purple_debug_error("bonjour", "Cannot listen on socket: %s\n", g_strerror(errno));
705 purple_connection_error_reason (jdata->account->gc,
706 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
707 _("Could not listen on socket"));
708 return -1;
709 }
710
711 #if 0
712 /* TODO: Why isn't this being used? */
713 data->socket = purple_network_listen(jdata->port, SOCK_STREAM);
714
715 if (jdata->socket == -1)
716 {
717 purple_debug_error("bonjour", "No se ha podido crear el socket\n");
718 }
719 #endif
720
721 /* Open a watcher in the socket we have just opened */
722 jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
723
724 return jdata->port;
725 }
726
727 static void
728 _connected_to_buddy(gpointer data, gint source, const gchar *error)
729 {
730 PurpleBuddy *pb = data;
731 BonjourBuddy *bb = pb->proto_data;
732
733 bb->conversation->connect_data = NULL;
734
735 if (source < 0) {
736 PurpleConversation *conv;
737
738 purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
739 purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error ? error : "(null)");
452 740
453 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); 741 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
454 if (conv != NULL) 742 if (conv != NULL)
455 purple_conversation_write(conv, NULL, 743 purple_conversation_write(conv, NULL,
456 _("Unable to send the message, the conversation couldn't be started."), 744 _("Unable to send the message, the conversation couldn't be started."),
457 PURPLE_MESSAGE_SYSTEM, time(NULL)); 745 PURPLE_MESSAGE_SYSTEM, time(NULL));
458 746
459 bonjour_jabber_close_conversation(bconv); 747 bonjour_jabber_close_conversation(bb->conversation);
460 bb->conversation = NULL; 748 bb->conversation = NULL;
461 749 return;
462 return; 750 }
463 } 751
464 752 if (!bonjour_jabber_send_stream_init(bb->conversation, source)) {
465 /* This is EXTREMELY unlikely to happen */
466 if (ret < len) {
467 char *tmp = g_strdup(ss->msg + ret);
468 g_free(ss->msg);
469 ss->msg = tmp;
470 return;
471 }
472
473 g_free(ss->msg);
474 g_free(ss);
475 bconv->stream_data = NULL;
476
477 /* Stream started; process the send buffer if there is one */
478 purple_input_remove(bconv->tx_handler);
479 bconv->tx_handler = 0;
480 bconv->sent_stream_start = FULLY_SENT;
481
482 bonjour_jabber_stream_started(pb);
483 }
484
485 static gboolean bonjour_jabber_send_stream_init(PurpleBuddy *pb, int client_socket)
486 {
487 int ret, len;
488 char *stream_start;
489 BonjourBuddy *bb = pb->proto_data;
490
491 stream_start = g_strdup_printf(DOCTYPE, purple_account_get_username(pb->account),
492 purple_buddy_get_name(pb));
493 len = strlen(stream_start);
494
495 bb->conversation->sent_stream_start = PARTIALLY_SENT;
496
497 /* Start the stream */
498 ret = send(client_socket, stream_start, len, 0);
499
500 if (ret == -1 && errno == EAGAIN)
501 ret = 0;
502 else if (ret <= 0) {
503 const char *err = g_strerror(errno);
504 const char *ip = NULL;
505
506 /* For better or worse, use the first IP*/
507 if (bb->ips)
508 ip = bb->ips->data;
509
510 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
511 purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
512
513 close(client_socket);
514 g_free(stream_start);
515
516 return FALSE;
517 }
518
519 /* This is unlikely to happen */
520 if (ret < len) {
521 struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
522 ss->msg = g_strdup(stream_start + ret);
523 bb->conversation->stream_data = ss;
524 /* Finish sending the stream start */
525 bb->conversation->tx_handler = purple_input_add(client_socket,
526 PURPLE_INPUT_WRITE, _start_stream, pb);
527 } else
528 bb->conversation->sent_stream_start = FULLY_SENT;
529
530 g_free(stream_start);
531
532 return TRUE;
533 }
534
535 static gboolean
536 _async_bonjour_jabber_close_conversation(gpointer data) {
537 BonjourJabberConversation *bconv = data;
538 bonjour_jabber_close_conversation(bconv);
539 return FALSE;
540 }
541
542 void bonjour_jabber_stream_started(PurpleBuddy *pb) {
543 BonjourBuddy *bb = pb->proto_data;
544 BonjourJabberConversation *bconv = bb->conversation;
545
546 if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(pb, bconv->socket)) {
547 const char *err = g_strerror(errno); 753 const char *err = g_strerror(errno);
548 PurpleConversation *conv; 754 PurpleConversation *conv;
549 const char *ip = NULL;
550
551 /* For better or worse, use the first IP*/
552 if (bb->ips)
553 ip = bb->ips->data;
554 755
555 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n", 756 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
556 purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)"); 757 purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)");
557 758
558 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account); 759 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
559 if (conv != NULL) 760 if (conv != NULL)
560 purple_conversation_write(conv, NULL, 761 purple_conversation_write(conv, NULL,
561 _("Unable to send the message, the conversation couldn't be started."), 762 _("Unable to send the message, the conversation couldn't be started."),
562 PURPLE_MESSAGE_SYSTEM, time(NULL)); 763 PURPLE_MESSAGE_SYSTEM, time(NULL));
563 764
564 close(bconv->socket); 765 close(source);
766 bonjour_jabber_close_conversation(bb->conversation);
767 bb->conversation = NULL;
768 return;
769 }
770
771 /* Start listening for the stream acknowledgement */
772 bb->conversation->socket = source;
773 bb->conversation->rx_handler = purple_input_add(source,
774 PURPLE_INPUT_READ, _client_socket_handler, bb->conversation);
775 }
776
777 void
778 bonjour_jabber_conv_match_by_name(BonjourJabberConversation *bconv) {
779 PurpleBuddy *pb;
780
781 g_return_if_fail(bconv->ip != NULL);
782 g_return_if_fail(bconv->pb == NULL);
783
784 pb = purple_find_buddy(bconv->account, bconv->buddy_name);
785 if (pb && pb->proto_data) {
786 BonjourBuddy *bb = pb->proto_data;
787 const char *ip;
788 GSList *tmp = bb->ips;
789
790 purple_debug_info("bonjour", "Found buddy %s for incoming conversation \"from\" attrib.\n",
791 purple_buddy_get_name(pb));
792
793 /* Check that one of the buddy's IPs matches */
794 while(tmp) {
795 ip = tmp->data;
796 if (ip != NULL && g_ascii_strcasecmp(ip, bconv->ip) == 0) {
797 BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
798
799 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation \"from\" attrib and IP (%s)\n",
800 purple_buddy_get_name(pb), bconv->ip);
801
802 /* Attach conv. to buddy and remove from pending list */
803 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
804
805 /* Check if the buddy already has a conversation and, if so, replace it */
806 if(bb->conversation != NULL && bb->conversation != bconv)
807 bonjour_jabber_close_conversation(bb->conversation);
808
809 bconv->pb = pb;
810 bb->conversation = bconv;
811
812 break;
813 }
814 tmp = tmp->next;
815 }
816 }
817
818 /* We've failed to match a buddy - give up */
819 if (bconv->pb == NULL) {
565 /* This must be asynchronous because it destroys the parser and we 820 /* This must be asynchronous because it destroys the parser and we
566 * may be in the middle of parsing. 821 * may be in the middle of parsing.
567 */ 822 */
568 purple_timeout_add(0, _async_bonjour_jabber_close_conversation, bb->conversation); 823 async_bonjour_jabber_close_conversation(bconv);
569 bb->conversation = NULL; 824 }
570 return; 825 }
571 } 826
572 827
573 /* If the stream has been completely started, we can start doing stuff */ 828 void
574 if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && purple_circ_buffer_get_max_read(bconv->tx_buf) > 0) { 829 bonjour_jabber_conv_match_by_ip(BonjourJabberConversation *bconv) {
575 /* Watch for when we can write the buffered messages */ 830 BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
576 bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE, 831 struct _match_buddies_by_address_t *mbba;
577 _send_data_write_cb, pb); 832
578 /* We can probably write the data right now. */ 833 mbba = g_new0(struct _match_buddies_by_address_t, 1);
579 _send_data_write_cb(pb, bconv->socket, PURPLE_INPUT_WRITE); 834 mbba->address = bconv->ip;
580 } 835 mbba->jdata = jdata;
581 836 g_hash_table_foreach(purple_get_blist()->buddies, _match_buddies_by_address, mbba);
582 } 837
583 838 /* If there is exactly one match, use it */
584 static void 839 if(mbba->matched_buddies != NULL) {
585 _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition) 840 if(mbba->matched_buddies->next != NULL)
586 { 841 purple_debug_error("bonjour", "More than one buddy matched for ip %s.\n", bconv->ip);
587 PurpleBuddy *pb = NULL; 842 else {
588 struct sockaddr_in their_addr; /* connector's address information */ 843 PurpleBuddy *pb = mbba->matched_buddies->data;
589 socklen_t sin_size = sizeof(struct sockaddr); 844 BonjourBuddy *bb = pb->proto_data;
590 int client_socket; 845
591 int flags; 846 purple_debug_info("bonjour", "Matched buddy %s to incoming conversation using IP (%s)\n",
592 BonjourBuddy *bb; 847 purple_buddy_get_name(pb), bconv->ip);
593 char *address_text = NULL; 848
594 PurpleBuddyList *bl = purple_get_blist(); 849 /* Attach conv. to buddy and remove from pending list */
595 struct _check_buddy_by_address_t *cbba; 850 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
596 851
597 /* Check that it is a read condition */ 852 /* Check if the buddy already has a conversation and, if so, replace it */
598 if (condition != PURPLE_INPUT_READ) 853 if (bb->conversation != NULL && bb->conversation != bconv)
599 return; 854 bonjour_jabber_close_conversation(bb->conversation);
600 855
601 if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) 856 bconv->pb = pb;
602 return; 857 bb->conversation = bconv;
603 858 }
604 flags = fcntl(client_socket, F_GETFL); 859 } else
605 fcntl(client_socket, F_SETFL, flags | O_NONBLOCK); 860 purple_debug_error("bonjour", "No buddies matched for ip %s.\n", bconv->ip);
606 861
607 /* Look for the buddy that has opened the conversation and fill information */ 862 /* We've failed to match a buddy - give up */
608 address_text = inet_ntoa(their_addr.sin_addr); 863 if (bconv->pb == NULL) {
609 purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text); 864 /* This must be asynchronous because it destroys the parser and we
610 cbba = g_new0(struct _check_buddy_by_address_t, 1); 865 * may be in the middle of parsing.
611 cbba->address = address_text; 866 */
612 cbba->pb = &pb; 867 async_bonjour_jabber_close_conversation(bconv);
613 cbba->bj = data; 868 }
614 g_hash_table_foreach(bl->buddies, _check_buddy_by_address, cbba); 869
615 g_free(cbba); 870 g_slist_free(mbba->matched_buddies);
616 if (pb == NULL) 871 g_free(mbba);
617 {
618 purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheros comic\n");
619 close(client_socket);
620 return;
621 }
622 bb = pb->proto_data;
623
624 /* Check if the conversation has been previously started */
625 /* This really shouldn't ever happen unless something weird is going on */
626 if (bb->conversation == NULL)
627 {
628 bb->conversation = bonjour_jabber_conv_new(pb);
629
630 /* We wait for the stream start before doing anything else */
631 bb->conversation->socket = client_socket;
632 bb->conversation->rx_handler = purple_input_add(client_socket,
633 PURPLE_INPUT_READ, _client_socket_handler, pb);
634
635 } else {
636 purple_debug_warning("bonjour", "Ignoring incoming connection because an existing connection exists.\n");
637 close(client_socket);
638 }
639 }
640
641 gint
642 bonjour_jabber_start(BonjourJabber *data)
643 {
644 struct sockaddr_in my_addr;
645 int yes = 1;
646 int i;
647 gboolean bind_successful;
648
649 /* Open a listening socket for incoming conversations */
650 if ((data->socket = socket(PF_INET, SOCK_STREAM, 0)) < 0)
651 {
652 purple_debug_error("bonjour", "Cannot open socket: %s\n", g_strerror(errno));
653 purple_connection_error_reason (data->account->gc,
654 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
655 _("Cannot open socket"));
656 return -1;
657 }
658
659 /* Make the socket reusable */
660 if (setsockopt(data->socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != 0)
661 {
662 purple_debug_error("bonjour", "Error setting socket options: %s\n", g_strerror(errno));
663 purple_connection_error_reason (data->account->gc,
664 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
665 _("Error setting socket options"));
666 return -1;
667 }
668
669 memset(&my_addr, 0, sizeof(struct sockaddr_in));
670 my_addr.sin_family = AF_INET;
671
672 /* Attempt to find a free port */
673 bind_successful = FALSE;
674 for (i = 0; i < 10; i++)
675 {
676 my_addr.sin_port = htons(data->port);
677 if (bind(data->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == 0)
678 {
679 bind_successful = TRUE;
680 break;
681 }
682 data->port++;
683 }
684
685 /* On no! We tried 10 ports and could not bind to ANY of them */
686 if (!bind_successful)
687 {
688 purple_debug_error("bonjour", "Cannot bind socket: %s\n", g_strerror(errno));
689 purple_connection_error_reason (data->account->gc,
690 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
691 _("Could not bind socket to port"));
692 return -1;
693 }
694
695 /* Attempt to listen on the bound socket */
696 if (listen(data->socket, 10) != 0)
697 {
698 purple_debug_error("bonjour", "Cannot listen on socket: %s\n", g_strerror(errno));
699 purple_connection_error_reason (data->account->gc,
700 PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
701 _("Could not listen on socket"));
702 return -1;
703 }
704
705 #if 0
706 /* TODO: Why isn't this being used? */
707 data->socket = purple_network_listen(data->port, SOCK_STREAM);
708
709 if (data->socket == -1)
710 {
711 purple_debug_error("bonjour", "No se ha podido crear el socket\n");
712 }
713 #endif
714
715 /* Open a watcher in the socket we have just opened */
716 data->watcher_id = purple_input_add(data->socket, PURPLE_INPUT_READ, _server_socket_handler, data);
717
718 return data->port;
719 }
720
721 static void
722 _connected_to_buddy(gpointer data, gint source, const gchar *error)
723 {
724 PurpleBuddy *pb = data;
725 BonjourBuddy *bb = pb->proto_data;
726
727 bb->conversation->connect_data = NULL;
728
729 if (source < 0) {
730 PurpleConversation *conv;
731 const char *ip = NULL;
732
733 /* For better or worse, use the first IP*/
734 if (bb->ips)
735 ip = bb->ips->data;
736
737 purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d error: %s\n",
738 purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, error ? error : "(null)");
739
740 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
741 if (conv != NULL)
742 purple_conversation_write(conv, NULL,
743 _("Unable to send the message, the conversation couldn't be started."),
744 PURPLE_MESSAGE_SYSTEM, time(NULL));
745
746 bonjour_jabber_close_conversation(bb->conversation);
747 bb->conversation = NULL;
748 return;
749 }
750
751 if (!bonjour_jabber_send_stream_init(pb, source)) {
752 const char *err = g_strerror(errno);
753 PurpleConversation *conv;
754 const char *ip = NULL;
755
756 /* For better or worse, use the first IP*/
757 if (bb->ips)
758 ip = bb->ips->data;
759
760 purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
761 purple_buddy_get_name(pb), ip ? ip : "(null)", bb->port_p2pj, err ? err : "(null)");
762
763 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, bb->name, pb->account);
764 if (conv != NULL)
765 purple_conversation_write(conv, NULL,
766 _("Unable to send the message, the conversation couldn't be started."),
767 PURPLE_MESSAGE_SYSTEM, time(NULL));
768
769 close(source);
770 bonjour_jabber_close_conversation(bb->conversation);
771 bb->conversation = NULL;
772 return;
773 }
774
775 /* Start listening for the stream acknowledgement */
776 bb->conversation->socket = source;
777 bb->conversation->rx_handler = purple_input_add(source,
778 PURPLE_INPUT_READ, _client_socket_handler, pb);
779 } 872 }
780 873
781 static PurpleBuddy * 874 static PurpleBuddy *
782 _find_or_start_conversation(BonjourJabber *data, const gchar *to) 875 _find_or_start_conversation(BonjourJabber *jdata, const gchar *to)
783 { 876 {
784 PurpleBuddy *pb = NULL; 877 PurpleBuddy *pb = NULL;
785 BonjourBuddy *bb = NULL; 878 BonjourBuddy *bb = NULL;
786 879
787 g_return_val_if_fail(data != NULL, NULL); 880 g_return_val_if_fail(jdata != NULL, NULL);
788 g_return_val_if_fail(to != NULL, NULL); 881 g_return_val_if_fail(to != NULL, NULL);
789 882
790 pb = purple_find_buddy(data->account, to); 883 pb = purple_find_buddy(jdata->account, to);
791 if (pb == NULL || pb->proto_data == NULL) 884 if (pb == NULL || pb->proto_data == NULL)
792 /* You can not send a message to an offline buddy */ 885 /* You can not send a message to an offline buddy */
793 return NULL; 886 return NULL;
794 887
795 bb = (BonjourBuddy *) pb->proto_data; 888 bb = (BonjourBuddy *) pb->proto_data;
797 /* Check if there is a previously open conversation */ 890 /* Check if there is a previously open conversation */
798 if (bb->conversation == NULL) 891 if (bb->conversation == NULL)
799 { 892 {
800 PurpleProxyConnectData *connect_data; 893 PurpleProxyConnectData *connect_data;
801 PurpleProxyInfo *proxy_info; 894 PurpleProxyInfo *proxy_info;
802 const char *ip = NULL;
803
804 /* For better or worse, use the first IP*/ 895 /* For better or worse, use the first IP*/
805 if (bb->ips) 896 const char *ip = bb->ips->data;
806 ip = bb->ips->data;
807 897
808 purple_debug_info("bonjour", "Starting conversation with %s\n", to); 898 purple_debug_info("bonjour", "Starting conversation with %s\n", to);
809 899
810 /* Make sure that the account always has a proxy of "none". 900 /* Make sure that the account always has a proxy of "none".
811 * This is kind of dirty, but proxy_connect_none() isn't exposed. */ 901 * This is kind of dirty, but proxy_connect_none() isn't exposed. */
812 proxy_info = purple_account_get_proxy_info(data->account); 902 proxy_info = purple_account_get_proxy_info(jdata->account);
813 if (proxy_info == NULL) { 903 if (proxy_info == NULL) {
814 proxy_info = purple_proxy_info_new(); 904 proxy_info = purple_proxy_info_new();
815 purple_account_set_proxy_info(data->account, proxy_info); 905 purple_account_set_proxy_info(jdata->account, proxy_info);
816 } 906 }
817 purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE); 907 purple_proxy_info_set_type(proxy_info, PURPLE_PROXY_NONE);
818 908
819 connect_data = purple_proxy_connect(NULL, data->account, 909 connect_data = purple_proxy_connect(NULL, jdata->account,
820 ip, bb->port_p2pj, _connected_to_buddy, pb); 910 ip, bb->port_p2pj, _connected_to_buddy, pb);
821 911
822 if (connect_data == NULL) { 912 if (connect_data == NULL) {
823 purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to); 913 purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
824 return NULL; 914 return NULL;
825 } 915 }
826 916
827 bb->conversation = bonjour_jabber_conv_new(pb); 917 bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
828 bb->conversation->connect_data = connect_data; 918 bb->conversation->connect_data = connect_data;
829 /* We don't want _send_data() to register the tx_handler; 919 /* We don't want _send_data() to register the tx_handler;
830 * that neeeds to wait until we're actually connected. */ 920 * that neeeds to wait until we're actually connected. */
831 bb->conversation->tx_handler = 0; 921 bb->conversation->tx_handler = 0;
832 } 922 }
833 return pb; 923 return pb;
834 } 924 }
835 925
836 int 926 int
837 bonjour_jabber_send_message(BonjourJabber *data, const gchar *to, const gchar *body) 927 bonjour_jabber_send_message(BonjourJabber *jdata, const gchar *to, const gchar *body)
838 { 928 {
839 xmlnode *message_node, *node, *node2; 929 xmlnode *message_node, *node, *node2;
840 gchar *message; 930 gchar *message;
841 PurpleBuddy *pb; 931 PurpleBuddy *pb;
842 BonjourBuddy *bb; 932 BonjourBuddy *bb;
843 int ret; 933 int ret;
844 934
845 pb = _find_or_start_conversation(data, to); 935 pb = _find_or_start_conversation(jdata, to);
846 if (pb == NULL) { 936 if (pb == NULL) {
847 purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to); 937 purple_debug_info("bonjour", "Can't send a message to an offline buddy (%s).\n", to);
848 /* You can not send a message to an offline buddy */ 938 /* You can not send a message to an offline buddy */
849 return -10000; 939 return -10000;
850 } 940 }
851 941
852 bb = pb->proto_data; 942 bb = pb->proto_data;
853 943
854 message_node = xmlnode_new("message"); 944 message_node = xmlnode_new("message");
855 xmlnode_set_attrib(message_node, "to", bb->name); 945 xmlnode_set_attrib(message_node, "to", bb->name);
856 xmlnode_set_attrib(message_node, "from", purple_account_get_username(data->account)); 946 xmlnode_set_attrib(message_node, "from", purple_account_get_username(jdata->account));
857 xmlnode_set_attrib(message_node, "type", "chat"); 947 xmlnode_set_attrib(message_node, "type", "chat");
858 948
859 /* Enclose the message from the UI within a "font" node */ 949 /* Enclose the message from the UI within a "font" node */
860 node = xmlnode_new_child(message_node, "body"); 950 node = xmlnode_new_child(message_node, "body");
861 message = purple_markup_strip_html(body); 951 message = purple_markup_strip_html(body);
883 g_free(message); 973 g_free(message);
884 974
885 return ret; 975 return ret;
886 } 976 }
887 977
978 static gboolean
979 _async_bonjour_jabber_close_conversation_cb(gpointer data) {
980 BonjourJabberConversation *bconv = data;
981 bonjour_jabber_close_conversation(bconv);
982 return FALSE;
983 }
984
985 void
986 async_bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) {
987 BonjourJabber *jdata = ((BonjourData*) bconv->account->gc->proto_data)->jabber_data;
988
989 jdata->pending_conversations = g_slist_remove(jdata->pending_conversations, bconv);
990
991 /* Disconnect this conv. from the buddy here so it can't be disposed of twice.*/
992 if(bconv->pb != NULL) {
993 BonjourBuddy *bb = bconv->pb->proto_data;
994 if (bb->conversation == bconv)
995 bb->conversation = NULL;
996 }
997
998 purple_timeout_add(0, _async_bonjour_jabber_close_conversation_cb, bconv);
999 }
1000
888 void 1001 void
889 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv) 1002 bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
890 { 1003 {
891 if (bconv != NULL) { 1004 if (bconv != NULL) {
892 GList *xfers, *tmp_next; 1005 BonjourData *bd = NULL;
893 BonjourData *bd = bconv->pb->account->gc->proto_data; 1006
1007 if(PURPLE_CONNECTION_IS_VALID(bconv->account->gc)) {
1008 bd = bconv->account->gc->proto_data;
1009 bd->jabber_data->pending_conversations = g_slist_remove(bd->jabber_data->pending_conversations, bconv);
1010 }
894 1011
895 /* Cancel any file transfers that are waiting to begin */ 1012 /* Cancel any file transfers that are waiting to begin */
896 xfers = bd->xfer_lists; 1013 /* There wont be any transfers if it hasn't been attached to a buddy */
897 while(xfers != NULL) { 1014 if (bconv->pb != NULL && bd != NULL) {
898 PurpleXfer *xfer = xfers->data; 1015 GSList *xfers, *tmp_next;
899 tmp_next = xfers->next; 1016 xfers = bd->xfer_lists;
900 /* We only need to cancel this if it hasn't actually started transferring. */ 1017 while(xfers != NULL) {
901 /* This will change if we ever support IBB transfers. */ 1018 PurpleXfer *xfer = xfers->data;
902 if (strcmp(xfer->who, bconv->pb->name) == 0 1019 tmp_next = xfers->next;
903 && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED 1020 /* We only need to cancel this if it hasn't actually started transferring. */
904 || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) { 1021 /* This will change if we ever support IBB transfers. */
905 purple_xfer_cancel_remote(xfer); 1022 if (strcmp(xfer->who, bconv->pb->name) == 0
1023 && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
1024 || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
1025 purple_xfer_cancel_remote(xfer);
1026 }
1027 xfers = tmp_next;
906 } 1028 }
907 xfers = tmp_next;
908 } 1029 }
909 1030
910 /* Close the socket and remove the watcher */ 1031 /* Close the socket and remove the watcher */
911 if (bconv->socket >= 0) { 1032 if (bconv->socket >= 0) {
912 /* Send the end of the stream to the other end of the conversation */ 1033 /* Send the end of the stream to the other end of the conversation */
931 } 1052 }
932 1053
933 if (bconv->context != NULL) 1054 if (bconv->context != NULL)
934 bonjour_parser_setup(bconv); 1055 bonjour_parser_setup(bconv);
935 1056
1057 g_free(bconv->buddy_name);
1058 g_free(bconv->ip);
936 g_free(bconv); 1059 g_free(bconv);
937 } 1060 }
938 } 1061 }
939 1062
940 void 1063 void
941 bonjour_jabber_stop(BonjourJabber *data) 1064 bonjour_jabber_stop(BonjourJabber *jdata)
942 { 1065 {
943 /* Close the server socket and remove the watcher */ 1066 /* Close the server socket and remove the watcher */
944 if (data->socket >= 0) 1067 if (jdata->socket >= 0)
945 close(data->socket); 1068 close(jdata->socket);
946 if (data->watcher_id > 0) 1069 if (jdata->watcher_id > 0)
947 purple_input_remove(data->watcher_id); 1070 purple_input_remove(jdata->watcher_id);
948 1071
949 /* Close all the conversation sockets and remove all the watchers after sending end streams */ 1072 /* Close all the conversation sockets and remove all the watchers after sending end streams */
950 if (data->account->gc != NULL) 1073 if (jdata->account->gc != NULL) {
951 {
952 GSList *buddies, *l; 1074 GSList *buddies, *l;
953 1075
954 buddies = purple_find_buddies(data->account, purple_account_get_username(data->account)); 1076 buddies = purple_find_buddies(jdata->account, NULL);
955 for (l = buddies; l; l = l->next) { 1077 for (l = buddies; l; l = l->next) {
956 BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data; 1078 BonjourBuddy *bb = ((PurpleBuddy*) l->data)->proto_data;
957 bonjour_jabber_close_conversation(bb->conversation); 1079 bonjour_jabber_close_conversation(bb->conversation);
958 bb->conversation = NULL; 1080 bb->conversation = NULL;
959 } 1081 }
960 1082
961 g_slist_free(buddies); 1083 g_slist_free(buddies);
1084 }
1085
1086 while (jdata->pending_conversations != NULL) {
1087 bonjour_jabber_close_conversation(jdata->pending_conversations->data);
1088 jdata->pending_conversations = g_slist_delete_link(jdata->pending_conversations, jdata->pending_conversations);
962 } 1089 }
963 } 1090 }
964 1091
965 XepIq * 1092 XepIq *
966 xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id) 1093 xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id)
1054 { 1181 {
1055 int ret = -1; 1182 int ret = -1;
1056 PurpleBuddy *pb = NULL; 1183 PurpleBuddy *pb = NULL;
1057 1184
1058 /* start the talk, reuse the message socket */ 1185 /* start the talk, reuse the message socket */
1059 pb = _find_or_start_conversation ((BonjourJabber*)iq->data, iq->to); 1186 pb = _find_or_start_conversation((BonjourJabber*) iq->data, iq->to);
1060 /* Send the message */ 1187 /* Send the message */
1061 if (pb != NULL) { 1188 if (pb != NULL) {
1062 /* Convert xml node into stream */ 1189 /* Convert xml node into stream */
1063 gchar *msg = xmlnode_to_str(iq->node, NULL); 1190 gchar *msg = xmlnode_to_str(iq->node, NULL);
1064 ret = _send_data(pb, msg); 1191 ret = _send_data(pb, msg);