mcabber/src/roster.c
changeset 438 b44be19d6229
parent 425 03f1e37759a6
child 439 63562fd409a1
equal deleted inserted replaced
437:170f1aa12989 438:b44be19d6229
    23 #include <string.h>
    23 #include <string.h>
    24 
    24 
    25 #include "roster.h"
    25 #include "roster.h"
    26 
    26 
    27 
    27 
       
    28 /* Resource structure */
       
    29 
       
    30 typedef struct {
       
    31   gchar *name;
       
    32   gchar prio;
       
    33   enum imstatus status;
       
    34   gchar *status_msg;
       
    35   enum imrole role;
       
    36   gchar *realjid;       /* for chatrooms, if buddy's real jid is known */
       
    37 } res;
       
    38 
    28 /* This is a private structure type for the roster */
    39 /* This is a private structure type for the roster */
    29 
    40 
    30 typedef struct {
    41 typedef struct {
    31   const gchar *name;
    42   gchar *name;
    32   const gchar *jid;
    43   gchar *jid;
    33   const gchar *status_msg;
       
    34   guint type;
    44   guint type;
    35   enum imstatus status;
    45   enum subscr subscription;
       
    46   GSList *resource;
       
    47   res *cur_res;
    36   guint flags;
    48   guint flags;
    37   // list: user -> points to his group; group -> points to its users list
    49   // list: user -> points to his group; group -> points to its users list
    38   GSList *list;
    50   GSList *list;
    39 } roster;
    51 } roster;
    40 
    52 
    47 GList *buddylist;
    59 GList *buddylist;
    48 GList *current_buddy;
    60 GList *current_buddy;
    49 GList *alternate_buddy;
    61 GList *alternate_buddy;
    50 
    62 
    51 
    63 
       
    64 /* ### Resources functions ### */
       
    65 
       
    66 static void free_all_resources(GSList **reslist)
       
    67 {
       
    68   GSList *lip;
       
    69   res *p_res;
       
    70 
       
    71   for ( lip = *reslist; lip ; lip = g_slist_next(lip)) {
       
    72     p_res = (res*)lip->data;
       
    73     if (p_res->status_msg) {
       
    74       g_free((gchar*)p_res->status_msg);
       
    75     }
       
    76     if (p_res->name) {
       
    77       g_free((gchar*)p_res->name);
       
    78     }
       
    79     if (p_res->realjid) {
       
    80       g_free((gchar*)p_res->realjid);
       
    81     }
       
    82   }
       
    83   // Free all nodes but the first (which is static)
       
    84   g_slist_free(*reslist);
       
    85   *reslist = NULL;
       
    86 }
       
    87 
       
    88 // Resources are sorted in ascending order
       
    89 static gint resource_compare_prio(res *a, res *b) {
       
    90   //return (a->prio - b->prio);
       
    91   if (a->prio < b->prio) return -1;
       
    92   else                   return 1;
       
    93 }
       
    94 
       
    95 //  get_resource(rost, resname)
       
    96 // Return a pointer to the resource with name resname, in rost's resources list
       
    97 // - if rost has no resources, return NULL
       
    98 // - if resname is defined, return the match or NULL
       
    99 // - if resname is NULL, the last resource is returned, currently
       
   100 //   This could change in the future, because we should return the best one
       
   101 //   (priority? last used? and fall back to the first resource)
       
   102 //
       
   103 static res *get_resource(roster *rost, const char *resname)
       
   104 {
       
   105   GSList *p;
       
   106   res *r = NULL;
       
   107 
       
   108   for (p = rost->resource; p; p = g_slist_next(p)) {
       
   109     r = p->data;
       
   110     if (resname && !strcmp(r->name, resname))
       
   111       return r;
       
   112   }
       
   113 
       
   114   // The last resource is one of the resources with the highest priority,
       
   115   // however, we don't know if it is the more-recently-used.
       
   116   if (!resname) return r;
       
   117   return NULL;
       
   118 }
       
   119 
       
   120 //  get_or_add_resource(rost, resname, priority)
       
   121 // - if there is a "resname" resource in rost's resources, return a pointer
       
   122 //   on this resource
       
   123 // - if not, add the resource, set the name, and return a pointer on this
       
   124 //   new resource
       
   125 static res *get_or_add_resource(roster *rost, const char *resname, gchar prio)
       
   126 {
       
   127   GSList *p;
       
   128   res *nres;
       
   129 
       
   130   if (!resname) return NULL;
       
   131 
       
   132   for (p = rost->resource; p; p = g_slist_next(p)) {
       
   133     res *r = p->data;
       
   134     if (!strcmp(r->name, resname))
       
   135       return r;
       
   136   }
       
   137 
       
   138   // Resource not found
       
   139   nres = g_new0(res, 1);
       
   140   nres->name = g_strdup(resname);
       
   141   nres->prio = prio;
       
   142   rost->resource = g_slist_insert_sorted(rost->resource, nres,
       
   143                                          (GCompareFunc)&resource_compare_prio);
       
   144   return nres;
       
   145 }
       
   146 
       
   147 static void del_resource(roster *rost, const char *resname)
       
   148 {
       
   149   GSList *p;
       
   150   GSList *p_res_elt = NULL;
       
   151   res *p_res;
       
   152 
       
   153   if (!resname) return;
       
   154 
       
   155   for (p = rost->resource; p; p = g_slist_next(p)) {
       
   156     res *r = p->data;
       
   157     if (!strcmp(r->name, resname))
       
   158       p_res_elt = p;
       
   159   }
       
   160 
       
   161   if (!p_res_elt) return;   // Resource not found
       
   162 
       
   163   p_res = p_res_elt->data;
       
   164   // Free allocations and delete resource node
       
   165   if (p_res->name)        g_free(p_res->name);
       
   166   if (p_res->status_msg)  g_free(p_res->status_msg);
       
   167   if (p_res->realjid)     g_free(p_res->realjid);
       
   168   rost->resource = g_slist_delete_link(rost->resource, p_res_elt);
       
   169   return;
       
   170 }
       
   171 
       
   172 
    52 /* ### Roster functions ### */
   173 /* ### Roster functions ### */
    53 
   174 
    54 // Comparison function used to search in the roster (compares jids and types)
   175 // Comparison function used to search in the roster (compares jids and types)
    55 static gint roster_compare_jid_type(roster *a, roster *b) {
   176 static gint roster_compare_jid_type(roster *a, roster *b) {
    56   if (! (a->type & b->type))
   177   if (! (a->type & b->type))
    78   if (!roster_type)
   199   if (!roster_type)
    79     roster_type = ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP;
   200     roster_type = ROSTER_TYPE_USER|ROSTER_TYPE_AGENT|ROSTER_TYPE_GROUP;
    80 
   201 
    81   sample.type = roster_type;
   202   sample.type = roster_type;
    82   if (type == jidsearch) {
   203   if (type == jidsearch) {
    83     sample.jid = jidname;
   204     sample.jid = (gchar*)jidname;
    84     comp = (GCompareFunc)&roster_compare_jid_type;
   205     comp = (GCompareFunc)&roster_compare_jid_type;
    85   } else if (type == namesearch) {
   206   } else if (type == namesearch) {
    86     sample.name = jidname;
   207     sample.name = (gchar*)jidname;
    87     comp = (GCompareFunc)&roster_compare_name;
   208     comp = (GCompareFunc)&roster_compare_name;
    88   } else
   209   } else
    89     return NULL;    // should not happen
   210     return NULL;    // should not happen
    90 
   211 
    91   while (sl_roster_elt) {
   212   while (sl_roster_elt) {
   181   if (node) unread_list = g_slist_delete_link(unread_list, node);
   302   if (node) unread_list = g_slist_delete_link(unread_list, node);
   182 
   303 
   183   // Let's free memory (jid, name, status message)
   304   // Let's free memory (jid, name, status message)
   184   if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   305   if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   185   if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   306   if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   186   if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg);
   307   free_all_resources(&roster_usr->resource);
   187   g_free(roster_usr);
   308   g_free(roster_usr);
   188 
   309 
   189   // That's a little complex, we need to dereference twice
   310   // That's a little complex, we need to dereference twice
   190   sl_group = ((roster*)sl_user->data)->list;
   311   sl_group = ((roster*)sl_user->data)->list;
   191   sl_group_listptr = &((roster*)(sl_group->data))->list;
   312   sl_group_listptr = &((roster*)(sl_group->data))->list;
   218     while (sl_usr) {
   339     while (sl_usr) {
   219       roster *roster_usr = (roster*)sl_usr->data;
   340       roster *roster_usr = (roster*)sl_usr->data;
   220       // Free name and jid
   341       // Free name and jid
   221       if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   342       if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   222       if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   343       if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   223       if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg);
   344       free_all_resources(&roster_usr->resource);
   224       g_free(roster_usr);
   345       g_free(roster_usr);
   225       sl_usr = g_slist_next(sl_usr);
   346       sl_usr = g_slist_next(sl_usr);
   226     }
   347     }
   227     // Free group's users list
   348     // Free group's users list
   228     if (roster_grp->list)
   349     if (roster_grp->list)
   241     if (buddylist)
   362     if (buddylist)
   242       buddylist_build();
   363       buddylist_build();
   243   }
   364   }
   244 }
   365 }
   245 
   366 
   246 void roster_setstatus(const char *jid, enum imstatus bstat,
   367 void roster_setstatus(const char *jid, const char *resname, gchar prio,
   247         const char *status_msg)
   368                       enum imstatus bstat, const char *status_msg)
   248 {
   369 {
   249   GSList *sl_user;
   370   GSList *sl_user;
   250   roster *roster_usr;
   371   roster *roster_usr;
       
   372   res *p_res;
   251 
   373 
   252   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   374   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   253   // If we can't find it, we add it
   375   // If we can't find it, we add it
   254   if (sl_user == NULL)
   376   if (sl_user == NULL)
   255     sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER);
   377     sl_user = roster_add_user(jid, NULL, NULL, ROSTER_TYPE_USER);
   256 
   378 
       
   379   // If there is no resource name, we can leave now
       
   380   if (!resname) return;
       
   381 
   257   roster_usr = (roster*)sl_user->data;
   382   roster_usr = (roster*)sl_user->data;
   258   roster_usr->status = bstat;
   383 
   259   if (roster_usr->status_msg) {
   384   // If bstat is offline, we MUST delete the resource, actually
   260     g_free((gchar*)roster_usr->status_msg);
   385   if (bstat == offline) {
   261     roster_usr->status_msg = NULL;
   386     del_resource(roster_usr, resname);
       
   387     return;
       
   388   }
       
   389 
       
   390   // New or updated resource
       
   391   p_res = get_or_add_resource(roster_usr, resname, prio);
       
   392   p_res->status = bstat;
       
   393   if (p_res->status_msg) {
       
   394     g_free((gchar*)p_res->status_msg);
       
   395     p_res->status_msg = NULL;
   262   }
   396   }
   263   if (status_msg)
   397   if (status_msg)
   264     roster_usr->status_msg = g_strdup(status_msg);
   398     p_res->status_msg = g_strdup(status_msg);
   265 }
   399 }
   266 
   400 
   267 //  roster_setflags()
   401 //  roster_setflags()
   268 // Set one or several flags to value (TRUE/FALSE)
   402 // Set one or several flags to value (TRUE/FALSE)
   269 void roster_setflags(const char *jid, guint flags, guint value)
   403 void roster_setflags(const char *jid, guint flags, guint value)
   345 
   479 
   346   roster_usr = (roster*)sl_user->data;
   480   roster_usr = (roster*)sl_user->data;
   347   roster_usr->type = type;
   481   roster_usr->type = type;
   348 }
   482 }
   349 
   483 
   350 enum imstatus roster_getstatus(const char *jid)
   484 enum imstatus roster_getstatus(const char *jid, const char *resname)
   351 {
   485 {
   352   GSList *sl_user;
   486   GSList *sl_user;
   353   roster *roster_usr;
   487   roster *roster_usr;
       
   488   res *p_res;
   354 
   489 
   355   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   490   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   356   if (sl_user == NULL)
   491   if (sl_user == NULL)
   357     return offline; // Not in the roster, anyway...
   492     return offline; // Not in the roster, anyway...
   358 
   493 
   359   roster_usr = (roster*)sl_user->data;
   494   roster_usr = (roster*)sl_user->data;
   360   return roster_usr->status;
   495   p_res = get_resource(roster_usr, resname);
   361 }
   496   if (p_res)
   362 
   497     return p_res->status;
   363 const char *roster_getstatusmsg(const char *jid)
   498   return offline;
       
   499 }
       
   500 
       
   501 const char *roster_getstatusmsg(const char *jid, const char *resname)
   364 {
   502 {
   365   GSList *sl_user;
   503   GSList *sl_user;
   366   roster *roster_usr;
   504   roster *roster_usr;
       
   505   res *p_res;
   367 
   506 
   368   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   507   sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER|ROSTER_TYPE_AGENT);
   369   if (sl_user == NULL)
   508   if (sl_user == NULL)
   370     return NULL; // Not in the roster, anyway...
   509     return NULL; // Not in the roster, anyway...
   371 
   510 
   372   roster_usr = (roster*)sl_user->data;
   511   roster_usr = (roster*)sl_user->data;
   373   return roster_usr->status_msg;
   512   p_res = get_resource(roster_usr, resname);
       
   513   if (p_res)
       
   514     return p_res->status_msg;
       
   515   return NULL;
   374 }
   516 }
   375 
   517 
   376 guint roster_gettype(const char *jid)
   518 guint roster_gettype(const char *jid)
   377 {
   519 {
   378   GSList *sl_user;
   520   GSList *sl_user;
   464       // - buddy has a lock (for example the buddy window is currently open)
   606       // - buddy has a lock (for example the buddy window is currently open)
   465       // - buddy has a pending (non-read) message
   607       // - buddy has a pending (non-read) message
   466       // - group isn't hidden (shrunk)
   608       // - group isn't hidden (shrunk)
   467       // - this is the current_buddy
   609       // - this is the current_buddy
   468       if (!hide_offline_buddies || roster_usrelt == roster_current_buddy ||
   610       if (!hide_offline_buddies || roster_usrelt == roster_current_buddy ||
   469           (buddy_getstatus((gpointer)roster_usrelt) != offline) ||
   611           (buddy_getstatus((gpointer)roster_usrelt, NULL) != offline) ||
   470           (buddy_getflags((gpointer)roster_usrelt) &
   612           (buddy_getflags((gpointer)roster_usrelt) &
   471                (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) {
   613                (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) {
   472         // This user should be added.  Maybe the group hasn't been added yet?
   614         // This user should be added.  Maybe the group hasn't been added yet?
   473         if (pending_group &&
   615         if (pending_group &&
   474             (hide_offline_buddies || roster_usrelt == roster_current_buddy)) {
   616             (hide_offline_buddies || roster_usrelt == roster_current_buddy)) {
   542 
   684 
   543   // Add the buddy to its new group; actually we "clone" this buddy...
   685   // Add the buddy to its new group; actually we "clone" this buddy...
   544   sl_clone = roster_add_user(roster_usr->jid, roster_usr->name,
   686   sl_clone = roster_add_user(roster_usr->jid, roster_usr->name,
   545           newgroupname, roster_usr->type);
   687           newgroupname, roster_usr->type);
   546   roster_clone = (roster*)sl_clone->data;
   688   roster_clone = (roster*)sl_clone->data;
   547   roster_clone->status = roster_usr->status;
   689   roster_clone->flags = roster_usr->flags;
   548   roster_clone->flags  = roster_usr->flags;
   690   roster_clone->resource = roster_usr->resource;
       
   691   roster_usr->resource = NULL;
   549 
   692 
   550   // Free old buddy
   693   // Free old buddy
   551   if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   694   if (roster_usr->jid)        g_free((gchar*)roster_usr->jid);
   552   if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   695   if (roster_usr->name)       g_free((gchar*)roster_usr->name);
   553   if (roster_usr->status_msg) g_free((gchar*)roster_usr->status_msg);
   696   free_all_resources(&roster_usr->resource);
   554   g_free(roster_usr);
   697   g_free(roster_usr);
   555 
   698 
   556   // If new new group is folded, the curren_buddy will be lost, and the
   699   // If new new group is folded, the curren_buddy will be lost, and the
   557   // chat window won't be correctly refreshed.  So we make sure it isn't...
   700   // chat window won't be correctly refreshed.  So we make sure it isn't...
   558   ((roster*)((GSList*)roster_clone->list)->data)->flags &= ~ROSTER_FLAG_HIDE;
   701   ((roster*)((GSList*)roster_clone->list)->data)->flags &= ~ROSTER_FLAG_HIDE;
   627 {
   770 {
   628   roster *roster_usr = rosterdata;
   771   roster *roster_usr = rosterdata;
   629   return roster_usr->type;
   772   return roster_usr->type;
   630 }
   773 }
   631 
   774 
   632 enum imstatus buddy_getstatus(gpointer rosterdata)
   775 enum imstatus buddy_getstatus(gpointer rosterdata, const char *resname)
   633 {
   776 {
   634   roster *roster_usr = rosterdata;
   777   roster *roster_usr = rosterdata;
   635   return roster_usr->status;
   778   res *p_res = get_resource(roster_usr, resname);
   636 }
   779   if (p_res)
   637 
   780     return p_res->status;
   638 const char *buddy_getstatusmsg(gpointer rosterdata)
   781   return offline;
   639 {
   782 }
   640   roster *roster_usr = rosterdata;
   783 
   641   return roster_usr->status_msg;
   784 const char *buddy_getstatusmsg(gpointer rosterdata, const char *resname)
       
   785 {
       
   786   roster *roster_usr = rosterdata;
       
   787   res *p_res = get_resource(roster_usr, resname);
       
   788   if (p_res)
       
   789     return p_res->status_msg;
       
   790   return NULL;
   642 }
   791 }
   643 
   792 
   644 //  buddy_setflags()
   793 //  buddy_setflags()
   645 // Set one or several flags to value (TRUE/FALSE)
   794 // Set one or several flags to value (TRUE/FALSE)
   646 void buddy_setflags(gpointer rosterdata, guint flags, guint value)
   795 void buddy_setflags(gpointer rosterdata, guint flags, guint value)