diff --git a/src/command/command.c b/src/command/command.c index 73c8b07a..648ab64c 100644 --- a/src/command/command.c +++ b/src/command/command.c @@ -1561,7 +1561,7 @@ _cmd_who(gchar **args, struct cmd_help_t help) win_show_room_roster(room, filtered, "unavailable"); - // online, show all status that indicate online + // online, available resources } else if (strcmp("online", presence) == 0) { GList *filtered = NULL; @@ -1575,7 +1575,7 @@ _cmd_who(gchar **args, struct cmd_help_t help) win_show_room_roster(room, filtered, "online"); - // online, show all status that indicate online + // offline, no available resources } else if (strcmp("offline", presence) == 0) { GList *filtered = NULL; @@ -1621,9 +1621,7 @@ _cmd_who(gchar **args, struct cmd_help_t help) while (list != NULL) { PContact contact = list->data; - const char * const contact_presence = (p_contact_presence(contact)); - if ((strcmp(contact_presence, "online") == 0) - || (strcmp(contact_presence, "chat") == 0)) { + if (p_contact_is_available(contact)) { filtered = g_slist_append(filtered, contact); } list = g_slist_next(list); @@ -1638,11 +1636,7 @@ _cmd_who(gchar **args, struct cmd_help_t help) while (list != NULL) { PContact contact = list->data; - const char * const contact_presence = (p_contact_presence(contact)); - if ((strcmp(contact_presence, "offline") == 0) - || (strcmp(contact_presence, "away") == 0) - || (strcmp(contact_presence, "dnd") == 0) - || (strcmp(contact_presence, "xa") == 0)) { + if (!p_contact_is_available(contact)) { filtered = g_slist_append(filtered, contact); } list = g_slist_next(list); @@ -1650,19 +1644,29 @@ _cmd_who(gchar **args, struct cmd_help_t help) cons_show_contacts(filtered); - // online, show all status that indicate online + // online, available resources } else if (strcmp("online", presence) == 0) { cons_show("Contacts (%s):", presence); GSList *filtered = NULL; while (list != NULL) { PContact contact = list->data; - const char * const contact_presence = (p_contact_presence(contact)); - if ((strcmp(contact_presence, "online") == 0) - || (strcmp(contact_presence, "away") == 0) - || (strcmp(contact_presence, "dnd") == 0) - || (strcmp(contact_presence, "xa") == 0) - || (strcmp(contact_presence, "chat") == 0)) { + if (p_contact_has_available_resource(contact)) { + filtered = g_slist_append(filtered, contact); + } + list = g_slist_next(list); + } + + cons_show_contacts(filtered); + + // offline, no available resources + } else if (strcmp("online", presence) == 0) { + cons_show("Contacts (%s):", presence); + GSList *filtered = NULL; + + while (list != NULL) { + PContact contact = list->data; + if (!p_contact_has_available_resource(contact)) { filtered = g_slist_append(filtered, contact); } list = g_slist_next(list); diff --git a/src/contact.c b/src/contact.c index ece79ca9..b63ff408 100644 --- a/src/contact.c +++ b/src/contact.c @@ -34,6 +34,7 @@ struct p_contact_t { char *barejid; char *name; char *subscription; + char *offline_message; gboolean pending_out; GDateTime *last_activity; GHashTable *available_resources; @@ -41,7 +42,8 @@ struct p_contact_t { PContact p_contact_new(const char * const barejid, const char * const name, - const char * const subscription, gboolean pending_out) + const char * const subscription, const char * const offline_message, + gboolean pending_out) { PContact contact = malloc(sizeof(struct p_contact_t)); contact->barejid = strdup(barejid); @@ -57,6 +59,11 @@ p_contact_new(const char * const barejid, const char * const name, else contact->subscription = strdup("none"); + if (offline_message != NULL) + contact->offline_message = strdup(offline_message); + else + contact->offline_message = NULL; + contact->pending_out = pending_out; contact->last_activity = NULL; @@ -101,6 +108,7 @@ p_contact_free(PContact contact) FREE_SET_NULL(contact->barejid); FREE_SET_NULL(contact->name); FREE_SET_NULL(contact->subscription); + FREE_SET_NULL(contact->offline_message); if (contact->last_activity != NULL) { g_date_time_unref(contact->last_activity); @@ -123,30 +131,62 @@ p_contact_name(const PContact contact) return contact->name; } -static resource_presence_t -_highest_presence(resource_presence_t first, resource_presence_t second) +static Resource * +_highest_presence(Resource *first, Resource *second) { - if (first == RESOURCE_CHAT) { + if (first->presence == RESOURCE_CHAT) { return first; - } else if (second == RESOURCE_CHAT) { + } else if (second->presence == RESOURCE_CHAT) { return second; - } else if (first == RESOURCE_ONLINE) { + } else if (first->presence == RESOURCE_ONLINE) { return first; - } else if (second == RESOURCE_ONLINE) { + } else if (second->presence == RESOURCE_ONLINE) { return second; - } else if (first == RESOURCE_AWAY) { + } else if (first->presence == RESOURCE_AWAY) { return first; - } else if (second == RESOURCE_AWAY) { + } else if (second->presence == RESOURCE_AWAY) { return second; - } else if (first == RESOURCE_XA) { + } else if (first->presence == RESOURCE_XA) { return first; - } else if (second == RESOURCE_XA) { + } else if (second->presence == RESOURCE_XA) { return second; } else { return first; } } +Resource * +_get_most_available_resource(PContact contact) +{ + // find resource with highest priority, if more than one, + // use highest availability, in the following order: + // chat + // online + // away + // xa + // dnd + GList *resources = g_hash_table_get_values(contact->available_resources); + Resource *current = resources->data; + Resource *highest = current; + resources = g_list_next(resources); + while (resources != NULL) { + current = resources->data; + + // priority is same as current highest, choose presence + if (current->priority == highest->priority) { + highest = _highest_presence(highest, current); + + // priority higher than current highest, set new presence + } else if (current->priority > highest->priority) { + highest = current; + } + + resources = g_list_next(resources); + } + + return highest; +} + const char * p_contact_presence(const PContact contact) { @@ -157,44 +197,24 @@ p_contact_presence(const PContact contact) return "offline"; } - // find resource with highest priority, if more than one, - // use highest availability, in the following order: - // chat - // online - // away - // xa - // dnd - GList *resources = g_hash_table_get_values(contact->available_resources); - Resource *resource = resources->data; - int highest_priority = resource->priority; - resource_presence_t presence = resource->presence; - resources = g_list_next(resources); - while (resources != NULL) { - resource = resources->data; + Resource *resource = _get_most_available_resource(contact); - // priority is same as current highest, choose presence - if (resource->priority == highest_priority) { - presence = _highest_presence(presence, resource->presence); - - // priority higher than current highest, set new presence - } else if (resource->priority > highest_priority) { - highest_priority = resource->priority; - presence = resource->presence; - } - - resources = g_list_next(resources); - } - - return string_from_resource_presence(presence); + return string_from_resource_presence(resource->presence); } const char * -p_contact_status(const PContact contact, const char * const resource) +p_contact_status(const PContact contact) { assert(contact != NULL); - assert(resource != NULL); - Resource *resourcep = g_hash_table_lookup(contact->available_resources, "default"); - return resourcep->status; + + // no available resources, use offline message + if (g_hash_table_size(contact->available_resources) == 0) { + return contact->offline_message; + } + + Resource *resource = _get_most_available_resource(contact); + + return resource->status; } const char * @@ -215,15 +235,12 @@ p_contact_last_activity(const PContact contact) return contact->last_activity; } -const char * -p_contact_caps_str(const PContact contact) +GList * +p_contact_get_available_resources(const PContact contact) { - if (g_hash_table_size(contact->available_resources) == 0) { - return NULL; - } else { - Resource *resource = g_hash_table_lookup(contact->available_resources, "default"); - return resource->caps_str; - } + assert(contact != NULL); + + return g_hash_table_get_values(contact->available_resources); } gboolean @@ -234,18 +251,14 @@ p_contact_is_available(const PContact contact) return FALSE; } - // if any resource is CHAT or ONLINE, available - GList *resources = g_hash_table_get_values(contact->available_resources); - while (resources != NULL) { - Resource *resource = resources->data; - resource_presence_t presence = resource->presence; - if ((presence == RESOURCE_ONLINE) || (presence == RESOURCE_CHAT)) { - return TRUE; - } - resources = g_list_next(resources); + // if most available resource is CHAT or ONLINE, available + Resource *most_available = _get_most_available_resource(contact); + if ((most_available->presence == RESOURCE_ONLINE) || + (most_available->presence == RESOURCE_CHAT)) { + return TRUE; + } else { + return FALSE; } - - return FALSE; } gboolean @@ -260,16 +273,6 @@ p_contact_set_presence(const PContact contact, Resource *resource) g_hash_table_replace(contact->available_resources, strdup(resource->name), resource); } -void -p_contact_set_status(const PContact contact, const char * const status) -{ - Resource *resource = g_hash_table_lookup(contact->available_resources, "default"); - FREE_SET_NULL(resource->status); - if (status != NULL) { - resource->status = strdup(status); - } -} - void p_contact_set_subscription(const PContact contact, const char * const subscription) { @@ -297,13 +300,3 @@ p_contact_set_last_activity(const PContact contact, GDateTime *last_activity) contact->last_activity = g_date_time_ref(last_activity); } } - -void -p_contact_set_caps_str(const PContact contact, const char * const caps_str) -{ - Resource *resource = g_hash_table_lookup(contact->available_resources, "default"); - FREE_SET_NULL(resource->caps_str); - if (caps_str != NULL) { - resource->caps_str = strdup(caps_str); - } -} diff --git a/src/contact.h b/src/contact.h index 91bc1024..beba5725 100644 --- a/src/contact.h +++ b/src/contact.h @@ -28,7 +28,8 @@ typedef struct p_contact_t *PContact; PContact p_contact_new(const char * const barejid, const char * const name, - const char * const subscription, gboolean pending_out); + const char * const subscription, const char * const offline_message, + gboolean pending_out); PContact p_contact_new_subscription(const char * const barejid, const char * const subscription, gboolean pending_out); void p_contact_add_resource(PContact contact, Resource *resource); @@ -37,17 +38,17 @@ void p_contact_free(PContact contact); const char* p_contact_barejid(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 * const resource); +const char* p_contact_status(PContact contact); const char* p_contact_subscription(const PContact contact); -const char* p_contact_caps_str(const PContact contact); +GList * p_contact_get_available_resources(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, Resource *resource); 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_caps_str(const PContact contact, const char * const caps_str); void p_contact_set_pending_out(const PContact contact, gboolean pending_out); void p_contact_set_last_activity(const PContact contact, GDateTime *last_activity); gboolean p_contact_is_available(const PContact contact); +gboolean p_contact_has_available_resource(const PContact contact); #endif diff --git a/src/contact_list.c b/src/contact_list.c index ad00a4c3..be454cb7 100644 --- a/src/contact_list.c +++ b/src/contact_list.c @@ -65,13 +65,15 @@ contact_list_reset_search_attempts(void) gboolean contact_list_add(const char * const barejid, const char * const name, - const char * const subscription, gboolean pending_out) + const char * const subscription, const char * const offline_message, + gboolean pending_out) { gboolean added = FALSE; PContact contact = g_hash_table_lookup(contacts, barejid); if (contact == NULL) { - contact = p_contact_new(barejid, name, subscription, pending_out); + contact = p_contact_new(barejid, name, subscription, offline_message, + pending_out); g_hash_table_insert(contacts, strdup(barejid), contact); autocomplete_add(ac, strdup(barejid)); added = TRUE; @@ -114,8 +116,11 @@ contact_list_contact_offline(const char * const barejid, if (contact == NULL) { return FALSE; } - - return p_contact_remove_resource(contact, resource); + if (resource == NULL) { + return TRUE; + } else { + return p_contact_remove_resource(contact, resource); + } } void diff --git a/src/contact_list.h b/src/contact_list.h index 581d6f66..eb15a6ab 100644 --- a/src/contact_list.h +++ b/src/contact_list.h @@ -33,7 +33,8 @@ void contact_list_free(void); void contact_list_reset_search_attempts(void); void contact_list_remove(const char * const barejid); gboolean contact_list_add(const char * const barejid, const char * const name, - const char * const subscription, gboolean pending_out); + const char * const subscription, const char * const offline_message, + gboolean pending_out); gboolean contact_list_update_presence(const char * const barejid, Resource *resource, GDateTime *last_activity); void contact_list_update_subscription(const char * const barejid, diff --git a/src/muc.c b/src/muc.c index 778b2ce3..dfa3f194 100644 --- a/src/muc.c +++ b/src/muc.c @@ -217,10 +217,10 @@ muc_add_to_roster(const char * const room, const char * const nick, updated = TRUE; autocomplete_add(chat_room->nick_ac, strdup(nick)); } else if ((g_strcmp0(p_contact_presence(old), show) != 0) || - (g_strcmp0(p_contact_status(old, nick), status) != 0)) { + (g_strcmp0(p_contact_status(old), status) != 0)) { updated = TRUE; } - PContact contact = p_contact_new(nick, NULL, NULL, FALSE); + PContact contact = p_contact_new(nick, NULL, NULL, NULL, FALSE); resource_presence_t resource_presence = resource_presence_from_string(show); Resource *resource = resource_new(nick, resource_presence, status, 0, caps_str); p_contact_set_presence(contact, resource); diff --git a/src/profanity.c b/src/profanity.c index d294a266..0052679b 100644 --- a/src/profanity.c +++ b/src/profanity.c @@ -339,7 +339,7 @@ prof_handle_contact_offline(char *contact, char *resource, char *status) { gboolean updated = contact_list_contact_offline(contact, resource, status); - if (updated) { + if (resource != NULL && updated) { PContact result = contact_list_get_contact(contact); if (p_contact_subscription(result) != NULL) { if (strcmp(p_contact_subscription(result), "none") != 0) { diff --git a/src/resource.c b/src/resource.c index 3ec664ec..0a7838f3 100644 --- a/src/resource.c +++ b/src/resource.c @@ -49,6 +49,36 @@ Resource * resource_new(const char * const name, resource_presence_t presence, return new_resource; } +int +resource_compare_availability(Resource *first, Resource *second) +{ + if (first->priority > second->priority) { + return -1; + } else if (first->priority < second->priority) { + return 1; + } else { // priorities equal + if (first->presence == RESOURCE_CHAT) { + return -1; + } else if (second->presence == RESOURCE_CHAT) { + return 1; + } else if (first->presence == RESOURCE_ONLINE) { + return -1; + } else if (second->presence == RESOURCE_ONLINE) { + return 1; + } else if (first->presence == RESOURCE_AWAY) { + return -1; + } else if (second->presence == RESOURCE_AWAY) { + return 1; + } else if (first->presence == RESOURCE_XA) { + return -1; + } else if (second->presence == RESOURCE_XA) { + return 1; + } else { + return -1; + } + } +} + void resource_destroy(Resource *resource) { assert(resource != NULL); diff --git a/src/resource.h b/src/resource.h index 534153e4..1db5dc0f 100644 --- a/src/resource.h +++ b/src/resource.h @@ -37,4 +37,6 @@ Resource * resource_new(const char * const name, resource_presence_t presence, const char * const status, const int priority, const char * const caps_str); void resource_destroy(Resource *resource); +int resource_compare_availability(Resource *first, Resource *second); + #endif diff --git a/src/ui/windows.c b/src/ui/windows.c index 2b269223..d23257ba 100644 --- a/src/ui/windows.c +++ b/src/ui/windows.c @@ -2405,9 +2405,9 @@ _win_show_info(WINDOW *win, PContact pcontact) const char *barejid = p_contact_barejid(pcontact); const char *name = p_contact_name(pcontact); const char *presence = p_contact_presence(pcontact); - const char *status = p_contact_status(pcontact); const char *sub = p_contact_subscription(pcontact); - const char *caps_str = p_contact_caps_str(pcontact); + GList *resources = p_contact_get_available_resources(pcontact); + GList *ordered_resources = NULL; GDateTime *last_activity = p_contact_last_activity(pcontact); _win_show_time(win, '-'); @@ -2418,22 +2418,12 @@ _win_show_info(WINDOW *win, PContact pcontact) if (name != NULL) { wprintw(win, " (%s)", name); } + _presence_colour_off(win, presence); wprintw(win, ":\n"); - _presence_colour_off(win, presence); - - _win_show_time(win, '-'); - wprintw(win, "Presence : "); - _presence_colour_on(win, presence); - wprintw(win, "%s", presence); - if (status != NULL) { - wprintw(win, ", \"%s\"", status); - } - wprintw(win, "\n"); - _presence_colour_off(win, presence); if (sub != NULL) { _win_show_time(win, '-'); - wprintw(win, "Subscription : %s\n", sub); + wprintw(win, "Subscription: %s\n", sub); } if (last_activity != NULL) { @@ -2441,7 +2431,7 @@ _win_show_info(WINDOW *win, PContact pcontact) GTimeSpan span = g_date_time_difference(now, last_activity); _win_show_time(win, '-'); - wprintw(win, "Last activity : "); + wprintw(win, "Last activity: "); int hours = span / G_TIME_SPAN_HOUR; span = span - hours * G_TIME_SPAN_HOUR; @@ -2461,53 +2451,80 @@ _win_show_info(WINDOW *win, PContact pcontact) g_date_time_unref(now); } - if (caps_str != NULL) { - Capabilities *caps = caps_get(caps_str); - if (caps != NULL) { - // show identity - if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) { - _win_show_time(win, '-'); - wprintw(win, "Identity : "); - if (caps->name != NULL) { - wprintw(win, "%s", caps->name); - if ((caps->category != NULL) || (caps->type != NULL)) { - wprintw(win, " "); - } - } - if (caps->type != NULL) { - wprintw(win, "%s", caps->type); - if (caps->category != NULL) { - wprintw(win, " "); - } - } - if (caps->category != NULL) { - wprintw(win, "%s", caps->category); - } - wprintw(win, "\n"); - } - if (caps->software != NULL) { - _win_show_time(win, '-'); - wprintw(win, "Software : %s", caps->software); - } - if (caps->software_version != NULL) { - wprintw(win, ", %s", caps->software_version); - } - if ((caps->software != NULL) || (caps->software_version != NULL)) { - wprintw(win, "\n"); - } - if (caps->os != NULL) { - _win_show_time(win, '-'); - wprintw(win, "OS : %s", caps->os); - } - if (caps->os_version != NULL) { - wprintw(win, ", %s\n", caps->os_version); - } - if ((caps->os != NULL) || (caps->os_version != NULL)) { - wprintw(win, "\n"); - } + if (resources != NULL) { + _win_show_time(win, '-'); + wprintw(win, "Resources:\n"); + + // sort in order of availabiltiy + while (resources != NULL) { + Resource *resource = resources->data; + ordered_resources = g_list_insert_sorted(ordered_resources, + resource, (GCompareFunc)resource_compare_availability); + resources = g_list_next(resources); } } + while (ordered_resources != NULL) { + Resource *resource = ordered_resources->data; + const char *resource_presence = string_from_resource_presence(resource->presence); + _win_show_time(win, '-'); + _presence_colour_on(win, resource_presence); + wprintw(win, " %s (%d), %s", resource->name, resource->priority, resource_presence); + if (resource->status != NULL) { + wprintw(win, ", \"%s\"", resource->status); + } + wprintw(win, "\n"); + _presence_colour_off(win, resource_presence); + + if (resource->caps_str != NULL) { + Capabilities *caps = caps_get(resource->caps_str); + if (caps != NULL) { + // show identity + if ((caps->category != NULL) || (caps->type != NULL) || (caps->name != NULL)) { + _win_show_time(win, '-'); + wprintw(win, " Identity: "); + if (caps->name != NULL) { + wprintw(win, "%s", caps->name); + if ((caps->category != NULL) || (caps->type != NULL)) { + wprintw(win, " "); + } + } + if (caps->type != NULL) { + wprintw(win, "%s", caps->type); + if (caps->category != NULL) { + wprintw(win, " "); + } + } + if (caps->category != NULL) { + wprintw(win, "%s", caps->category); + } + wprintw(win, "\n"); + } + if (caps->software != NULL) { + _win_show_time(win, '-'); + wprintw(win, " Software: %s", caps->software); + } + if (caps->software_version != NULL) { + wprintw(win, ", %s", caps->software_version); + } + if ((caps->software != NULL) || (caps->software_version != NULL)) { + wprintw(win, "\n"); + } + if (caps->os != NULL) { + _win_show_time(win, '-'); + wprintw(win, " OS: %s", caps->os); + } + if (caps->os_version != NULL) { + wprintw(win, ", %s", caps->os_version); + } + if ((caps->os != NULL) || (caps->os_version != NULL)) { + wprintw(win, "\n"); + } + } + } + + ordered_resources = g_list_next(ordered_resources); + } } void diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c index 525c7cf3..9d0b43e9 100644 --- a/src/xmpp/iq.c +++ b/src/xmpp/iq.c @@ -144,7 +144,7 @@ _iq_handle_roster_result(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, pending_out = TRUE; } - gboolean added = contact_list_add(barejid, name, sub, pending_out); + gboolean added = contact_list_add(barejid, name, sub, NULL, pending_out); if (!added) { log_warning("Attempt to add contact twice: %s", barejid); diff --git a/src/xmpp/presence.c b/src/xmpp/presence.c index 5773e349..4d152a20 100644 --- a/src/xmpp/presence.c +++ b/src/xmpp/presence.c @@ -342,7 +342,7 @@ _unavailable_handler(xmpp_conn_t * const conn, status_str = NULL; if (strcmp(my_jid->barejid, from_jid->barejid) !=0) { - prof_handle_contact_offline(from_jid->barejid, "default", status_str); + prof_handle_contact_offline(from_jid->barejid, from_jid->resourcepart, status_str); } jid_destroy(my_jid); @@ -422,7 +422,7 @@ _available_handler(xmpp_conn_t * const conn, if (strcmp(my_jid->barejid, from_jid->barejid) !=0) { // create the resource resource_presence_t presence = resource_presence_from_string(show_str); - Resource *resource = resource_new("default", presence, + Resource *resource = resource_new(from_jid->resourcepart, presence, status_str, priority, caps_key); prof_handle_contact_online(from_jid->barejid, resource, last_activity); }