about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--src/contact.c57
-rw-r--r--src/contact.h14
-rw-r--r--src/contact_list.c23
-rw-r--r--src/contact_list.h2
-rw-r--r--src/jabber.c14
-rw-r--r--src/profanity.c9
-rw-r--r--src/profanity.h3
-rw-r--r--src/stanza.c32
-rw-r--r--src/stanza.h2
-rw-r--r--src/ui.h2
-rw-r--r--src/windows.c65
11 files changed, 162 insertions, 61 deletions
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,34 +68,9 @@ p_contact_new(const char * const jid, const char * const name,
 
     contact->pending_out = pending_out;
 
-    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;
+    contact->last_activity = NULL;
 
-    if (contact->subscription != NULL)
-        copy->subscription = strdup(contact->subscription);
-    else
-        copy->subscription = NULL;
-
-    return copy;
+    return contact;
 }
 
 void
@@ -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 <stdlib.h>
 #include <string.h>
 
 #include <glib.h>
@@ -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));
     }