summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Lundblad <malu@pidgin.im>2009-04-07 05:10:52 +0000
committerMarcus Lundblad <malu@pidgin.im>2009-04-07 05:10:52 +0000
commit62926c130d799029258a152b474fe123c9c862dc (patch)
tree5b94e942bd579ee7131d3d47e8bee332661c4803
parentd0fb66b7f858f7fdb6bb22fbfb8ba73cb6866cb1 (diff)
parentba489b21a4cd4f3876b00558f258514523418143 (diff)
downloadpidgin-62926c130d799029258a152b474fe123c9c862dc.tar.gz
propagate from branch 'im.pidgin.pidgin' (head 1a0c8298eba4353493067c53b1fd74c74f962516)
to branch 'im.pidgin.cpw.malu.xmpp.idle' (head 04c4a7a7865fc7ef0adacb9d33f517ad0866d027)
-rw-r--r--libpurple/plugins/statenotify.c4
-rw-r--r--libpurple/protocols/jabber/buddy.c76
-rw-r--r--libpurple/protocols/jabber/buddy.h1
-rw-r--r--libpurple/protocols/jabber/jabber.c160
-rw-r--r--libpurple/protocols/jabber/jabber.h1
-rw-r--r--libpurple/protocols/jabber/presence.c70
6 files changed, 223 insertions, 89 deletions
diff --git a/libpurple/plugins/statenotify.c b/libpurple/plugins/statenotify.c
index 7feeba9558..bfbe595e50 100644
--- a/libpurple/plugins/statenotify.c
+++ b/libpurple/plugins/statenotify.c
@@ -71,9 +71,9 @@ buddy_idle_changed_cb(PurpleBuddy *buddy, gboolean old_idle, gboolean idle,
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."));
}
}
diff --git a/libpurple/protocols/jabber/buddy.c b/libpurple/protocols/jabber/buddy.c
index 3c8d517f94..3e2e1c6f24 100644
--- a/libpurple/protocols/jabber/buddy.c
+++ b/libpurple/protocols/jabber/buddy.c
@@ -98,36 +98,41 @@ JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb,
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:
@@ -135,9 +140,9 @@ JabberBuddyResource *jabber_buddy_find_resource(JabberBuddy *jb,
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;
}
}
@@ -1653,12 +1658,49 @@ static void jabber_last_parse(JabberStream *js, const char *from,
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);
+ }
}
}
}
diff --git a/libpurple/protocols/jabber/buddy.h b/libpurple/protocols/jabber/buddy.h
index 94d48dda06..4a993918f4 100644
--- a/libpurple/protocols/jabber/buddy.h
+++ b/libpurple/protocols/jabber/buddy.h
@@ -69,6 +69,7 @@ typedef struct _JabberBuddyResource {
int priority;
JabberBuddyState state;
char *status;
+ time_t idle;
JabberCapabilities capabilities;
char *thread_id;
enum {
diff --git a/libpurple/protocols/jabber/jabber.c b/libpurple/protocols/jabber/jabber.c
index beee95abfe..40bfed5e81 100644
--- a/libpurple/protocols/jabber/jabber.c
+++ b/libpurple/protocols/jabber/jabber.c
@@ -36,6 +36,7 @@
#include "prpl.h"
#include "request.h"
#include "server.h"
+#include "status.h"
#include "util.h"
#include "version.h"
#include "xmlnode.h"
@@ -700,6 +701,7 @@ jabber_login(PurpleAccount *account)
const char *connect_server = purple_account_get_string(account,
"connect_server", "");
JabberStream *js;
+ PurplePresence *presence;
JabberBuddy *my_jb = NULL;
gc->flags |= PURPLE_CONNECTION_HTML |
@@ -726,6 +728,14 @@ jabber_login(PurpleAccount *account)
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,
@@ -1505,8 +1515,14 @@ char *jabber_get_next_id(JabberStream *js)
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,
@@ -1703,6 +1719,56 @@ char *jabber_status_text(PurpleBuddy *b)
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;
@@ -1726,28 +1792,28 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
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) {
@@ -1772,49 +1838,27 @@ void jabber_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboole
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(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;
+ 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");
}
- 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) {
purple_notify_user_info_add_pair(user_info, _("Error"), jb->error_msg);
}
diff --git a/libpurple/protocols/jabber/jabber.h b/libpurple/protocols/jabber/jabber.h
index 35718e48f4..012a149258 100644
--- a/libpurple/protocols/jabber/jabber.h
+++ b/libpurple/protocols/jabber/jabber.h
@@ -158,6 +158,7 @@ struct _JabberStream
GList *file_transfers;
time_t idle;
+ time_t old_idle;
JabberID *user;
PurpleConnection *gc;
diff --git a/libpurple/protocols/jabber/presence.c b/libpurple/protocols/jabber/presence.c
index 141a0d2100..79f9b8b205 100644
--- a/libpurple/protocols/jabber/presence.c
+++ b/libpurple/protocols/jabber/presence.c
@@ -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 @@ void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
(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);
@@ -177,6 +179,7 @@ void jabber_presence_send(PurpleAccount *account, PurpleStatus *status)
js->old_avatarhash = g_strdup(js->avatar_hash);
js->old_state = state;
js->old_priority = priority;
+ js->old_idle = js->idle;
}
g_free(stripped);
@@ -259,6 +262,16 @@ xmlnode *jabber_presence_create_js(JabberStream *js, JabberBuddyState state, con
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");
@@ -354,12 +367,19 @@ jabber_vcard_parse_avatar(JabberStream *js, const char *from,
(( (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);
}
}
@@ -425,6 +445,7 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
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;
@@ -432,7 +453,8 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
gboolean muc = FALSE;
char *avatar_hash = NULL;
xmlnode *caps = NULL;
-
+ int idle = 0;
+
if(!(jb = jabber_buddy_find(js, from, TRUE)))
return;
@@ -508,19 +530,19 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
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;
@@ -563,16 +585,33 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
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;
@@ -747,6 +786,12 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
} 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");
@@ -765,6 +810,7 @@ void jabber_presence_parse(JabberStream *js, xmlnode *packet)
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);
}