/* Silky - A GTK+ client for SILC. Copyright (C) 2003, 2004, 2005 Toni Willberg - Buddy list management functions 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 http://silky.sourceforge.net/ */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H # include #endif #include "ui_buddylist.h" #include "buddylist.h" #include "xmllayout.h" #include "xmlconfig.h" #include "log.h" #include "keys.h" #include "support.h" SilkyBuddy *buddies = NULL; extern xmlDocPtr config_buddies; extern SilkyXMLElement xml_elements[NUM_CONFIG][NUM_CM]; extern gchar *buddiespath; extern silkyStruct *silky; SilkyBuddy *buddy_find_by_nick(gchar *nick) { SilkyBuddy *b; for( b = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(b->nickname, nick) ) return b; } return NULL; } SilkyBuddy *buddy_find_by_fp(gchar *fp) { SilkyBuddy *b; for( b = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(b->fingerprint, fp) ) return b; } return NULL; } /* Adds watch for fingerprint, loads pubkey from file. Stolen code from SILC Client's SILC_CLIENT_CMD_FUNC(watch) input: hashed fingerprint */ gboolean watch_add_fingerprint(gchar *fingerprint) { SilcPublicKey pk; SilcBuffer idp, buffer = NULL, args = NULL; const gchar *pubkey; debug_info("adding watch for %s", fingerprint); g_assert(silky->conn); g_assert(fingerprint); pubkey = gen_keyfile_path(fingerprint, "client"); if (!pubkey) { debug_message("public key not found"); return FALSE; } if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_PEM)) { if (!silc_pkcs_load_public_key(pubkey, &pk, SILC_PKCS_FILE_BIN)) { debug_info("Couldn't load public key"); return FALSE; } } debug("loaded pubkey %s", pubkey); args = silc_buffer_alloc_size(2); silc_buffer_format(args, SILC_STR_UI_SHORT(1), SILC_STR_END); buffer = silc_pkcs_public_key_payload_encode(pk); args = silc_argument_payload_encode_one(args, buffer->data, buffer->len, 0x00); idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT); silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_WATCH, 0, 2, 1, idp->data, idp->len, 4, args->data, args->len); silc_buffer_free(buffer); silc_pkcs_public_key_free(pk); silc_buffer_free(args); silc_buffer_free(idp); return TRUE; } /* Sends WATCH command */ void watch_add(gchar *nick) { SilcBuffer idp; if (!silky || !silky->conn || !silky->conn->local_id) { debug("can't find own id, aborting"); return; } debug_info("adding watch for '%s'", nick); idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT); if (!idp) { debug("unable to encode payload"); return; } silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_WATCH, 0, 4, 1, idp->data, idp->len, 2, nick, strlen(nick), 3, NULL, NULL, 4, NULL, NULL ); silc_buffer_free(idp); return; } void watch_del(gchar *nick) { SilcBuffer idp; if (!silky || !silky->conn || !silky->conn->local_id) { debug("can't find own id, aborting"); return; } debug_info("deleting watch for '%s'", nick); idp = silc_id_payload_encode(silky->conn->local_id, SILC_ID_CLIENT); silc_client_command_send(silky->client, silky->conn, SILC_COMMAND_WATCH, 0, 4, 1, idp->data, idp->len, 2, NULL, NULL, 3, nick, strlen(nick), 4, NULL, NULL ); silc_buffer_free(idp); return; } gboolean buddy_add(gchar *nick, gchar *fingerprint) { SilkyBuddy *b, *newb; if( buddy_find_by_fp(fingerprint) ) { debug_info("Buddy with this fingerprint is already in the list, ignoring"); return FALSE; } debug("creating struct for new buddy"); newb = malloc( sizeof( SilkyBuddy ) ); newb->next = NULL; /* let's make sure that no string in here is NULL */ if( nick ) newb->nickname = g_strdup(nick); else newb->nickname = g_strdup(""); newb->realname = NULL; if( fingerprint ) newb->fingerprint = g_strdup(fingerprint); else newb->fingerprint = g_strdup(""); newb->keyverified = 0; newb->languages = NULL; newb->preferred_contact = NULL; newb->timezone = NULL; newb->geolocation = NULL; newb->client_id = NULL; newb->notes = NULL; debug_info("Adding '%s' (%s)", newb->nickname, newb->fingerprint); if( !buddies ) { debug("first buddy, creating linked list"); buddies = newb; } else { debug("there are already some buddies, appending"); for( b = buddies; b->next; b = b->next ) {} b->next = newb; } debug("starting to watch for this buddy"); if (silky->conn) watch_add_fingerprint(fingerprint); return TRUE; } /** * buddy_set_mode_by_fp: * @fingerprint: the fingerprint of user to modify * @mode: new mode of the user * * This function changes the user mode of a user stored in Buddy List. * * Returns: gboolean **/ gboolean buddy_set_mode_by_fp(gchar *fingerprint, SilcUInt32 mode) { SilkyBuddy *b; debug_info("setting umode %d "); for ( b = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(b->fingerprint, fingerprint) ) { b->mode = mode; return TRUE; } } return FALSE; } SilcUInt32 buddy_get_mode_by_fp(gchar *fingerprint) { SilkyBuddy *b = buddy_find_by_fp(fingerprint); if( !b ) { debug("Buddy not found"); return -1; } if( b->mode ) { return( b->mode ); } return -1; } /** * buddy_init_watchlist: * * This function sends WATCH command to server for every buddy * * Returns: nothing **/ void buddy_init_watchlist() { SilkyBuddy *b; g_assert(silky->conn); debug_info("sending watch command for all users in the buddy list, if any"); for ( b = buddies; b; b = b->next ) { watch_add_fingerprint(b->fingerprint); } } /** * buddy_set_keyverified_by_fp: * * This function sets 'keyverified' parameter of the buddy entity * * Returns: gboolean **/ gboolean buddy_set_keyverified_by_fp(const gchar *fingerprint, const gint keyverified) { SilkyBuddy *b = buddy_find_by_fp((gchar *)fingerprint); if( !b ) return FALSE; debug_info("setting keyverified to '%d'", keyverified); b->keyverified = keyverified; return TRUE; } gboolean buddy_set_nick_by_fp(gchar *fingerprint, gchar *nick) { SilkyBuddy *b; if( !nick ) { debug("nick == NULL"); return FALSE; } debug_info("setting nick to '%s'", nick); for ( b = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(b->fingerprint, fingerprint) ) { if (b->nickname) { g_free(b->nickname); } b->nickname = g_strdup(nick); return TRUE; } } return FALSE; } gboolean buddy_set_realname_by_fp(gchar *fingerprint, gchar *realname) { SilkyBuddy *b; if( !realname ) { debug("realname == NULL"); return FALSE; } debug_info("setting realname to '%s'", realname); for ( b = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(b->fingerprint, fingerprint) ) { if (b->realname) { g_free(b->realname); } b->realname = g_strdup(realname); return TRUE; } } return FALSE; } gboolean buddy_set_languages_by_fp(gchar *fingerprint, gchar *languages) { SilkyBuddy *b; if( !languages ) { debug("languages == NULL"); return FALSE; } b = buddy_find_by_fp(fingerprint); if (b) { if (b->languages) { g_free(b->languages); } b->languages = g_strdup(languages); return TRUE; } return FALSE; } gboolean buddy_set_pref_contact_by_fp(gchar *fingerprint, gchar *pc) { SilkyBuddy *b; if( !pc ) { debug("pc == NULL"); return FALSE; } b = buddy_find_by_fp(fingerprint); if (b) { if (b->preferred_contact) { g_free(b->preferred_contact); } b->preferred_contact = g_strdup(pc); return TRUE; } return FALSE; } gboolean buddy_set_timezone_by_fp(gchar *fingerprint, gchar *tz) { SilkyBuddy *b; if( !tz ) { debug("tz == NULL"); return FALSE; } b = buddy_find_by_fp(fingerprint); if (b) { if (b->timezone) { g_free(b->timezone); } b->timezone = g_strdup(tz); return TRUE; } return FALSE; } gboolean buddy_set_clientid_by_fp(gchar *fingerprint, SilcClientID *id) { SilkyBuddy *b; gchar *str_fingerprint = silc_fingerprint(fingerprint, 20 ); debug("setting clientid for %s", str_fingerprint ); b = buddy_find_by_fp(fingerprint); if (b) { if (b->client_id) { g_free(b->client_id); } b->client_id = id; // b->client_id = silc_id_dup(id, SILC_ID_CLIENT); /* copy */ return TRUE; } return FALSE; } gboolean buddy_set_geolocation_by_fp(gchar *fingerprint, gchar *gl) { SilkyBuddy *b; if( !gl ) { debug("gl == NULL"); return FALSE; } b = buddy_find_by_fp(fingerprint); if (b) { if (b->geolocation) { g_free(b->geolocation); } b->geolocation = g_strdup(gl); return TRUE; } return FALSE; } gboolean buddy_remove_by_fp(gchar *fingerprint) { SilkyBuddy *b, *prevb; gint found = 0; debug_info("trying to remove buddy"); if( !buddies ) { debug("No buddies, returning"); return FALSE; } for( b = buddies, prevb = buddies; b; b = b->next ) { if( !g_ascii_strcasecmp(fingerprint, b->fingerprint) ) { debug("found it, nick '%s'", b->nickname); found++; break; } prevb = b; } if( !found ) { debug("could not find buddy to remove, returning"); return FALSE; } debug("removing buddy from list"); if( b == buddies ) { debug("it is first buddy"); buddies = buddies->next; } else { debug("not first buddy"); prevb->next = b->next; } debug("freeing buddy from memory"); if (b) { g_free(b); } return TRUE; } /* This loads buddylist from the file, and calls parse_buddies() Called from main.c */ gboolean init_buddies(gchar *buddiespath) { debug_info("Trying to load buddies from %s", buddiespath); if( g_file_test(buddiespath, G_FILE_TEST_EXISTS) ) { debug("file found"); if( !(config_buddies = xml_read_config(CONFIG_BUDDIES, buddiespath)) ) { debug("Failed to load buddies from file %s", buddiespath); return FALSE; } if( !parse_buddies() ) { debug("Failed to parse buddies, none loaded"); /* FIXME display warning to user? */ return TRUE; } debug("Buddies succesfully loaded, continuing"); return TRUE; } return FALSE; } /* Parses the buddylist XML file. Adds buddies the UI list. */ gboolean parse_buddies() { xmlXPathObjectPtr result; xmlXPathContextPtr context; xmlNodePtr node, n; gint i; gchar *nick = NULL; gchar *realname = NULL; gchar *fp = NULL; gchar *keyverified = NULL; xml_cleanup_config(CONFIG_BUDDIES); debug_info("Populating SilkyBuddies list..."); context = xmlXPathNewContext(config_buddies); result = xmlXPathEval(g_strdup_printf("/%s/%s", xml_elements[CONFIG_BUDDIES][CB_ROOT].name, xml_elements[CONFIG_BUDDIES][CB_BUDDY].name), context); if( !result ) { debug("No result found for buddies"); return FALSE; } if( xmlXPathNodeSetIsEmpty(result->nodesetval) ) { debug_info("no buddies found."); return TRUE; } xmlXPathFreeContext(context); for( i = 0; i < result->nodesetval->nodeNr; i++ ) { node = result->nodesetval->nodeTab[i]; for( n = node->children; n && g_ascii_strcasecmp(n->name, xml_elements[CONFIG_BUDDIES][CB_BUDDY].name) ; n = n->next ) { if( !g_ascii_strcasecmp(n->name, xml_elements[CONFIG_BUDDIES][CB_BUDDY_NICKNAME].name) ) nick = g_strdup(xmlNodeGetContent(n)); if( !g_ascii_strcasecmp(n->name, xml_elements[CONFIG_BUDDIES][CB_BUDDY_REALNAME].name) ) realname = g_strdup(xmlNodeGetContent(n)); if( !g_ascii_strcasecmp(n->name, xml_elements[CONFIG_BUDDIES][CB_BUDDY_FINGERPRINT].name) ) fp = g_strdup(xmlNodeGetContent(n)); if( !g_ascii_strcasecmp(n->name, xml_elements[CONFIG_BUDDIES][CB_BUDDY_KEYVERIFIED].name) ) keyverified = g_strdup(xmlNodeGetContent(n)); } if( (!fp || !g_utf8_strlen(fp, -1)) || (!nick || !g_utf8_strlen(nick, -1)) ) { debug("Fingerprint empty, ignoring buddy"); } else { debug("Adding buddy '%s', fingerprint '%s'", nick, fp); buddy_add(nick, fp); buddy_set_realname_by_fp(fp, realname); buddy_set_keyverified_by_fp(fp, atoi(keyverified)); } } xmlXPathFreeObject(result); if( !i ) debug("no buddies found."); return TRUE; } /* Saves the buddylist file */ gboolean save_buddies() { SilkyBuddy *b; xmlNodePtr bn, n; debug_info("Starting to save buddies to file '%s'", buddiespath); xmlFreeDoc(config_buddies); config_buddies = xml_create_config(CONFIG_BUDDIES); for( b = buddies; b; b = b->next ) { debug("Saving '%s' (%s)", b->nickname, b->fingerprint); bn = xmlNewTextChild(xmlDocGetRootElement(config_buddies), NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY].name, NULL); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_NICKNAME].name, b->nickname); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_REALNAME].name, b->realname); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_FINGERPRINT].name, b->fingerprint); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_KEYVERIFIED].name, g_strdup_printf("%d", b->keyverified)); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_NOTES].name, b->notes); bn = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_ATTR].name, NULL); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_ATTR_LANGUAGES].name, b->languages); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_ATTR_PREFCONTACT].name, b->preferred_contact); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_ATTR_TIMEZONE].name, b->timezone); n = xmlNewTextChild(bn, NULL, xml_elements[CONFIG_BUDDIES][CB_BUDDY_ATTR_GEOLOCATION].name, b->geolocation); } xml_save_config(CONFIG_BUDDIES, buddiespath); return TRUE; } gchar *buddy_get_notes_by_fp(gchar *fingerprint) { SilkyBuddy *b = buddy_find_by_fp(fingerprint); if( !b ) { debug("Buddy not found"); return NULL; } if( b->notes ) { return( g_strdup(b->notes) ); } return NULL; } gboolean buddy_set_notes_by_fp(gchar *fingerprint, gchar *notes) { SilkyBuddy *b = buddy_find_by_fp(fingerprint); if( !notes ) { debug("notes == NULL"); return FALSE; } if( !b ) { debug("Buddy with such fingerprint not found."); return FALSE; } if( b->notes != NULL ) { g_free(b->notes); } b->notes = g_strdup(notes); debug("Set notes: \"%s\"", notes); return TRUE; }