| 506 out: |
536 out: |
| 507 g_free(userdata->from); |
537 g_free(userdata->from); |
| 508 g_free(userdata); |
538 g_free(userdata); |
| 509 } |
539 } |
| 510 |
540 |
| |
541 gboolean |
| |
542 handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet) |
| |
543 { |
| |
544 static int i = 1; |
| |
545 PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; |
| |
546 JabberChat *chat = presence->chat; |
| |
547 |
| |
548 if (presence->state == JABBER_BUDDY_STATE_ERROR) { |
| |
549 char *title, *msg = jabber_parse_error(js, packet, NULL); |
| |
550 |
| |
551 if (!chat->conv) { |
| |
552 title = g_strdup_printf(_("Error joining chat %s"), presence->from); |
| |
553 purple_serv_got_join_chat_failed(js->gc, chat->components); |
| |
554 } else { |
| |
555 title = g_strdup_printf(_("Error in chat %s"), presence->from); |
| |
556 if (g_hash_table_size(chat->members) == 0) |
| |
557 serv_got_chat_left(js->gc, chat->id); |
| |
558 } |
| |
559 purple_notify_error(js->gc, title, title, msg); |
| |
560 g_free(title); |
| |
561 g_free(msg); |
| |
562 |
| |
563 if (g_hash_table_size(chat->members) == 0) |
| |
564 /* Only destroy the chat if the error happened while joining */ |
| |
565 jabber_chat_destroy(chat); |
| |
566 return FALSE; |
| |
567 } |
| |
568 |
| |
569 if (presence->type == JABBER_PRESENCE_AVAILABLE) { |
| |
570 const char *jid = NULL; |
| |
571 const char *affiliation = NULL; |
| |
572 const char *role = NULL; |
| |
573 gboolean is_our_resource = FALSE; /* Is the presence about us? */ |
| |
574 JabberBuddyResource *jbr; |
| |
575 |
| |
576 /* |
| |
577 * XEP-0045 mandates the presence to include a resource (which is |
| |
578 * treated as the chat nick). Some non-compliant servers allow |
| |
579 * joining without a nick. |
| |
580 */ |
| |
581 if (!presence->jid_from->resource) |
| |
582 return FALSE; |
| |
583 |
| |
584 if (presence->chat_info.item) { |
| |
585 jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); |
| |
586 affiliation = xmlnode_get_attrib(presence->chat_info.item, "affiliation"); |
| |
587 role = xmlnode_get_attrib(presence->chat_info.item, "role"); |
| |
588 } |
| |
589 |
| |
590 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) |
| |
591 is_our_resource = TRUE; |
| |
592 |
| |
593 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(201))) { |
| |
594 chat->config_dialog_type = PURPLE_REQUEST_ACTION; |
| |
595 chat->config_dialog_handle = |
| |
596 purple_request_action(js->gc, |
| |
597 _("Create New Room"), |
| |
598 _("Create New Room"), |
| |
599 _("You are creating a new room. Would" |
| |
600 " you like to configure it, or" |
| |
601 " accept the default settings?"), |
| |
602 /* Default Action */ 1, |
| |
603 purple_connection_get_account(js->gc), NULL, chat->conv, |
| |
604 chat, 2, |
| |
605 _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), |
| |
606 _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); |
| |
607 } |
| |
608 |
| |
609 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(210))) { |
| |
610 /* server rewrote room-nick */ |
| |
611 g_free(chat->handle); |
| |
612 chat->handle = g_strdup(presence->jid_from->resource); |
| |
613 } |
| |
614 |
| |
615 if (purple_strequal(affiliation, "owner")) |
| |
616 flags |= PURPLE_CBFLAGS_FOUNDER; |
| |
617 if (role) { |
| |
618 if (g_str_equal(role, "moderator")) |
| |
619 flags |= PURPLE_CBFLAGS_OP; |
| |
620 else if (g_str_equal(role, "participant")) |
| |
621 flags |= PURPLE_CBFLAGS_VOICE; |
| |
622 } |
| |
623 |
| |
624 if(!chat->conv) { |
| |
625 char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain); |
| |
626 chat->id = i++; |
| |
627 chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid); |
| |
628 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle); |
| |
629 |
| |
630 jabber_chat_disco_traffic(chat); |
| |
631 g_free(room_jid); |
| |
632 } |
| |
633 |
| |
634 jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status); |
| |
635 jbr->commands_fetched = TRUE; |
| |
636 |
| |
637 jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role); |
| |
638 |
| |
639 if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource)) |
| |
640 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, |
| |
641 jid, flags, !presence->delayed); |
| |
642 else |
| |
643 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, |
| |
644 flags); |
| |
645 } else if (presence->type == JABBER_PRESENCE_UNAVAILABLE) { |
| |
646 gboolean nick_change = FALSE; |
| |
647 gboolean kick = FALSE; |
| |
648 gboolean is_our_resource = FALSE; /* Is the presence about us? */ |
| |
649 |
| |
650 const char *jid = NULL; |
| |
651 |
| |
652 /* If the chat nick is invalid, we haven't yet joined, or we've |
| |
653 * already left (it was probably us leaving after we closed the |
| |
654 * chat), we don't care. |
| |
655 */ |
| |
656 if (!presence->jid_from->resource || !chat->conv || chat->left) { |
| |
657 if (chat->left && |
| |
658 presence->jid_from->resource && chat->handle && !strcmp(presence->jid_from->resource, chat->handle)) |
| |
659 jabber_chat_destroy(chat); |
| |
660 return FALSE; |
| |
661 } |
| |
662 |
| |
663 is_our_resource = (0 == g_utf8_collate(presence->jid_from->resource, chat->handle)); |
| |
664 |
| |
665 jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); |
| |
666 |
| |
667 if (presence->chat_info.item) |
| |
668 jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); |
| |
669 |
| |
670 if (chat->muc) { |
| |
671 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) |
| |
672 is_our_resource = TRUE; |
| |
673 |
| |
674 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(301))) { |
| |
675 /* XXX: We got banned. YAY! (No GIR, that's bad) */ |
| |
676 } |
| |
677 |
| |
678 |
| |
679 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) { |
| |
680 const char *nick = NULL; |
| |
681 if (presence->chat_info.item) |
| |
682 nick = xmlnode_get_attrib(presence->chat_info.item, "nick"); |
| |
683 |
| |
684 /* nick change */ |
| |
685 if (nick) { |
| |
686 purple_debug_warning("jabber", "Chat presence indicating a nick change, but no new nickname!\n"); |
| |
687 } else { |
| |
688 nick_change = TRUE; |
| |
689 |
| |
690 if (g_str_equal(presence->jid_from->resource, chat->handle)) { |
| |
691 /* Changing our own nickname */ |
| |
692 g_free(chat->handle); |
| |
693 chat->handle = g_strdup(nick); |
| |
694 } |
| |
695 |
| |
696 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), |
| |
697 presence->jid_from->resource, |
| |
698 nick); |
| |
699 jabber_chat_remove_handle(chat, |
| |
700 presence->jid_from->resource); |
| |
701 } |
| |
702 } |
| |
703 |
| |
704 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(307))) { |
| |
705 /* Someone was kicked from the room */ |
| |
706 const char *actor = NULL; |
| |
707 char *reason = NULL; |
| |
708 char *tmp; |
| |
709 |
| |
710 kick = TRUE; |
| |
711 |
| |
712 if (presence->chat_info.item) { |
| |
713 xmlnode *node; |
| |
714 |
| |
715 node = xmlnode_get_child(presence->chat_info.item, "actor"); |
| |
716 if (node) |
| |
717 actor = xmlnode_get_attrib(node, "jid"); |
| |
718 node = xmlnode_get_child(presence->chat_info.item, "reason"); |
| |
719 if (node) |
| |
720 reason = xmlnode_get_data(node); |
| |
721 } |
| |
722 |
| |
723 if (reason == NULL) |
| |
724 reason = g_strdup(_("No reason")); |
| |
725 |
| |
726 if (is_our_resource) { |
| |
727 if (actor) |
| |
728 tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), |
| |
729 actor, reason); |
| |
730 else |
| |
731 tmp = g_strdup_printf(_("You have been kicked: (%s)"), |
| |
732 reason); |
| |
733 } else { |
| |
734 if (actor) |
| |
735 tmp = g_strdup_printf(_("Kicked by %s (%s)"), |
| |
736 actor, reason); |
| |
737 else |
| |
738 tmp = g_strdup_printf(_("Kicked (%s)"), |
| |
739 reason); |
| |
740 } |
| |
741 |
| |
742 g_free(presence->status); |
| |
743 presence->status = tmp; |
| |
744 |
| |
745 g_free(reason); |
| |
746 } |
| |
747 |
| |
748 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(321))) { |
| |
749 /* XXX: removed due to an affiliation change */ |
| |
750 } |
| |
751 |
| |
752 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(322))) { |
| |
753 /* XXX: removed because room is now members-only */ |
| |
754 } |
| |
755 |
| |
756 if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(332))) { |
| |
757 /* XXX: removed due to system shutdown */ |
| |
758 } |
| |
759 } |
| |
760 |
| |
761 /* |
| |
762 * Possibly another connected resource of our JID (see XEP-0045 |
| |
763 * v1.24 section 7.1.10) being disconnected. Should be |
| |
764 * distinguished by the item_jid. |
| |
765 * Also possibly works around bits of an Openfire bug. See |
| |
766 * #8319. |
| |
767 */ |
| |
768 if (is_our_resource && jid && !purple_strequal(presence->to, jid)) { |
| |
769 /* TODO: When the above is a loop, this needs to still act |
| |
770 * sanely for all cases (this code is a little fragile). */ |
| |
771 if (!kick && !nick_change) |
| |
772 /* Presumably, kicks and nick changes also affect us. */ |
| |
773 is_our_resource = FALSE; |
| |
774 } |
| |
775 |
| |
776 if(!nick_change) { |
| |
777 if (is_our_resource) { |
| |
778 if (kick) |
| |
779 purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, |
| |
780 presence->status, PURPLE_MESSAGE_SYSTEM, time(NULL)); |
| |
781 |
| |
782 serv_got_chat_left(js->gc, chat->id); |
| |
783 jabber_chat_destroy(chat); |
| |
784 } else { |
| |
785 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, |
| |
786 presence->status); |
| |
787 jabber_chat_remove_handle(chat, presence->jid_from->resource); |
| |
788 } |
| |
789 } |
| |
790 } |
| |
791 |
| |
792 return TRUE; |
| |
793 } |
| |
794 |
| |
795 gboolean |
| |
796 handle_presence_contact(JabberStream *js, JabberPresence *presence) |
| |
797 { |
| |
798 JabberBuddyResource *jbr; |
| |
799 PurpleAccount *account; |
| |
800 PurpleBuddy *b; |
| |
801 char *buddy_name; |
| |
802 PurpleConversation *conv; |
| |
803 |
| |
804 buddy_name = jabber_id_get_bare_jid(presence->jid_from); |
| |
805 |
| |
806 account = purple_connection_get_account(js->gc); |
| |
807 b = purple_find_buddy(account, buddy_name); |
| |
808 |
| |
809 /* |
| |
810 * Unbind/unlock from sending messages to a specific resource on |
| |
811 * presence changes. This is locked to a specific resource when |
| |
812 * receiving a message (in message.c). |
| |
813 */ |
| |
814 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, |
| |
815 buddy_name, account); |
| |
816 if (conv) { |
| |
817 purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", |
| |
818 purple_conversation_get_name(conv), buddy_name); |
| |
819 purple_conversation_set_name(conv, buddy_name); |
| |
820 } |
| |
821 |
| |
822 if (b == NULL) { |
| |
823 if (presence->jb != js->user_jb) { |
| |
824 purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", |
| |
825 buddy_name, purple_account_get_username(account), account); |
| |
826 return FALSE; |
| |
827 } else { |
| |
828 /* this is a different resource of our own account. Resume even when this account isn't on our blist */ |
| |
829 } |
| |
830 } |
| |
831 |
| |
832 if(b && presence->vcard_avatar_hash) { |
| |
833 const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); |
| |
834 if(!avatar_hash2 || strcmp(presence->vcard_avatar_hash, avatar_hash2)) { |
| |
835 JabberIq *iq; |
| |
836 xmlnode *vcard; |
| |
837 |
| |
838 /* XXX this is a crappy way of trying to prevent |
| |
839 * someone from spamming us with presence packets |
| |
840 * and causing us to DoS ourselves...what we really |
| |
841 * need is a queue system that can throttle itself, |
| |
842 * but i'm too tired to write that right now */ |
| |
843 if(!g_slist_find(js->pending_avatar_requests, presence->jb)) { |
| |
844 |
| |
845 js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, presence->jb); |
| |
846 |
| |
847 iq = jabber_iq_new(js, JABBER_IQ_GET); |
| |
848 xmlnode_set_attrib(iq->node, "to", buddy_name); |
| |
849 vcard = xmlnode_new_child(iq->node, "vCard"); |
| |
850 xmlnode_set_namespace(vcard, "vcard-temp"); |
| |
851 |
| |
852 jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); |
| |
853 jabber_iq_send(iq); |
| |
854 } |
| |
855 } |
| |
856 } |
| |
857 |
| |
858 if (presence->state == JABBER_BUDDY_STATE_ERROR || |
| |
859 presence->type == JABBER_PRESENCE_UNAVAILABLE || |
| |
860 presence->type == JABBER_PRESENCE_UNSUBSCRIBED) { |
| |
861 jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); |
| |
862 } else { |
| |
863 jbr = jabber_buddy_track_resource(presence->jb, |
| |
864 presence->jid_from->resource, presence->priority, |
| |
865 presence->state, presence->status); |
| |
866 jbr->idle = presence->idle ? time(NULL) - presence->idle : 0; |
| |
867 } |
| |
868 |
| |
869 jbr = jabber_buddy_find_resource(presence->jb, NULL); |
| |
870 if (jbr) { |
| |
871 jabber_google_presence_incoming(js, buddy_name, jbr); |
| |
872 purple_prpl_got_user_status(account, buddy_name, |
| |
873 jabber_buddy_state_get_status_id(jbr->state), |
| |
874 "priority", jbr->priority, |
| |
875 "message", jbr->status, |
| |
876 NULL); |
| |
877 purple_prpl_got_user_idle(account, buddy_name, |
| |
878 jbr->idle, jbr->idle); |
| |
879 if (presence->nickname) |
| |
880 serv_got_alias(js->gc, buddy_name, presence->nickname); |
| |
881 } else { |
| |
882 purple_prpl_got_user_status(account, buddy_name, |
| |
883 jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), |
| |
884 presence->status ? "message" : NULL, presence->status, |
| |
885 NULL); |
| |
886 } |
| |
887 g_free(buddy_name); |
| |
888 |
| |
889 return TRUE; |
| |
890 } |
| |
891 |
| 511 void jabber_presence_parse(JabberStream *js, xmlnode *packet) |
892 void jabber_presence_parse(JabberStream *js, xmlnode *packet) |
| 512 { |
893 { |
| 513 const char *from; |
|
| 514 const char *type; |
894 const char *type; |
| 515 char *status = NULL; |
895 JabberBuddyResource *jbr = NULL; |
| 516 int priority = 0; |
896 gboolean signal_return, ret; |
| 517 JabberID *jid; |
897 JabberPresence presence; |
| 518 JabberChat *chat; |
898 xmlnode *child; |
| 519 JabberBuddy *jb; |
899 |
| 520 JabberBuddyResource *jbr = NULL, *found_jbr = NULL; |
900 memset(&presence, 0, sizeof(presence)); |
| 521 PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; |
901 /* defaults */ |
| 522 gboolean delayed = FALSE; |
902 presence.state = JABBER_BUDDY_STATE_UNKNOWN; |
| 523 const gchar *stamp = NULL; /* from <delayed/> element */ |
903 presence.sent = time(NULL); |
| 524 PurpleAccount *account; |
904 /* interesting values */ |
| 525 PurpleBuddy *b = NULL; |
905 presence.from = xmlnode_get_attrib(packet, "from"); |
| 526 char *buddy_name; |
906 presence.to = xmlnode_get_attrib(packet, "to"); |
| 527 JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; |
|
| 528 xmlnode *y; |
|
| 529 char *avatar_hash = NULL; |
|
| 530 xmlnode *caps = NULL; |
|
| 531 int idle = 0; |
|
| 532 gchar *nickname = NULL; |
|
| 533 gboolean signal_return; |
|
| 534 |
|
| 535 from = xmlnode_get_attrib(packet, "from"); |
|
| 536 type = xmlnode_get_attrib(packet, "type"); |
907 type = xmlnode_get_attrib(packet, "type"); |
| 537 |
908 presence.type = str_to_presence_type(type); |
| 538 jb = jabber_buddy_find(js, from, TRUE); |
909 |
| 539 g_return_if_fail(jb != NULL); |
910 presence.jb = jabber_buddy_find(js, presence.from, TRUE); |
| |
911 g_return_if_fail(presence.jb != NULL); |
| |
912 |
| |
913 presence.jid_from = jabber_id_new(presence.from); |
| |
914 if (presence.jid_from == NULL) { |
| |
915 purple_debug_error("jabber", "Ignoring presence with malformed 'from' " |
| |
916 "JID: %s\n", presence.from); |
| |
917 return; |
| |
918 } |
| 540 |
919 |
| 541 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), |
920 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), |
| 542 "jabber-receiving-presence", js->gc, type, from, packet)); |
921 "jabber-receiving-presence", js->gc, type, presence.from, packet)); |
| 543 if (signal_return) |
922 if (signal_return) { |
| 544 return; |
923 goto out; |
| 545 |
924 } |
| 546 account = purple_connection_get_account(js->gc); |
925 |
| 547 |
926 presence.chat = jabber_chat_find(js, presence.jid_from->node, |
| 548 jid = jabber_id_new(from); |
927 presence.jid_from->domain); |
| 549 if (jid == NULL) { |
928 if(presence.jb->error_msg) { |
| 550 purple_debug_error("jabber", "Ignoring presence with malformed 'from' " |
929 g_free(presence.jb->error_msg); |
| 551 "JID: %s\n", from); |
930 presence.jb->error_msg = NULL; |
| 552 return; |
931 } |
| 553 } |
932 |
| 554 |
933 if (presence.type == JABBER_PRESENCE_AVAILABLE) { |
| 555 if(jb->error_msg) { |
934 presence.state = JABBER_BUDDY_STATE_ONLINE; |
| 556 g_free(jb->error_msg); |
935 } else if (presence.type == JABBER_PRESENCE_ERROR) { |
| 557 jb->error_msg = NULL; |
936 /* TODO: Is this handled properly? Should it be treated as per-jbr? */ |
| 558 } |
|
| 559 |
|
| 560 if (type == NULL) { |
|
| 561 xmlnode *show; |
|
| 562 char *show_data = NULL; |
|
| 563 |
|
| 564 state = JABBER_BUDDY_STATE_ONLINE; |
|
| 565 |
|
| 566 show = xmlnode_get_child(packet, "show"); |
|
| 567 if (show) { |
|
| 568 show_data = xmlnode_get_data(show); |
|
| 569 if (show_data) { |
|
| 570 state = jabber_buddy_show_get_state(show_data); |
|
| 571 g_free(show_data); |
|
| 572 } else |
|
| 573 purple_debug_warning("jabber", "<show/> present on presence, " |
|
| 574 "but no contents!\n"); |
|
| 575 } |
|
| 576 } else if (g_str_equal(type, "error")) { |
|
| 577 char *msg = jabber_parse_error(js, packet, NULL); |
937 char *msg = jabber_parse_error(js, packet, NULL); |
| 578 |
938 presence.state = JABBER_BUDDY_STATE_ERROR; |
| 579 state = JABBER_BUDDY_STATE_ERROR; |
939 presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); |
| 580 jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); |
940 } else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) { |
| 581 } else if (g_str_equal(type, "subscribe")) { |
941 /* TODO: Move to handle_subscribe() (so nick is extracted by the |
| |
942 * PresenceHandler */ |
| 582 struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); |
943 struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); |
| 583 gboolean onlist = FALSE; |
944 gboolean onlist = FALSE; |
| |
945 PurpleAccount *account; |
| 584 PurpleBuddy *buddy; |
946 PurpleBuddy *buddy; |
| 585 JabberBuddy *jb = NULL; |
|
| 586 xmlnode *nick; |
947 xmlnode *nick; |
| 587 |
948 |
| 588 buddy = purple_find_buddy(account, from); |
949 account = purple_connection_get_account(js->gc); |
| |
950 buddy = purple_find_buddy(account, presence.from); |
| 589 nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); |
951 nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); |
| 590 if (nick) |
952 if (nick) |
| 591 nickname = xmlnode_get_data(nick); |
953 presence.nickname = xmlnode_get_data(nick); |
| 592 |
954 |
| 593 if (buddy) { |
955 if (buddy) { |
| 594 jb = jabber_buddy_find(js, from, TRUE); |
956 if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) |
| 595 if ((jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) |
|
| 596 onlist = TRUE; |
957 onlist = TRUE; |
| 597 } |
958 } |
| 598 |
959 |
| 599 jap->gc = js->gc; |
960 jap->gc = js->gc; |
| 600 jap->who = g_strdup(from); |
961 jap->who = g_strdup(presence.from); |
| 601 jap->js = js; |
962 jap->js = js; |
| 602 |
963 |
| 603 purple_account_request_authorization(account, from, NULL, nickname, |
964 purple_account_request_authorization(account, presence.from, NULL, presence.nickname, |
| 604 NULL, onlist, authorize_add_cb, deny_add_cb, jap); |
965 NULL, onlist, authorize_add_cb, deny_add_cb, jap); |
| 605 |
966 |
| 606 g_free(nickname); |
967 goto out; |
| 607 jabber_id_free(jid); |
968 } else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) { |
| 608 return; |
969 /* This case (someone has approved our subscribe request) is handled |
| 609 } else if (g_str_equal(type, "subscribed")) { |
970 * by the roster push the server sends along with this. |
| 610 /* we've been allowed to see their presence, but we don't care */ |
971 */ |
| 611 jabber_id_free(jid); |
972 goto out; |
| 612 return; |
973 } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBE) { |
| 613 } else if (g_str_equal(type, "unsubscribe")) { |
|
| 614 /* XXX I'm not sure this is the right way to handle this, it |
974 /* XXX I'm not sure this is the right way to handle this, it |
| 615 * might be better to add "unsubscribe" to the presence status |
975 * might be better to add "unsubscribe" to the presence status |
| 616 * if lower down, but I'm not sure. */ |
976 * if lower down, but I'm not sure. */ |
| 617 /* they are unsubscribing from our presence, we don't care */ |
977 /* they are unsubscribing from our presence, we don't care */ |
| 618 /* Well, maybe just a little, we might want/need to start |
978 /* Well, maybe just a little, we might want/need to start |
| 619 * acknowledging this (and the others) at some point. */ |
979 * acknowledging this (and the others) at some point. */ |
| 620 jabber_id_free(jid); |
980 goto out; |
| 621 return; |
981 } else if (presence.type == JABBER_PRESENCE_PROBE) { |
| 622 } else if (g_str_equal(type, "probe")) { |
|
| 623 purple_debug_warning("jabber", "Ignoring presence probe\n"); |
982 purple_debug_warning("jabber", "Ignoring presence probe\n"); |
| 624 jabber_id_free(jid); |
983 goto out; |
| 625 return; |
984 } else if (presence.type == JABBER_PRESENCE_UNAVAILABLE) { |
| 626 } else if (g_str_equal(type, "unavailable")) { |
985 presence.state = JABBER_BUDDY_STATE_UNAVAILABLE; |
| 627 state = JABBER_BUDDY_STATE_UNAVAILABLE; |
986 } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBED) { |
| 628 } else if (g_str_equal(type, "unsubscribed")) { |
987 presence.state = JABBER_BUDDY_STATE_UNKNOWN; |
| 629 state = JABBER_BUDDY_STATE_UNKNOWN; |
|
| 630 } else { |
988 } else { |
| 631 purple_debug_warning("jabber", "Ignoring presence with invalid type " |
989 purple_debug_warning("jabber", "Ignoring presence with invalid type " |
| 632 "'%s'\n", type); |
990 "'%s'\n", type); |
| 633 jabber_id_free(jid); |
991 goto out; |
| 634 return; |
992 } |
| 635 } |
993 |
| 636 |
994 for (child = packet->child; child; child = child->next) { |
| 637 |
995 char *key; |
| 638 for(y = packet->child; y; y = y->next) { |
996 JabberPresenceHandler *pih; |
| 639 const char *xmlns; |
997 if (child->type != XMLNODE_TYPE_TAG) |
| 640 if(y->type != XMLNODE_TYPE_TAG) |
|
| 641 continue; |
998 continue; |
| 642 xmlns = xmlnode_get_namespace(y); |
999 |
| 643 |
1000 key = g_strdup_printf("%s %s", child->name, xmlnode_get_namespace(child)); |
| 644 if(!strcmp(y->name, "status")) { |
1001 pih = g_hash_table_lookup(presence_handlers, key); |
| 645 g_free(status); |
1002 g_free(key); |
| 646 status = xmlnode_get_data(y); |
1003 if (pih) |
| 647 } else if(!strcmp(y->name, "priority")) { |
1004 pih(js, &presence, child); |
| 648 char *p = xmlnode_get_data(y); |
1005 } |
| 649 if(p) { |
1006 |
| 650 priority = atoi(p); |
1007 if (presence.delayed && presence.idle) { |
| 651 g_free(p); |
1008 /* Delayed and idle, so update idle time */ |
| 652 } |
1009 presence.idle = presence.idle + (time(NULL) - presence.sent); |
| 653 } else if(xmlns == NULL) { |
1010 } |
| 654 /* The rest of the cases used to check xmlns individually. */ |
1011 |
| 655 continue; |
1012 /* TODO: Handle tracking jb(r) here? */ |
| 656 } else if(!strcmp(y->name, "delay") && !strcmp(xmlns, NS_DELAYED_DELIVERY)) { |
1013 |
| 657 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ |
1014 if (presence.chat) |
| 658 delayed = TRUE; |
1015 ret = handle_presence_chat(js, &presence, packet); |
| 659 stamp = xmlnode_get_attrib(y, "stamp"); |
1016 else |
| 660 } else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) { |
1017 ret = handle_presence_contact(js, &presence); |
| 661 caps = y; /* store for later, when creating buddy resource */ |
1018 if (!ret) |
| 662 } else if (g_str_equal(y->name, "nick") && g_str_equal(xmlns, "http://jabber.org/protocol/nick")) { |
1019 goto out; |
| 663 nickname = xmlnode_get_data(y); |
1020 |
| 664 } else if(!strcmp(y->name, "x")) { |
1021 if (presence.caps && presence.type == JABBER_PRESENCE_AVAILABLE) { |
| 665 if(!strcmp(xmlns, NS_DELAYED_DELIVERY_LEGACY)) { |
|
| 666 /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ |
|
| 667 delayed = TRUE; |
|
| 668 stamp = xmlnode_get_attrib(y, "stamp"); |
|
| 669 } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { |
|
| 670 } else if(!strcmp(xmlns, "vcard-temp:x:update")) { |
|
| 671 xmlnode *photo = xmlnode_get_child(y, "photo"); |
|
| 672 if(photo) { |
|
| 673 g_free(avatar_hash); |
|
| 674 avatar_hash = xmlnode_get_data(photo); |
|
| 675 } |
|
| 676 } |
|
| 677 } else if (!strcmp(y->name, "query") && |
|
| 678 !strcmp(xmlnode_get_namespace(y), NS_LAST_ACTIVITY)) { |
|
| 679 /* resource has specified idle */ |
|
| 680 const gchar *seconds = xmlnode_get_attrib(y, "seconds"); |
|
| 681 if (seconds) { |
|
| 682 /* we may need to take "delayed" into account here */ |
|
| 683 idle = atoi(seconds); |
|
| 684 } |
|
| 685 } |
|
| 686 } |
|
| 687 |
|
| 688 if (idle && delayed && stamp) { |
|
| 689 /* if we have a delayed presence, we need to add the delay to the idle |
|
| 690 value */ |
|
| 691 time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL, |
|
| 692 NULL); |
|
| 693 purple_debug_info("jabber", "got delay %s yielding %ld s offset\n", |
|
| 694 stamp, offset); |
|
| 695 idle += offset; |
|
| 696 } |
|
| 697 |
|
| 698 /* DEALING WITH CHATS */ |
|
| 699 if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) { |
|
| 700 static int i = 1; |
|
| 701 |
|
| 702 if(state == JABBER_BUDDY_STATE_ERROR) { |
|
| 703 char *title, *msg = jabber_parse_error(js, packet, NULL); |
|
| 704 |
|
| 705 if (!chat->conv) { |
|
| 706 title = g_strdup_printf(_("Error joining chat %s"), from); |
|
| 707 purple_serv_got_join_chat_failed(js->gc, chat->components); |
|
| 708 } else { |
|
| 709 title = g_strdup_printf(_("Error in chat %s"), from); |
|
| 710 if (g_hash_table_size(chat->members) == 0) |
|
| 711 serv_got_chat_left(js->gc, chat->id); |
|
| 712 } |
|
| 713 purple_notify_error(js->gc, title, title, msg); |
|
| 714 g_free(title); |
|
| 715 g_free(msg); |
|
| 716 |
|
| 717 if (g_hash_table_size(chat->members) == 0) |
|
| 718 /* Only destroy the chat if the error happened while joining */ |
|
| 719 jabber_chat_destroy(chat); |
|
| 720 jabber_id_free(jid); |
|
| 721 g_free(status); |
|
| 722 g_free(avatar_hash); |
|
| 723 g_free(nickname); |
|
| 724 return; |
|
| 725 } |
|
| 726 |
|
| 727 if (type == NULL) { |
|
| 728 xmlnode *x; |
|
| 729 const char *real_jid = NULL; |
|
| 730 const char *affiliation = NULL; |
|
| 731 const char *role = NULL; |
|
| 732 gboolean is_our_resource = FALSE; /* Is the presence about us? */ |
|
| 733 |
|
| 734 /* |
|
| 735 * XEP-0045 mandates the presence to include a resource (which is |
|
| 736 * treated as the chat nick). Some non-compliant servers allow |
|
| 737 * joining without a nick. |
|
| 738 */ |
|
| 739 if (!jid->resource) { |
|
| 740 jabber_id_free(jid); |
|
| 741 g_free(avatar_hash); |
|
| 742 g_free(nickname); |
|
| 743 g_free(status); |
|
| 744 return; |
|
| 745 } |
|
| 746 |
|
| 747 x = xmlnode_get_child_with_namespace(packet, "x", |
|
| 748 "http://jabber.org/protocol/muc#user"); |
|
| 749 if (x) { |
|
| 750 xmlnode *status_node; |
|
| 751 xmlnode *item_node; |
|
| 752 |
|
| 753 for (status_node = xmlnode_get_child(x, "status"); status_node; |
|
| 754 status_node = xmlnode_get_next_twin(status_node)) { |
|
| 755 const char *code = xmlnode_get_attrib(status_node, "code"); |
|
| 756 if (!code) |
|
| 757 continue; |
|
| 758 |
|
| 759 if (g_str_equal(code, "110")) { |
|
| 760 is_our_resource = TRUE; |
|
| 761 } else if (g_str_equal(code, "201")) { |
|
| 762 if ((chat = jabber_chat_find(js, jid->node, jid->domain))) { |
|
| 763 chat->config_dialog_type = PURPLE_REQUEST_ACTION; |
|
| 764 chat->config_dialog_handle = |
|
| 765 purple_request_action(js->gc, |
|
| 766 _("Create New Room"), |
|
| 767 _("Create New Room"), |
|
| 768 _("You are creating a new room. Would" |
|
| 769 " you like to configure it, or" |
|
| 770 " accept the default settings?"), |
|
| 771 /* Default Action */ 1, |
|
| 772 account, NULL, chat->conv, |
|
| 773 chat, 2, |
|
| 774 _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), |
|
| 775 _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); |
|
| 776 } |
|
| 777 } else if (g_str_equal(code, "210")) { |
|
| 778 /* server rewrote room-nick */ |
|
| 779 if((chat = jabber_chat_find(js, jid->node, jid->domain))) { |
|
| 780 g_free(chat->handle); |
|
| 781 chat->handle = g_strdup(jid->resource); |
|
| 782 } |
|
| 783 } |
|
| 784 } |
|
| 785 |
|
| 786 item_node = xmlnode_get_child(x, "item"); |
|
| 787 if (item_node) { |
|
| 788 real_jid = xmlnode_get_attrib(item_node, "jid"); |
|
| 789 affiliation = xmlnode_get_attrib(item_node, "affiliation"); |
|
| 790 role = xmlnode_get_attrib(item_node, "role"); |
|
| 791 |
|
| 792 if (purple_strequal(affiliation, "owner")) |
|
| 793 flags |= PURPLE_CBFLAGS_FOUNDER; |
|
| 794 if (role) { |
|
| 795 if (g_str_equal(role, "moderator")) |
|
| 796 flags |= PURPLE_CBFLAGS_OP; |
|
| 797 else if (g_str_equal(role, "participant")) |
|
| 798 flags |= PURPLE_CBFLAGS_VOICE; |
|
| 799 } |
|
| 800 } |
|
| 801 } |
|
| 802 |
|
| 803 if(!chat->conv) { |
|
| 804 char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain); |
|
| 805 chat->id = i++; |
|
| 806 chat->muc = (x != NULL); |
|
| 807 chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid); |
|
| 808 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle); |
|
| 809 |
|
| 810 jabber_chat_disco_traffic(chat); |
|
| 811 g_free(room_jid); |
|
| 812 } |
|
| 813 |
|
| 814 jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, |
|
| 815 status); |
|
| 816 jbr->commands_fetched = TRUE; |
|
| 817 |
|
| 818 jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role); |
|
| 819 |
|
| 820 if(!jabber_chat_find_buddy(chat->conv, jid->resource)) |
|
| 821 purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, |
|
| 822 real_jid, flags, !delayed); |
|
| 823 else |
|
| 824 purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource, |
|
| 825 flags); |
|
| 826 } else if (g_str_equal(type, "unavailable")) { |
|
| 827 xmlnode *x; |
|
| 828 gboolean nick_change = FALSE; |
|
| 829 gboolean kick = FALSE; |
|
| 830 gboolean is_our_resource = FALSE; /* Is the presence about us? */ |
|
| 831 |
|
| 832 /* If the chat nick is invalid, we haven't yet joined, or we've |
|
| 833 * already left (it was probably us leaving after we closed the |
|
| 834 * chat), we don't care. |
|
| 835 */ |
|
| 836 if (!jid->resource || !chat->conv || chat->left) { |
|
| 837 if (chat->left && |
|
| 838 jid->resource && chat->handle && !strcmp(jid->resource, chat->handle)) |
|
| 839 jabber_chat_destroy(chat); |
|
| 840 jabber_id_free(jid); |
|
| 841 g_free(status); |
|
| 842 g_free(avatar_hash); |
|
| 843 g_free(nickname); |
|
| 844 return; |
|
| 845 } |
|
| 846 |
|
| 847 is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle)); |
|
| 848 |
|
| 849 jabber_buddy_remove_resource(jb, jid->resource); |
|
| 850 |
|
| 851 x = xmlnode_get_child_with_namespace(packet, "x", |
|
| 852 "http://jabber.org/protocol/muc#user"); |
|
| 853 if (chat->muc && x) { |
|
| 854 const char *nick; |
|
| 855 const char *item_jid = NULL; |
|
| 856 const char *to; |
|
| 857 xmlnode *stat; |
|
| 858 xmlnode *item; |
|
| 859 |
|
| 860 item = xmlnode_get_child(x, "item"); |
|
| 861 if (item) |
|
| 862 item_jid = xmlnode_get_attrib(item, "jid"); |
|
| 863 |
|
| 864 for (stat = xmlnode_get_child(x, "status"); stat; |
|
| 865 stat = xmlnode_get_next_twin(stat)) { |
|
| 866 const char *code = xmlnode_get_attrib(stat, "code"); |
|
| 867 |
|
| 868 if (!code) |
|
| 869 continue; |
|
| 870 |
|
| 871 if (g_str_equal(code, "110")) { |
|
| 872 is_our_resource = TRUE; |
|
| 873 } else if(!strcmp(code, "301")) { |
|
| 874 /* XXX: we got banned */ |
|
| 875 } else if(!strcmp(code, "303") && item && |
|
| 876 (nick = xmlnode_get_attrib(item, "nick"))) { |
|
| 877 nick_change = TRUE; |
|
| 878 if(!strcmp(jid->resource, chat->handle)) { |
|
| 879 g_free(chat->handle); |
|
| 880 chat->handle = g_strdup(nick); |
|
| 881 } |
|
| 882 |
|
| 883 /* TODO: This should probably be moved out of the loop */ |
|
| 884 purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick); |
|
| 885 jabber_chat_remove_handle(chat, jid->resource); |
|
| 886 continue; |
|
| 887 } else if(!strcmp(code, "307")) { |
|
| 888 /* Someone was kicked from the room */ |
|
| 889 xmlnode *reason = NULL, *actor = NULL; |
|
| 890 const char *actor_name = NULL; |
|
| 891 char *reason_text = NULL; |
|
| 892 char *tmp; |
|
| 893 |
|
| 894 kick = TRUE; |
|
| 895 |
|
| 896 if (item) { |
|
| 897 reason = xmlnode_get_child(item, "reason"); |
|
| 898 actor = xmlnode_get_child(item, "actor"); |
|
| 899 |
|
| 900 if (reason != NULL) |
|
| 901 reason_text = xmlnode_get_data(reason); |
|
| 902 if (actor != NULL) |
|
| 903 actor_name = xmlnode_get_attrib(actor, "jid"); |
|
| 904 } |
|
| 905 |
|
| 906 if (reason_text == NULL) |
|
| 907 reason_text = g_strdup(_("No reason")); |
|
| 908 |
|
| 909 if (is_our_resource) { |
|
| 910 if (actor_name != NULL) |
|
| 911 tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), |
|
| 912 actor_name, reason_text); |
|
| 913 else |
|
| 914 tmp = g_strdup_printf(_("You have been kicked: (%s)"), |
|
| 915 reason_text); |
|
| 916 } else { |
|
| 917 if (actor_name != NULL) |
|
| 918 tmp = g_strdup_printf(_("Kicked by %s (%s)"), |
|
| 919 actor_name, reason_text); |
|
| 920 else |
|
| 921 tmp = g_strdup_printf(_("Kicked (%s)"), |
|
| 922 reason_text); |
|
| 923 } |
|
| 924 |
|
| 925 g_free(reason_text); |
|
| 926 g_free(status); |
|
| 927 status = tmp; |
|
| 928 } else if(!strcmp(code, "321")) { |
|
| 929 /* XXX: removed due to an affiliation change */ |
|
| 930 } else if(!strcmp(code, "322")) { |
|
| 931 /* XXX: removed because room is now members-only */ |
|
| 932 } else if(!strcmp(code, "332")) { |
|
| 933 /* XXX: removed due to system shutdown */ |
|
| 934 } |
|
| 935 } |
|
| 936 |
|
| 937 /* |
|
| 938 * Possibly another connected resource of our JID (see XEP-0045 |
|
| 939 * v1.24 section 7.1.10) being disconnected. Should be |
|
| 940 * distinguished by the item_jid. |
|
| 941 * Also possibly works around bits of an Openfire bug. See |
|
| 942 * #8319. |
|
| 943 */ |
|
| 944 to = xmlnode_get_attrib(packet, "to"); |
|
| 945 if (is_our_resource && item_jid && !purple_strequal(to, item_jid)) { |
|
| 946 /* TODO: When the above is a loop, this needs to still act |
|
| 947 * sanely for all cases (this code is a little fragile). */ |
|
| 948 if (!kick && !nick_change) |
|
| 949 /* Presumably, kicks and nick changes also affect us. */ |
|
| 950 is_our_resource = FALSE; |
|
| 951 } |
|
| 952 } |
|
| 953 if(!nick_change) { |
|
| 954 if (is_our_resource) { |
|
| 955 if (kick) |
|
| 956 purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource, |
|
| 957 status, PURPLE_MESSAGE_SYSTEM, time(NULL)); |
|
| 958 |
|
| 959 serv_got_chat_left(js->gc, chat->id); |
|
| 960 jabber_chat_destroy(chat); |
|
| 961 } else { |
|
| 962 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, |
|
| 963 status); |
|
| 964 jabber_chat_remove_handle(chat, jid->resource); |
|
| 965 } |
|
| 966 } |
|
| 967 } else { |
|
| 968 /* A type that isn't available or unavailable */ |
|
| 969 purple_debug_error("jabber", "MUC presence with bad type: %s\n", |
|
| 970 type); |
|
| 971 |
|
| 972 jabber_id_free(jid); |
|
| 973 g_free(avatar_hash); |
|
| 974 g_free(status); |
|
| 975 g_free(nickname); |
|
| 976 g_return_if_reached(); |
|
| 977 } |
|
| 978 /* End of DEALING WITH CHATS...about 5000 lines ago */ |
|
| 979 } else { |
|
| 980 /* DEALING WITH CONTACT (i.e. not a chat) */ |
|
| 981 PurpleConversation *conv; |
|
| 982 |
|
| 983 buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", |
|
| 984 jid->node ? "@" : "", jid->domain); |
|
| 985 |
|
| 986 /* |
|
| 987 * Unbind/unlock from sending messages to a specific resource on |
|
| 988 * presence changes. This is locked to a specific resource when |
|
| 989 * receiving a message (in message.c). |
|
| 990 */ |
|
| 991 conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, |
|
| 992 buddy_name, account); |
|
| 993 if (conv) { |
|
| 994 purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", |
|
| 995 purple_conversation_get_name(conv), buddy_name); |
|
| 996 purple_conversation_set_name(conv, buddy_name); |
|
| 997 } |
|
| 998 |
|
| 999 if((b = purple_find_buddy(account, buddy_name)) == NULL) { |
|
| 1000 if (jb != js->user_jb) { |
|
| 1001 purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", |
|
| 1002 buddy_name, purple_account_get_username(account), account); |
|
| 1003 jabber_id_free(jid); |
|
| 1004 g_free(avatar_hash); |
|
| 1005 g_free(buddy_name); |
|
| 1006 g_free(nickname); |
|
| 1007 g_free(status); |
|
| 1008 return; |
|
| 1009 } else { |
|
| 1010 /* this is a different resource of our own account. Resume even when this account isn't on our blist */ |
|
| 1011 } |
|
| 1012 } |
|
| 1013 |
|
| 1014 if(b && avatar_hash) { |
|
| 1015 const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); |
|
| 1016 if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) { |
|
| 1017 JabberIq *iq; |
|
| 1018 xmlnode *vcard; |
|
| 1019 |
|
| 1020 /* XXX this is a crappy way of trying to prevent |
|
| 1021 * someone from spamming us with presence packets |
|
| 1022 * and causing us to DoS ourselves...what we really |
|
| 1023 * need is a queue system that can throttle itself, |
|
| 1024 * but i'm too tired to write that right now */ |
|
| 1025 if(!g_slist_find(js->pending_avatar_requests, jb)) { |
|
| 1026 |
|
| 1027 js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, jb); |
|
| 1028 |
|
| 1029 iq = jabber_iq_new(js, JABBER_IQ_GET); |
|
| 1030 xmlnode_set_attrib(iq->node, "to", buddy_name); |
|
| 1031 vcard = xmlnode_new_child(iq->node, "vCard"); |
|
| 1032 xmlnode_set_namespace(vcard, "vcard-temp"); |
|
| 1033 |
|
| 1034 jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); |
|
| 1035 jabber_iq_send(iq); |
|
| 1036 } |
|
| 1037 } |
|
| 1038 } |
|
| 1039 |
|
| 1040 if(state == JABBER_BUDDY_STATE_ERROR || |
|
| 1041 (type && (g_str_equal(type, "unavailable") || |
|
| 1042 g_str_equal(type, "unsubscribed")))) { |
|
| 1043 jabber_buddy_remove_resource(jb, jid->resource); |
|
| 1044 } else { |
|
| 1045 jbr = jabber_buddy_track_resource(jb, jid->resource, priority, |
|
| 1046 state, status); |
|
| 1047 if (idle) { |
|
| 1048 jbr->idle = time(NULL) - idle; |
|
| 1049 } else { |
|
| 1050 jbr->idle = 0; |
|
| 1051 } |
|
| 1052 } |
|
| 1053 |
|
| 1054 if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { |
|
| 1055 jabber_google_presence_incoming(js, buddy_name, found_jbr); |
|
| 1056 purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); |
|
| 1057 purple_prpl_got_user_idle(account, buddy_name, found_jbr->idle, found_jbr->idle); |
|
| 1058 if (nickname) |
|
| 1059 serv_got_alias(js->gc, buddy_name, nickname); |
|
| 1060 } else { |
|
| 1061 purple_prpl_got_user_status(account, buddy_name, "offline", status ? "message" : NULL, status, NULL); |
|
| 1062 } |
|
| 1063 g_free(buddy_name); |
|
| 1064 } |
|
| 1065 |
|
| 1066 if (caps && !type) { |
|
| 1067 /* handle Entity Capabilities (XEP-0115) */ |
1022 /* handle Entity Capabilities (XEP-0115) */ |
| 1068 const char *node = xmlnode_get_attrib(caps, "node"); |
1023 const char *node = xmlnode_get_attrib(presence.caps, "node"); |
| 1069 const char *ver = xmlnode_get_attrib(caps, "ver"); |
1024 const char *ver = xmlnode_get_attrib(presence.caps, "ver"); |
| 1070 const char *hash = xmlnode_get_attrib(caps, "hash"); |
1025 const char *hash = xmlnode_get_attrib(presence.caps, "hash"); |
| 1071 const char *ext = xmlnode_get_attrib(caps, "ext"); |
1026 const char *ext = xmlnode_get_attrib(presence.caps, "ext"); |
| 1072 |
1027 |
| 1073 /* v1.3 uses: node, ver, and optionally ext. |
1028 /* v1.3 uses: node, ver, and optionally ext. |
| 1074 * v1.5 uses: node, ver, and hash. */ |
1029 * v1.5 uses: node, ver, and hash. */ |
| 1075 if (node && *node && ver && *ver) { |
1030 if (node && *node && ver && *ver) { |
| 1076 gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL; |
1031 gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL; |
| 1077 jbr = jabber_buddy_find_resource(jb, jid->resource); |
1032 jbr = jabber_buddy_find_resource(presence.jb, presence.jid_from->resource); |
| 1078 |
1033 |
| 1079 /* Look it up if we don't already have all this information */ |
1034 /* Look it up if we don't already have all this information */ |
| 1080 if (!jbr || !jbr->caps.info || |
1035 if (!jbr || !jbr->caps.info || |
| 1081 !g_str_equal(node, jbr->caps.info->tuple.node) || |
1036 !g_str_equal(node, jbr->caps.info->tuple.node) || |
| 1082 !g_str_equal(ver, jbr->caps.info->tuple.ver) || |
1037 !g_str_equal(ver, jbr->caps.info->tuple.ver) || |
| 1083 !purple_strequal(hash, jbr->caps.info->tuple.hash) || |
1038 !purple_strequal(hash, jbr->caps.info->tuple.hash) || |
| 1084 !jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) { |
1039 !jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) { |
| 1085 JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); |
1040 JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); |
| 1086 userdata->js = js; |
1041 userdata->js = js; |
| 1087 userdata->jb = jb; |
1042 userdata->jb = presence.jb; |
| 1088 userdata->from = g_strdup(from); |
1043 userdata->from = g_strdup(presence.from); |
| 1089 jabber_caps_get_info(js, from, node, ver, hash, exts, |
1044 jabber_caps_get_info(js, presence.from, node, ver, hash, exts, |
| 1090 (jabber_caps_get_info_cb)jabber_presence_set_capabilities, |
1045 (jabber_caps_get_info_cb)jabber_presence_set_capabilities, |
| 1091 userdata); |
1046 userdata); |
| 1092 } else { |
1047 } else { |
| 1093 if (exts) |
1048 if (exts) |
| 1094 g_strfreev(exts); |
1049 g_strfreev(exts); |
| 1095 } |
1050 } |
| 1096 } |
1051 } |
| 1097 } |
1052 } |
| 1098 |
1053 |
| 1099 g_free(nickname); |
1054 out: |
| 1100 g_free(status); |
1055 g_free(presence.nickname); |
| 1101 jabber_id_free(jid); |
1056 g_free(presence.status); |
| 1102 g_free(avatar_hash); |
1057 jabber_id_free(presence.jid_from); |
| |
1058 g_free(presence.nickname); |
| |
1059 g_free(presence.vcard_avatar_hash); |
| 1103 } |
1060 } |
| 1104 |
1061 |
| 1105 void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type) |
1062 void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type) |
| 1106 { |
1063 { |
| 1107 xmlnode *presence = xmlnode_new("presence"); |
1064 xmlnode *presence = xmlnode_new("presence"); |