diff options
author | Thomas White <taw@bitwiz.org.uk> | 2009-07-17 12:46:27 +0100 |
---|---|---|
committer | Thomas White <taw@bitwiz.org.uk> | 2009-07-17 12:46:27 +0100 |
commit | 9ae0abe3414ea26f83fe3e01a37c3cd4819a82b9 (patch) | |
tree | d9e87a4b4bc035132a7e93b71c97ba90f257faec /src/contactlist.c |
Initial import
Diffstat (limited to 'src/contactlist.c')
-rw-r--r-- | src/contactlist.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/src/contactlist.c b/src/contactlist.c new file mode 100644 index 0000000..03e2a53 --- /dev/null +++ b/src/contactlist.c @@ -0,0 +1,901 @@ +/* + * contactlist.c + * + * Contact list (FL, BL, AL and RL) data structures + * + * (c) 2002-2006 Thomas White <taw27@srcf.ucam.org> + * Part of TuxMessenger - GTK+-based MSN Messenger client + * + * This package 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; version 2 dated June, 1991. + * + * This package 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 package; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#include "debug.h" +#include "contactlist.h" +#include "mainwindow.h" +#include "msngenerics.h" +#include "sbsessions.h" +#include "avatars.h" +#include "xml.h" + +/* The record format for a contact. The same record can be referenced from more than one list. Opaque. */ +typedef struct { + + char *username; /* Username */ + char *friendlyname; /* Friendlyname */ + OnlineState status; /* Online status */ + unsigned int features; /* Client features (e.g. webcam) */ + char *dpobject; /* Display Picture MSNObject */ + ContactSource source; /* The last source the details were updated from */ + int references; /* Number of times this contact is referenced by ContactItems */ + UIContact *uicontact; /* src/mainwindow.c's representation of the contact */ + char *dpsha1d; /* SHA1D field (if any) from user's DP MSNObject (if any). */ + char *ubxdata; /* UBX data for this contact. */ + size_t ubxdatalen; /* Length of UBX data. */ + char *guid; /* Contact's GUID */ + +} Contact; +/* ... but ContactItems actually appear on the lists ... */ + +typedef struct contactitem { + + Contact *contact; /* The contact's record */ + struct contactitem *next; /* Link to the next contact on this list */ + +} ContactItem; + +static ContactItem *contactlist_forward = NULL; +static ContactItem *contactlist_block = NULL; +static ContactItem *contactlist_allow = NULL; +static ContactItem *contactlist_reverse = NULL; +static ContactItem *contactlist_pending = NULL; +static ContactItem *contactlist_temporary = NULL; + +static ContactItem *contactlist_lastcontactitem(ContactItem *contact_list) { + + ContactItem *contactitem = contact_list; + + if ( contactitem == NULL ) { + return NULL; + } + + while ( contactitem ) { + + assert(contactitem != NULL); + assert(contactitem->contact != NULL); + assert(contactitem->contact->username != NULL); + + if ( contactitem->next == NULL ) { + return contactitem; + } else { + contactitem = contactitem->next; + } + + } + + /* Should never get here */ + debug_print("CL: Reached end of contactlist_lastcontactitem(). D'oh.\n"); + return contactitem; + +} + + +static ContactItem **contactlist_translatelist(const char *list) { + + if ( strcmp(list, "AL") == 0 ) { + return &contactlist_allow; + } else if ( strcmp(list, "FL") == 0 ) { + return &contactlist_forward; + } else if ( strcmp(list, "BL") == 0 ) { + return &contactlist_block; + } else if ( strcmp(list, "RL") == 0 ) { + return &contactlist_reverse; + } else if ( strcmp(list, "PL") == 0 ) { + return &contactlist_pending; + } else { + return NULL; + } + +} + +/* Link a contact into a given contact list, creating the ContactItem structure */ +static void contactlist_link(ContactItem **contact_list, Contact *contact) { + + ContactItem *last_contactitem; + ContactItem *new_contactitem; + + new_contactitem = malloc(sizeof(ContactItem)); + new_contactitem->contact = contact; + new_contactitem->next = NULL; + + last_contactitem = contactlist_lastcontactitem(*contact_list); + if ( last_contactitem != NULL ) { + assert(last_contactitem->next == NULL); + last_contactitem->next = new_contactitem; + } else { + *contact_list = new_contactitem;; + } + + contact->references++; + +} + +static ContactItem *contactlist_previous(ContactItem **contact_list, ContactItem *s_contactitem) { + + ContactItem *contactitem; + + contactitem = *contact_list; + + while ( contactitem ) { + + assert(contactitem != NULL); + assert(contactitem->contact != NULL); + assert(contactitem->contact->username != NULL); + + if ( contactitem->next == s_contactitem ) { + return contactitem; + } else { + contactitem = contactitem->next; + } + + } + + /* Didn't find it - means it was the first item on the list. */ + return NULL; + +} + +/* Unlink a contact from a given contact list, destroying it if there's no more references */ +static void contactlist_unlink(ContactItem **contact_list, ContactItem *contactitem) { + + Contact *contact; + ContactItem *previous_contact; + ContactItem *next_contact; + + contact = contactitem->contact; + +/* debug_print("CL: Unlinking %s\n", contact->username);*/ + + /* First link it out of the list. */ + previous_contact = contactlist_previous(contact_list, contactitem); + next_contact = contactitem->next; + if ( previous_contact != NULL ) { + previous_contact->next = next_contact; + } else { + *contact_list = next_contact; + } + contact->references--; +/* debug_print("CL: Reference count now %i\n", contact->references);*/ + + if ( (contact_list == &contactlist_forward) && (contact->uicontact != NULL) ) { + /* This has probably already happened. */ + debug_print("CL: Destroying UIContact\n"); + mainwindow_removecontact(contact->uicontact); + mainwindow_destroycontact(contact->uicontact); + } + + if ( contact->references == 0 ) { + + debug_print("CL: Destroying contact record for %s\n", contact->username); + + /* Destroy the contact record */ + free(contact->username); + if ( contact->friendlyname != NULL ) { + free(contact->friendlyname); + } + if ( contact->dpobject != NULL ) { + free(contact->dpobject); + } + if ( contact->dpsha1d != NULL ) { + free(contact->dpsha1d); + } + if ( contact->ubxdata != NULL ) { + free(contact->ubxdata); + } + if ( contact->guid != NULL ) { + free(contact->guid); + } + free(contact); + + } + + free(contactitem); + +} + +/* Locate a contact's record in a given list by username */ +static ContactItem *contactlist_findcontact(ContactItem *contact_list, const char *username) { + + ContactItem *contactitem = contact_list; + + if ( contactitem == NULL ) { + return NULL; + } + + while ( contactitem ) { + + assert(contactitem != NULL); + assert(contactitem->contact != NULL); + assert(contactitem->contact->username != NULL); + + /* Case-insensitive here, like in sbsessions_find_username(). Username + may have different case depending on its source, since the servers + seem to change usernames to lower case but clients might not in + (e.g.) TypingUser controls. */ + if ( strcasecmp(contactitem->contact->username, username) == 0 ) { + return contactitem; + } else { + contactitem = contactitem->next; + } + + } + + return NULL; + +} + +/* Locate a contact's record in a given list by GUID */ +static ContactItem *contactlist_findcontactguid(ContactItem *contact_list, const char *guid) { + + ContactItem *contactitem = contact_list; + + if ( contactitem == NULL ) { + return NULL; + } + + while ( contactitem ) { + + assert(contactitem != NULL); + assert(contactitem->contact != NULL); + assert(contactitem->contact->guid != NULL); + + /* Case-insensitive */ + if ( strcasecmp(contactitem->contact->guid, guid) == 0 ) { + return contactitem; + } else { + contactitem = contactitem->next; + } + + } + + return NULL; + +} + +void contactlist_removecontact(const char *list, const char *username) { + + ContactItem **contact_list = contactlist_translatelist(list); + ContactItem *contactitem; + + debug_print("CL: Removing %s from %s\n", username, list); + + contactitem = contactlist_findcontact(*contact_list, username); + assert(contactitem != NULL); + contactlist_unlink(contact_list, contactitem); + +} + +void contactlist_removecontactguid(const char *list, const char *guid) { + + ContactItem **contact_list = contactlist_translatelist(list); + ContactItem *contactitem; + + debug_print("CL: Removing %s from %s\n", guid, list); + + contactitem = contactlist_findcontactguid(*contact_list, guid); + assert(contactitem != NULL); + contactlist_unlink(contact_list, contactitem); + +} + +static Contact *contactlist_findcontactrecord(ContactItem *contact_list, const char *username) { + + ContactItem *contactitem; + + contactitem = contactlist_findcontact(contact_list, username); + if ( contactitem == NULL ) { + return NULL; + } + + return contactitem->contact; + +} + +/* Create a new contact record */ +static Contact *contactlist_createcontact(ContactSource source, const char *username, const char *friendlyname, OnlineState status, int features, const char *dpobject, const char *guid) { + + Contact *newcontact; + + newcontact = malloc(sizeof(Contact)); + assert(newcontact != NULL); + + newcontact->username = strdup(username); + if ( friendlyname != NULL ) { + newcontact->friendlyname = strdup(friendlyname); + } else { + newcontact->friendlyname = NULL; + } + if ( dpobject != NULL ) { + newcontact->dpobject = strdup(dpobject); + } else { + newcontact->dpobject = NULL; /* Don't try to strdup(NULL) */ + } + if ( guid != NULL ) { + newcontact->guid = strdup(guid); + } else { + newcontact->guid = NULL; + } + newcontact->source = source; + newcontact->uicontact = NULL; /* This is filled in if/when the contact is linked into the FL */ + newcontact->features = features; + newcontact->status = status; + newcontact->references = 0; + newcontact->dpsha1d = NULL; + newcontact->ubxdata = NULL; + + /* Caller probably means to call contactlist_link pretty soon... */ + return newcontact; + +} + +/* Add a user to the a given list or update details if already there */ +static void contactlist_details(ContactItem **contact_list, ContactSource source, const char *username, const char *friendlyname, int features, const char *dpobject, OnlineState status, const char *guid) { + + ContactItem *contact_item; + Contact *contact; + char *list_string; + + assert(username != NULL); + assert(contact_list != NULL); /* But *contact_list is allowed to be NULL (empty list) */ + + if ( contact_list == &contactlist_allow ) { + list_string = "AL"; + } else if ( contact_list == &contactlist_forward ) { + list_string = "FL"; + } else if ( contact_list == &contactlist_block ) { + list_string = "BL"; + } else if ( contact_list == &contactlist_reverse ) { + list_string = "RL"; + } else if ( contact_list == &contactlist_temporary ) { + list_string = "TL"; + } else if ( contact_list == &contactlist_pending ) { + list_string = "PL"; + } else { + list_string = "??"; /* Whoops */ + } + + /* Look in all the lists to try and find the record for this user */ + contact_item = contactlist_findcontact(contactlist_forward, username); + if ( contact_item == NULL ) { + contact_item = contactlist_findcontact(contactlist_reverse, username); + } + if ( contact_item == NULL ) { + contact_item = contactlist_findcontact(contactlist_allow, username); + } + if ( contact_item == NULL ) { + contact_item = contactlist_findcontact(contactlist_block, username); + } + if ( contact_item == NULL ) { + contact_item = contactlist_findcontact(contactlist_temporary, username); + } + if ( contact_item == NULL ) { + contact_item = contactlist_findcontact(contactlist_pending, username); + } + + /* If they're not found, create them. */ + if ( contact_item == NULL ) { + + debug_print("CL: Creating contact record: %s / %s\n", username, friendlyname); + contact = contactlist_createcontact(source, username, friendlyname, status, features, dpobject, guid); + assert(contact != NULL); /* Shouldn't ever happen */ + + } else { + + /* Found the contact, so check if they need to be updated */ + contact = contact_item->contact; + assert(contact != NULL); + + if ( contact->source <= source ) { + + int dpchanged = 0; + + debug_print("CL: Updating record: %s / %s\n", username, friendlyname); + + /* Don't change friendlyname non-NULL to NULL */ + if ( !((contact->friendlyname != NULL) && (friendlyname == NULL)) ) { + if ( contact->friendlyname != NULL ) { + free(contact->friendlyname); + } + if ( friendlyname != NULL ) { + contact->friendlyname = strdup(friendlyname); + } else { + contact->friendlyname = NULL; + } + } + + /* Don't change GUID non-NULL to NULL either. In fact, this shouldn't be changing + anyway, but I'm not willing to assume that. */ + if ( !((contact->guid != NULL) && (guid == NULL)) ) { + if ( contact->guid != NULL ) { + free(contact->guid); + } + if ( guid != NULL ) { + contact->guid = strdup(guid); + } else { + contact->guid = NULL; + } + } + + /* This detects DP->no DP and No DP->DP transitions. */ + if ( contact->dpobject != dpobject ) { + dpchanged = 1; + } + /* This detects DP->DP transitions and invalidates same-DP changes. */ + if ( contact->dpobject != NULL ) { + + if ( dpobject != NULL ) { + + if ( strcmp(dpobject, contact->dpobject) != 0 ) { + dpchanged = 1; + } else { + dpchanged = 0; + } + + } + free(contact->dpobject); + if ( contact->dpsha1d != NULL ) { + free(contact->dpsha1d); + } + + } + + contact->features = features; + if ( dpobject != NULL ) { + contact->dpobject = strdup(dpobject); + contact->dpsha1d = avatars_unwrapsha1d(dpobject); + } else { + contact->dpobject = NULL; + contact->dpsha1d = NULL; + } + + if ( dpchanged ) { + + /* Change of DP */ + debug_print("CL: Display picture for %s changed.\n", username); + messagewindow_picturekick(username); + + } + + contact->source = source; + contact->status = status; + + } + + } + + /* Check if they're in the right list. If not, link them into it. */ + if ( contactlist_findcontact(*contact_list, username) == NULL ) { + + assert(contact != NULL); + assert(contact->username != NULL); + debug_print("CL: Linking into %s: %s\n", list_string, contact->username); + + contactlist_link(contact_list, contact); + + if ( contact_list == &contactlist_forward ) { + /* Linking into the FL so create an UIContact for them. */ + if ( contact->uicontact == NULL ) { + contact->uicontact = mainwindow_addcontact(contact->username, contact->friendlyname, status); + } + } + + } + + /* If an NLN, ILN or FLN (for the FL) is BEING HANDLED, and the details have been updated, sort the UI out. + N.B. What's being handled doesn't necessarily have anything to do with the contact's status. */ + if ( ((source == CONTACT_SOURCE_NLN) || (source == CONTACT_SOURCE_FLN)) && (contact_list == &contactlist_forward) ) { + assert(contact->uicontact != NULL); + mainwindow_setcontactstatus(contact->uicontact, contact->username, contact->friendlyname, status); + } + +} + +/* Add a user to the FL or update details if already there */ +void contactlist_fldetails(ContactSource source, const char *username, const char *friendlyname, int features, const char *dpobject, OnlineState status, const char *guid) { + contactlist_details(&contactlist_forward, source, username, friendlyname, features, dpobject, status, guid); +} + +/* Add a user to the AL or update details if already there */ +void contactlist_aldetails(ContactSource source, const char *username, const char *friendlyname, OnlineState status) { + contactlist_details(&contactlist_allow, source, username, friendlyname, 0, NULL, status, NULL); +} + +/* Add a user to the BL or update details if already there */ +void contactlist_bldetails(ContactSource source, const char *username, const char *friendlyname, OnlineState status) { + contactlist_details(&contactlist_block, source, username, friendlyname, 0, NULL, status, NULL); +} + +/* Add a user to the RL or update details if already there */ +void contactlist_rldetails(ContactSource source, const char *username, const char *friendlyname, OnlineState status) { + contactlist_details(&contactlist_reverse, source, username, friendlyname, 0, NULL, status, NULL); +} + +/* Add a user to the TL (temporary list) */ +void contactlist_tldetails(ContactSource source, const char *username, const char *friendlyname, OnlineState status) { + contactlist_details(&contactlist_temporary, source, username, friendlyname, 0, NULL, status, NULL); +} + +/* Add a user to the PL (pending list) */ +void contactlist_pldetails(ContactSource source, const char *username, const char *friendlyname, OnlineState status) { + contactlist_details(&contactlist_pending, source, username, friendlyname, 0, NULL, status, NULL); +} + +/* Do the same as messagewindow_picturekick, but identify "kickees" by DP SHA1D field. */ +void contactlist_picturekick_sha1d(const char *sha1d) { + + /* Find all users who have this DP SHA1D */ + ContactItem *contactitem = contactlist_forward; + if ( contactitem == NULL ) { + return; + } + + while ( contactitem ) { + + assert(contactitem->contact != NULL); + if ( contactitem->contact->dpsha1d != NULL ) { + if ( strcmp(contactitem->contact->dpsha1d , sha1d) == 0 ) { + messagewindow_picturekick(contactitem->contact->username); + } + } + + contactitem = contactitem->next; + + } + +} + +static void contactlist_clear_list(ContactItem **contact_list) { + + ContactItem *contactitem = *contact_list; + + /* Check the list isn't empty first. */ + if ( contactitem == NULL ) { + debug_print("CL: List is empty\n"); + return; + } + + while ( contactitem ) { + + ContactItem *next_contactitem = NULL; + + assert(contactitem != NULL); + assert(contactitem->contact != NULL); + assert(contactitem->contact->username != NULL); + + next_contactitem = contactitem->next; + + contactlist_unlink(contact_list, contactitem); + contactitem = next_contactitem; + + } + +} + +int contactlist_isonlist(const char *list, const char *username) { + + ContactItem **contact_list = contactlist_translatelist(list); + + if ( contactlist_findcontact(*contact_list, username) == NULL ) { + return 0; + } + + return 1; + +} + +/* Called by src/msnprotocol.c to wipe the contact list at sign-out, and by src/listcache.c to invalidate the lists. */ +void contactlist_clear() { + + debug_print("CL: Clearing FL\n"); + contactlist_clear_list(&contactlist_forward); + debug_print("CL: Clearing RL\n"); + contactlist_clear_list(&contactlist_reverse); + debug_print("CL: Clearing BL\n"); + contactlist_clear_list(&contactlist_block); + debug_print("CL: Clearing AL\n"); + contactlist_clear_list(&contactlist_allow); + debug_print("CL: Clearing TL\n"); + contactlist_clear_list(&contactlist_temporary); + debug_print("CL: Clearing PL\n"); + contactlist_clear_list(&contactlist_pending); + + /* Check */ + assert(contactlist_forward == NULL); + assert(contactlist_block == NULL); + assert(contactlist_allow == NULL); + assert(contactlist_reverse == NULL); + assert(contactlist_temporary == NULL); + +} + +/* Return contact data as a string. */ +static char *contactlist_getcontact(const char *list, ContactItem **item) { + + char *r_contact; + size_t length; + + if ( (*item == NULL) && (strcmp(list, "FL") == 0) ) { + *item = contactlist_forward; + } else if ( (*item == NULL) && (strcmp(list, "AL") == 0) ) { + *item = contactlist_allow; + } else if ( (*item == NULL) && (strcmp(list, "BL") == 0) ) { + *item = contactlist_block; + } else if ( (*item == NULL) && (strcmp(list, "RL") == 0) ) { + *item = contactlist_reverse; + } else if ( (*item == NULL) && (strcmp(list, "PL") == 0) ) { + *item = contactlist_pending; + } else { + assert(item != NULL); + assert(*item != NULL); + *item = (*item)->next; + } + + if ( *item == NULL ) { + return NULL; + } + + length = strlen((*item)->contact->username) + 1; + if ( (*item)->contact->friendlyname != NULL ) { + length += strlen((*item)->contact->friendlyname) + 1; + } + if ( (*item)->contact->guid != NULL ) { + length += strlen((*item)->contact->guid) + 1; + if ( (*item)->contact->friendlyname == NULL ) { + /* Whoops! GUID but no friendly name. */ + length += 2; + } + } + r_contact = malloc(length); + + strcpy(r_contact, (*item)->contact->username); + if ( (*item)->contact->friendlyname != NULL ) { + strcat(r_contact, " "); + strcat(r_contact, (*item)->contact->friendlyname); + } + if ( (*item)->contact->guid != NULL ) { + if ( (*item)->contact->friendlyname == NULL ) { + /* Shouldn't ever happen. Nasty situation. */ + strcat(r_contact, " -"); + } + strcat(r_contact, " "); + strcat(r_contact, (*item)->contact->guid); + } + + return r_contact; + +} + +/* Tweak line ending before sending a line to a file. */ +static int contactlist_writeout(FILE *fh, const char *list, const char *contact) { + + char *line; + int rval; + + line = malloc(strlen(list) + strlen(contact) + 3); + strcpy(line, list); + strcat(line, " "); + strcat(line, contact); + line[strlen(line)+1] = '\0'; + line[strlen(line)] = '\n'; + + rval = fputs(line, fh); + free(line); + + if ( rval == EOF ) { + return -1; + } + + return 0; + +} + +/* Dump a given list into the file handle given. */ +int contactlist_dumplist(FILE *fh, const char *list) { + + ContactItem *token; + char *contact; + + token = NULL; + contact = contactlist_getcontact(list, &token); + while ( token ) { + + if ( contactlist_writeout(fh, list, contact) != 0 ) { + free(contact); + return -1; + } + free(contact); + contact = contactlist_getcontact(list, &token); + + } + + return 0; + +} + +const char *contactlist_friendlyname(const char *username) { + + ContactItem *contact; + + contact = contactlist_findcontact(contactlist_forward, username); + if ( contact == NULL ) { + contact = contactlist_findcontact(contactlist_reverse, username); + } + if ( contact == NULL ) { + contact = contactlist_findcontact(contactlist_allow, username); + } + if ( contact == NULL ) { + contact = contactlist_findcontact(contactlist_block, username); + } + if ( contact == NULL ) { + contact = contactlist_findcontact(contactlist_temporary, username); + } + if ( contact == NULL ) { + contact = contactlist_findcontact(contactlist_pending, username); + } + + /* Tricky one to handle. User isn't in any of the contact lists. Caller will probably create a + * record in the temporary list (which isn't stored on the server or in the cache) and use + * that as the SPOT. */ + if ( contact == NULL ) { + debug_print("CL: contactlist_friendlyname: couldn't find user data.\n"); + return NULL; + } + + assert(contact != NULL); + assert(contact->contact != NULL); + + /* Don't try to free this! You may also want to urldecode it. */ + return contact->contact->friendlyname; + +} + +/* Check if a user has a display picture or not. Return NULL or an MSNObject as appropriate. */ +const char *contactlist_haspicture(const char *username) { + + Contact *contact; + + contact = contactlist_findcontactrecord(contactlist_forward, username); + if ( contact == NULL ) { + return NULL; /* e.g. if user was only on TL */ + } + if ( (contact->dpobject != NULL) && (strlen(contact->dpobject) > 0) ) { + return contact->dpobject; + } + + return NULL; + +} + +/* Return a contact's DP SHA1D, if they have one.. */ +const char *contactlist_dpsha1d(const char *username) { + + Contact *contact; + + contact = contactlist_findcontactrecord(contactlist_forward, username); + if ( contact != NULL ) { + return NULL; + } + + if ( contact->dpsha1d != NULL ) { + return contact->dpsha1d; + } + + return NULL; + +} + +/* Set UBX data for a contact. */ +void contactlist_setubx(const char *username, const char *ubxdata, size_t ubxdatalen) { + + Contact *contact; + char *newubx; + + contact = contactlist_findcontactrecord(contactlist_forward, username); + g_return_if_fail(contact != NULL); + + if ( contact->ubxdata != NULL ) { + free(contact->ubxdata); + } + + newubx = malloc(ubxdatalen); + memcpy(newubx, ubxdata, ubxdatalen); + contact->ubxdata = newubx; + contact->ubxdatalen = ubxdatalen; + + /* Kick the UI */ + debug_print("CL: Kicking UIContact because of UBX change.\n"); + mainwindow_setcontactstatus(contact->uicontact, contact->username, contact->friendlyname, contact->status); + +} + +/* Return the Customised Status Message for a contact. */ +char *contactlist_csm(const char *username, int replace_entities) { + + Contact *contact; + + contact = contactlist_findcontactrecord(contactlist_forward, username); + if ( contact == NULL ) { + return NULL; + } + if ( contact->ubxdata == NULL ) { + return NULL; + } + + /* Don't translate entities - Pango will do that later. */ + return xml_getblock(contact->ubxdata, contact->ubxdatalen, "Data", "PSM", replace_entities); + +} + +ContactListIter *contactlist_iter_new() { + + ContactListIter *iter = malloc(sizeof(ContactListIter)); + *iter = 0; + + return iter; + +} + +void contactlist_iter_destroy(ContactListIter *iter) { + free(iter); +} + +const char *contactlist_getusername(ContactListIter *iter, const char *listl) { + + unsigned int i; + ContactItem **list = contactlist_translatelist(listl); + ContactItem *item = *list; + + if ( list == NULL ) { + debug_print("CL: contactlist_getusername: invalid list.\n"); + return NULL; + } + + for ( i=0; i<*iter; i++ ) { + if ( item == NULL ) { + break; + } + item = item->next; + } + + if ( item == NULL ) { + return NULL; + } + + (*iter)++; + return item->contact->username; + +} + +int contactlist_msnc(const char *username) { +} |