Mercurial > pidgin.yaz
view pidgin/plugins/gevolution/gevolution.c @ 27240:75aaadd295ed
Stop the watchers before closing the fds
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Tue, 23 Jun 2009 17:39:00 +0000 |
parents | c952aa75df18 |
children | 9e916fd25f48 |
line wrap: on
line source
/* * Evolution integration plugin for Purple * * Copyright (C) 2003 Christian Hammond. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA * 02111-1301, USA. */ #include "internal.h" #include "pidgin.h" #include "connection.h" #include "debug.h" #include "prefs.h" #include "notify.h" #include "signals.h" #include "util.h" #include "version.h" #include "gtkblist.h" #include "gtkconv.h" #include "gtkplugin.h" #include "gtkutils.h" #include "gevolution.h" #include <libedata-book/Evolution-DataServer-Addressbook.h> #include <libedata-book/e-data-book-factory.h> /* TODO: bonobo is going away eventually, we'll need to find an alternative */ #include <bonobo/bonobo-main.h> #include <glib.h> #define GEVOLUTION_PLUGIN_ID "gtk-x11-gevolution" #define E_DATA_BOOK_FACTORY_OAF_ID \ "OAFIID:GNOME_Evolution_DataServer_BookFactory" enum { COLUMN_AUTOADD, COLUMN_ICON, COLUMN_USERNAME, COLUMN_DATA, NUM_COLUMNS }; static PurpleBlistUiOps *backup_blist_ui_ops = NULL; static PurpleBlistUiOps *blist_ui_ops = NULL; static EBook *book = NULL; static gulong timer = 0; static gulong book_view_tag = 0; static EBookView *book_view = NULL; static void update_ims_from_contact(EContact *contact, const char *name, const char *prpl_id, EContactField field) { GList *ims = e_contact_get(contact, field); GList *l, *l2; if (ims == NULL) return; for (l = purple_connections_get_all(); l != NULL; l = l->next) { PurpleConnection *gc = (PurpleConnection *)l->data; PurpleAccount *account = purple_connection_get_account(gc); char *me; if (strcmp(purple_account_get_protocol_id(account), prpl_id)) continue; if (!purple_account_get_bool(account, "gevo-autoadd", FALSE)) continue; me = g_strdup(purple_normalize(account, purple_account_get_username(account))); for (l2 = ims; l2 != NULL; l2 = l2->next) { if (purple_find_buddy(account, l2->data) != NULL || !strcmp(me, purple_normalize(account, l2->data))) continue; gevo_add_buddy(account, _("Buddies"), l2->data, name); } g_free(me); } g_list_foreach(ims, (GFunc)g_free, NULL); g_list_free(ims); } static void update_buddies_from_contact(EContact *contact) { const char *name; name = e_contact_get_const(contact, E_CONTACT_FULL_NAME); update_ims_from_contact(contact, name, "prpl-aim", E_CONTACT_IM_AIM); update_ims_from_contact(contact, name, "prpl-oscar", E_CONTACT_IM_AIM); update_ims_from_contact(contact, name, "prpl-jabber", E_CONTACT_IM_JABBER); update_ims_from_contact(contact, name, "prpl-yahoo", E_CONTACT_IM_YAHOO); update_ims_from_contact(contact, name, "prpl-msn", E_CONTACT_IM_MSN); update_ims_from_contact(contact, name, "prpl-icq", E_CONTACT_IM_ICQ); update_ims_from_contact(contact, name, "prpl-oscar", E_CONTACT_IM_ICQ); update_ims_from_contact(contact, name, "prpl-novell", E_CONTACT_IM_GROUPWISE); } static void contacts_changed_cb(EBookView *book_view, GList *contacts) { GList *l; if (purple_connections_get_all() == NULL) return; for (l = contacts; l != NULL; l = l->next) { EContact *contact = (EContact *)l->data; update_buddies_from_contact(contact); } } static void request_add_buddy(PurpleAccount *account, const char *username, const char *group, const char *alias) { if (book == NULL) { backup_blist_ui_ops->request_add_buddy(account, username, group, alias); } else { gevo_add_buddy_dialog_show(account, username, group, alias); } } static void got_book_view_cb(EBook *book, EBookStatus status, EBookView *view, gpointer user_data) { book_view_tag = 0; if (status != E_BOOK_ERROR_OK) { purple_debug_error("evolution", "Unable to retrieve book view! :(\n"); return; } book_view = view; g_object_ref(book_view); g_signal_connect(G_OBJECT(book_view), "contacts_changed", G_CALLBACK(contacts_changed_cb), book); g_signal_connect(G_OBJECT(book_view), "contacts_added", G_CALLBACK(contacts_changed_cb), book); e_book_view_start(view); } static void signed_on_cb(PurpleConnection *gc) { EBookQuery *query; gboolean status; GList *contacts; GList *l; if (book == NULL) return; query = e_book_query_any_field_contains(""); status = e_book_get_contacts(book, query, &contacts, NULL); e_book_query_unref(query); if (!status) return; for (l = contacts; l != NULL; l = l->next) { EContact *contact = E_CONTACT(l->data); update_buddies_from_contact(contact); g_object_unref(contact); } g_list_free(contacts); } static void menu_item_activate_cb(PurpleBlistNode *node, gpointer user_data) { PurpleBuddy *buddy = (PurpleBuddy *)node; gevo_associate_buddy_dialog_new(buddy); } static void menu_item_send_mail_activate_cb(PurpleBlistNode *node, gpointer user_data) { PurpleBuddy *buddy = (PurpleBuddy *)node; char *mail = NULL; mail = gevo_get_email_for_buddy(buddy); if (mail != NULL) { char *app = g_find_program_in_path("evolution"); if (app != NULL) { char *command_line = g_strdup_printf("%s mailto:%s", app, mail); char *quoted = g_shell_quote(command_line); g_free(app); g_free(mail); g_spawn_command_line_async(quoted, NULL); g_free(command_line); g_free(quoted); } else { purple_notify_error(NULL, NULL, _("Unable to send email"), _("The evolution executable was not found in the PATH.")); } } else { purple_notify_error(NULL, NULL, _("Unable to send email"), _("An email address was not found for this buddy.")); } } static void blist_node_extended_menu_cb(PurpleBlistNode *node, GList **menu) { PurpleMenuAction *act; PurpleBuddy *buddy; PurpleAccount *account; EContact *contact; char *mail; if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) return; buddy = (PurpleBuddy *)node; account = purple_buddy_get_account(buddy); if (!gevo_prpl_is_supported(account, buddy)) return; contact = gevo_search_buddy_in_contacts(buddy, NULL); if (contact == NULL) { act = purple_menu_action_new(_("Add to Address Book"), PURPLE_CALLBACK(menu_item_activate_cb), NULL, NULL); *menu = g_list_append(*menu, act); } else g_object_unref(contact); mail = gevo_get_email_for_buddy(buddy); if (mail != NULL) { act = purple_menu_action_new(_("Send Email"), PURPLE_CALLBACK(menu_item_send_mail_activate_cb), NULL, NULL); *menu = g_list_append(*menu, act); g_free(mail); } } /* TODO: Something in here leaks 1 reference to a bonobo object! */ static gboolean load_timeout(gpointer data) { PurplePlugin *plugin = (PurplePlugin *)data; EBookQuery *query; timer = 0; /* Maybe this is it? */ if (!gevo_load_addressbook(NULL, &book, NULL)) return FALSE; query = e_book_query_any_field_contains(""); /* Is it this? */ book_view_tag = e_book_async_get_book_view(book, query, NULL, -1, got_book_view_cb, NULL); e_book_query_unref(query); purple_signal_connect(purple_blist_get_handle(), "blist-node-extended-menu", plugin, PURPLE_CALLBACK(blist_node_extended_menu_cb), NULL); return FALSE; } static gboolean plugin_load(PurplePlugin *plugin) { bonobo_activate(); backup_blist_ui_ops = purple_blist_get_ui_ops(); blist_ui_ops = g_memdup(backup_blist_ui_ops, sizeof(PurpleBlistUiOps)); blist_ui_ops->request_add_buddy = request_add_buddy; purple_blist_set_ui_ops(blist_ui_ops); purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin, PURPLE_CALLBACK(signed_on_cb), NULL); timer = g_timeout_add(1, load_timeout, plugin); return TRUE; } static gboolean plugin_unload(PurplePlugin *plugin) { purple_blist_set_ui_ops(backup_blist_ui_ops); g_free(blist_ui_ops); backup_blist_ui_ops = NULL; blist_ui_ops = NULL; if (book_view != NULL) { e_book_view_stop(book_view); g_object_unref(book_view); book_view = NULL; } if (book != NULL) { g_object_unref(book); book = NULL; } return TRUE; } static void plugin_destroy(PurplePlugin *plugin) { bonobo_debug_shutdown(); } static void autoadd_toggled_cb(GtkCellRendererToggle *renderer, gchar *path_str, gpointer data) { PurpleAccount *account; GtkTreeModel *model = (GtkTreeModel *)data; GtkTreeIter iter; gboolean autoadd; gtk_tree_model_get_iter_from_string(model, &iter, path_str); gtk_tree_model_get(model, &iter, COLUMN_DATA, &account, COLUMN_AUTOADD, &autoadd, -1); purple_account_set_bool(account, "gevo-autoadd", !autoadd); gtk_list_store_set(GTK_LIST_STORE(model), &iter, COLUMN_AUTOADD, !autoadd, -1); } static GtkWidget * get_config_frame(PurplePlugin *plugin) { GtkWidget *ret; GtkWidget *vbox; GtkWidget *label; GtkWidget *sw; GtkWidget *treeview; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GdkPixbuf *pixbuf; GtkListStore *model; GList *l; /* Outside container */ ret = gtk_vbox_new(FALSE, 18); gtk_container_set_border_width(GTK_CONTAINER(ret), 12); /* Configuration frame */ vbox = pidgin_make_frame(ret, _("Evolution Integration Configuration")); /* Label */ label = gtk_label_new(_("Select all accounts that buddies should be " "auto-added to.")); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); gtk_widget_show(label); /* Scrolled window */ sw = gtk_scrolled_window_new(0, 0); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0); gtk_widget_set_size_request(sw, 300, 300); gtk_widget_show(sw); /* Create the list model for the treeview. */ model = gtk_list_store_new(NUM_COLUMNS, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); /* Setup the treeview */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); gtk_container_add(GTK_CONTAINER(sw), treeview); gtk_widget_show(treeview); /* Setup the column */ column = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(column, _("Account")); gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1); /* Checkbox */ renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(autoadd_toggled_cb), model); gtk_tree_view_column_pack_start(column, renderer, FALSE); gtk_tree_view_column_add_attribute(column, renderer, "active", COLUMN_AUTOADD); /* Icon */ renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, FALSE); gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", COLUMN_ICON); /* Username */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", COLUMN_USERNAME); /* Populate */ for (l = purple_accounts_get_all(); l != NULL; l = l->next) { PurpleAccount *account = (PurpleAccount *)l->data; GtkTreeIter iter; purple_debug_info("evolution", "Adding account\n"); gtk_list_store_append(model, &iter); pixbuf = pidgin_create_prpl_icon(account, 0.5); if ((pixbuf != NULL) && (!purple_account_is_connected(account))) gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE); gtk_list_store_set(model, &iter, COLUMN_AUTOADD, purple_account_get_bool(account, "gevo-autoadd", FALSE), COLUMN_ICON, pixbuf, COLUMN_USERNAME, purple_account_get_username(account), COLUMN_DATA, account, -1); if (pixbuf != NULL) g_object_unref(G_OBJECT(pixbuf)); } gtk_widget_show_all(ret); return ret; } static PidginPluginUiInfo ui_info = { get_config_frame, /**< get_config_frame */ 0, /**< page_num */ /* Padding */ NULL, NULL, NULL, NULL }; static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_STANDARD, /**< type */ PIDGIN_PLUGIN_TYPE, /**< ui_requirement */ 0, /**< flags */ NULL, /**< dependencies */ PURPLE_PRIORITY_DEFAULT, /**< priority */ GEVOLUTION_PLUGIN_ID, /**< id */ N_("Evolution Integration"), /**< name */ DISPLAY_VERSION, /**< version */ /** summary */ N_("Provides integration with Evolution."), /** description */ N_("Provides integration with Evolution."), "Christian Hammond <chipx86@chipx86.com>", /**< author */ PURPLE_WEBSITE, /**< homepage */ plugin_load, /**< load */ plugin_unload, /**< unload */ plugin_destroy, /**< destroy */ &ui_info, /**< ui_info */ NULL, /**< extra_info */ NULL, NULL, /* Padding */ NULL, NULL, NULL, NULL }; static void init_plugin(PurplePlugin *plugin) { /* TODO: Change to core-remote when possible. */ /* info.dependencies = g_list_append(info.dependencies, "gtk-remote"); */ /* * I'm going to rant a little bit here... * * For some reason, when we init bonobo from inside a plugin, it will * segfault when destroyed. The backtraces are within gmodule somewhere. * There's not much I can do, and I'm not sure where the bug lies. * However, plugins are only destroyed when Purple is shutting down. After * destroying the plugins, purple ends, and anything else is of course * freed. That includes this, if we make the module resident, which * prevents us from being able to actually unload it. * * So, in conclusion, this is an evil hack, but it doesn't harm anything * and it works. */ g_module_make_resident(plugin->handle); if (!bonobo_init_full(NULL, NULL, bonobo_activation_orb_get(), CORBA_OBJECT_NIL, CORBA_OBJECT_NIL)) { purple_debug_error("evolution", "Unable to initialize bonobo.\n"); } } PURPLE_INIT_PLUGIN(gevolution, init_plugin, info)