comparison libpurple/protocols/jabber/presence.c @ 27666:a08e84032814

merge of '2348ff22f0ff3453774b8b25b36238465580c609' and 'e76f11543c2a4aa05bdf584f087cbe3439029661'
author Paul Aurich <paul@darkrain42.org>
date Sun, 12 Jul 2009 05:43:38 +0000
parents 048bcf41deef ed284238509b
children f44eda839ea4
comparison
equal deleted inserted replaced
27186:048bcf41deef 27666:a08e84032814
57 xmlnode_set_attrib(presence, "to", chat_full_jid); 57 xmlnode_set_attrib(presence, "to", chat_full_jid);
58 jabber_send(chat->js, presence); 58 jabber_send(chat->js, presence);
59 g_free(chat_full_jid); 59 g_free(chat_full_jid);
60 } 60 }
61 61
62 void jabber_presence_fake_to_self(JabberStream *js, const PurpleStatus *gstatus) { 62 void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status)
63 char *my_base_jid; 63 {
64 64 PurpleAccount *account;
65 if(!js->user) 65 const char *username;
66 return; 66
67 67 g_return_if_fail(js->user != NULL);
68 my_base_jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); 68
69 if(purple_find_buddy(js->gc->account, my_base_jid)) { 69 account = purple_connection_get_account(js->gc);
70 JabberBuddy *jb; 70 username = purple_account_get_username(account);
71 if (status == NULL)
72 status = purple_account_get_active_status(account);
73
74 if (purple_find_buddy(account, username)) {
75 JabberBuddy *jb = jabber_buddy_find(js, username, TRUE);
71 JabberBuddyResource *jbr; 76 JabberBuddyResource *jbr;
72 if((jb = jabber_buddy_find(js, my_base_jid, TRUE))) { 77 JabberBuddyState state;
73 JabberBuddyState state; 78 char *msg;
74 char *msg; 79 int priority;
75 int priority; 80
76 81 g_return_if_fail(jb != NULL);
77 purple_status_to_jabber(gstatus, &state, &msg, &priority); 82
78 83 purple_status_to_jabber(status, &state, &msg, &priority);
79 if (state == JABBER_BUDDY_STATE_UNAVAILABLE || state == JABBER_BUDDY_STATE_UNKNOWN) { 84
80 jabber_buddy_remove_resource(jb, js->user->resource); 85 if (state == JABBER_BUDDY_STATE_UNAVAILABLE ||
81 } else { 86 state == JABBER_BUDDY_STATE_UNKNOWN) {
82 jabber_buddy_track_resource(jb, js->user->resource, priority, state, msg); 87 jabber_buddy_remove_resource(jb, js->user->resource);
83 } 88 } else {
84 if((jbr = jabber_buddy_find_resource(jb, NULL))) { 89 jabber_buddy_track_resource(jb, js->user->resource, priority,
85 purple_prpl_got_user_status(js->gc->account, my_base_jid, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL); 90 state, msg);
86 } else { 91 }
87 purple_prpl_got_user_status(js->gc->account, my_base_jid, "offline", msg ? "message" : NULL, msg, NULL); 92
88 } 93 if ((jbr = jabber_buddy_find_resource(jb, NULL))) {
89 94 purple_prpl_got_user_status(js->gc->account, username, jabber_buddy_state_get_status_id(jbr->state), "priority", jbr->priority, jbr->status ? "message" : NULL, jbr->status, NULL);
90 g_free(msg); 95 } else {
91 } 96 purple_prpl_got_user_status(js->gc->account, username, "offline", msg ? "message" : NULL, msg, NULL);
92 } 97 }
93 g_free(my_base_jid); 98 g_free(msg);
99 }
94 } 100 }
95 101
96 void jabber_set_status(PurpleAccount *account, PurpleStatus *status) 102 void jabber_set_status(PurpleAccount *account, PurpleStatus *status)
97 { 103 {
98 PurpleConnection *gc; 104 PurpleConnection *gc;
131 account = purple_connection_get_account(js->gc); 137 account = purple_connection_get_account(js->gc);
132 p = purple_account_get_presence(account); 138 p = purple_account_get_presence(account);
133 status = purple_presence_get_active_status(p); 139 status = purple_presence_get_active_status(p);
134 140
135 /* we don't want to send presence before we've gotten our roster */ 141 /* we don't want to send presence before we've gotten our roster */
136 if(!js->roster_parsed) { 142 if (js->state != JABBER_STREAM_CONNECTED) {
137 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n"); 143 purple_debug_info("jabber", "attempt to send presence before roster retrieved\n");
138 return; 144 return;
139 } 145 }
140 146
141 purple_status_to_jabber(status, &state, &stripped, &priority); 147 purple_status_to_jabber(status, &state, &stripped, &priority);
439 } 445 }
440 446
441 jbr->caps.info = info; 447 jbr->caps.info = info;
442 jbr->caps.exts = exts; 448 jbr->caps.exts = exts;
443 449
450 if (info == NULL)
451 goto out;
452
444 if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) { 453 if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
445 JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); 454 JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
446 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items"); 455 xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", "http://jabber.org/protocol/disco#items");
447 xmlnode_set_attrib(iq->node, "to", userdata->from); 456 xmlnode_set_attrib(iq->node, "to", userdata->from);
448 xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands"); 457 xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
450 jabber_iq_send(iq); 459 jabber_iq_send(iq);
451 460
452 jbr->commands_fetched = TRUE; 461 jbr->commands_fetched = TRUE;
453 } 462 }
454 463
464 if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/chatstates"))
465 jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
466 else
467 jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
468
469 out:
455 g_free(userdata->from); 470 g_free(userdata->from);
456 g_free(userdata); 471 g_free(userdata);
457 } 472 }
458 473
459 void jabber_presence_parse(JabberStream *js, xmlnode *packet) 474 void jabber_presence_parse(JabberStream *js, xmlnode *packet)
460 { 475 {
461 const char *from; 476 const char *from;
462 const char *type; 477 const char *type;
463 const char *real_jid = NULL;
464 const char *affiliation = NULL;
465 const char *role = NULL;
466 char *status = NULL; 478 char *status = NULL;
467 int priority = 0; 479 int priority = 0;
468 JabberID *jid; 480 JabberID *jid;
469 JabberChat *chat; 481 JabberChat *chat;
470 JabberBuddy *jb; 482 JabberBuddy *jb;
474 const gchar *stamp = NULL; /* from <delayed/> element */ 486 const gchar *stamp = NULL; /* from <delayed/> element */
475 PurpleBuddy *b = NULL; 487 PurpleBuddy *b = NULL;
476 char *buddy_name; 488 char *buddy_name;
477 JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; 489 JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN;
478 xmlnode *y; 490 xmlnode *y;
479 gboolean muc = FALSE;
480 char *avatar_hash = NULL; 491 char *avatar_hash = NULL;
481 xmlnode *caps = NULL; 492 xmlnode *caps = NULL;
482 int idle = 0; 493 int idle = 0;
483 gchar *nickname = NULL; 494 gchar *nickname = NULL;
484 gboolean signal_return; 495 gboolean signal_return;
485 496
486 from = xmlnode_get_attrib(packet, "from"); 497 from = xmlnode_get_attrib(packet, "from");
487 type = xmlnode_get_attrib(packet, "type"); 498 type = xmlnode_get_attrib(packet, "type");
488 499
489 if(!(jb = jabber_buddy_find(js, from, TRUE))) 500 jb = jabber_buddy_find(js, from, TRUE);
490 return; 501 g_return_if_fail(jb != NULL);
491 502
492 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin, 503 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(jabber_plugin,
493 "jabber-receiving-presence", js->gc, type, from, packet)); 504 "jabber-receiving-presence", js->gc, type, from, packet));
494 if (signal_return) 505 if (signal_return)
495 return; 506 return;
496 507
497 if(!(jid = jabber_id_new(from))) 508 jid = jabber_id_new(from);
498 return; 509 if (jid == NULL) {
510 purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
511 "JID: %s\n", from);
512 return;
513 }
499 514
500 if(jb->error_msg) { 515 if(jb->error_msg) {
501 g_free(jb->error_msg); 516 g_free(jb->error_msg);
502 jb->error_msg = NULL; 517 jb->error_msg = NULL;
503 } 518 }
504 519
505 if(type && !strcmp(type, "error")) { 520 if (type == NULL) {
521 xmlnode *show;
522 char *show_data = NULL;
523
524 state = JABBER_BUDDY_STATE_ONLINE;
525
526 show = xmlnode_get_child(packet, "show");
527 if (show) {
528 show_data = xmlnode_get_data(show);
529 if (show_data) {
530 state = jabber_buddy_show_get_state(show_data);
531 g_free(show_data);
532 } else
533 purple_debug_warning("jabber", "<show/> present on presence, "
534 "but no contents!\n");
535 }
536 } else if (g_str_equal(type, "error")) {
506 char *msg = jabber_parse_error(js, packet, NULL); 537 char *msg = jabber_parse_error(js, packet, NULL);
507 538
508 state = JABBER_BUDDY_STATE_ERROR; 539 state = JABBER_BUDDY_STATE_ERROR;
509 jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); 540 jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
510 } else if(type && !strcmp(type, "subscribe")) { 541 } else if (g_str_equal(type, "subscribe")) {
511 struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); 542 struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1);
512 gboolean onlist = FALSE; 543 gboolean onlist = FALSE;
513 PurpleBuddy *buddy = purple_find_buddy(purple_connection_get_account(js->gc), from); 544 PurpleAccount *account;
545 PurpleBuddy *buddy;
514 JabberBuddy *jb = NULL; 546 JabberBuddy *jb = NULL;
515 xmlnode *nick; 547 xmlnode *nick;
516 548
549 account = purple_connection_get_account(js->gc);
550 buddy = purple_find_buddy(account, from);
517 nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); 551 nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
518 if (nick) 552 if (nick)
519 nickname = xmlnode_get_data(nick); 553 nickname = xmlnode_get_data(nick);
520 554
521 if (buddy) { 555 if (buddy) {
526 560
527 jap->gc = js->gc; 561 jap->gc = js->gc;
528 jap->who = g_strdup(from); 562 jap->who = g_strdup(from);
529 jap->js = js; 563 jap->js = js;
530 564
531 purple_account_request_authorization(purple_connection_get_account(js->gc), from, NULL, nickname, NULL, onlist, 565 purple_account_request_authorization(account, from, NULL, nickname,
532 authorize_add_cb, deny_add_cb, jap); 566 NULL, onlist, authorize_add_cb, deny_add_cb, jap);
567
533 g_free(nickname); 568 g_free(nickname);
534 jabber_id_free(jid); 569 jabber_id_free(jid);
535 return; 570 return;
536 } else if(type && !strcmp(type, "subscribed")) { 571 } else if (g_str_equal(type, "subscribed")) {
537 /* we've been allowed to see their presence, but we don't care */ 572 /* we've been allowed to see their presence, but we don't care */
538 jabber_id_free(jid); 573 jabber_id_free(jid);
539 return; 574 return;
540 } else if(type && !strcmp(type, "unsubscribe")) { 575 } else if (g_str_equal(type, "unsubscribe")) {
541 /* XXX I'm not sure this is the right way to handle this, it 576 /* XXX I'm not sure this is the right way to handle this, it
542 * might be better to add "unsubscribe" to the presence status 577 * might be better to add "unsubscribe" to the presence status
543 * if lower down, but I'm not sure. */ 578 * if lower down, but I'm not sure. */
544 /* they are unsubscribing from our presence, we don't care */ 579 /* they are unsubscribing from our presence, we don't care */
545 /* Well, maybe just a little, we might want/need to start 580 /* Well, maybe just a little, we might want/need to start
546 * acknowledging this (and the others) at some point. */ 581 * acknowledging this (and the others) at some point. */
547 jabber_id_free(jid); 582 jabber_id_free(jid);
548 return; 583 return;
584 } else if (g_str_equal(type, "probe")) {
585 purple_debug_warning("jabber", "Ignoring presence probe\n");
586 jabber_id_free(jid);
587 return;
588 } else if (g_str_equal(type, "unavailable")) {
589 state = JABBER_BUDDY_STATE_UNAVAILABLE;
590 } else if (g_str_equal(type, "unsubscribed")) {
591 state = JABBER_BUDDY_STATE_UNKNOWN;
549 } else { 592 } else {
550 if((y = xmlnode_get_child(packet, "show"))) { 593 purple_debug_warning("jabber", "Ignoring presence with invalid type "
551 char *show = xmlnode_get_data(y); 594 "'%s'\n", type);
552 state = jabber_buddy_show_get_state(show); 595 jabber_id_free(jid);
553 g_free(show); 596 return;
554 } else {
555 state = JABBER_BUDDY_STATE_ONLINE;
556 }
557 } 597 }
558 598
559 599
560 for(y = packet->child; y; y = y->next) { 600 for(y = packet->child; y; y = y->next) {
561 const char *xmlns; 601 const char *xmlns;
587 if(!strcmp(xmlns, "jabber:x:delay")) { 627 if(!strcmp(xmlns, "jabber:x:delay")) {
588 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ 628 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */
589 delayed = TRUE; 629 delayed = TRUE;
590 stamp = xmlnode_get_attrib(y, "stamp"); 630 stamp = xmlnode_get_attrib(y, "stamp");
591 } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { 631 } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) {
592 xmlnode *z; 632 } else if(!strcmp(xmlns, "vcard-temp:x:update")) {
593 633 xmlnode *photo = xmlnode_get_child(y, "photo");
594 muc = TRUE; 634 if(photo) {
595 if((z = xmlnode_get_child(y, "status"))) { 635 g_free(avatar_hash);
596 const char *code = xmlnode_get_attrib(z, "code"); 636 avatar_hash = xmlnode_get_data(photo);
597 if(code && !strcmp(code, "201")) { 637 }
598 if((chat = jabber_chat_find(js, jid->node, jid->domain))) { 638 }
639 } else if (!strcmp(y->name, "query") &&
640 !strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) {
641 /* resource has specified idle */
642 const gchar *seconds = xmlnode_get_attrib(y, "seconds");
643 if (seconds) {
644 /* we may need to take "delayed" into account here */
645 idle = atoi(seconds);
646 }
647 }
648 }
649
650 if (idle && delayed && stamp) {
651 /* if we have a delayed presence, we need to add the delay to the idle
652 value */
653 time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL,
654 NULL);
655 purple_debug_info("jabber", "got delay %s yielding %ld s offset\n",
656 stamp, offset);
657 idle += offset;
658 }
659
660 if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
661 static int i = 1;
662
663 if(state == JABBER_BUDDY_STATE_ERROR) {
664 char *title, *msg = jabber_parse_error(js, packet, NULL);
665
666 if (!chat->conv) {
667 title = g_strdup_printf(_("Error joining chat %s"), from);
668 purple_serv_got_join_chat_failed(js->gc, chat->components);
669 } else {
670 title = g_strdup_printf(_("Error in chat %s"), from);
671 if (g_hash_table_size(chat->members) == 0)
672 serv_got_chat_left(js->gc, chat->id);
673 }
674 purple_notify_error(js->gc, title, title, msg);
675 g_free(title);
676 g_free(msg);
677
678 if (g_hash_table_size(chat->members) == 0)
679 /* Only destroy the chat if the error happened while joining */
680 jabber_chat_destroy(chat);
681 jabber_id_free(jid);
682 g_free(status);
683 g_free(avatar_hash);
684 g_free(nickname);
685 return;
686 }
687
688 if (type == NULL) {
689 xmlnode *x;
690 const char *real_jid = NULL;
691 const char *affiliation = NULL;
692 const char *role = NULL;
693
694 /*
695 * XEP-0045 mandates the presence to include a resource (which is
696 * treated as the chat nick). Some non-compliant servers allow
697 * joining without a nick.
698 */
699 if (!jid->resource) {
700 jabber_id_free(jid);
701 g_free(avatar_hash);
702 g_free(nickname);
703 g_free(status);
704 return;
705 }
706
707 x = xmlnode_get_child_with_namespace(packet, "x",
708 "http://jabber.org/protocol/muc#user");
709 if (x) {
710 xmlnode *status_node;
711 xmlnode *item_node;
712
713 status_node = xmlnode_get_child(x, "status");
714 if (status_node) {
715 const char *code = xmlnode_get_attrib(status_node, "code");
716 if (purple_strequal(code, "201")) {
717 if ((chat = jabber_chat_find(js, jid->node, jid->domain))) {
599 chat->config_dialog_type = PURPLE_REQUEST_ACTION; 718 chat->config_dialog_type = PURPLE_REQUEST_ACTION;
600 chat->config_dialog_handle = 719 chat->config_dialog_handle =
601 purple_request_action(js->gc, 720 purple_request_action(js->gc,
602 _("Create New Room"), 721 _("Create New Room"),
603 _("Create New Room"), 722 _("Create New Room"),
608 purple_connection_get_account(js->gc), NULL, chat->conv, 727 purple_connection_get_account(js->gc), NULL, chat->conv,
609 chat, 2, 728 chat, 2,
610 _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), 729 _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure),
611 _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); 730 _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room));
612 } 731 }
613 } else if(code && !strcmp(code, "210")) { 732 } else if (purple_strequal(code, "210")) {
614 /* server rewrote room-nick */ 733 /* server rewrote room-nick */
615 if((chat = jabber_chat_find(js, jid->node, jid->domain))) { 734 if((chat = jabber_chat_find(js, jid->node, jid->domain))) {
616 g_free(chat->handle); 735 g_free(chat->handle);
617 chat->handle = g_strdup(jid->resource); 736 chat->handle = g_strdup(jid->resource);
618 } 737 }
619 } 738 }
620 } 739 }
621 if((z = xmlnode_get_child(y, "item"))) { 740
622 real_jid = xmlnode_get_attrib(z, "jid"); 741 item_node = xmlnode_get_child(x, "item");
623 affiliation = xmlnode_get_attrib(z, "affiliation"); 742 if (item_node) {
624 role = xmlnode_get_attrib(z, "role"); 743 real_jid = xmlnode_get_attrib(item_node, "jid");
625 if(affiliation != NULL && !strcmp(affiliation, "owner")) 744 affiliation = xmlnode_get_attrib(item_node, "affiliation");
745 role = xmlnode_get_attrib(item_node, "role");
746
747 if (purple_strequal(affiliation, "owner"))
626 flags |= PURPLE_CBFLAGS_FOUNDER; 748 flags |= PURPLE_CBFLAGS_FOUNDER;
627 if (role != NULL) { 749 if (role) {
628 if (!strcmp(role, "moderator")) 750 if (g_str_equal(role, "moderator"))
629 flags |= PURPLE_CBFLAGS_OP; 751 flags |= PURPLE_CBFLAGS_OP;
630 else if (!strcmp(role, "participant")) 752 else if (g_str_equal(role, "participant"))
631 flags |= PURPLE_CBFLAGS_VOICE; 753 flags |= PURPLE_CBFLAGS_VOICE;
632 } 754 }
633 } 755 }
634 } else if(!strcmp(xmlns, "vcard-temp:x:update")) { 756 }
635 xmlnode *photo = xmlnode_get_child(y, "photo"); 757
636 if(photo) { 758 if(!chat->conv) {
637 g_free(avatar_hash); 759 char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
638 avatar_hash = xmlnode_get_data(photo); 760 chat->id = i++;
639 } 761 chat->muc = (x != NULL);
640 } 762 chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
641 } else if (!strcmp(y->name, "query") && 763 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
642 !strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) { 764
643 /* resource has specified idle */ 765 jabber_chat_disco_traffic(chat);
644 const gchar *seconds = xmlnode_get_attrib(y, "seconds"); 766 g_free(room_jid);
645 if (seconds) { 767 }
646 /* we may need to take "delayed" into account here */ 768
647 idle = atoi(seconds); 769 jabber_buddy_track_resource(jb, jid->resource, priority, state,
648 } 770 status);
649 } 771
650 } 772 jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
651 773
652 if (idle && delayed && stamp) { 774 if(!jabber_chat_find_buddy(chat->conv, jid->resource))
653 /* if we have a delayed presence, we need to add the delay to the idle 775 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
654 value */ 776 real_jid, flags, !delayed);
655 time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL, 777 else
656 NULL); 778 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
657 purple_debug_info("jabber", "got delay %s yielding %ld s offset\n", 779 flags);
658 stamp, offset); 780 } else if (g_str_equal(type, "unavailable")) {
659 idle += offset; 781 xmlnode *x;
660 }
661
662 if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) {
663 static int i = 1;
664
665 if(state == JABBER_BUDDY_STATE_ERROR) {
666 char *title, *msg = jabber_parse_error(js, packet, NULL);
667
668 if (!chat->conv) {
669 title = g_strdup_printf(_("Error joining chat %s"), from);
670 purple_serv_got_join_chat_failed(js->gc, chat->components);
671 } else {
672 title = g_strdup_printf(_("Error in chat %s"), from);
673 if (g_hash_table_size(chat->members) == 0)
674 serv_got_chat_left(js->gc, chat->id);
675 }
676 purple_notify_error(js->gc, title, title, msg);
677 g_free(title);
678 g_free(msg);
679
680 if (g_hash_table_size(chat->members) == 0)
681 /* Only destroy the chat if the error happened while joining */
682 jabber_chat_destroy(chat);
683 jabber_id_free(jid);
684 g_free(status);
685 g_free(avatar_hash);
686 g_free(nickname);
687 return;
688 }
689
690
691 if(type && !strcmp(type, "unavailable")) {
692 gboolean nick_change = FALSE; 782 gboolean nick_change = FALSE;
783 gboolean kick = FALSE;
784 gboolean is_our_resource = FALSE; /* Is the presence about us? */
693 785
694 /* If the chat nick is invalid, we haven't yet joined, or we've 786 /* If the chat nick is invalid, we haven't yet joined, or we've
695 * already left (it was probably us leaving after we closed the 787 * already left (it was probably us leaving after we closed the
696 * chat), we don't care. 788 * chat), we don't care.
697 */ 789 */
704 g_free(avatar_hash); 796 g_free(avatar_hash);
705 g_free(nickname); 797 g_free(nickname);
706 return; 798 return;
707 } 799 }
708 800
801 is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle));
802
709 jabber_buddy_remove_resource(jb, jid->resource); 803 jabber_buddy_remove_resource(jb, jid->resource);
710 if(chat->muc) { 804
711 xmlnode *x; 805 x = xmlnode_get_child_with_namespace(packet, "x",
712 for(x = xmlnode_get_child(packet, "x"); x; x = xmlnode_get_next_twin(x)) { 806 "http://jabber.org/protocol/muc#user");
713 const char *xmlns, *nick, *code; 807 if (chat->muc && x) {
714 xmlnode *stat, *item; 808 const char *nick;
715 if(!(xmlns = xmlnode_get_namespace(x)) || 809 const char *code = NULL;
716 strcmp(xmlns, "http://jabber.org/protocol/muc#user")) 810 const char *item_jid = NULL;
717 continue; 811 xmlnode *stat;
718 if(!(stat = xmlnode_get_child(x, "status"))) 812 xmlnode *item;
719 continue; 813
720 if(!(code = xmlnode_get_attrib(stat, "code"))) 814 item = xmlnode_get_child(x, "item");
721 continue; 815 if (item)
816 item_jid = xmlnode_get_attrib(item, "jid");
817
818
819 stat = xmlnode_get_child(x, "status");
820
821 if (stat)
822 code = xmlnode_get_attrib(stat, "code");
823
824 if (code) {
722 if(!strcmp(code, "301")) { 825 if(!strcmp(code, "301")) {
723 /* XXX: we got banned */ 826 /* XXX: we got banned */
724 } else if(!strcmp(code, "303")) { 827 } else if(!strcmp(code, "303") && item &&
725 if(!(item = xmlnode_get_child(x, "item"))) 828 (nick = xmlnode_get_attrib(item, "nick"))) {
726 continue;
727 if(!(nick = xmlnode_get_attrib(item, "nick")))
728 continue;
729 nick_change = TRUE; 829 nick_change = TRUE;
730 if(!strcmp(jid->resource, chat->handle)) { 830 if(!strcmp(jid->resource, chat->handle)) {
731 g_free(chat->handle); 831 g_free(chat->handle);
732 chat->handle = g_strdup(nick); 832 chat->handle = g_strdup(nick);
733 } 833 }
734 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick); 834 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick);
735 jabber_chat_remove_handle(chat, jid->resource); 835 jabber_chat_remove_handle(chat, jid->resource);
736 break; 836 /* TODO: Enable this when this is in a for-loop...
837 break; */
737 } else if(!strcmp(code, "307")) { 838 } else if(!strcmp(code, "307")) {
738 /* XXX: we got kicked */ 839 /* Someone was kicked from the room */
840 xmlnode *reason = NULL, *actor = NULL;
841 const char *actor_name = NULL;
842 char *reason_text = NULL;
843 char *tmp;
844
845 kick = TRUE;
846
847 if (item) {
848 reason = xmlnode_get_child(item, "reason");
849 actor = xmlnode_get_child(item, "actor");
850
851 if (reason != NULL)
852 reason_text = xmlnode_get_data(reason);
853 if (actor != NULL)
854 actor_name = xmlnode_get_attrib(actor, "jid");
855 }
856
857 if (reason_text == NULL)
858 reason_text = g_strdup(_("No reason"));
859
860 if (is_our_resource) {
861 if (actor_name != NULL)
862 tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"),
863 actor_name, reason_text);
864 else
865 tmp = g_strdup_printf(_("You have been kicked: (%s)"),
866 reason_text);
867 } else {
868 if (actor_name != NULL)
869 tmp = g_strdup_printf(_("Kicked by %s (%s)"),
870 actor_name, reason_text);
871 else
872 tmp = g_strdup_printf(_("Kicked (%s)"),
873 reason_text);
874 }
875
876 g_free(reason_text);
877 g_free(status);
878 status = tmp;
739 } else if(!strcmp(code, "321")) { 879 } else if(!strcmp(code, "321")) {
740 /* XXX: removed due to an affiliation change */ 880 /* XXX: removed due to an affiliation change */
741 } else if(!strcmp(code, "322")) { 881 } else if(!strcmp(code, "322")) {
742 /* XXX: removed because room is now members-only */ 882 /* XXX: removed because room is now members-only */
743 } else if(!strcmp(code, "332")) { 883 } else if(!strcmp(code, "332")) {
744 /* XXX: removed due to system shutdown */ 884 /* XXX: removed due to system shutdown */
745 } 885 }
746 } 886 }
887
888 /*
889 * Possibly another connected resource of our JID (see XEP-0045
890 * v1.24 section 7.1.10) being disconnected. Should be
891 * distinguished by the item_jid.
892 * Also possibly works around bits of an Openfire bug. See
893 * #8319.
894 */
895 if (is_our_resource && !purple_strequal(from, item_jid)) {
896 /* TODO: When the above is a loop, this needs to still act
897 * sanely for all cases (this code is a little fragile). */
898 if (!kick && !nick_change)
899 /* Presumably, kicks and nick changes also affect us. */
900 is_our_resource = FALSE;
901 }
747 } 902 }
748 if(!nick_change) { 903 if(!nick_change) {
749 if(!g_utf8_collate(jid->resource, chat->handle)) { 904 if (is_our_resource) {
905 if (kick)
906 purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource,
907 status, PURPLE_MESSAGE_SYSTEM, time(NULL));
908
750 serv_got_chat_left(js->gc, chat->id); 909 serv_got_chat_left(js->gc, chat->id);
751 jabber_chat_destroy(chat); 910 jabber_chat_destroy(chat);
752 } else { 911 } else {
753 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, 912 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
754 status); 913 status);
755 jabber_chat_remove_handle(chat, jid->resource); 914 jabber_chat_remove_handle(chat, jid->resource);
756 } 915 }
757 } 916 }
758 } else { 917 } else {
759 /* 918 /* A type that isn't available or unavailable */
760 * XEP-0045 mandates the presence to include a resource (which is 919 purple_debug_error("jabber", "MUC presence with bad type: %s\n",
761 * treated as the chat nick). Some non-compliant servers allow 920 type);
762 * joining without a nick. 921
763 */ 922 jabber_id_free(jid);
764 if (!jid->resource) { 923 g_free(avatar_hash);
765 jabber_id_free(jid); 924 g_free(status);
766 g_free(avatar_hash); 925 g_free(nickname);
767 g_free(nickname); 926 g_return_if_reached();
768 g_free(status);
769 return;
770 }
771
772 if(!chat->conv) {
773 char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain);
774 chat->id = i++;
775 chat->muc = muc;
776 chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid);
777 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle);
778
779 jabber_chat_disco_traffic(chat);
780 g_free(room_jid);
781 }
782
783 jabber_buddy_track_resource(jb, jid->resource, priority, state,
784 status);
785
786 jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role);
787
788 if(!jabber_chat_find_buddy(chat->conv, jid->resource))
789 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource,
790 real_jid, flags, !delayed);
791 else
792 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource,
793 flags);
794 } 927 }
795 } else { 928 } else {
796 buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", 929 buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "",
797 jid->node ? "@" : "", jid->domain); 930 jid->node ? "@" : "", jid->domain);
798 if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) { 931 if((b = purple_find_buddy(js->gc->account, buddy_name)) == NULL) {
835 } 968 }
836 } 969 }
837 } 970 }
838 971
839 if(state == JABBER_BUDDY_STATE_ERROR || 972 if(state == JABBER_BUDDY_STATE_ERROR ||
840 (type && (!strcmp(type, "unavailable") || 973 (type && (g_str_equal(type, "unavailable") ||
841 !strcmp(type, "unsubscribed")))) { 974 g_str_equal(type, "unsubscribed")))) {
842 PurpleConversation *conv; 975 PurpleConversation *conv;
843 976
844 jabber_buddy_remove_resource(jb, jid->resource); 977 jabber_buddy_remove_resource(jb, jid->resource);
845 if((conv = jabber_find_unnormalized_conv(from, js->gc->account))) 978 if((conv = jabber_find_unnormalized_conv(from, js->gc->account)))
846 purple_conversation_set_name(conv, buddy_name); 979 purple_conversation_set_name(conv, buddy_name);
865 purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL); 998 purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL);
866 } 999 }
867 g_free(buddy_name); 1000 g_free(buddy_name);
868 } 1001 }
869 1002
870 if (caps && (!type || g_str_equal(type, "available"))) { 1003 if (caps && !type) {
871 /* handle Entity Capabilities (XEP-0115) */ 1004 /* handle Entity Capabilities (XEP-0115) */
872 const char *node = xmlnode_get_attrib(caps, "node"); 1005 const char *node = xmlnode_get_attrib(caps, "node");
873 const char *ver = xmlnode_get_attrib(caps, "ver"); 1006 const char *ver = xmlnode_get_attrib(caps, "ver");
874 const char *hash = xmlnode_get_attrib(caps, "hash"); 1007 const char *hash = xmlnode_get_attrib(caps, "hash");
875 const char *ext = xmlnode_get_attrib(caps, "ext"); 1008 const char *ext = xmlnode_get_attrib(caps, "ext");