# HG changeset patch # User mikael # Date 1113646495 0 # Node ID 9b7f0d313e338a7640638cc286102c840527b017 # Parent 1e9d4949bcfd1f5ccc603c2b087d40065b714c9e [/trunk] Changeset 86 by mikael * New roster/buddylist implementation diff -r 1e9d4949bcfd -r 9b7f0d313e33 mcabber/src/roster.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mcabber/src/roster.c Sat Apr 16 10:14:55 2005 +0000 @@ -0,0 +1,305 @@ +/* + * roster.c -- Local roster implementation + * + * Copyright (C) 2005 Mikael Berthe + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include + +#include "roster.h" + + +/* This is a private structure type for the roster */ + +typedef struct { + char *name; + char *jid; + guint type; + enum imstatus status; + guint flags; + // list: user -> points to his group; group -> points to its users list + GSList *list; +} roster; + + +/* ### Variables ### */ + +static int hide_offline_buddies; +static GSList *groups; +GList *buddylist; + +#ifdef MCABBER_TESTUNIT +// Export groups for testing routines +GSList **pgroups = &groups; +#endif + + +/* ### Roster functions ### */ + +// Comparison function used to search in the roster (compares jids and types) +gint roster_compare_jid_type(roster *a, roster *b) { + if (a->type != b->type) + return -1; // arbitrary (but should be != , of course) + return strcasecmp(a->jid, b->jid); +} + +// Comparison function used to sort the roster (by name) +gint roster_compare_name(roster *a, roster *b) { + return strcasecmp(a->name, b->name); +} + +// Finds a roster element (user, group, agent...), by jid or name +// Returns the roster GSList element, or NULL if jid/name not found +GSList *roster_find(char *jidname, enum findwhat type, guint roster_type) +{ + GSList *sl_roster_elt = groups; + GSList *res; + roster sample; + GCompareFunc comp; + + if (!jidname) + return NULL; // should not happen + + sample.type = roster_type; + if (type == jidsearch) { + sample.jid = jidname; + comp = (GCompareFunc)&roster_compare_jid_type; + } else if (type == namesearch) { + sample.name = jidname; + comp = (GCompareFunc)&roster_compare_name; + } else + return NULL; // should not happen + + while (sl_roster_elt) { + roster *roster_elt = (roster*)sl_roster_elt->data; + if (roster_type & ROSTER_TYPE_GROUP) { + if ((type == namesearch) && !strcasecmp(jidname, roster_elt->name)) + return sl_roster_elt; + } else { + res = g_slist_find_custom(roster_elt->list, &sample, comp); + if (res) + return res; + } + sl_roster_elt = g_slist_next(sl_roster_elt); + } + return NULL; +} + +// Returns pointer to new group, or existing group with that name +GSList *roster_add_group(char *name) +{ + roster *roster_grp; + // #1 Check name doesn't already exist + if (!roster_find(name, namesearch, ROSTER_TYPE_GROUP)) { + // #2 Create the group node + roster_grp = g_new0(roster, 1); + roster_grp->name = g_strdup(name); + roster_grp->type = ROSTER_TYPE_GROUP; + // #3 Insert (sorted) + groups = g_slist_insert_sorted(groups, roster_grp, + (GCompareFunc)&roster_compare_name); + } + return roster_find(name, namesearch, ROSTER_TYPE_GROUP); +} + +// Returns a pointer to the new user, or existing user with that name +GSList *roster_add_user(char *jid, char *name, char *group, guint type) +{ + roster *roster_usr; + roster *my_group; + GSList *slist; + + if ((type != ROSTER_TYPE_USER) && (type != ROSTER_TYPE_AGENT)) { + // XXX Error message? + return NULL; + } + + // #1 Check this user doesn't already exist + if ((slist = roster_find(jid, jidsearch, type)) != NULL) + return slist; + // #2 add group if necessary + slist = roster_add_group(group); + if (!slist) return NULL; + my_group = (roster*)slist->data; + // #3 Create user node + roster_usr = g_new0(roster, 1); + roster_usr->jid = g_strdup(jid); + roster_usr->name = g_strdup(name); + roster_usr->type = type; //ROSTER_TYPE_USER; + roster_usr->list = slist; // (my_group SList element) + // #4 Insert node (sorted) + my_group->list = g_slist_insert_sorted(my_group->list, roster_usr, + (GCompareFunc)&roster_compare_name); + return roster_find(jid, jidsearch, type); +} + +// Removes user (jid) from roster, frees allocated memory +void roster_del_user(char *jid) +{ + GSList *sl_user, *sl_group; + GSList **sl_group_listptr; + roster *roster_usr; + + if ((sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER)) == NULL) + return; + // Let's free memory (jid, name) + roster_usr = (roster*)sl_user->data; + if (roster_usr->jid) + g_free(roster_usr->jid); + if (roster_usr->name) + g_free(roster_usr->name); + + // That's a little complex, we need to dereference twice + sl_group = ((roster*)sl_user->data)->list; + sl_group_listptr = &((roster*)(sl_group->data))->list; + *sl_group_listptr = g_slist_delete_link(*sl_group_listptr, sl_user); +} + +void roster_setstatus(char *jid, enum imstatus bstat) +{ + GSList *sl_user; + roster *roster_usr; + + if ((sl_user = roster_find(jid, jidsearch, ROSTER_TYPE_USER)) == NULL) + return; + + roster_usr = (roster*)sl_user->data; + roster_usr->status = bstat; +} + +// char *roster_getgroup(...) / Or *GSList? Which use?? +// ... setgroup(char*) ?? +// guint roster_gettype(...) / settype +// guchar roster_getflags(...) / setflags +// guchar roster_getname(...) / setname ?? +// roster_del_group? + + +/* ### BuddyList functions ### */ + +// buddylist_hide_offline_buddies(hide) +// "hide" values: 1=hide 0=show_all -1=invert +void buddylist_hide_offline_buddies(int hide) +{ + if (hide < 0) // NEG (invert) + hide_offline_buddies = !hide_offline_buddies; + else if (hide == 0) // FALSE (don't hide) + hide_offline_buddies = 0; + else // TRUE (hide) + hide_offline_buddies = 1; +} + +// buddylist_build() +// Creates the buddylist from the roster entries. +void buddylist_build(void) +{ + GSList *sl_roster_elt = groups; + roster *roster_elt; + int pending_group; + + // Destroy old buddylist + if (buddylist) { + g_list_free(buddylist); + buddylist = NULL; + } + + // Create the new list + while (sl_roster_elt) { + GSList *sl_roster_usrelt; + roster *roster_usrelt; + roster_elt = (roster*) sl_roster_elt->data; + + // Add the group now unless hide_offline_buddies is set, + // in which case we'll add it only if an online buddy belongs to it. + if (!hide_offline_buddies) + buddylist = g_list_append(buddylist, roster_elt); + else + pending_group = TRUE; + + sl_roster_usrelt = roster_elt->list; + while (sl_roster_usrelt) { + roster_usrelt = (roster*) sl_roster_usrelt->data; + + // Buddy will be added if either: + // - hide_offline_buddies is FALSE + // - buddy is not offline + // - buddy has a lock (for example the buddy window is currently open) + // - buddy has a pending (non-read) message + if (!hide_offline_buddies || + (buddy_getstatus((gpointer)roster_usrelt) != offline) || + (buddy_getflags((gpointer)roster_usrelt) & + (ROSTER_FLAG_LOCK | ROSTER_FLAG_MSG))) { + // This user should be added. Maybe the group hasn't been added yet? + if (hide_offline_buddies && pending_group) { + // It hasn't been done yet + buddylist = g_list_append(buddylist, roster_elt); + pending_group = FALSE; + } + // Add user + buddylist = g_list_append(buddylist, roster_usrelt); + } + + sl_roster_usrelt = g_slist_next(sl_roster_usrelt); + } + sl_roster_elt = g_slist_next(sl_roster_elt); + } +} + +// buddy_hide_group(roster, hide) +// "hide" values: 1=hide 0=show_all -1=invert +void buddy_hide_group(gpointer rosterdata, int hide) +{ + roster *roster = rosterdata; + if (hide > 0) // TRUE (hide) + roster->flags |= ROSTER_FLAG_HIDE; + else if (hide < 0) // NEG (invert) + roster->flags ^= ROSTER_FLAG_HIDE; + else // FALSE (don't hide) + roster->flags &= ~ROSTER_FLAG_HIDE; +} + +const char *buddy_getjid(gpointer rosterdata) +{ + roster *roster = rosterdata; + return roster->jid; +} + +const char *buddy_getname(gpointer rosterdata) +{ + roster *roster = rosterdata; + return roster->name; +} + +guint buddy_gettype(gpointer rosterdata) +{ + roster *roster = rosterdata; + return roster->type; +} + +enum imstatus buddy_getstatus(gpointer rosterdata) +{ + roster *roster = rosterdata; + return roster->status; +} + +guint buddy_getflags(gpointer rosterdata) +{ + roster *roster = rosterdata; + return roster->flags; +} + diff -r 1e9d4949bcfd -r 9b7f0d313e33 mcabber/src/roster.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mcabber/src/roster.h Sat Apr 16 10:14:55 2005 +0000 @@ -0,0 +1,45 @@ +#ifndef __ROSTER_H__ +#define __ROSTER_H__ 1 + +#include + +#ifdef MCABBER_TESTUNIT +# include "test_roster_main.h" +#else +# include "jabglue.h" +#endif + +enum findwhat { + jidsearch, + namesearch +}; + +// Roster_type is a set of flags, so values should be 2^n +#define ROSTER_TYPE_USER 1 +#define ROSTER_TYPE_GROUP 2 +#define ROSTER_TYPE_AGENT 4 + +// Flags: +#define ROSTER_FLAG_MSG 1 // Message not read +#define ROSTER_FLAG_HIDE 2 // Group hidden (or buddy window closed) +#define ROSTER_FLAG_LOCK 4 // Node should not be removed from buddylist +// ROSTER_FLAG_LOCAL 8 // Buddy not on server's roster (??) + +extern GList *buddylist; + +// prototypes... +GSList *roster_add_group(char *name); +GSList *roster_add_user(char *jid, char *name, char *group, guint type); +void roster_del_user(char *jid); +void roster_setstatus(char *jid, enum imstatus bstat); + +void buddylist_hide_offline_buddies(int hide); +void buddy_hide_group(gpointer rosterdata, int hide); +void buddylist_build(void); +const char *buddy_getjid(gpointer rosterdata); +const char *buddy_getname(gpointer rosterdata); +guint buddy_gettype(gpointer rosterdata); +enum imstatus buddy_getstatus(gpointer rosterdata); +guint buddy_getflags(gpointer rosterdata); + +#endif /* __ROSTER_H__ */