mcabber/src/roster.c
changeset 72 9b7f0d313e33
child 78 d001d8fb876d
equal deleted inserted replaced
71:1e9d4949bcfd 72:9b7f0d313e33
       
     1 /*
       
     2  * roster.c     -- Local roster implementation
       
     3  * 
       
     4  * Copyright (C) 2005 Mikael Berthe <bmikael@lists.lilotux.net>
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; either version 2 of the License, or (at
       
     9  * your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful, but
       
    12  * WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       
    19  * USA
       
    20  */
       
    21 
       
    22 #include <string.h>
       
    23 
       
    24 #include "roster.h"
       
    25 
       
    26 
       
    27 /* This is a private structure type for the roster */
       
    28 
       
    29 typedef struct {
       
    30   char *name;
       
    31   char *jid;
       
    32   guint type;
       
    33   enum imstatus status;
       
    34   guint flags;
       
    35   // list: user -> points to his group; group -> points to its users list
       
    36   GSList *list;
       
    37 } roster;
       
    38 
       
    39 
       
    40 /* ### Variables ### */
       
    41 
       
    42 static int hide_offline_buddies;
       
    43 static GSList *groups;
       
    44 GList *buddylist;
       
    45 
       
    46 #ifdef MCABBER_TESTUNIT
       
    47 // Export groups for testing routines
       
    48 GSList **pgroups = &groups;
       
    49 #endif
       
    50 
       
    51 
       
    52 /* ### Roster functions ### */
       
    53 
       
    54 // Comparison function used to search in the roster (compares jids and types)
       
    55 gint roster_compare_jid_type(roster *a, roster *b) {
       
    56   if (a->type != b->type)
       
    57     return -1; // arbitrary (but should be != , of course)
       
    58   return strcasecmp(a->jid, b->jid);
       
    59 }
       
    60 
       
    61 // Comparison function used to sort the roster (by name)
       
    62 gint roster_compare_name(roster *a, roster *b) {
       
    63   return strcasecmp(a->name, b->name);
       
    64 }
       
    65 
       
    66 // Finds a roster element (user, group, agent...), by jid or name
       
    67 // Returns the roster GSList element, or NULL if jid/name not found
       
    68 GSList *roster_find(char *jidname, enum findwhat type, guint roster_type)
       
    69 {
       
    70   GSList *sl_roster_elt = groups;
       
    71   GSList *res;
       
    72   roster sample;
       
    73   GCompareFunc comp;
       
    74 
       
    75   if (!jidname)
       
    76     return NULL;    // should not happen
       
    77 
       
    78   sample.type = roster_type;
       
    79   if (type == jidsearch) {
       
    80     sample.jid = jidname;
       
    81     comp = (GCompareFunc)&roster_compare_jid_type;
       
    82   } else if (type == namesearch) {
       
    83     sample.name = jidname;
       
    84     comp = (GCompareFunc)&roster_compare_name;
       
    85   } else
       
    86     return NULL;    // should not happen
       
    87 
       
    88   while (sl_roster_elt) {
       
    89     roster *roster_elt = (roster*)sl_roster_elt->data;
       
    90     if (roster_type & ROSTER_TYPE_GROUP) {
       
    91       if ((type == namesearch) && !strcasecmp(jidname, roster_elt->name))
       
    92         return sl_roster_elt;
       
    93     } else {
       
    94       res = g_slist_find_custom(roster_elt->list, &sample, comp);
       
    95       if (res)
       
    96         return res;
       
    97     }
       
    98     sl_roster_elt = g_slist_next(sl_roster_elt);
       
    99   }
       
   100   return NULL;
       
   101 }
       
   102 
       
   103 // Returns pointer to new group, or existing group with that name
       
   104 GSList *roster_add_group(char *name)
       
   105 {
       
   106   roster *roster_grp;
       
   107   // #1 Check name doesn't already exist
       
   108   if (!roster_find(name, namesearch, ROSTER_TYPE_GROUP)) {
       
   109     // #2 Create the group node
       
   110     roster_grp = g_new0(roster, 1);
       
   111     roster_grp->name = g_strdup(name);
       
   112     roster_grp->type = ROSTER_TYPE_GROUP;
       
   113     // #3 Insert (sorted)
       
   114     groups = g_slist_insert_sorted(groups, roster_grp,
       
   115             (GCompareFunc)&roster_compare_name);
       
   116   }
       
   117   return roster_find(name, namesearch, ROSTER_TYPE_GROUP);
       
   118 }
       
   119 
       
   120 // Returns a pointer to the new user, or existing user with that name
       
   121 GSList *roster_add_user(char *jid, char *name, char *group, guint type)
       
   122 {
       
   123   roster *roster_usr;
       
   124   roster *my_group;
       
   125   GSList *slist;
       
   126 
       
   127   if ((type != ROSTER_TYPE_USER) && (type != ROSTER_TYPE_AGENT)) {
       
   128     // XXX Error message?
       
   129     return NULL;
       
   130   }
       
   131 
       
   132   // #1 Check this user doesn't already exist
       
   133   if ((slist = roster_find(jid, jidsearch, type)) != NULL)
       
   134     return slist;
       
   135   // #2 add group if necessary
       
   136   slist = roster_add_group(group);
       
   137   if (!slist) return NULL;
       
   138   my_group = (roster*)slist->data;
       
   139   // #3 Create user node
       
   140   roster_usr = g_new0(roster, 1);
       
   141   roster_usr->jid   = g_strdup(jid);
       
   142   roster_usr->name  = g_strdup(name);
       
   143   roster_usr->type  = type; //ROSTER_TYPE_USER;
       
   144   roster_usr->list  = slist;    // (my_group SList element)
       
   145   // #4 Insert node (sorted)
       
   146   my_group->list = g_slist_insert_sorted(my_group->list, roster_usr,
       
   147           (GCompareFunc)&roster_compare_name);
       
   148   return roster_find(jid, jidsearch, type);
       
   149 }
       
   150 
       
   151 // Removes user (jid) from roster, frees allocated memory
       
   152 void roster_del_user(char *jid)
       
   153 {
       
   154   GSList *sl_user, *sl_group;
       
   155   GSList **sl_group_listptr;
       
   156   roster *roster_usr;
       
   157 
       
   158   if ((sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER)) == NULL)
       
   159     return;
       
   160   // Let's free memory (jid, name)
       
   161   roster_usr = (roster*)sl_user->data;
       
   162   if (roster_usr->jid)
       
   163     g_free(roster_usr->jid);
       
   164   if (roster_usr->name)
       
   165     g_free(roster_usr->name);
       
   166 
       
   167   // That's a little complex, we need to dereference twice
       
   168   sl_group = ((roster*)sl_user->data)->list;
       
   169   sl_group_listptr = &((roster*)(sl_group->data))->list;
       
   170   *sl_group_listptr = g_slist_delete_link(*sl_group_listptr, sl_user);
       
   171 }
       
   172 
       
   173 void roster_setstatus(char *jid, enum imstatus bstat)
       
   174 {
       
   175   GSList *sl_user;
       
   176   roster *roster_usr;
       
   177 
       
   178   if ((sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER)) == NULL)
       
   179     return;
       
   180 
       
   181   roster_usr = (roster*)sl_user->data;
       
   182   roster_usr->status = bstat;
       
   183 }
       
   184 
       
   185 // char *roster_getgroup(...)   / Or *GSList?  Which use??
       
   186 // ... setgroup(char*) ??
       
   187 // guint  roster_gettype(...)   / settype
       
   188 // guchar roster_getflags(...)  / setflags
       
   189 // guchar roster_getname(...)   / setname ??
       
   190 // roster_del_group?
       
   191 
       
   192 
       
   193 /* ### BuddyList functions ### */
       
   194 
       
   195 //  buddylist_hide_offline_buddies(hide)
       
   196 // "hide" values: 1=hide 0=show_all -1=invert
       
   197 void buddylist_hide_offline_buddies(int hide)
       
   198 {
       
   199   if (hide < 0)                     // NEG   (invert)
       
   200     hide_offline_buddies = !hide_offline_buddies;
       
   201   else if (hide == 0)               // FALSE (don't hide)
       
   202     hide_offline_buddies = 0;
       
   203   else                              // TRUE  (hide)
       
   204     hide_offline_buddies = 1;
       
   205 }
       
   206 
       
   207 //  buddylist_build()
       
   208 // Creates the buddylist from the roster entries.
       
   209 void buddylist_build(void)
       
   210 {
       
   211   GSList *sl_roster_elt = groups;
       
   212   roster *roster_elt;
       
   213   int pending_group;
       
   214 
       
   215   // Destroy old buddylist
       
   216   if (buddylist) {
       
   217     g_list_free(buddylist);
       
   218     buddylist = NULL;
       
   219   }
       
   220 
       
   221   // Create the new list
       
   222   while (sl_roster_elt) {
       
   223     GSList *sl_roster_usrelt;
       
   224     roster *roster_usrelt;
       
   225     roster_elt = (roster*) sl_roster_elt->data;
       
   226 
       
   227     // Add the group now unless hide_offline_buddies is set,
       
   228     // in which case we'll add it only if an online buddy belongs to it.
       
   229     if (!hide_offline_buddies)
       
   230       buddylist = g_list_append(buddylist, roster_elt);
       
   231     else
       
   232        pending_group = TRUE;
       
   233 
       
   234     sl_roster_usrelt = roster_elt->list;
       
   235     while (sl_roster_usrelt) {
       
   236       roster_usrelt = (roster*) sl_roster_usrelt->data;
       
   237 
       
   238       // Buddy will be added if either:
       
   239       // - hide_offline_buddies is FALSE
       
   240       // - buddy is not offline
       
   241       // - buddy has a lock (for example the buddy window is currently open)
       
   242       // - buddy has a pending (non-read) message
       
   243       if (!hide_offline_buddies ||
       
   244           (buddy_getstatus((gpointer)roster_usrelt) != offline) ||
       
   245           (buddy_getflags((gpointer)roster_usrelt) &
       
   246                (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) {
       
   247         // This user should be added.  Maybe the group hasn't been added yet?
       
   248         if (hide_offline_buddies && pending_group) {
       
   249           // It hasn't been done yet
       
   250           buddylist = g_list_append(buddylist, roster_elt);
       
   251           pending_group = FALSE;
       
   252         }
       
   253         // Add user
       
   254         buddylist = g_list_append(buddylist, roster_usrelt);
       
   255       }
       
   256 
       
   257       sl_roster_usrelt = g_slist_next(sl_roster_usrelt);
       
   258     }
       
   259     sl_roster_elt = g_slist_next(sl_roster_elt);
       
   260   }
       
   261 }
       
   262 
       
   263 //  buddy_hide_group(roster, hide)
       
   264 // "hide" values: 1=hide 0=show_all -1=invert
       
   265 void buddy_hide_group(gpointer rosterdata, int hide)
       
   266 {
       
   267   roster *roster = rosterdata;
       
   268   if (hide > 0)                     // TRUE   (hide)
       
   269     roster->flags |= ROSTER_FLAG_HIDE;
       
   270   else if (hide < 0)                // NEG    (invert)
       
   271     roster->flags ^= ROSTER_FLAG_HIDE;
       
   272   else                              // FALSE  (don't hide)
       
   273     roster->flags &= ~ROSTER_FLAG_HIDE;
       
   274 }
       
   275 
       
   276 const char *buddy_getjid(gpointer rosterdata)
       
   277 {
       
   278   roster *roster = rosterdata;
       
   279   return roster->jid;
       
   280 }
       
   281 
       
   282 const char *buddy_getname(gpointer rosterdata)
       
   283 {
       
   284   roster *roster = rosterdata;
       
   285   return roster->name;
       
   286 }
       
   287 
       
   288 guint buddy_gettype(gpointer rosterdata)
       
   289 {
       
   290   roster *roster = rosterdata;
       
   291   return roster->type;
       
   292 }
       
   293 
       
   294 enum imstatus buddy_getstatus(gpointer rosterdata)
       
   295 {
       
   296   roster *roster = rosterdata;
       
   297   return roster->status;
       
   298 }
       
   299 
       
   300 guint buddy_getflags(gpointer rosterdata)
       
   301 {
       
   302   roster *roster = rosterdata;
       
   303   return roster->flags;
       
   304 }
       
   305