# HG changeset patch # User Hermitifier # Date 1330611041 -3600 # Node ID 8dc418af3e72b2be28e74c0395ead9eb252f00e2 # Parent 2039ea6bd7a55ee0a8aefe2eec003f84a01c6fb5 Allow to select to which buddy resource messages go. Closes issue #55 diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/doc/help/en/hlp_roster.txt --- a/mcabber/doc/help/en/hlp_roster.txt Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/doc/help/en/hlp_roster.txt Thu Mar 01 15:10:41 2012 +0100 @@ -6,6 +6,7 @@ /ROSTER item_lock|item_unlock|item_toggle_lock /ROSTER hide|show|toggle /ROSTER note [-|text] + /ROSTER resource_lock|resource_unlock The 'roster' command manipulates the roster/buddylist. Here are the available parameters: @@ -57,3 +58,7 @@ Set/update/delete an annotation. If there is no text, the current item's annotation is displayed -- if you are in the status buffer, all notes are displayed. If text is "-", the note is erased. +/roster resource_lock [full jid|resource] + Sets active resource for a buddy in roster. Accepts special jid "./resource" for current buddy, or just a resource name. +/roster resource_unlock [jid] + Unsets active resource for a buddy. Accepts special jid "." for current buddy. diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/commands.c --- a/mcabber/mcabber/commands.c Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/commands.c Thu Mar 01 15:10:41 2012 +0100 @@ -280,6 +280,8 @@ compl_add_category_word(COMPL_ROSTER, "unread_first"); compl_add_category_word(COMPL_ROSTER, "unread_next"); compl_add_category_word(COMPL_ROSTER, "note"); + compl_add_category_word(COMPL_ROSTER, "resource_lock"); + compl_add_category_word(COMPL_ROSTER, "resource_unlock"); // Buffer category compl_add_category_word(COMPL_BUFFER, "clear"); @@ -620,6 +622,69 @@ } } +static void roster_resourcelock(char *jidres, gboolean lock) { + gpointer bud = NULL; + char *resource = NULL; + + if (!jidres) { + if (lock) return; + jidres = "."; + } + + if (jidres[0] == '.' && + (jidres[1] == '\0' || jidres[1] == JID_RESOURCE_SEPARATOR)) { + //Special jid: . or ./resource + switch (jidres[1]) { + case JID_RESOURCE_SEPARATOR: + resource = jidres+2; + case '\0': + if (current_buddy) + bud = BUDDATA(current_buddy); + } + } else { + char *tmp; + if (!check_jid_syntax(jidres) && + (tmp = strchr(jidres, JID_RESOURCE_SEPARATOR))) { + //Any other valid full jid + *tmp = '\0'; // for roster search by bare jid; + resource = tmp+1; + GSList *roster_elt; + roster_elt = roster_find(jidres, jidsearch, + ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); + if (roster_elt) + bud = roster_elt->data; + *tmp = JID_RESOURCE_SEPARATOR; + } + if (!bud) { + //Resource for current buddy + if (current_buddy) + bud = BUDDATA(current_buddy); + resource = jidres; + } + } + + if (bud && buddy_gettype(bud) & (ROSTER_TYPE_USER|ROSTER_TYPE_AGENT)) { + if (lock) { + GSList *resources, *p_res; + gboolean found = FALSE; + resources = buddy_getresources(bud); + for (p_res = resources ; p_res ; p_res = g_slist_next(p_res)) { + if (!g_strcmp0((char*)p_res->data, resource)) + found = TRUE; + g_free(p_res->data); + } + g_slist_free(resources); + if (!found) { + scr_LogPrint(LPRINT_NORMAL, "No such resource <%s>...", jidres); + return; + } + } else { + resource = NULL; + } + buddy_setactiveresource(bud, resource); + scr_update_chat_status(TRUE); + } +} // display_and_free_note(note, winId) // Display the note information in the winId buffer, and free note // (winId is a bare jid or NULL for the status window, in which case we @@ -825,6 +890,10 @@ scr_roster_next_group(); } else if (!strcasecmp(subcmd, "note")) { roster_note(arg); + } else if (!strcasecmp(subcmd, "resource_lock")) { + roster_resourcelock(arg, TRUE); + } else if (!strcasecmp(subcmd, "resource_unlock")) { + roster_resourcelock(arg, FALSE); } else scr_LogPrint(LPRINT_NORMAL, "Unrecognized parameter!"); free_arg_lst(paramlst); @@ -1286,6 +1355,8 @@ LmMessageSubType type_overwrite) { const char *bjid; + char *jid; + const char *activeres; if (!current_buddy) { scr_LogPrint(LPRINT_NORMAL, "No buddy is currently selected."); @@ -1298,7 +1369,14 @@ return; } - send_message_to(bjid, msg, subj, type_overwrite, FALSE); + activeres = buddy_getactiveresource(BUDDATA(current_buddy)); + if (activeres) + jid = g_strdup_printf("%s/%s", bjid, activeres); + else + jid = g_strdup(bjid); + + send_message_to(jid, msg, subj, type_overwrite, FALSE); + g_free(jid); } static LmMessageSubType scan_mtype(char **arg) diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/hooks.c --- a/mcabber/mcabber/hooks.c Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/hooks.c Thu Mar 01 15:10:41 2012 +0100 @@ -332,6 +332,9 @@ } } } + } else if (settings_opt_get_int("roster_autolock_resource")) { + buddy_setactiveresource(roster_usr->data, resname); + scr_update_chat_status(FALSE); } if (error_msg_subtype) { diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/roster.c --- a/mcabber/mcabber/roster.c Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/roster.c Thu Mar 01 15:10:41 2012 +0100 @@ -94,6 +94,7 @@ guint type; enum subscr subscription; GSList *resource; + res *active_resource; /* For groupchats */ gchar *nickname; @@ -265,6 +266,9 @@ p_res->status_msg = NULL; } + if (rost->active_resource == p_res) + rost->active_resource = NULL; + // Free allocations and delete resource node free_resource_data(p_res); rost->resource = g_slist_delete_link(rost->resource, p_res_elt); @@ -279,6 +283,7 @@ if (!roster_usr) return; g_free((gchar*)roster_usr->jid); + //g_free((gchar*)roster_usr->active_resource); g_free((gchar*)roster_usr->name); g_free((gchar*)roster_usr->nickname); g_free((gchar*)roster_usr->topic); @@ -1451,6 +1456,32 @@ return reslist; } +// buddy_getactiveresource(roster_data) +// Returns name of active (selected for chat) resource +const char *buddy_getactiveresource(gpointer rosterdata) +{ + roster *roster_usr = rosterdata; + res *resource; + + if (!roster_usr) { + if (!current_buddy) return NULL; + roster_usr = BUDDATA(current_buddy); + } + + resource = roster_usr->active_resource; + if (!resource) return NULL; + return resource->name; +} + +void buddy_setactiveresource(gpointer rosterdata, const char *resname) +{ + roster *roster_usr = rosterdata; + res *p_res = NULL; + if (resname) + p_res = get_resource(roster_usr, resname); + roster_usr->active_resource = p_res; +} + /* // buddy_isresource(roster_data) // Return true if there is at least one resource diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/roster.h --- a/mcabber/mcabber/roster.h Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/roster.h Thu Mar 01 15:10:41 2012 +0100 @@ -238,6 +238,8 @@ //int buddy_isresource(gpointer rosterdata); GSList *buddy_getresources(gpointer rosterdata); GSList *buddy_getresources_locale(gpointer rosterdata); +const char *buddy_getactiveresource(gpointer rosterdata); +void buddy_setactiveresource(gpointer rosterdata, const char *resname); void buddy_resource_setname(gpointer rosterdata, const char *resname, const char *newname); void buddy_resource_setevents(gpointer rosterdata, const char *resname, diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/screen.c --- a/mcabber/mcabber/screen.c Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/screen.c Thu Mar 01 15:10:41 2012 +0100 @@ -1790,6 +1790,8 @@ unsigned short btype, isgrp, ismuc, isspe; const char *btypetext = "Unknown"; const char *fullname; + char *fullnameres = NULL; + const char *activeres; const char *msg = NULL; char status; char *buf, *buf_locale; @@ -1856,6 +1858,8 @@ status = '?'; + activeres = buddy_getactiveresource(BUDDATA(current_buddy)); + if (ismuc) { if (buddy_getinsideroom(BUDDATA(current_buddy))) status = 'C'; @@ -1863,24 +1867,30 @@ status = 'x'; } else if (xmpp_getstatus() != offline) { enum imstatus budstate; - budstate = buddy_getstatus(BUDDATA(current_buddy), NULL); + budstate = buddy_getstatus(BUDDATA(current_buddy), activeres); if (budstate < imstatus_size) status = imstatus2char[budstate]; } // No status message for MUC rooms if (!ismuc) { - GSList *resources, *p_res, *p_next_res; - resources = buddy_getresources(BUDDATA(current_buddy)); - - for (p_res = resources ; p_res ; p_res = p_next_res) { - p_next_res = g_slist_next(p_res); - // Store the status message of the latest resource (highest priority) - if (!p_next_res) - msg = buddy_getstatusmsg(BUDDATA(current_buddy), p_res->data); - g_free(p_res->data); + if (activeres) { + fullnameres = g_strdup_printf("%s/%s", fullname, activeres); + fullname = fullnameres; + msg = buddy_getstatusmsg(BUDDATA(current_buddy), activeres); + } else { + GSList *resources, *p_res, *p_next_res; + resources = buddy_getresources(BUDDATA(current_buddy)); + + for (p_res = resources ; p_res ; p_res = p_next_res) { + p_next_res = g_slist_next(p_res); + // Store the status message of the latest resource (highest priority) + if (!p_next_res) + msg = buddy_getstatusmsg(BUDDATA(current_buddy), p_res->data); + g_free(p_res->data); + } + g_slist_free(resources); } - g_slist_free(resources); } else { msg = buddy_gettopic(BUDDATA(current_buddy)); } @@ -1892,6 +1902,7 @@ replace_nl_with_dots(buf); buf_locale = from_utf8(buf); mvwprintw(chatstatusWnd, 0, 1, "%s", buf_locale); + g_free(fullnameres); g_free(buf_locale); g_free(buf); @@ -1900,9 +1911,9 @@ char eventchar = 0; guint event; - // We do not specify the resource here, so one of the resources with the - // highest priority will be used. - event = buddy_resource_getevents(BUDDATA(current_buddy), NULL); + // We specify active resource here, so when there is none then the resource + // with the highest priority will be used. + event = buddy_resource_getevents(BUDDATA(current_buddy), activeres); if (event == ROSTER_EVENT_ACTIVE) eventchar = 'A'; @@ -2703,7 +2714,12 @@ hbuf_free(&win_entry->bd->hbuf); if (*p_closebuf) { + GSList *roster_elt; retval = TRUE; + roster_elt = roster_find(key, jidsearch, + ROSTER_TYPE_USER|ROSTER_TYPE_AGENT); + if (roster_elt) + buddy_setactiveresource(roster_elt->data, NULL); } else { win_entry->bd->cleared = FALSE; win_entry->bd->top = NULL; @@ -2749,7 +2765,6 @@ roster_msg_setflag(cjid, FALSE, FALSE); g_free(p_closebuf); if (closebuf && !hold_chatmode) { - //buddy_setactiveresource(bud, resource); scr_set_chatmode(FALSE); currentWindow = NULL; } diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabber/xmpp.c --- a/mcabber/mcabber/xmpp.c Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabber/xmpp.c Thu Mar 01 15:10:41 2012 +0100 @@ -639,6 +639,7 @@ void xmpp_send_chatstate(gpointer buddy, guint chatstate) { const char *bjid; + const char *activeres; #ifdef XEP0085 GSList *resources, *p_res, *p_next; struct xep0085 *xep85 = NULL; @@ -649,6 +650,7 @@ bjid = buddy_getjid(buddy); if (!bjid) return; + activeres = buddy_getactiveresource(buddy); #ifdef XEP0085 /* Send the chatstate to the last resource (which should have the highest @@ -662,9 +664,11 @@ xep85 = buddy_resource_xep85(buddy, p_res->data); if (xep85 && xep85->support == CHATSTATES_SUPPORT_OK) { // If p_next is NULL, this is the highest (prio) resource, i.e. - // the one we are probably writing to. - if (!p_next || (xep85->last_state_sent != ROSTER_EVENT_ACTIVE && - chatstate == ROSTER_EVENT_ACTIVE)) + // the one we are probably writing to - unless there is defined an + // active resource + if (!g_strcmp0(p_res->data, activeres) || (!p_next && !activeres) || + (xep85->last_state_sent != ROSTER_EVENT_ACTIVE && + chatstate == ROSTER_EVENT_ACTIVE)) xmpp_send_xep85_chatstate(bjid, p_res->data, chatstate); } g_free(p_res->data); @@ -676,7 +680,7 @@ return; #endif #ifdef XEP0022 - xep22 = buddy_resource_xep22(buddy, NULL); + xep22 = buddy_resource_xep22(buddy, activeres); if (xep22 && xep22->support == CHATSTATES_SUPPORT_OK) { xmpp_send_xep22_event(bjid, chatstate); } diff -r 2039ea6bd7a5 -r 8dc418af3e72 mcabber/mcabberrc.example --- a/mcabber/mcabberrc.example Thu Mar 01 14:46:49 2012 +0100 +++ b/mcabber/mcabberrc.example Thu Mar 01 15:10:41 2012 +0100 @@ -482,6 +482,11 @@ # # Set 'info' to anything you'd like to see in your lower status line. #set info = woot +# +# Automatic locking on buddy resource that messages are coming from. +# Useful when your buddies are chatting from their non-highest priority +# resources, forcing you to use /say_to command. +#set roster_autolock_resource = 1 # Contacts PGP information # You can provide a PGP key to be used for a given Jabber user, or