diff --git a/src/contact.c b/src/contact.c index 552150cb..f00e44a6 100644 --- a/src/contact.c +++ b/src/contact.c @@ -34,6 +34,7 @@ struct p_contact_t { char *status; char *subscription; gboolean pending_out; + GDateTime *last_activity; }; PContact @@ -67,36 +68,11 @@ p_contact_new(const char * const jid, const char * const name, contact->pending_out = pending_out; + contact->last_activity = NULL; + return contact; } -PContact -p_contact_copy(PContact contact) -{ - PContact copy = malloc(sizeof(struct p_contact_t)); - copy->jid = strdup(contact->jid); - - if (contact->name != NULL) { - copy->name = strdup(contact->name); - } else { - copy->name = NULL; - } - - copy->presence = strdup(contact->presence); - - if (contact->status != NULL) - copy->status = strdup(contact->status); - else - copy->status = NULL; - - if (contact->subscription != NULL) - copy->subscription = strdup(contact->subscription); - else - copy->subscription = NULL; - - return copy; -} - void p_contact_free(PContact contact) { @@ -125,6 +101,10 @@ p_contact_free(PContact contact) contact->subscription = NULL; } + if (contact->last_activity != NULL) { + g_date_time_unref(contact->last_activity); + } + free(contact); contact = NULL; } @@ -165,6 +145,12 @@ p_contact_pending_out(const PContact contact) return contact->pending_out; } +GDateTime * +p_contact_last_activity(const PContact contact) +{ + return contact->last_activity; +} + void p_contact_set_presence(const PContact contact, const char * const presence) { @@ -216,14 +202,15 @@ p_contact_set_pending_out(const PContact contact, gboolean pending_out) contact->pending_out = pending_out; } -int -p_contacts_equal_deep(const PContact c1, const PContact c2) +void +p_contact_set_last_activity(const PContact contact, GDateTime *last_activity) { - int jid_eq = (g_strcmp0(c1->jid, c2->jid) == 0); - int name_eq = (g_strcmp0(c1->name, c2->name) == 0); - int presence_eq = (g_strcmp0(c1->presence, c2->presence) == 0); - int status_eq = (g_strcmp0(c1->status, c2->status) == 0); - int subscription_eq = (g_strcmp0(c1->subscription, c2->subscription) == 0); + if (contact->last_activity != NULL) { + g_date_time_unref(contact->last_activity); + contact->last_activity = NULL; + } - return (jid_eq && name_eq && presence_eq && status_eq && subscription_eq); + if (last_activity != NULL) { + contact->last_activity = g_date_time_ref(last_activity); + } } diff --git a/src/contact.h b/src/contact.h index c86e624e..72400d79 100644 --- a/src/contact.h +++ b/src/contact.h @@ -28,18 +28,18 @@ typedef struct p_contact_t *PContact; PContact p_contact_new(const char * const jid, const char * const name, const char * const presence, const char * const status, const char * const subscription, gboolean pending_out); -PContact p_contact_copy(PContact contact); void p_contact_free(PContact contact); -const char * p_contact_jid(PContact contact); -const char * p_contact_name(PContact contact); -const char * p_contact_presence(PContact contact); -const char * p_contact_status(PContact contact); -const char * p_contact_subscription(const PContact contact); +const char* p_contact_jid(PContact contact); +const char* p_contact_name(PContact contact); +const char* p_contact_presence(PContact contact); +const char* p_contact_status(PContact contact); +const char* p_contact_subscription(const PContact contact); +GDateTime* p_contact_last_activity(const PContact contact); gboolean p_contact_pending_out(const PContact contact); void p_contact_set_presence(const PContact contact, const char * const presence); void p_contact_set_status(const PContact contact, const char * const status); void p_contact_set_subscription(const PContact contact, const char * const subscription); void p_contact_set_pending_out(const PContact contact, gboolean pending_out); -int p_contacts_equal_deep(const PContact c1, const PContact c2); +void p_contact_set_last_activity(const PContact contact, GDateTime *last_activity); #endif diff --git a/src/contact_list.c b/src/contact_list.c index 8caa8784..f277e2f0 100644 --- a/src/contact_list.c +++ b/src/contact_list.c @@ -31,6 +31,7 @@ static PAutocomplete ac; static GHashTable *contacts; static gboolean _key_equals(void *key1, void *key2); +static gboolean _datetimes_equal(GDateTime *dt1, GDateTime *dt2); void contact_list_init(void) @@ -80,7 +81,7 @@ contact_list_remove(const char * const jid) gboolean contact_list_update_contact(const char * const jid, const char * const presence, - const char * const status) + const char * const status, GDateTime *last_activity) { gboolean changed = FALSE; PContact contact = g_hash_table_lookup(contacts, jid); @@ -99,6 +100,11 @@ contact_list_update_contact(const char * const jid, const char * const presence, changed = TRUE; } + if (!_datetimes_equal(p_contact_last_activity(contact), last_activity)) { + p_contact_set_last_activity(contact, last_activity); + changed = TRUE; + } + return changed; } @@ -173,3 +179,18 @@ gboolean _key_equals(void *key1, void *key2) return (g_strcmp0(str1, str2) == 0); } + +static gboolean +_datetimes_equal(GDateTime *dt1, GDateTime *dt2) +{ + if ((dt1 == NULL) && (dt2 == NULL)) { + return TRUE; + } else if ((dt1 == NULL) && (dt2 != NULL)) { + return FALSE; + } else if ((dt1 != NULL) && (dt2 == NULL)) { + return FALSE; + } else { + return g_date_time_equal(dt1, dt2); + } +} + diff --git a/src/contact_list.h b/src/contact_list.h index 863c27b7..8c221b6f 100644 --- a/src/contact_list.h +++ b/src/contact_list.h @@ -35,7 +35,7 @@ gboolean contact_list_add(const char * const jid, const char * const name, const char * const presence, const char * const status, const char * const subscription, gboolean pending_out); gboolean contact_list_update_contact(const char * const jid, const char * const presence, - const char * const status); + const char * const status, GDateTime *last_activity); void contact_list_update_subscription(const char * const jid, const char * const subscription, gboolean pending_out); gboolean contact_list_has_pending_subscriptions(void); diff --git a/src/jabber.c b/src/jabber.c index 433e1de7..93affb80 100644 --- a/src/jabber.c +++ b/src/jabber.c @@ -1043,6 +1043,14 @@ _presence_handler(xmpp_conn_t * const conn, char *short_from = strtok(from, "/"); char *type = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_TYPE); char *show_str, *status_str; + int idle_seconds = stanza_get_idle_time(stanza); + GDateTime *last_activity = NULL; + + if (idle_seconds > 0) { + GDateTime *now = g_date_time_new_now_local(); + last_activity = g_date_time_add_seconds(now, 0 - idle_seconds); + g_date_time_unref(now); + } xmpp_stanza_t *status = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_STATUS); if (status != NULL) @@ -1058,13 +1066,17 @@ _presence_handler(xmpp_conn_t * const conn, show_str = "online"; if (strcmp(short_jid, short_from) !=0) { - prof_handle_contact_online(short_from, show_str, status_str); + prof_handle_contact_online(short_from, show_str, status_str, last_activity); } } else if (strcmp(type, STANZA_TYPE_UNAVAILABLE) == 0) { if (strcmp(short_jid, short_from) !=0) { prof_handle_contact_offline(short_from, "offline", status_str); } + if (last_activity != NULL) { + g_date_time_unref(last_activity); + } + // subscriptions } else if (strcmp(type, STANZA_TYPE_SUBSCRIBE) == 0) { prof_handle_subscription(short_from, PRESENCE_SUBSCRIBE); diff --git a/src/profanity.c b/src/profanity.c index 2fdea3f6..c751b524 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -321,15 +321,16 @@ prof_handle_leave_room(const char * const room) } void -prof_handle_contact_online(char *contact, char *show, char *status) +prof_handle_contact_online(char *contact, char *show, char *status, + GDateTime *last_activity) { - gboolean updated = contact_list_update_contact(contact, show, status); + gboolean updated = contact_list_update_contact(contact, show, status, last_activity); if (updated) { PContact result = contact_list_get_contact(contact); if (p_contact_subscription(result) != NULL) { if (strcmp(p_contact_subscription(result), "none") != 0) { - ui_contact_online(contact, show, status); + ui_contact_online(contact, show, status, last_activity); win_current_page_off(); } } @@ -339,7 +340,7 @@ prof_handle_contact_online(char *contact, char *show, char *status) void prof_handle_contact_offline(char *contact, char *show, char *status) { - gboolean updated = contact_list_update_contact(contact, "offline", status); + gboolean updated = contact_list_update_contact(contact, "offline", status, NULL); if (updated) { PContact result = contact_list_get_contact(contact); diff --git a/src/profanity.h b/src/profanity.h index 284226f5..ce624f59 100644 --- a/src/profanity.h +++ b/src/profanity.h @@ -32,7 +32,8 @@ void prof_handle_lost_connection(void); void prof_handle_disconnect(const char * const jid); void prof_handle_failed_login(void); void prof_handle_typing(char *from); -void prof_handle_contact_online(char *contact, char *show, char *status); +void prof_handle_contact_online(char *contact, char *show, char *status, + GDateTime *last_activity); void prof_handle_contact_offline(char *contact, char *show, char *status); void prof_handle_incoming_message(char *from, char *message, gboolean priv); void prof_handle_delayed_message(char *from, char *message, GTimeVal tv_stamp, diff --git a/src/stanza.c b/src/stanza.c index ba3e0b16..9b253891 100644 --- a/src/stanza.c +++ b/src/stanza.c @@ -20,6 +20,7 @@ * */ +#include #include #include @@ -359,3 +360,34 @@ stanza_get_new_nick(xmpp_stanza_t * const stanza) return NULL; } } + +int +stanza_get_idle_time(xmpp_stanza_t * const stanza) +{ + xmpp_stanza_t *query = xmpp_stanza_get_child_by_name(stanza, STANZA_NAME_QUERY); + + if (query == NULL) { + return 0; + } + + char *ns = xmpp_stanza_get_ns(query); + if (ns == NULL) { + return 0; + } + + if (strcmp(ns, STANZA_NS_LASTACTIVITY) != 0) { + return 0; + } + + char *seconds_str = xmpp_stanza_get_attribute(query, STANZA_ATTR_SECONDS); + if (seconds_str == NULL) { + return 0; + } + + int result = atoi(seconds_str); + if (result < 1) { + return 0; + } else { + return result; + } +} diff --git a/src/stanza.h b/src/stanza.h index 7d380e8c..53fb86f8 100644 --- a/src/stanza.h +++ b/src/stanza.h @@ -117,4 +117,6 @@ gboolean stanza_is_room_nick_change(xmpp_stanza_t * const stanza); char* stanza_get_new_nick(xmpp_stanza_t * const stanza); +int stanza_get_idle_time(xmpp_stanza_t * const stanza); + #endif diff --git a/src/ui.h b/src/ui.h index 8a1f4e23..5e236ca2 100644 --- a/src/ui.h +++ b/src/ui.h @@ -69,7 +69,7 @@ void ui_idle(void); void ui_show_incoming_msg(const char * const from, const char * const message, GTimeVal *tv_stamp, gboolean priv); void ui_contact_online(const char * const from, const char * const show, - const char * const status); + const char * const status, GDateTime *last_activity); void ui_contact_offline(const char * const from, const char * const show, const char * const status); void ui_disconnected(void); diff --git a/src/windows.c b/src/windows.c index 7682cd5c..2f8b22d3 100644 --- a/src/windows.c +++ b/src/windows.c @@ -89,7 +89,8 @@ static void _win_show_user(WINDOW *win, const char * const user, const int colou static void _win_show_message(WINDOW *win, const char * const message); static void _win_show_error_msg(WINDOW *win, const char * const message); static void _show_status_string(WINDOW *win, const char * const from, - const char * const show, const char * const status, const char * const pre, + const char * const show, const char * const status, + GDateTime *last_activity, const char * const pre, const char * const default_show); static void _cons_show_typing(const char * const short_from); static void _cons_show_incoming_message(const char * const short_from, @@ -427,14 +428,16 @@ ui_show_incoming_msg(const char * const from, const char * const message, void ui_contact_online(const char * const from, const char * const show, - const char * const status) + const char * const status, GDateTime *last_activity) { - _show_status_string(console->win, from, show, status, "++", "online"); + _show_status_string(console->win, from, show, status, last_activity, "++", + "online"); int win_index = _find_prof_win_index(from); if (win_index != NUM_WINS) { WINDOW *win = windows[win_index]->win; - _show_status_string(win, from, show, status, "++", "online"); + _show_status_string(win, from, show, status, last_activity, "++", + "online"); } if (win_index == current_index) @@ -445,12 +448,12 @@ void ui_contact_offline(const char * const from, const char * const show, const char * const status) { - _show_status_string(console->win, from, show, status, "--", "offline"); + _show_status_string(console->win, from, show, status, NULL, "--", "offline"); int win_index = _find_prof_win_index(from); if (win_index != NUM_WINS) { WINDOW *win = windows[win_index]->win; - _show_status_string(win, from, show, status, "--", "offline"); + _show_status_string(win, from, show, status, NULL, "--", "offline"); } if (win_index == current_index) @@ -694,7 +697,7 @@ win_new_chat_win(const char * const to) if (strcmp(p_contact_presence(contact), "offline") == 0) { const char const *show = p_contact_presence(contact); const char const *status = p_contact_status(contact); - _show_status_string(win, to, show, status, "--", "offline"); + _show_status_string(win, to, show, status, NULL, "--", "offline"); } } @@ -734,7 +737,7 @@ win_show_outgoing_msg(const char * const from, const char * const to, if (strcmp(p_contact_presence(contact), "offline") == 0) { const char const *show = p_contact_presence(contact); const char const *status = p_contact_status(contact); - _show_status_string(win, to, show, status, "--", "offline"); + _show_status_string(win, to, show, status, NULL, "--", "offline"); } } @@ -876,7 +879,7 @@ win_show_room_member_presence(const char * const room, const char * const nick, int win_index = _find_prof_win_index(room); if (win_index != NUM_WINS) { WINDOW *win = windows[win_index]->win; - _show_status_string(win, nick, show, status, "++", "online"); + _show_status_string(win, nick, show, status, NULL, "++", "online"); } if (win_index == current_index) @@ -1882,7 +1885,8 @@ _win_resize_all(void) static void _show_status_string(WINDOW *win, const char * const from, - const char * const show, const char * const status, const char * const pre, + const char * const show, const char * const status, + GDateTime *last_activity, const char * const pre, const char * const default_show) { _win_show_time(win); @@ -1914,6 +1918,26 @@ _show_status_string(WINDOW *win, const char * const from, else wprintw(win, " is %s", default_show); + if (last_activity != NULL) { + GDateTime *now = g_date_time_new_now_local(); + GTimeSpan span = g_date_time_difference(now, last_activity); + + wprintw(win, ", idle "); + + int hours = span / G_TIME_SPAN_HOUR; + span = span - hours * G_TIME_SPAN_HOUR; + if (hours > 0) { + wprintw(win, "%dh", hours); + } + + int minutes = span / G_TIME_SPAN_MINUTE; + span = span - minutes * G_TIME_SPAN_MINUTE; + wprintw(win, "%dm", minutes); + + int seconds = span / G_TIME_SPAN_SECOND; + wprintw(win, "%ds", seconds); + } + if (status != NULL) wprintw(win, ", \"%s\"", status); @@ -1965,6 +1989,7 @@ _cons_show_contact(PContact contact) const char *name = p_contact_name(contact); const char *presence = p_contact_presence(contact); const char *status = p_contact_status(contact); + GDateTime *last_activity = p_contact_last_activity(contact); _win_show_time(console->win); @@ -1990,6 +2015,26 @@ _cons_show_contact(PContact contact) wprintw(console->win, " is %s", presence); + if (last_activity != NULL) { + GDateTime *now = g_date_time_new_now_local(); + GTimeSpan span = g_date_time_difference(now, last_activity); + + wprintw(console->win, ", idle "); + + int hours = span / G_TIME_SPAN_HOUR; + span = span - hours * G_TIME_SPAN_HOUR; + if (hours > 0) { + wprintw(console->win, "%dh", hours); + } + + int minutes = span / G_TIME_SPAN_MINUTE; + span = span - minutes * G_TIME_SPAN_MINUTE; + wprintw(console->win, "%dm", minutes); + + int seconds = span / G_TIME_SPAN_SECOND; + wprintw(console->win, "%ds", seconds); + } + if (status != NULL) { wprintw(console->win, ", \"%s\"", p_contact_status(contact)); }