/*
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 <silcincludes.h>
#include <silcclient.h>
#include <libxml/parser.h>
#include <libxml/xpath.h>
#include <glib.h>
#include <glade/glade.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1