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