Mercurial > pidgin
changeset 26705:fef989505ea2
propagate from branch 'im.pidgin.pidgin' (head 7281ad5ba8e68debd285e06dafebd991e980f3da)
to branch 'im.pidgin.cpw.darkrain42.xmpp.avatars' (head 56f140d2f02a2008e8a7a76ecbd2bbb1b1117f93)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sun, 26 Apr 2009 23:06:56 +0000 |
parents | 09e9b7e50df9 (current diff) baa23b7a4d42 (diff) |
children | fbb2b8e2d91e |
files | libpurple/protocols/jabber/buddy.c libpurple/protocols/jabber/buddy.h libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h libpurple/protocols/jabber/presence.c |
diffstat | 11 files changed, 244 insertions(+), 91 deletions(-) [+] |
line wrap: on
line diff
--- a/AUTHORS Sun Apr 26 20:51:21 2009 +0000 +++ b/AUTHORS Sun Apr 26 23:06:56 2009 +0000 @@ -25,6 +25,7 @@ Bartosz Oler - Developer Etan 'deryni' Reisner - Developer Tim 'marv' Ringenbach - Developer +Michael 'Maiku' Ruprecht - Developer, voice and video Elliott 'QuLogic' Sales de Andrade - Developer Luke 'LSchiere' Schierer - Support Megan 'Cae' Schneider - support/QA
--- a/ChangeLog Sun Apr 26 20:51:21 2009 +0000 +++ b/ChangeLog Sun Apr 26 23:06:56 2009 +0000 @@ -28,6 +28,7 @@ type 'headline'. * The Ad-Hoc commands associated with our server are now always shown at login. + * Support showing and reporting idle times (XEP-0256) IRC: * Correctly handle WHOIS for users who are joined to a large number of
--- a/doc/pidgin.1.in Sun Apr 26 20:51:21 2009 +0000 +++ b/doc/pidgin.1.in Sun Apr 26 23:06:56 2009 +0000 @@ -602,7 +602,9 @@ .br Daniel 'datallah' Atallah (developer) .br - John 'rekkanoryo' Bailey (developer) + Paul 'darkrain42' Aurich (developer) +.br + John 'rekkanoryo' Bailey (developer and bugmaster) .br Ethan 'Paco-Paco' Blanton (developer) .br @@ -632,6 +634,8 @@ .br Tim 'marv' Ringenbach (developer) <\fImarv_sf@users.sf.net\fR> .br + Michael 'Maiku' Ruprecht (developer, voice and video) +.br Elliott 'QuLogic' Sales de Andrade (developer) .br Luke 'LSchiere' Schierer (support)
--- a/libpurple/plugins/statenotify.c Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/plugins/statenotify.c Sun Apr 26 23:06:56 2009 +0000 @@ -71,9 +71,9 @@ void *data) { if (purple_prefs_get_bool("/plugins/core/statenotify/notify_idle")) { - if (idle) { + if (idle && !old_idle) { write_status(buddy, _("%s has become idle.")); - } else { + } else if (!idle && old_idle) { write_status(buddy, _("%s is no longer idle.")); } }
--- a/libpurple/protocols/jabber/buddy.c Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.c Sun Apr 26 23:06:56 2009 +0000 @@ -97,36 +97,41 @@ for(l = jb->resources; l; l = l->next) { - if(!jbr && !resource) { - jbr = l->data; - } else if(!resource) { - if(((JabberBuddyResource *)l->data)->priority > jbr->priority) - jbr = l->data; - else if(((JabberBuddyResource *)l->data)->priority == jbr->priority) { + JabberBuddyResource *tmp = (JabberBuddyResource *) l->data; + if (!jbr && !resource) { + jbr = tmp; + } else if (!resource) { + if (tmp->priority > jbr->priority) + jbr = tmp; + else if (tmp->priority == jbr->priority) { /* Determine if this resource is more available than the one we've currently chosen */ - switch(((JabberBuddyResource *)l->data)->state) { + switch(tmp->state) { case JABBER_BUDDY_STATE_ONLINE: case JABBER_BUDDY_STATE_CHAT: /* This resource is online/chatty. Prefer to one which isn't either. */ - if ((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) - jbr = l->data; + if (((jbr->state != JABBER_BUDDY_STATE_ONLINE) && (jbr->state != JABBER_BUDDY_STATE_CHAT)) + || (jbr->idle && !tmp->idle) + || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) + jbr = tmp; break; case JABBER_BUDDY_STATE_AWAY: case JABBER_BUDDY_STATE_DND: /* This resource is away/dnd. Prefer to one which is extended away, unavailable, or unknown. */ - if ((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || + if (((jbr->state == JABBER_BUDDY_STATE_XA) || (jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + || (jbr->idle && !tmp->idle) + || (jbr->idle && tmp->idle && tmp->idle > jbr->idle)) + jbr = tmp; break; case JABBER_BUDDY_STATE_XA: /* This resource is extended away. That's better than unavailable or unknown. */ if ((jbr->state == JABBER_BUDDY_STATE_UNAVAILABLE) || (jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + jbr = tmp; break; case JABBER_BUDDY_STATE_UNAVAILABLE: /* This resource is unavailable. That's better than unknown. */ if ((jbr->state == JABBER_BUDDY_STATE_UNKNOWN) || (jbr->state == JABBER_BUDDY_STATE_ERROR)) - jbr = l->data; + jbr = tmp; break; case JABBER_BUDDY_STATE_UNKNOWN: case JABBER_BUDDY_STATE_ERROR: @@ -134,9 +139,9 @@ break; } } - } else if(((JabberBuddyResource *)l->data)->name) { - if(!strcmp(((JabberBuddyResource *)l->data)->name, resource)) { - jbr = l->data; + } else if(tmp->name) { + if(!strcmp(tmp->name, resource)) { + jbr = tmp; break; } } @@ -1428,12 +1433,49 @@ if(seconds) { char *end = NULL; long sec = strtol(seconds, &end, 10); - if(end != seconds) { + JabberBuddy *jb = NULL; + char *resource = NULL; + char *buddy_name = NULL; + JabberBuddyResource *jbr = NULL; + + if(end != seconds) { JabberBuddyInfoResource *jbir = g_hash_table_lookup(jbi->resources, resource_name); if(jbir) { jbir->idle_seconds = sec; } } + /* Update the idle time of the buddy resource, if we got it. + This will correct the value when a server doesn't mark + delayed presence and we got the presence when signing on */ + jb = jabber_buddy_find(js, from, FALSE); + if (jb) { + resource = jabber_get_resource(from); + buddy_name = jabber_get_bare_jid(from); + /* if the resource already has an idle time set, we + must have gotten it originally from a presence. In + this case we update it. Otherwise don't update it, to + avoid setting an idle and not getting informed about + the resource getting unidle */ + if (resource && buddy_name) { + jbr = jabber_buddy_find_resource(jb, resource); + + if (jbr->idle) { + if (sec) { + jbr->idle = time(NULL) - sec; + } else { + jbr->idle = 0; + } + + if (jbr == + jabber_buddy_find_resource(jb, NULL)) { + purple_prpl_got_user_idle(js->gc->account, + buddy_name, jbr->idle, jbr->idle); + } + } + } + g_free(resource); + g_free(buddy_name); + } } } }
--- a/libpurple/protocols/jabber/buddy.h Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/protocols/jabber/buddy.h Sun Apr 26 23:06:56 2009 +0000 @@ -66,6 +66,7 @@ int priority; JabberBuddyState state; char *status; + time_t idle; JabberCapabilities capabilities; char *thread_id; enum {
--- a/libpurple/protocols/jabber/jabber.c Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Sun Apr 26 23:06:56 2009 +0000 @@ -37,6 +37,7 @@ #include "prpl.h" #include "request.h" #include "server.h" +#include "status.h" #include "util.h" #include "version.h" #include "xmlnode.h" @@ -708,6 +709,7 @@ const char *connect_server = purple_account_get_string(account, "connect_server", ""); JabberStream *js; + PurplePresence *presence; PurpleStoredImage *image; JabberBuddy *my_jb = NULL; @@ -735,6 +737,13 @@ js->stun_port = 0; js->stun_query = NULL; + /* if we are idle, set idle-ness on the stream (this could happen if we get + disconnected and the reconnects while being idle. I don't think it makes + sense to do this when registering a new account... */ + presence = purple_account_get_presence(account); + if (purple_presence_is_idle(presence)) + js->idle = purple_presence_get_idle_time(presence); + if(!js->user) { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, @@ -1536,8 +1545,14 @@ void jabber_idle_set(PurpleConnection *gc, int idle) { JabberStream *js = gc->proto_data; + PurpleAccount *account = purple_connection_get_account(gc); + PurpleStatus *status = purple_account_get_active_status(account); js->idle = idle ? time(NULL) - idle : idle; + + /* send out an updated prescence */ + purple_debug_info("jabber", "sending updated presence for idle\n"); + jabber_presence_send(account, status); } static void jabber_blocklist_parse(JabberStream *js, const char *from, @@ -1735,6 +1750,56 @@ return ret; } +static void +jabber_tooltip_add_resource_text(JabberBuddyResource *jbr, + PurpleNotifyUserInfo *user_info, gboolean multiple_resources) +{ + char *text = NULL; + char *res = NULL; + char *label, *value; + const char *state; + + if(jbr->status) { + char *tmp; + text = purple_strreplace(jbr->status, "\n", "<br />\n"); + tmp = purple_markup_strip_html(text); + g_free(text); + text = g_markup_escape_text(tmp, -1); + g_free(tmp); + } + + if(jbr->name) + res = g_strdup_printf(" (%s)", jbr->name); + + state = jabber_buddy_state_get_name(jbr->state); + if (text != NULL && !purple_utf8_strcasecmp(state, text)) { + g_free(text); + text = NULL; + } + + label = g_strdup_printf("%s%s", _("Status"), (res ? res : "")); + value = g_strdup_printf("%s%s%s", state, (text ? ": " : ""), (text ? text : "")); + + purple_notify_user_info_add_pair(user_info, label, value); + g_free(label); + g_free(value); + g_free(text); + + /* if the resource is idle, show that */ + /* only show it if there is more than one resource available for + the buddy, since the "general" idleness will be shown anyway, + this way we can see see the idleness of lower-priority resources */ + if (jbr->idle && multiple_resources) { + gchar *idle_str = + purple_str_seconds_to_string(time(NULL) - jbr->idle); + label = g_strdup_printf("%s%s", _("Idle"), (res ? res : "")); + purple_notify_user_info_add_pair(user_info, label, idle_str); + g_free(idle_str); + g_free(label); + } + g_free(res); +} + void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { JabberBuddy *jb; @@ -1758,28 +1823,28 @@ const char *sub; GList *l; const char *mood; - + gboolean multiple_resources = + jb->resources && g_list_next(jb->resources); + JabberBuddyResource *top_jbr = jabber_buddy_find_resource(jb, NULL); + + /* resource-specific info for the top resource */ + if (top_jbr) { + jabber_tooltip_add_resource_text(top_jbr, user_info, + multiple_resources); + } + + for(l=jb->resources; l; l = l->next) { + jbr = l->data; + /* the remaining resources */ + if (jbr != top_jbr) { + jabber_tooltip_add_resource_text(jbr, user_info, + multiple_resources); + } + } + if (full) { PurpleStatus *status; - if(jb->subscription & JABBER_SUB_FROM) { - if(jb->subscription & JABBER_SUB_TO) - sub = _("Both"); - else if(jb->subscription & JABBER_SUB_PENDING) - sub = _("From (To pending)"); - else - sub = _("From"); - } else { - if(jb->subscription & JABBER_SUB_TO) - sub = _("To"); - else if(jb->subscription & JABBER_SUB_PENDING) - sub = _("None (To pending)"); - else - sub = _("None"); - } - - purple_notify_user_info_add_pair(user_info, _("Subscription"), sub); - status = purple_presence_get_active_status(presence); mood = purple_status_get_attr_string(status, "mood"); if(mood != NULL) { @@ -1804,47 +1869,25 @@ g_free(playing); } } - } - - for(l=jb->resources; l; l = l->next) { - char *text = NULL; - char *res = NULL; - char *label, *value; - const char *state; - - jbr = l->data; - - if(jbr->status) { - char *tmp; - text = purple_strreplace(jbr->status, "\n", "<br />\n"); - tmp = purple_markup_strip_html(text); - g_free(text); - text = g_markup_escape_text(tmp, -1); - g_free(tmp); + + if(jb->subscription & JABBER_SUB_FROM) { + if(jb->subscription & JABBER_SUB_TO) + sub = _("Both"); + else if(jb->subscription & JABBER_SUB_PENDING) + sub = _("From (To pending)"); + else + sub = _("From"); + } else { + if(jb->subscription & JABBER_SUB_TO) + sub = _("To"); + else if(jb->subscription & JABBER_SUB_PENDING) + sub = _("None (To pending)"); + else + sub = _("None"); } - if(jbr->name) - res = g_strdup_printf(" (%s)", jbr->name); - - state = jabber_buddy_state_get_name(jbr->state); - if (text != NULL && !purple_utf8_strcasecmp(state, text)) { - g_free(text); - text = NULL; - } - - label = g_strdup_printf("%s%s", - _("Status"), (res ? res : "")); - value = g_strdup_printf("%s%s%s", - state, - (text ? ": " : ""), - (text ? text : "")); - - purple_notify_user_info_add_pair(user_info, label, value); - - g_free(label); - g_free(value); - g_free(text); - g_free(res); + purple_notify_user_info_add_pair(user_info, _("Subscription"), sub); + } if(!PURPLE_BUDDY_IS_ONLINE(b) && jb->error_msg) {
--- a/libpurple/protocols/jabber/jabber.h Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Sun Apr 26 23:06:56 2009 +0000 @@ -158,6 +158,7 @@ GList *file_transfers; time_t idle; + time_t old_idle; JabberID *user; PurpleConnection *gc;
--- a/libpurple/protocols/jabber/presence.c Sun Apr 26 20:51:21 2009 +0000 +++ b/libpurple/protocols/jabber/presence.c Sun Apr 26 23:06:56 2009 +0000 @@ -21,6 +21,7 @@ #include "internal.h" #include "account.h" +#include "cipher.h" #include "conversation.h" #include "debug.h" #include "notify.h" @@ -150,7 +151,8 @@ (a && !b) || (a && a[0] != '\0' && b && b[0] == '\0') || (a && b && strcmp(a,b))) /* check if there are any differences to the <presence> and send them in that case */ if (allowBuzz != js->allowBuzz || js->old_state != state || CHANGED(js->old_msg, stripped) || - js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash)) { + js->old_priority != priority || CHANGED(js->old_avatarhash, js->avatar_hash) || + js->old_idle != js->idle) { js->allowBuzz = allowBuzz; presence = jabber_presence_create_js(js, state, stripped, priority); @@ -185,6 +187,7 @@ js->old_avatarhash = g_strdup(js->avatar_hash); js->old_state = state; js->old_priority = priority; + js->old_idle = js->idle; } g_free(stripped); @@ -267,6 +270,16 @@ g_free(pstr); } + /* if we are idle and not offline, include idle */ + if (js->idle && state != JABBER_BUDDY_STATE_UNAVAILABLE) { + xmlnode *query = xmlnode_new_child(presence, "query"); + gchar seconds[10]; + g_snprintf(seconds, 10, "%d", (int) (time(NULL) - js->idle)); + + xmlnode_set_namespace(query, "jabber:iq:last"); + xmlnode_set_attrib(query, "seconds", seconds); + } + /* JEP-0115 */ c = xmlnode_new_child(presence, "c"); xmlnode_set_namespace(c, "http://jabber.org/protocol/caps"); @@ -362,12 +375,19 @@ (( (binval = xmlnode_get_child(photo, "BINVAL")) && (text = xmlnode_get_data(binval))) || (text = xmlnode_get_data(photo)))) { - char *hash; + unsigned char hashval[20]; + char hash[41], *p; + int i; data = purple_base64_decode(text, &size); - hash = jabber_calculate_data_sha1sum(data, size); + + purple_cipher_digest_region("sha1", data, size, + sizeof(hashval), hashval, NULL); + p = hash; + for(i=0; i<20; i++, p+=2) + snprintf(p, 3, "%02x", hashval[i]); + purple_buddy_icons_set_for_user(js->gc->account, from, data, size, hash); - g_free(hash); g_free(text); } } @@ -433,6 +453,7 @@ JabberBuddyResource *jbr = NULL, *found_jbr = NULL; PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; gboolean delayed = FALSE; + const gchar *stamp = NULL; /* from <delayed/> element */ PurpleBuddy *b = NULL; char *buddy_name; JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; @@ -440,7 +461,8 @@ gboolean muc = FALSE; char *avatar_hash = NULL; xmlnode *caps = NULL; - + int idle = 0; + if(!(jb = jabber_buddy_find(js, from, TRUE))) return; @@ -516,19 +538,19 @@ priority = atoi(p); g_free(p); } - } else if(xmlns == NULL) { - /* The rest of the cases used to check xmlns individually. */ - continue; } else if(!strcmp(y->name, "delay") && !strcmp(xmlns, "urn:xmpp:delay")) { /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ delayed = TRUE; - } else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) { + stamp = xmlnode_get_attrib(y, "stamp"); + } else if(xmlns && !strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) { caps = y; /* store for later, when creating buddy resource */ } else if(!strcmp(y->name, "x")) { - if(!strcmp(xmlns, "jabber:x:delay")) { + const char *xmlns = xmlnode_get_namespace(y); + if(xmlns && !strcmp(xmlns, "jabber:x:delay")) { /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ delayed = TRUE; - } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { + stamp = xmlnode_get_attrib(y, "stamp"); + } else if(xmlns && !strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { xmlnode *z; muc = TRUE; @@ -571,16 +593,33 @@ flags |= PURPLE_CBFLAGS_VOICE; } } - } else if(!strcmp(xmlns, "vcard-temp:x:update")) { + } else if(xmlns && !strcmp(xmlns, "vcard-temp:x:update")) { xmlnode *photo = xmlnode_get_child(y, "photo"); if(photo) { g_free(avatar_hash); avatar_hash = xmlnode_get_data(photo); } } + } else if (!strcmp(y->name, "query") && + !strcmp(xmlnode_get_namespace(y), "jabber:iq:last")) { + /* resource has specified idle */ + const gchar *seconds = xmlnode_get_attrib(y, "seconds"); + if (seconds) { + /* we may need to take "delayed" into account here */ + idle = atoi(seconds); + } } } + if (idle && delayed && stamp) { + /* if we have a delayed presence, we need to add the delay to the idle + value */ + time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL, + NULL); + purple_debug_info("jabber", "got delay %s yielding %ld s offset\n", + stamp, offset); + idle += offset; + } if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) { static int i = 1; @@ -755,6 +794,12 @@ } else { jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, status); + if (idle) { + jbr->idle = time(NULL) - idle; + } else { + jbr->idle = 0; + } + if(caps) { const char *node = xmlnode_get_attrib(caps,"node"); const char *ver = xmlnode_get_attrib(caps,"ver"); @@ -773,6 +818,7 @@ if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { jabber_google_presence_incoming(js, buddy_name, found_jbr); purple_prpl_got_user_status(js->gc->account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); + purple_prpl_got_user_idle(js->gc->account, buddy_name, found_jbr->idle, found_jbr->idle); } else { purple_prpl_got_user_status(js->gc->account, buddy_name, "offline", status ? "message" : NULL, status, NULL); }
--- a/pidgin/gtkdialogs.c Sun Apr 26 20:51:21 2009 +0000 +++ b/pidgin/gtkdialogs.c Sun Apr 26 23:06:56 2009 +0000 @@ -91,6 +91,7 @@ {"Bartosz Oler", NULL, NULL}, {"Etan 'deryni' Reisner", NULL, NULL}, {"Tim 'marv' Ringenbach", NULL, NULL}, + {"Michael 'Maiku' Ruprecht", N_("voice and video"), NULL}, {"Elliott 'QuLogic' Sales de Andrade", NULL, NULL}, {"Luke 'LSchiere' Schierer", N_("support"), "lschiere@users.sf.net"}, {"Evan Schoenberg", NULL, NULL},
--- a/pidgin/gtkstatusbox.c Sun Apr 26 20:51:21 2009 +0000 +++ b/pidgin/gtkstatusbox.c Sun Apr 26 23:06:56 2009 +0000 @@ -602,6 +602,18 @@ ); } +static GdkPixbuf * +pidgin_status_box_get_pixbuf(PidginStatusBox *status_box, const char *stock) +{ + GdkPixbuf *pixbuf; + GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); + + pixbuf = gtk_widget_render_icon (GTK_WIDGET(status_box), stock, + icon_size, "PidginStatusBox"); + + return pixbuf; +} + /** * This updates the text displayed on the status box so that it shows * the current status. This is the only function in this file that @@ -707,6 +719,7 @@ } stock = pidgin_stock_id_from_status_primitive(prim); + pixbuf = pidgin_status_box_get_pixbuf(status_box, stock); } if (status_box->account != NULL) { @@ -922,7 +935,6 @@ /* Get an appropriate status icon */ prim = purple_savedstatus_get_type(saved); - if (purple_savedstatus_is_transient(saved)) { /* @@ -2146,6 +2158,7 @@ } stock = pidgin_stock_id_from_status_primitive(prim); + pixbuf = pidgin_status_box_get_pixbuf(status_box, stock); } gtk_list_store_append(status_box->dropdown_store, &iter);