aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/bbdb
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/bbdb')
-rw-r--r--plugins/bbdb/ChangeLog19
-rw-r--r--plugins/bbdb/Makefile.am8
-rw-r--r--plugins/bbdb/bbdb.c200
-rw-r--r--plugins/bbdb/bbdb.h22
-rw-r--r--plugins/bbdb/gaimbuddies.c459
-rw-r--r--plugins/bbdb/test-evobuddy.c15
6 files changed, 685 insertions, 38 deletions
diff --git a/plugins/bbdb/ChangeLog b/plugins/bbdb/ChangeLog
index d471940c29..423d30da74 100644
--- a/plugins/bbdb/ChangeLog
+++ b/plugins/bbdb/ChangeLog
@@ -1,3 +1,22 @@
+2004-10-25 Nat Friedman <nat@novell.com>
+
+ * bbdb.c (e_plugin_lib_enable): Sync the Gaim buddy list. Set a
+ timer to check for Gaim buddy list chnages to sync.
+ (bbdb_do_it): Free some memory we were leaking before.
+ (bbdb_open_addressbook): New function for Gaim buddy list support.
+ (bbdb_check_gaim_enabled): Likewise.
+ (enable_gaim_toggled_cb): Likewise.
+ (synchronize_button_clicked_cb): Likewise.
+ (bbdb_page_factory): Added UI for Gaim buddy list sync support.
+
+ * gaimbuddies.c: New file, contains routines to synchronize IM
+ information and buddy icons from a Gaim buddy list.
+
+ * bbdb.h: New file, contains shared macros and prototypes.
+
+ * test-evobuddy.c (main): New function, tests a gaim buddy list
+ sync.
+
2004-10-23 Nat Friedman <nat@novell.com>
* bbdb.c (bbdb_do_it): Change assertions to if statements, so as
diff --git a/plugins/bbdb/Makefile.am b/plugins/bbdb/Makefile.am
index 140cc9e2fc..aa84ee194d 100644
--- a/plugins/bbdb/Makefile.am
+++ b/plugins/bbdb/Makefile.am
@@ -8,5 +8,11 @@ INCLUDES = \
plugin_DATA = org-gnome-evolution-bbdb.eplug
plugin_LTLIBRARIES = liborg-gnome-evolution-bbdb.la
-liborg_gnome_evolution_bbdb_la_SOURCES = bbdb.c
+liborg_gnome_evolution_bbdb_la_SOURCES = bbdb.c gaimbuddies.c
liborg_gnome_evolution_bbdb_la_LDFLAGS = -module -avoid-version
+
+noinst_PROGRAMS = test-evobuddy
+
+test_evobuddy_LDADD = $(top_builddir)/camel/libcamel.la $(EVOLUTION_ADDRESSBOOK_LIBS) $(EVOLUTION_MAIL_LIBS) $(CAMEL_LIBS)
+test_evobuddy_SOURCES = test-evobuddy.c gaimbuddies.c bbdb.c
+test_evobuddy_INCLUDES = $(INCLUDES)
diff --git a/plugins/bbdb/bbdb.c b/plugins/bbdb/bbdb.c
index bc1b2cc4fe..66e56879f3 100644
--- a/plugins/bbdb/bbdb.c
+++ b/plugins/bbdb/bbdb.c
@@ -47,11 +47,10 @@
#include <mail/em-event.h>
#include <camel/camel-mime-message.h>
-/* Where to store the config values */
-#define GCONF_KEY_ENABLE "/apps/evolution/mail/autopopulate_addressbook"
-#define GCONF_KEY_WHICH_ADDRESSBOOK "/apps/evolution/mail/autopopulate_source"
+#include "bbdb.h"
/* Plugin hooks */
+int e_plugin_lib_enable (EPluginLib *ep, int enable);
void bbdb_handle_reply (EPlugin *ep, EMEventTargetMessage *target);
GtkWidget *bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data);
GtkWidget *bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data);
@@ -63,9 +62,11 @@ struct bbdb_stuff {
GtkWidget *option_menu;
GtkWidget *check;
+ GtkWidget *check_gaim;
};
/* Static forward declarations */
+static gboolean bbdb_timeout (gpointer data);
static void bbdb_do_it (EBook *book, const char *name, const char *email);
static void add_email_to_contact (EContact *contact, const char *email);
static void enable_toggled_cb (GtkWidget *widget, gpointer data);
@@ -73,7 +74,31 @@ static void source_changed_cb (GtkWidget *widget, ESource *source, gpointer data
static GtkWidget *create_addressbook_option_menu (struct bbdb_stuff *stuff);
static void cleanup_cb (GObject *o, gpointer data);
+int
+e_plugin_lib_enable (EPluginLib *ep, int enable)
+{
+ /* Start up the plugin. */
+ if (enable) {
+ fprintf (stderr, "BBDB spinning up...\n");
+
+ if (bbdb_check_gaim_enabled ())
+ bbdb_sync_buddy_list_check ();
+
+ g_timeout_add (BBDB_BLIST_CHECK_INTERVAL,
+ (GSourceFunc) bbdb_timeout,
+ NULL);
+ }
+ return 0;
+}
+
+static gboolean
+bbdb_timeout (gpointer data)
+{
+ bbdb_sync_buddy_list_check ();
+
+ return TRUE;
+}
/* Code to populate addressbook when you reply to a mail follows */
@@ -83,33 +108,11 @@ bbdb_handle_reply (EPlugin *ep, EMEventTargetMessage *target)
const CamelInternetAddress *cia;
const char *name;
const char *email;
- char *uri;
EBook *book = NULL;
int i;
- GConfClient *gconf;
-
- gboolean status;
- GError *error;
-
- gconf = gconf_client_get_default ();
- uri = gconf_client_get_string (gconf, GCONF_KEY_WHICH_ADDRESSBOOK, NULL);
- g_object_unref (G_OBJECT (gconf));
- if (uri == NULL)
- book = e_book_new_system_addressbook (&error);
- else
- book = e_book_new_from_uri (uri, &error);
- if (book == NULL) {
- g_warning ("bbdb: failed to open addressbook: %s\n", error->message);
- return;
- }
-
- status = e_book_open (book, FALSE, NULL);
-
- if (status == FALSE) {
- g_warning ("bbdb: failed to open local addressbook\n");
- return;
- }
+ /* Open the addressbook */
+ book = bbdb_open_addressbook ();
cia = camel_mime_message_get_from (target->message);
for (i = 0; i < camel_address_length CAMEL_ADDRESS (cia); i ++) {
@@ -143,7 +146,7 @@ bbdb_do_it (EBook *book, const char *name, const char *email)
{
char *query_string;
EBookQuery *query;
- GList *contacts;
+ GList *contacts, *l;
EContact *contact;
gboolean status;
@@ -166,8 +169,15 @@ bbdb_do_it (EBook *book, const char *name, const char *email)
g_free (query_string);
status = e_book_get_contacts (book, query, &contacts, NULL);
- if (contacts != NULL)
+ e_book_query_unref (query);
+ if (contacts != NULL) {
+ GList *l;
+ for (l = contacts; l != NULL; l = l->next)
+ g_object_unref ((GObject *)l->data);
+ g_list_free (contacts);
+
return;
+ }
/* If a contact exists with this name, add the email address to it. */
query_string = g_strdup_printf ("(is \"full_name\" \"%s\")", name);
@@ -175,17 +185,26 @@ bbdb_do_it (EBook *book, const char *name, const char *email)
g_free (query_string);
status = e_book_get_contacts (book, query, &contacts, NULL);
+ e_book_query_unref (query);
if (contacts != NULL) {
- /* If there's more than one contact with this name,
- just give up; we're not smart enough for this. */
+ /* FIXME: If there's more than one contact with this
+ name, just give up; we're not smart enough for
+ this. */
if (contacts->next != NULL)
return;
contact = (EContact *) contacts->data;
add_email_to_contact (contact, email);
- if (! e_book_commit_contact (book, contact, &error))
+ if (! e_book_commit_contact (book, contact, &error)) {
g_warning ("bbdb: Could not modify contact: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ for (l = contacts; l != NULL; l = l->next)
+ g_object_unref ((GObject *)l->data);
+ g_list_free (contacts);
+
return;
}
@@ -196,8 +215,69 @@ bbdb_do_it (EBook *book, const char *name, const char *email)
if (! e_book_add_contact (book, contact, &error)) {
g_warning ("bbdb: Failed to add new contact: %s\n", error->message);
+ g_error_free (error);
return;
}
+
+ g_object_unref (G_OBJECT (contact));
+}
+
+EBook *
+bbdb_open_addressbook (void)
+{
+ GConfClient *gconf;
+ char *uri;
+ EBook *book = NULL;
+
+ gboolean enable;
+
+ gboolean status;
+ GError *error;
+
+ gconf = gconf_client_get_default ();
+
+ /* Check to see if we're supposed to be running */
+ enable = gconf_client_get_bool (gconf, GCONF_KEY_ENABLE, NULL);
+ if (! enable) {
+ g_object_unref (G_OBJECT (gconf));
+ return NULL;
+ }
+
+ /* Open the appropriate addresbook. */
+ uri = gconf_client_get_string (gconf, GCONF_KEY_WHICH_ADDRESSBOOK, NULL);
+ g_object_unref (G_OBJECT (gconf));
+ if (uri == NULL)
+ book = e_book_new_system_addressbook (&error);
+ else
+ book = e_book_new_from_uri (uri, &error);
+ if (book == NULL) {
+ g_warning ("bbdb: failed to get addressbook: %s\n", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ status = e_book_open (book, FALSE, &error);
+ if (! status) {
+ g_warning ("bbdb: failed to open addressbook: %s\n", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ return book;
+}
+
+gboolean
+bbdb_check_gaim_enabled ()
+{
+ GConfClient *gconf;
+ gboolean gaim_enabled;
+
+ gconf = gconf_client_get_default ();
+ gaim_enabled = gconf_client_get_bool (gconf, GCONF_KEY_ENABLE_GAIM, NULL);
+
+ g_object_unref (G_OBJECT (gconf));
+
+ return gaim_enabled;
}
static void
@@ -229,6 +309,24 @@ enable_toggled_cb (GtkWidget *widget, gpointer data)
}
static void
+enable_gaim_toggled_cb (GtkWidget *widget, gpointer data)
+{
+ struct bbdb_stuff *stuff = (struct bbdb_stuff *) data;
+ gboolean active;
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+
+ /* Save the new setting to gconf */
+ gconf_client_set_bool (stuff->target->gconf, GCONF_KEY_ENABLE_GAIM, active, NULL);
+}
+
+static void
+synchronize_button_clicked_cb (GtkWidget *button)
+{
+ bbdb_sync_buddy_list ();
+}
+
+static void
source_changed_cb (GtkWidget *widget, ESource *source, gpointer data)
{
struct bbdb_stuff *stuff = (struct bbdb_stuff *) data;
@@ -276,6 +374,8 @@ bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data)
GtkWidget *inner_vbox;
GtkWidget *check;
GtkWidget *option;
+ GtkWidget *check_gaim;
+ GtkWidget *button;
/* A structure to pass some stuff around */
stuff = g_new0 (struct bbdb_stuff, 1);
@@ -289,9 +389,9 @@ bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data)
/* Frame */
frame = gtk_vbox_new (FALSE, 6);
- gtk_box_pack_start (GTK_BOX (page), frame, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
- /* Label */
+ /* "Automatic Contacts" */
frame_label = gtk_label_new ("");
gtk_label_set_markup (GTK_LABEL (frame_label), _("<span weight=\"bold\">Automatic Contacts</span>"));
GTK_MISC (frame_label)->xalign = 0.0;
@@ -319,6 +419,35 @@ bbdb_page_factory (EPlugin *ep, EConfigHookItemFactoryData *hook_data)
gtk_box_pack_start (GTK_BOX (inner_vbox), option, FALSE, FALSE, 0);
stuff->option_menu = option;
+ /* "Instant Messaging Contacts" */
+ frame = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (page), frame, TRUE, TRUE, 24);
+
+ frame_label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (frame_label), _("<span weight=\"bold\">Instant Messaging Contacts</span>"));
+ GTK_MISC (frame_label)->xalign = 0.0;
+ gtk_box_pack_start (GTK_BOX (frame), frame_label, FALSE, FALSE, 0);
+
+ /* Indent/padding */
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (frame), hbox, FALSE, TRUE, 0);
+ padding_label = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), padding_label, FALSE, FALSE, 0);
+ inner_vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox), inner_vbox, FALSE, FALSE, 0);
+
+ /* Enable Gaim Checkbox */
+ check_gaim = gtk_check_button_new_with_mnemonic (_("Periodically synchronize contact information and images from my _instant messenger"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_gaim), gconf_client_get_bool (target->gconf, GCONF_KEY_ENABLE_GAIM, NULL));
+ g_signal_connect (GTK_TOGGLE_BUTTON (check_gaim), "toggled", G_CALLBACK (enable_gaim_toggled_cb), stuff);
+ gtk_box_pack_start (GTK_BOX (inner_vbox), check_gaim, FALSE, FALSE, 0);
+ stuff->check_gaim = check_gaim;
+
+ /* Synchronize now button. */
+ button = gtk_button_new_with_label (_("Synchronize with _buddy list now"));
+ g_signal_connect (GTK_BUTTON (button), "clicked", G_CALLBACK (synchronize_button_clicked_cb), stuff);
+ gtk_box_pack_start (GTK_BOX (inner_vbox), button, FALSE, FALSE, 0);
+
/* Clean up */
g_signal_connect (page, "destroy", G_CALLBACK (cleanup_cb), stuff);
@@ -335,6 +464,3 @@ cleanup_cb (GObject *o, gpointer data)
g_object_unref (stuff->source_list);
g_free (stuff);
}
-
-
-
diff --git a/plugins/bbdb/bbdb.h b/plugins/bbdb/bbdb.h
new file mode 100644
index 0000000000..5fe9e39bdd
--- /dev/null
+++ b/plugins/bbdb/bbdb.h
@@ -0,0 +1,22 @@
+#ifndef __BBDB_H__
+#define __BBDB_H__
+
+/* Where to store the config values */
+#define GCONF_KEY_ENABLE "/apps/evolution/autocontacts/enable_autocontacts"
+#define GCONF_KEY_ENABLE_GAIM "/apps/evolution/autocontacts/auto_sync_gaim"
+#define GCONF_KEY_WHICH_ADDRESSBOOK "/apps/evolution/autocontacts/addressbook_source"
+#define GCONF_KEY_GAIM_LAST_SYNC "/apps/evolution/autocontacts/gaim_last_sync_time"
+
+/* How often to poll the buddy list for changes (every two minutes) */
+#define BBDB_BLIST_CHECK_INTERVAL (2 * 60 * 1000)
+
+/* bbdb.c */
+EBook *bbdb_open_addressbook (void);
+gboolean bbdb_check_gaim_enabled (void);
+
+/* gaimbuddies.c */
+void bbdb_sync_buddy_list (void);
+void bbdb_sync_buddy_list_check (void);
+
+
+#endif /* __BBDB_H__ */
diff --git a/plugins/bbdb/gaimbuddies.c b/plugins/bbdb/gaimbuddies.c
new file mode 100644
index 0000000000..a9bd81bc9b
--- /dev/null
+++ b/plugins/bbdb/gaimbuddies.c
@@ -0,0 +1,459 @@
+/*
+ * Routines to copy information from a Gaim buddy list into an
+ * Evolution addressbook.
+ *
+ * I currently copy IM account names and buddy icons, provided you
+ * don't already have a buddy icon defined for a person.
+ *
+ * This works today (25 October 2004), but is pretty sure to break
+ * later on as the Gaim buddylist file format shifts.
+ *
+ * Nat Friedman <nat@novell.com>
+ *
+ * Copyright 2004 Novell, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <glib.h>
+#include <libxml/tree.h>
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+#include <gal/util/e-xml-utils.h>
+
+#include <gtk/gtk.h>
+#include <libgnome/gnome-i18n.h>
+#include <string.h>
+
+#include <libebook/e-book.h>
+#include <libedataserverui/e-source-option-menu.h>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <e-util/e-config.h>
+
+#include "bbdb.h"
+
+typedef struct {
+ char *account_name;
+ char *proto;
+ char *alias;
+ char *icon;
+} GaimBuddy;
+
+/* Defined in bbdb.c */
+EBook *bbdb_open_addressbook (void);
+
+/* Forward declarations for this file. */
+void bbdb_sync_buddy_list (void);
+static EBookQuery *e_book_query_im_field_contains (const char *im);
+static void bbdb_merge_buddy_to_contact (EBook *book, GaimBuddy *b, EContact *c);
+static GList *bbdb_get_gaim_buddy_list (void);
+static char *get_node_text (xmlNodePtr node);
+static char *get_buddy_icon_from_setting (xmlNodePtr setting);
+static char *get_node_text (xmlNodePtr node);
+static void free_contact_list (GList *contacts);
+static void free_buddy_list (GList *blist);
+static void parse_buddy_group (xmlNodePtr group, GList **buddies);
+static EContactField proto_to_contact_field (const char *proto);
+
+void
+bbdb_sync_buddy_list_check (void)
+{
+ GConfClient *gconf;
+ struct stat statbuf;
+ time_t last_sync;
+ char *blist_path;
+ char *last_sync_str;
+
+ gconf = gconf_client_get_default ();
+
+ if (! gconf_client_get_bool (gconf, GCONF_KEY_ENABLE_GAIM, NULL)) {
+ g_object_unref (G_OBJECT (gconf));
+ return;
+ }
+
+ blist_path = g_build_path ("/", getenv ("HOME"), ".gaim/blist.xml", NULL);
+ if (stat (blist_path, &statbuf) < 0) {
+ g_object_unref (G_OBJECT (gconf));
+ return;
+ }
+
+ /* Reprocess the buddy list if it's been updated. */
+ last_sync_str = gconf_client_get_string (gconf, GCONF_KEY_GAIM_LAST_SYNC, NULL);
+ if (last_sync_str == NULL || ! strcmp (last_sync_str, ""))
+ last_sync = (time_t) 0;
+ else
+ last_sync = (time_t) g_ascii_strtoull (last_sync_str, NULL, 10);
+
+ g_free (last_sync_str);
+ g_object_unref (G_OBJECT (gconf));
+
+ printf ("bbdb: Last sync: %ld\n", last_sync);
+ printf ("bbdb: Modified: %ld\n", statbuf.st_mtime);
+
+ if (statbuf.st_mtime > last_sync) {
+ fprintf (stderr, "bbdb: Buddy list dirty!\n");
+
+ bbdb_sync_buddy_list ();
+ }
+}
+
+void
+bbdb_sync_buddy_list (void)
+{
+ GList *blist, *l;
+ EBook *book = NULL;
+
+ /* Get the Gaim buddy list */
+ blist = bbdb_get_gaim_buddy_list ();
+ if (blist == NULL)
+ return;
+
+ /* Open the addressbook */
+ book = bbdb_open_addressbook ();
+ if (book == NULL) {
+ free_buddy_list (blist);
+ return;
+ }
+
+ /* Walk the buddy list */
+ for (l = blist; l != NULL; l = l->next) {
+ GaimBuddy *b = l->data;
+ EBookQuery *query;
+ GList *contacts;
+ GError *error;
+ EContact *c;
+
+ if (b->alias == NULL || strlen (b->alias) == 0)
+ continue;
+
+ /* Check to see if the buddy is already in the addressbook */
+ query = e_book_query_im_field_contains (b->account_name);
+ e_book_get_contacts (book, query, &contacts, NULL);
+ e_book_query_unref (query);
+ if (contacts != NULL) {
+ free_contact_list (contacts);
+ continue;
+ }
+ free_contact_list (contacts);
+
+ /* Look for an exact match full name == buddy alias */
+ query = e_book_query_field_test (E_CONTACT_FULL_NAME, E_BOOK_QUERY_IS, b->alias);
+ e_book_get_contacts (book, query, &contacts, NULL);
+ e_book_query_unref (query);
+ if (contacts != NULL) {
+
+ /* FIXME: If there's more than one contact with this
+ name, just give up; we're not smart enough for
+ this. */
+ if (contacts->next != NULL)
+ continue;
+
+ c = E_CONTACT (contacts->data);
+
+ bbdb_merge_buddy_to_contact (book, b, c);
+
+ /* Write it out to the addressbook */
+ if (! e_book_commit_contact (book, c, &error)) {
+ g_warning ("bbdb: Could not modify contact: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ continue;
+ }
+
+ /* Otherwise, create a new contact. */
+ c = e_contact_new ();
+ e_contact_set (c, E_CONTACT_FULL_NAME, (gpointer) b->alias);
+ bbdb_merge_buddy_to_contact (book, b, c);
+ if (! e_book_add_contact (book, c, &error)) {
+ g_warning ("bbdb: Failed to add new contact: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+ g_object_unref (G_OBJECT (c));
+ }
+
+
+ /* Update the last-sync'd time */
+ {
+ GConfClient *gconf;
+ time_t last_sync;
+ char *last_sync_str;
+
+ gconf = gconf_client_get_default ();
+
+ time (&last_sync);
+ last_sync_str = g_strdup_printf ("%ld", (glong) last_sync);
+ printf ("Str: %s\n", last_sync_str);
+ gconf_client_set_string (gconf, GCONF_KEY_GAIM_LAST_SYNC, last_sync_str, NULL);
+ g_free (last_sync_str);
+
+ g_object_unref (G_OBJECT (gconf));
+ }
+}
+
+static EBookQuery *
+e_book_query_im_field_contains (const char *im)
+{
+ char *query_string;
+ EBookQuery *query;
+
+ query_string = g_strdup_printf (
+ "(or "
+ "(is \"im_aim\" \"%s\") "
+ "(is \"im_yahoo\" \"%s\") "
+ "(is \"im_msn\" \"%s\") "
+ "(is \"im_icq\" \"%s\") "
+ "(is \"im_jabber\" \"%s\") "
+ "(is \"im_groupwise\" \"%s\")"
+ ")",
+ im, im, im, im, im, im);
+
+ query = e_book_query_from_string (query_string);
+
+ g_free (query_string);
+
+ return query;
+}
+
+static void
+bbdb_merge_buddy_to_contact (EBook *book, GaimBuddy *b, EContact *c)
+{
+ EContactField field;
+ GList *ims, *l;
+
+ EContactPhoto *photo = NULL;
+
+ GError *error = NULL;
+
+ /* Set the IM account */
+ field = proto_to_contact_field (b->proto);
+ ims = e_contact_get (c, field);
+ ims = g_list_append (ims, (gpointer) b->account_name);
+ e_contact_set (c, field, (gpointer) ims);
+
+ /* Set the photo if it's not set */
+ if (b->icon != NULL) {
+ photo = e_contact_get (c, E_CONTACT_PHOTO);
+ if (photo == NULL) {
+
+ photo = g_new0 (EContactPhoto, 1);
+
+ if (! g_file_get_contents (b->icon, &photo->data, &photo->length, &error)) {
+ g_warning ("bbdb: Could not read buddy icon: %s\n", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ e_contact_set (c, E_CONTACT_PHOTO, (gpointer) photo);
+ }
+ }
+
+ /* Clean up */
+ if (photo != NULL) {
+ g_free (photo->data);
+ g_free (photo);
+ }
+
+ for (l = ims; l != NULL; l = l->next)
+ g_free ((char *) l->data);
+ g_list_free (ims);
+}
+
+static EContactField
+proto_to_contact_field (const char *proto)
+{
+ if (! strcmp (proto, "prpl-oscar"))
+ return E_CONTACT_IM_AIM;
+ if (! strcmp (proto, "prpl-novell"))
+ return E_CONTACT_IM_GROUPWISE;
+ if (! strcmp (proto, "prpl-msn"))
+ return E_CONTACT_IM_MSN;
+ if (! strcmp (proto, "prpl-icq"))
+ return E_CONTACT_IM_ICQ;
+ if (! strcmp (proto, "prpl-yahoo"))
+ return E_CONTACT_IM_YAHOO;
+ if (! strcmp (proto, "prpl-jabber"))
+ return E_CONTACT_IM_JABBER;
+
+ return E_CONTACT_IM_AIM;
+}
+
+static void
+free_contact_list (GList *contacts)
+{
+ GList *l;
+
+ for (l = contacts; l != NULL; l = l->next)
+ g_object_unref (G_OBJECT (l->data));
+ g_list_free (contacts);
+}
+
+static GList *
+bbdb_get_gaim_buddy_list (void)
+{
+ char *blist_path;
+ xmlDocPtr buddy_xml;
+ xmlNodePtr root, child, blist;
+ GList *buddies = NULL;
+
+ blist_path = g_build_path ("/", getenv ("HOME"), ".gaim/blist.xml", NULL);
+
+ buddy_xml = xmlParseFile (blist_path);
+ g_free (blist_path);
+ if (! buddy_xml) {
+ fprintf (stderr, "bbdb: Could not open Gaim buddy list.\n");
+ return NULL;
+ }
+
+ root = xmlDocGetRootElement (buddy_xml);
+ if (strcmp (root->name, "gaim")) {
+ fprintf (stderr, "bbdb: Could not parse Gaim buddy list.\n");
+ xmlFreeDoc (buddy_xml);
+ return NULL;
+ }
+
+ blist = NULL;
+ for (child = root->children; child != NULL; child = child->next) {
+ if (! strcmp (child->name, "blist")) {
+ blist = child;
+ break;
+ }
+ }
+ if (blist == NULL) {
+ fprintf (stderr, "bbdb: Could not find 'blist' element in Gaim buddy list.\n");
+ xmlFreeDoc (buddy_xml);
+ return NULL;
+ }
+
+ for (child = blist->children; child != NULL; child = child->next) {
+ if (! strcmp (child->name, "group"))
+ parse_buddy_group (child, &buddies);
+ }
+
+ xmlFreeDoc (buddy_xml);
+
+ return buddies;
+}
+
+static void
+free_buddy_list (GList *blist)
+{
+ GList *l;
+
+ for (l = blist; l != NULL; l = l->next) {
+ GaimBuddy *gb = l->data;
+
+ g_free (gb->icon);
+ g_free (gb->alias);
+ g_free (gb->account_name);
+ g_free (gb->proto);
+ g_free (gb);
+ }
+
+ g_list_free (l);
+}
+
+static char *
+get_node_text (xmlNodePtr node)
+{
+ if (node->children == NULL || node->children->content == NULL ||
+ strcmp (node->children->name, "text"))
+ return NULL;
+
+ return g_strdup (node->children->content);
+}
+
+static char *
+get_buddy_icon_from_setting (xmlNodePtr setting)
+{
+ char *icon = NULL;
+
+ icon = get_node_text (setting);
+ if (icon [0] != '/') {
+ char *path;
+
+ path = g_build_path ("/", getenv ("HOME"), ".gaim/icons", icon, NULL);
+ g_free (icon);
+ icon = path;
+ }
+
+
+ return icon;
+}
+
+static void
+parse_contact (xmlNodePtr contact, GList **buddies)
+{
+ xmlNodePtr child;
+ xmlNodePtr buddy = NULL;
+ GaimBuddy *gb;
+
+ for (child = contact->children; child != NULL; child = child->next) {
+ if (! strcmp (child->name, "buddy")) {
+ buddy = child;
+ break;
+ }
+ }
+
+ if (buddy == NULL) {
+ fprintf (stderr, "bbdb: Could not find buddy in contact. Malformed Gaim buddy list file.\n");
+ return;
+ }
+
+ gb = g_new0 (GaimBuddy, 1);
+
+ gb->proto = e_xml_get_string_prop_by_name (buddy, "proto");
+
+ for (child = buddy->children; child != NULL; child = child->next) {
+ if (! strcmp (child->name, "setting")) {
+ char *setting_type;
+ setting_type = e_xml_get_string_prop_by_name (child, "name");
+
+ if (! strcmp (setting_type, "buddy_icon"))
+ gb->icon = get_buddy_icon_from_setting (child);
+
+ g_free (setting_type);
+ } else if (! strcmp (child->name, "name"))
+ gb->account_name = get_node_text (child);
+ else if (! strcmp (child->name, "alias"))
+ gb->alias = get_node_text (child);
+
+ }
+
+ *buddies = g_list_prepend (*buddies, gb);
+}
+
+static void
+parse_buddy_group (xmlNodePtr group, GList **buddies)
+{
+ xmlNodePtr child;
+
+ for (child = group->children; child != NULL; child = child->next) {
+ if (strcmp (child->name, "contact"))
+ continue;
+
+ parse_contact (child, buddies);
+ }
+}
diff --git a/plugins/bbdb/test-evobuddy.c b/plugins/bbdb/test-evobuddy.c
new file mode 100644
index 0000000000..269999beb1
--- /dev/null
+++ b/plugins/bbdb/test-evobuddy.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+
+void bbdb_sync_buddy_list (void);
+
+int
+main (void)
+{
+ printf ("Syncing...\n");
+
+ bbdb_sync_buddy_list ();
+
+ printf ("Done!\n");
+
+ return 0;
+}