Mercurial > pidgin
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); |