# HG changeset patch # User Paul Aurich # Date 1243304869 0 # Node ID 08964b4b2fe4e5a399bc0426bb011304c70c5f25 # Parent fe1603ecf5798edd25e42d3a56d4a006518bc7eb Convert the disco browser to a plugin. Refs #556 diff -r fe1603ecf579 -r 08964b4b2fe4 configure.ac --- a/configure.ac Sun May 24 18:39:32 2009 +0000 +++ b/configure.ac Tue May 26 02:27:49 2009 +0000 @@ -2476,6 +2476,7 @@ pidgin/pixmaps/emotes/small/16/Makefile pidgin/plugins/Makefile pidgin/plugins/cap/Makefile + pidgin/plugins/disco/Makefile pidgin/plugins/gestures/Makefile pidgin/plugins/gevolution/Makefile pidgin/plugins/musicmessaging/Makefile diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/Makefile.am --- a/libpurple/Makefile.am Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/Makefile.am Tue May 26 02:27:49 2009 +0000 @@ -47,7 +47,6 @@ core.c \ debug.c \ desktopitem.c \ - disco.c \ eventloop.c \ ft.c \ idle.c \ @@ -108,7 +107,6 @@ dbus-maybe.h \ debug.h \ desktopitem.h \ - disco.h \ eventloop.h \ ft.h \ gaim-compat.h \ @@ -185,7 +183,7 @@ dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \ - connection.h conversation.h core.h disco.h ft.h log.h notify.h prefs.h roomlist.h \ + connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \ savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \ diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/Makefile.mingw --- a/libpurple/Makefile.mingw Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/Makefile.mingw Tue May 26 02:27:49 2009 +0000 @@ -41,7 +41,6 @@ conversation.c \ core.c \ debug.c \ - disco.c \ dnsquery.c \ dnssrv.c \ eventloop.c \ diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/dbus-analyze-functions.py --- a/libpurple/dbus-analyze-functions.py Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/dbus-analyze-functions.py Tue May 26 02:27:49 2009 +0000 @@ -31,13 +31,6 @@ "purple_account_unregister", "purple_connection_new_unregister", - # Similar to all the above: - "purple_disco_list_set_service_close_func", - "purple_disco_list_set_cancel_func", - "purple_disco_list_set_protocol_data", - "purple_disco_list_set_expand_func", - "purple_disco_list_set_register_func", - # These functions are excluded because they involve setting arbitrary # data via pointers for protocols and UIs. This just won't work. "purple_blist_get_ui_data", diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/disco.c --- a/libpurple/disco.c Sun May 24 18:39:32 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -/** - * @file disco.c Service Discovery API - * @ingroup core - */ - -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 "debug.h" - -#include "disco.h" - -/**************************************************************************/ -/* Main structures, members and constants */ -/**************************************************************************/ - -/** - * Represents a list of services for a given connection on a given protocol. - */ -struct _PurpleDiscoList { - PurpleAccount *account; /**< The account this list belongs to. */ - GList *services; /**< The list of services. */ - - gboolean in_progress; - - gpointer ui_data; /**< UI private data. */ - gpointer proto_data; /**< Prpl private data. */ - guint ref; /**< The reference count. */ - - struct { - PurpleDiscoCancelFunc cancel_cb; - PurpleDiscoCloseFunc close_cb; /**< Callback to free the prpl data */ - PurpleDiscoServiceCloseFunc service_close_cb; - PurpleDiscoServiceExpandFunc expand_cb; /**< Expand a service (iteratively - look for things that are - "sub-elements" in the tree */ - PurpleDiscoRegisterFunc register_cb; - } ops; -}; - -/** - * Represents a list of services for a given connection on a given protocol. - */ -struct _PurpleDiscoService { - PurpleDiscoList *list; - gchar *name; /**< The name of the service. */ - gchar *description; /**< The name of the service. */ - - gpointer proto_data; - - gchar *gateway_type; /**< The type of the gateway service. */ - PurpleDiscoServiceType type; /**< The type of service. */ - PurpleDiscoServiceFlags flags; -}; - -static PurpleDiscoUiOps *ops = NULL; - -PurpleDiscoList *purple_disco_list_new(PurpleAccount *account) -{ - PurpleDiscoList *list; - - g_return_val_if_fail(account != NULL, NULL); - - list = g_new0(PurpleDiscoList, 1); - list->account = account; - list->ref = 1; - - if (ops && ops->create) - ops->create(list); - - return list; -} - -PurpleDiscoList *purple_disco_list_ref(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, NULL); - - list->ref++; - purple_debug_misc("disco", "reffing list, ref count now %d\n", list->ref); - - return list; -} - -static void purple_disco_list_service_destroy(PurpleDiscoList *list, PurpleDiscoService *r) -{ - if (list->ops.service_close_cb) - list->ops.service_close_cb(r); - - g_free(r->name); - g_free(r->description); - g_free(r->gateway_type); - g_free(r); -} - -static void purple_disco_list_destroy(PurpleDiscoList *list) -{ - GList *l; - - purple_debug_misc("disco", "destroying list %p\n", list); - - if (ops && ops->destroy) - ops->destroy(list); - - if (list->ops.close_cb) - list->ops.close_cb(list); - - for (l = list->services; l; l = l->next) { - PurpleDiscoService *s = l->data; - purple_disco_list_service_destroy(list, s); - } - g_list_free(list->services); - - g_free(list); -} - -void purple_disco_list_unref(PurpleDiscoList *list) -{ - g_return_if_fail(list != NULL); - g_return_if_fail(list->ref > 0); - - list->ref--; - - purple_debug_misc("disco", "unreffing list, ref count now %d\n", list->ref); - if (list->ref == 0) - purple_disco_list_destroy(list); -} - -void purple_disco_list_service_add(PurpleDiscoList *list, - PurpleDiscoService *service, - PurpleDiscoService *parent) -{ - g_return_if_fail(list != NULL); - g_return_if_fail(service != NULL); - - list->services = g_list_append(list->services, service); - service->list = list; - - if (ops && ops->add_service) - ops->add_service(list, service, parent); -} - -PurpleDiscoService *purple_disco_list_service_new(PurpleDiscoServiceType type, - const gchar *name, const gchar *description, - PurpleDiscoServiceFlags flags, gpointer proto_data) -{ - PurpleDiscoService *s; - - g_return_val_if_fail(name != NULL, NULL); - g_return_val_if_fail(type != PURPLE_DISCO_SERVICE_TYPE_UNSET, NULL); - - s = g_new0(PurpleDiscoService, 1); - s->name = g_strdup(name); - s->type = type; - s->description = g_strdup(description); - s->flags = flags; - s->proto_data = proto_data; - - return s; -} - -PurpleDiscoList *purple_disco_get_list(PurpleConnection *pc) -{ - PurplePlugin *prpl = NULL; - PurplePluginProtocolInfo *prpl_info = NULL; - - g_return_val_if_fail(pc != NULL, NULL); - - prpl = purple_connection_get_prpl(pc); - if (prpl != NULL) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); - - if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_get_list)) - return prpl_info->disco_get_list(pc); - - return NULL; -} - -void purple_disco_cancel_get_list(PurpleDiscoList *list) -{ - PurpleConnection *pc; - - g_return_if_fail(list != NULL); - - pc = purple_account_get_connection(list->account); - - g_return_if_fail(pc != NULL); - - if (list->ops.cancel_cb) - list->ops.cancel_cb(list); - - purple_disco_list_set_in_progress(list, FALSE); -} - -void purple_disco_service_expand(PurpleDiscoService *service) -{ - PurpleDiscoList *list; - PurpleConnection *pc; - - g_return_if_fail(service != NULL); - g_return_if_fail((service->flags & PURPLE_DISCO_BROWSE)); - - list = service->list; - pc = purple_account_get_connection(list->account); - - g_return_if_fail(pc != NULL); - - purple_disco_list_set_in_progress(list, TRUE); - - if (list->ops.expand_cb) - list->ops.expand_cb(list, service); - else - purple_debug_warning("disco", "Cannot expand %s for account %s, " - "protocol did not provide expand op.\n", - service->name, - purple_account_get_username(list->account)); -} - -void purple_disco_service_register(PurpleDiscoService *service) -{ - PurpleDiscoList *list; - PurpleConnection *pc; - - g_return_if_fail(service != NULL); - - list = service->list; - pc = purple_account_get_connection(list->account); - - g_return_if_fail(pc != NULL); - - if (list->ops.register_cb) - list->ops.register_cb(pc, service); - else - purple_debug_warning("disco", "Cannot register to %s for account %s, " - "protocol did not provide register op.\n", - service->name, - purple_account_get_username(list->account)); -} - -const gchar *purple_disco_service_get_name(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, NULL); - - return service->name; -} - -const gchar *purple_disco_service_get_description(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, NULL); - - return service->description; -} - -gpointer -purple_disco_service_get_protocol_data(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, NULL); - - return service->proto_data; -} - -PurpleDiscoServiceType -purple_disco_service_get_type(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, PURPLE_DISCO_SERVICE_TYPE_UNSET); - - return service->type; -} - -PurpleDiscoServiceFlags -purple_disco_service_get_flags(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, PURPLE_DISCO_NONE); - - return service->flags; -} - -void purple_disco_service_set_gateway_type(PurpleDiscoService *service, const gchar *type) -{ - g_return_if_fail(service != NULL); - - g_free(service->gateway_type); - service->gateway_type = g_strdup(type); -} - -const gchar *purple_disco_service_get_gateway_type(PurpleDiscoService *service) -{ - g_return_val_if_fail(service != NULL, NULL); - - return service->gateway_type; -} - -PurpleAccount* purple_disco_list_get_account(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, NULL); - - return list->account; -} - -GList* purple_disco_list_get_services(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, NULL); - - return list->services; -} - -void purple_disco_list_set_ui_data(PurpleDiscoList *list, gpointer ui_data) -{ - g_return_if_fail(list != NULL); - - list->ui_data = ui_data; -} - -gpointer purple_disco_list_get_ui_data(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, NULL); - - return list->ui_data; -} - -void purple_disco_list_set_in_progress(PurpleDiscoList *list, - gboolean in_progress) -{ - g_return_if_fail(list != NULL); - - list->in_progress = in_progress; - - if (ops && ops->in_progress) - ops->in_progress(list, in_progress); -} - -gboolean purple_disco_list_get_in_progress(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, FALSE); - - return list->in_progress; -} - -void purple_disco_list_set_protocol_data(PurpleDiscoList *list, - gpointer proto_data, - PurpleDiscoCloseFunc cb) -{ - g_return_if_fail(list != NULL); - - list->proto_data = proto_data; - list->ops.close_cb = cb; -} - -gpointer purple_disco_list_get_protocol_data(PurpleDiscoList *list) -{ - g_return_val_if_fail(list != NULL, NULL); - - return list->proto_data; -} - -void purple_disco_list_set_service_close_func(PurpleDiscoList *list, - PurpleDiscoServiceCloseFunc cb) -{ - g_return_if_fail(list != NULL); - - list->ops.service_close_cb = cb; -} - -void purple_disco_list_set_cancel_func(PurpleDiscoList *list, PurpleDiscoCancelFunc cb) -{ - g_return_if_fail(list != NULL); - - list->ops.cancel_cb = cb; -} - -void purple_disco_list_set_expand_func(PurpleDiscoList *list, PurpleDiscoServiceExpandFunc cb) -{ - g_return_if_fail(list != NULL); - - list->ops.expand_cb = cb; -} - -void purple_disco_list_set_register_func(PurpleDiscoList *list, PurpleDiscoRegisterFunc cb) -{ - g_return_if_fail(list != NULL); - - list->ops.register_cb = cb; -} - -void purple_disco_set_ui_ops(PurpleDiscoUiOps *ui_ops) -{ - ops = ui_ops; -} - -PurpleDiscoUiOps *purple_disco_get_ui_ops(void) -{ - return ops; -} diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/disco.h --- a/libpurple/disco.h Sun May 24 18:39:32 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,414 +0,0 @@ -/** - * @file disco.h Service Discovery API - * @ingroup core - */ - -/* purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - */ - -#ifndef _PURPLE_DISCO_H_ -#define _PURPLE_DISCO_H_ - - -typedef struct _PurpleDiscoList PurpleDiscoList; -typedef struct _PurpleDiscoService PurpleDiscoService; -typedef struct _PurpleDiscoUiOps PurpleDiscoUiOps; - -#include "account.h" -#include "connection.h" - -/** - * A prpl callback called to tell the prpl to cancel creating the list. - * The prpl must implement this and must not add services to the - * PurpleDiscoList after this is called. - * - * @param list The disco list. - */ -typedef void (*PurpleDiscoCancelFunc)(PurpleDiscoList *list); - -/** - * A prpl callback called to tell the prpl to tell it to destroy - * prpl-specific data relating to this PurpleDiscoList (which will be - * destroyed imminently). - * - * @param list The disco list. - */ -typedef void (*PurpleDiscoCloseFunc)(PurpleDiscoList *list); -typedef void (*PurpleDiscoServiceCloseFunc)(PurpleDiscoService *service); - -typedef void (*PurpleDiscoServiceExpandFunc)(PurpleDiscoList *list, - PurpleDiscoService *service); - -/** - * A prpl callback called to initiate registration with the specificed - * service. - * - * @param pc The connection. - * @param service The service to which to register. - * - */ -typedef void (*PurpleDiscoRegisterFunc)(PurpleConnection *pc, - PurpleDiscoService *service); - -/** - * The types of services. - */ -typedef enum -{ - PURPLE_DISCO_SERVICE_TYPE_UNSET, - /** - * A registerable gateway to another protocol. An example would be - * XMPP legacy transports. - */ - PURPLE_DISCO_SERVICE_TYPE_GATEWAY, - - /** - * A directory (e.g. allows the user to search for other users). - */ - PURPLE_DISCO_SERVICE_TYPE_DIRECTORY, - - /** - * A chat (multi-user conversation). - */ - PURPLE_DISCO_SERVICE_TYPE_CHAT, - - /** - * Something else. Do we need more categories? - */ - PURPLE_DISCO_SERVICE_TYPE_OTHER -} PurpleDiscoServiceType; - -/** - * The flags of services. - */ -typedef enum -{ - PURPLE_DISCO_NONE = 0x0000, - PURPLE_DISCO_ADD = 0x0001, /**< Supports an 'add' operation */ - PURPLE_DISCO_BROWSE = 0x0002, /**< Supports browsing */ - PURPLE_DISCO_REGISTER = 0x0004 /**< Supports a 'register' operation */ -} PurpleDiscoServiceFlags; - -struct _PurpleDiscoUiOps { - /** Ask the UI to display a dialog for the specified account. - */ - void (*dialog_show_with_account)(PurpleAccount* account); - void (*create)(PurpleDiscoList *list); /**< Sets UI-specific data on a disco list */ - void (*destroy)(PurpleDiscoList *list); /**< Free UI-specific data on the disco list */ - void (*add_service)(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent); /**< Add service to dialog */ - void (*in_progress)(PurpleDiscoList *list, gboolean in_progress); /**< Set progress to dialog */ - - /* Padding */ - void (*_purple_reserved1)(void); - void (*_purple_reserved2)(void); - void (*_purple_reserved3)(void); - void (*_purple_reserved4)(void); -}; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Returns a newly created service discovery object. - * - * It has an initial reference count of 1. - * - * @param account The account that's listing rooms. - * @return The new service discovery list handle. - */ -PurpleDiscoList *purple_disco_list_new(PurpleAccount *account); - -/** - * Increases the reference count on the service discovery list. - * - * @param list The disco list to ref. - */ -PurpleDiscoList *purple_disco_list_ref(PurpleDiscoList *list); - -/** - * Decreases the reference count on the service discovery list. - * - * The room list will be destroyed when this reaches 0. - * - * @param list The room list object to unref and possibly - * destroy. - */ -void purple_disco_list_unref(PurpleDiscoList *list); - -/** - * Instructs the prpl to start fetching the list. - * - */ -PurpleDiscoList *purple_disco_get_list(PurpleConnection *gc); - -/** - * Tells the prpl to stop fetching the list. - * If this is possible and done, the prpl will - * call set_in_progress with @c FALSE and possibly - * unref the list if it took a reference. - * - * @param list The service list to cancel a get_list on. - */ -void purple_disco_cancel_get_list(PurpleDiscoList *list); - -/** - * Create new service object - */ -PurpleDiscoService * -purple_disco_list_service_new(PurpleDiscoServiceType type, const gchar *name, - const gchar *description, - PurpleDiscoServiceFlags flags, - gpointer proto_data); - -/** - * Add service to list - */ -void purple_disco_list_service_add(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent); - -/** - * Expand a (browsable) service. The UI should call this in order to - * iteratively browse the children of this service. The service must - * have the PURPLE_DISCO_BROWSE flag set. - * - * You probably don't want to call this if the service already has children. - * - * @param service The browsable disco service. - * - * @since TODO - */ -void purple_disco_service_expand(PurpleDiscoService *service); - -/** - * Register service - * @param service The service that will be registered - */ -void purple_disco_service_register(PurpleDiscoService *service); - -/** - * Returns a service's name. - * - * @param service The service. - * @return The name. - * - * @since TODO - */ -const gchar *purple_disco_service_get_name(PurpleDiscoService *service); - -/** - * Return a service's description. - * - * @param service The service. - * @return The description. - * - * @since TODO - */ -const gchar* purple_disco_service_get_description(PurpleDiscoService *service); - -/** - * Returns the service's protocol-specific data. - * - * This should only be called from the associated prpl. - * - * @param service The disco service. - * @return The protocol data. - * - * @since TODO - */ -gpointer purple_disco_service_get_protocol_data(PurpleDiscoService *service); - -/** - * Return a service's type. - * - * @param service The service. - * @return The type. - * - * @since TODO - */ -PurpleDiscoServiceType purple_disco_service_get_type(PurpleDiscoService *service); - -/** - * Return a service's flags. - * - * @param service The service. - * @return The flags. - * - * @since TODO - */ -PurpleDiscoServiceFlags purple_disco_service_get_flags(PurpleDiscoService *service); - -/** - * Set the gateway type for a gateway service. The gateway type is a string - * that represents a canonical name of the protocol to which this service is - * a gateway. For example, for an XMPP legacy transport to AIM, this would - * be "aim". - * - * These strings should conform to the names of the libpurple prpls where - * possible (so a UI can easily map types to icons) and, as a backup, the - * XMPP registry list of gateways at - * http://xmpp.org/registrar/disco-categories.html#gateway. - */ -void purple_disco_service_set_gateway_type(PurpleDiscoService *service, - const gchar *type); - -/** - * Get the gateway type for a gateway service. - * - * @param service The service. - * @returns The gateway type or NULL if none was set or service is not - * a gateway. - * - * @see purple_disco_service_set_gateway_type(). - */ -const gchar *purple_disco_service_get_gateway_type(PurpleDiscoService *service); - -/** - * Get the account associated with a service list. - * - * @param list The service list. - * @return The account - * - * @since TODO - */ -PurpleAccount* purple_disco_list_get_account(PurpleDiscoList *list); - -/** - * Get a list of the services associated with this service list. - * - * @param dl The serivce list. - * @returns A list of PurpleDiscoService items. - * - * @since TODO - */ -GList* purple_disco_list_get_services(PurpleDiscoList *dl); - -/** - * Set the service list's UI data. - * - * @param list The service list. - * @param data The data. - * - * @see purple_disco_list_get_ui_data() - * @since TODO - */ -void purple_disco_list_set_ui_data(PurpleDiscoList *list, gpointer data); - -/** - * Get the service list's UI data. - * - * @param list The service list. - * @return The data. - * - * @see purple_disco_list_set_ui_data() - * @since TODO - */ -gpointer purple_disco_list_get_ui_data(PurpleDiscoList *list); - -/** - * Set the "in progress" state of the Service Discovery. - * - * The UI is encouraged to somehow hint to the user - * whether or not we're busy downloading a service list or not. - * - * @param list The service list. - * @param in_progress We're downloading it, or we're not. - * - * @see purple_disco_list_get_in_progress() - * @since TODO - */ -void purple_disco_list_set_in_progress(PurpleDiscoList *list, gboolean in_progress); - -/** - * Gets the "in progress" state of the Service Discovery. - * - * The UI is encouraged to somehow hint to the user - * whether or not we're busy downloading a service list or not. - * - * @param list The service list. - * @return True if we're downloading it, or false if we're not. - * - * @see purple_disco_list_set_in_progress() - * @since TODO - */ -gboolean purple_disco_list_get_in_progress(PurpleDiscoList *list); - -/** - * Sets the disco list's protocol-specific data. - * - * This should only be called from the associated prpl. - * - * @param list The disco list. - * @param data The protocol data. - * @param close_cb The function to be called when destroying the disco list to - * free the protocol data. May be NULL if the data does not need - * to be freed. - * - * @see purple_disco_list_get_protocol_data() - * @since TODO - */ -void purple_disco_list_set_protocol_data(PurpleDiscoList *list, gpointer data, - PurpleDiscoCloseFunc cb); - -/** - * Returns the disco list's protocol-specific data. - * - * This should only be called from the associated prpl. - * - * @param list The disco list. - * @return The protocol data. - * - * @see purple_disco_list_set_protocol_data() - * @since TODO - */ -gpointer purple_disco_list_get_protocol_data(PurpleDiscoList *list); - -void purple_disco_list_set_service_close_func(PurpleDiscoList *list, - PurpleDiscoServiceCloseFunc cb); -void purple_disco_list_set_cancel_func(PurpleDiscoList *list, - PurpleDiscoCancelFunc cb); -void purple_disco_list_set_expand_func(PurpleDiscoList *list, - PurpleDiscoServiceExpandFunc cb); -void purple_disco_list_set_register_func(PurpleDiscoList *list, - PurpleDiscoRegisterFunc cb); - -/** - * Sets the UI operations structure to be used in all purple service discovery. - * - * @param ops The UI operations structure. - * - * @since TODO - */ -void purple_disco_set_ui_ops(PurpleDiscoUiOps *ui_ops); - -/** - * Returns the service discovery UI operations structure. - * - * @return A filled-out PurpleDiscoUiOps structure. - * - * @since TODO - */ -PurpleDiscoUiOps *purple_disco_get_ui_ops(void); - -#ifdef __cplusplus -} -#endif - -#endif /* _PURPLE_DISCO_H_ */ diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/protocols/jabber/disco.c Tue May 26 02:27:49 2009 +0000 @@ -51,18 +51,6 @@ xmlnode_set_attrib(feature, "var", x); \ } -struct jabber_disco_list_data { - JabberStream *js; /* TODO: Needed? */ - PurpleDiscoList *list; - char *server; - int fetch_count; -}; - -struct jabber_disco_service_data { - char *jid; - char *node; -}; - static void jabber_disco_bytestream_server_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, @@ -403,12 +391,6 @@ } -struct _disco_data { - struct jabber_disco_list_data *list_data; - PurpleDiscoService *parent; - char *node; -}; - static void jabber_disco_server_info_result_cb(JabberStream *js, const char *from, JabberIqType type, const char *id, @@ -571,530 +553,3 @@ jabber_iq_send(iq); } -static void -jabber_disco_list_data_destroy(struct jabber_disco_list_data *data) -{ - g_free(data->server); - g_free(data); -} - -static void -disco_proto_data_destroy_cb(PurpleDiscoList *list) -{ - struct jabber_disco_list_data *data; - - data = purple_disco_list_get_protocol_data(list); - jabber_disco_list_data_destroy(data); -} - -static PurpleDiscoServiceType -jabber_disco_category_from_string(const gchar *str) -{ - if (!g_ascii_strcasecmp(str, "gateway")) - return PURPLE_DISCO_SERVICE_TYPE_GATEWAY; - else if (!g_ascii_strcasecmp(str, "directory")) - return PURPLE_DISCO_SERVICE_TYPE_DIRECTORY; - else if (!g_ascii_strcasecmp(str, "conference")) - return PURPLE_DISCO_SERVICE_TYPE_CHAT; - - return PURPLE_DISCO_SERVICE_TYPE_OTHER; -} - -static const struct { - const char *from; - const char *to; -} disco_type_mappings[] = { - { "gadu-gadu", "gg" }, - { "sametime", "meanwhile" }, - { "myspaceim", "myspace" }, - { "xmpp", "jabber" }, - { NULL, NULL } -}; - -static const gchar * -jabber_disco_type_from_string(const gchar *str) -{ - int i = 0; - - g_return_val_if_fail(str != NULL, ""); - - for ( ; disco_type_mappings[i].from; ++i) { - if (!strcasecmp(str, disco_type_mappings[i].from)) - return disco_type_mappings[i].to; - } - - /* fallback to the string itself */ - return str; -} - -static void -jabber_disco_service_info_cb(JabberStream *js, const char *from, - JabberIqType type, const char *id, - xmlnode *packet, gpointer data); - -static void -jabber_disco_service_items_cb(JabberStream *js, const char *who, const char *node, - GSList *items, gpointer data) -{ - GSList *l; - struct _disco_data *disco_data; - struct jabber_disco_list_data *list_data; - PurpleDiscoList *list; - PurpleDiscoService *parent; - PurpleDiscoServiceType parent_type; - const char *parent_node; - - disco_data = data; - list_data = disco_data->list_data; - list = list_data->list; - - --list_data->fetch_count; - - if (list_data->list == NULL) { - if (list_data->fetch_count == 0) - jabber_disco_list_data_destroy(list_data); - - return; - } - - if (items == NULL) { - if (list_data->fetch_count == 0) - purple_disco_list_set_in_progress(list, FALSE); - - purple_disco_list_unref(list); - return; - } - - parent = disco_data->parent; - parent_type = purple_disco_service_get_type(parent); - parent_node = disco_data->node; - - for (l = items; l; l = l->next) { - JabberDiscoItem *item = l->data; - - if (parent_type & PURPLE_DISCO_SERVICE_TYPE_CHAT) { - /* A chat server's items are chats. I promise. */ - PurpleDiscoService *service; - struct jabber_disco_service_data *service_data; - JabberID *jid = jabber_id_new(item->jid); - - if (jid) { - service_data = g_new0(struct jabber_disco_service_data, 1); - service_data->jid = g_strdup(item->jid); - - service = purple_disco_list_service_new(PURPLE_DISCO_SERVICE_TYPE_CHAT, - jid->node, item->name, PURPLE_DISCO_ADD, service_data); - - purple_disco_list_service_add(list, service, parent); - - jabber_id_free(jid); - } - } else { - JabberIq *iq; - struct _disco_data *req_data; - char *full_node; - - if (parent_node && !item->node) - continue; - - if (parent_node) - full_node = g_strconcat(parent_node, "/", item->node, NULL); - else - full_node = g_strdup(item->node); - - req_data = g_new0(struct _disco_data, 1); - req_data->list_data = list_data; - req_data->parent = parent; - req_data->node = full_node; - - ++list_data->fetch_count; - purple_disco_list_ref(list); - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info"); - xmlnode_set_attrib(iq->node, "to", item->jid); - if (full_node) - xmlnode_set_attrib(xmlnode_get_child(iq->node, "query"), - "node", full_node); - jabber_iq_set_callback(iq, jabber_disco_service_info_cb, req_data); - - jabber_iq_send(iq); - } - } - - g_slist_foreach(items, (GFunc)jabber_disco_item_free, NULL); - g_slist_free(items); - - if (list_data->fetch_count == 0) - purple_disco_list_set_in_progress(list, FALSE); - - purple_disco_list_unref(list); - - g_free(disco_data->node); - g_free(disco_data); -} - -static void -jabber_disco_service_info_cb(JabberStream *js, const char *from, - JabberIqType type, const char *id, - xmlnode *packet, gpointer data) -{ - struct _disco_data *disco_data; - struct jabber_disco_list_data *list_data; - xmlnode *query, *identity, *child; - const char *name; - char *node; - - PurpleDiscoList *list; - PurpleDiscoService *parent, *service; - PurpleDiscoServiceType service_type; - PurpleDiscoServiceFlags flags; - struct jabber_disco_service_data *service_data; - - disco_data = data; - list_data = disco_data->list_data; - list = list_data->list; - parent = disco_data->parent; - - node = disco_data->node; - disco_data->node = NULL; - g_free(disco_data); - - --list_data->fetch_count; - - if (!purple_disco_list_get_in_progress(list)) { - purple_disco_list_unref(list); - return; - } - - if (!from || type == JABBER_IQ_ERROR - || (!(query = xmlnode_get_child(packet, "query"))) - || (!(identity = xmlnode_get_child(query, "identity")))) { - if (list_data->fetch_count == 0) - purple_disco_list_set_in_progress(list, FALSE); - - purple_disco_list_unref(list); - return; - } - - service_type = jabber_disco_category_from_string( - xmlnode_get_attrib(identity, "category")); - - /* Default to allowing things to be add-able */ - flags = PURPLE_DISCO_ADD; - - for (child = xmlnode_get_child(query, "feature"); child; - child = xmlnode_get_next_twin(child)) { - const char *var; - - if (!(var = xmlnode_get_attrib(child, "var"))) - continue; - - if (g_str_equal(var, "jabber:iq:register")) - flags |= PURPLE_DISCO_REGISTER; - - if (g_str_equal(var, "http://jabber.org/protocol/disco#items")) - flags |= PURPLE_DISCO_BROWSE; - - if (g_str_equal(var, "http://jabber.org/protocol/muc")) { - flags |= PURPLE_DISCO_BROWSE; - service_type = PURPLE_DISCO_SERVICE_TYPE_CHAT; - } - } - - if (node) - name = node; - else - name = from; - - service_data = g_new0(struct jabber_disco_service_data, 1); - service_data->jid = g_strdup(from); - service_data->node = node; - - service = purple_disco_list_service_new(service_type, name, - xmlnode_get_attrib(identity, "name"), flags, service_data); - - if (service_type == PURPLE_DISCO_SERVICE_TYPE_GATEWAY) - purple_disco_service_set_gateway_type(service, - jabber_disco_type_from_string(xmlnode_get_attrib(identity, - "type"))); - - purple_disco_list_service_add(list, service, parent); - - if (list_data->fetch_count == 0) - purple_disco_list_set_in_progress(list, FALSE); - - purple_disco_list_unref(list); -} - -static void -jabber_disco_server_items_cb(JabberStream *js, const char *from, - JabberIqType type, const char *id, - xmlnode *packet, gpointer data) -{ - struct jabber_disco_list_data *list_data; - xmlnode *query, *child; - gboolean has_items = FALSE; - - list_data = data; - --list_data->fetch_count; - - if (!from || type == JABBER_IQ_ERROR || - !purple_disco_list_get_in_progress(list_data->list)) { - purple_disco_list_unref(list_data->list); - return; - } - - query = xmlnode_get_child(packet, "query"); - - for(child = xmlnode_get_child(query, "item"); child; - child = xmlnode_get_next_twin(child)) { - JabberIq *iq; - const char *jid; - struct _disco_data *disco_data; - - if(!(jid = xmlnode_get_attrib(child, "jid"))) - continue; - - disco_data = g_new0(struct _disco_data, 1); - disco_data->list_data = list_data; - - has_items = TRUE; - ++list_data->fetch_count; - purple_disco_list_ref(list_data->list); - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info"); - xmlnode_set_attrib(iq->node, "to", jid); - jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data); - - jabber_iq_send(iq); - } - - if (!has_items) - purple_disco_list_set_in_progress(list_data->list, FALSE); - - purple_disco_list_unref(list_data->list); -} - -static void -jabber_disco_server_info_cb(JabberStream *js, const char *who, JabberCapabilities caps, gpointer data) -{ - struct jabber_disco_list_data *list_data; - - list_data = data; - --list_data->fetch_count; - - if (!list_data->list) { - purple_disco_list_unref(list_data->list); - - if (list_data->fetch_count == 0) - jabber_disco_list_data_destroy(list_data); - - return; - } - - if (caps & JABBER_CAP_ITEMS) { - JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); - xmlnode_set_attrib(iq->node, "to", who); - jabber_iq_set_callback(iq, jabber_disco_server_items_cb, list_data); - - ++list_data->fetch_count; - purple_disco_list_ref(list_data->list); - - jabber_iq_send(iq); - } else { - purple_notify_error(NULL, _("Error"), _("Server doesn't support service discovery"), NULL); - purple_disco_list_set_in_progress(list_data->list, FALSE); - } - - purple_disco_list_unref(list_data->list); -} - -static void discolist_cancel_cb(struct jabber_disco_list_data *list_data, const char *server) -{ - purple_disco_list_set_in_progress(list_data->list, FALSE); - purple_disco_list_unref(list_data->list); -} - -static void discolist_ok_cb(struct jabber_disco_list_data *list_data, const char *server) -{ - JabberStream *js; - - js = list_data->js; - - if (!server || !*server) { - purple_notify_error(js->gc, _("Invalid Server"), _("Invalid Server"), NULL); - - purple_disco_list_set_in_progress(list_data->list, FALSE); - purple_disco_list_unref(list_data->list); - return; - } - - list_data->server = g_strdup(server); - if (js->last_disco_server) - g_free(js->last_disco_server); - js->last_disco_server = g_strdup(server); - - purple_disco_list_set_in_progress(list_data->list, TRUE); - - ++list_data->fetch_count; - jabber_disco_info_do(js, list_data->server, jabber_disco_server_info_cb, list_data); -} - -static void -jabber_disco_service_close(PurpleDiscoService *service) -{ - struct jabber_disco_service_data *data; - - data = purple_disco_service_get_protocol_data(service); - g_free(data->jid); - g_free(data->node); - g_free(data); -} - -#if 0 -static void -jabber_disco_cancel(PurpleDiscoList *list) -{ - /* This space intentionally letft blank */ -} -#endif - -static void -jabber_disco_list_expand(PurpleDiscoList *list, PurpleDiscoService *service) -{ - PurpleAccount *account; - PurpleConnection *pc; - JabberStream *js; - struct jabber_disco_list_data *list_data; - struct jabber_disco_service_data *service_data; - struct _disco_data *disco_data; - - account = purple_disco_list_get_account(list); - pc = purple_account_get_connection(account); - js = purple_connection_get_protocol_data(pc); - - list_data = purple_disco_list_get_protocol_data(list); - - ++list_data->fetch_count; - purple_disco_list_ref(list); - disco_data = g_new0(struct _disco_data, 1); - disco_data->list_data = list_data; - disco_data->parent = service; - - service_data = purple_disco_service_get_protocol_data(service); - - jabber_disco_items_do(js, service_data->jid, service_data->node, - jabber_disco_service_items_cb, disco_data); -} - -static void -jabber_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service) -{ - JabberStream *js = purple_connection_get_protocol_data(gc); - - jabber_register_gateway(js, purple_disco_service_get_name(service)); -} - - -PurpleDiscoList * -jabber_disco_get_list(PurpleConnection *gc) -{ - PurpleAccount *account; - PurpleDiscoList *list; - JabberStream *js; - struct jabber_disco_list_data *disco_list_data; - - account = purple_connection_get_account(gc); - js = purple_connection_get_protocol_data(gc); - - /* We start with a ref */ - list = purple_disco_list_new(account); - - disco_list_data = g_new0(struct jabber_disco_list_data, 1); - disco_list_data->list = list; - disco_list_data->js = js; - purple_disco_list_set_protocol_data(list, disco_list_data, - disco_proto_data_destroy_cb); - purple_disco_list_set_service_close_func(list, jabber_disco_service_close); -#if 0 - purple_disco_list_set_cancel_func(list, jabber_disco_cancel); -#endif - purple_disco_list_set_expand_func(list, jabber_disco_list_expand); - purple_disco_list_set_register_func(list, jabber_disco_service_register); - - purple_request_input(gc, _("Server name request"), _("Enter an XMPP Server"), - _("Select an XMPP server to query"), - js->last_disco_server ? js->last_disco_server : js->user->domain, - FALSE, FALSE, NULL, - _("Find Services"), PURPLE_CALLBACK(discolist_ok_cb), - _("Cancel"), PURPLE_CALLBACK(discolist_cancel_cb), - account, NULL, NULL, disco_list_data); - - return list; -} - -static void -jabber_disco_items_cb(JabberStream *js, const char *from, JabberIqType type, - const char *id, xmlnode *packet, gpointer data) -{ - struct _jabber_disco_items_cb_data *jdicd; - xmlnode *query, *child; - const char *node = NULL; - GSList *items = NULL; - - jdicd = data; - - query = xmlnode_get_child(packet, "query"); - - if (query) - node = xmlnode_get_attrib(query, "node"); - - if (!from || !query || type == JABBER_IQ_ERROR) { - jdicd->callback(js, from, node, NULL, jdicd->data); - g_free(jdicd); - return; - } - - for (child = xmlnode_get_child(query, "item"); child; - child = xmlnode_get_next_twin(child)) { - JabberDiscoItem *item; - - item = g_new0(JabberDiscoItem, 1); - item->jid = g_strdup(xmlnode_get_attrib(child, "jid")); - item->node = g_strdup(xmlnode_get_attrib(child, "node")); - item->name = g_strdup(xmlnode_get_attrib(child, "name")); - - items = g_slist_prepend(items, item); - } - - items = g_slist_reverse(items); - jdicd->callback(js, from, node, items, jdicd->data); - g_free(jdicd); -} - -void jabber_disco_items_do(JabberStream *js, const char *who, const char *node, - JabberDiscoItemsCallback *callback, gpointer data) -{ - struct _jabber_disco_items_cb_data *jdicd; - JabberIq *iq; - - jdicd = g_new0(struct _jabber_disco_items_cb_data, 1); - jdicd->data = data; - jdicd->callback = callback; - - iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items"); - if (node) - xmlnode_set_attrib(xmlnode_get_child(iq->node, "query"), "node", node); - - xmlnode_set_attrib(iq->node, "to", who); - - jabber_iq_set_callback(iq, jabber_disco_items_cb, jdicd); - jabber_iq_send(iq); -} - -void jabber_disco_item_free(JabberDiscoItem *item) -{ - g_free((char *)item->jid); - g_free((char *)item->node); - g_free((char *)item->name); - g_free(item); -} diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/protocols/jabber/disco.h --- a/libpurple/protocols/jabber/disco.h Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/protocols/jabber/disco.h Tue May 26 02:27:49 2009 +0000 @@ -46,10 +46,4 @@ void jabber_disco_info_do(JabberStream *js, const char *who, JabberDiscoInfoCallback *callback, gpointer data); -PurpleDiscoList *jabber_disco_get_list(PurpleConnection *gc); - -void jabber_disco_items_do(JabberStream *js, const char *jid, const char *node, - JabberDiscoItemsCallback *callback, gpointer data); -void jabber_disco_item_free(JabberDiscoItem *); - #endif /* PURPLE_JABBER_DISCO_H_ */ diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue May 26 02:27:49 2009 +0000 @@ -1554,7 +1554,6 @@ g_free(js->old_uri); g_free(js->old_track); g_free(js->expected_rspauth); - g_free(js->last_disco_server); if (js->keepalive_timeout != -1) purple_timeout_remove(js->keepalive_timeout); diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue May 26 02:27:49 2009 +0000 @@ -269,12 +269,6 @@ int stun_port; PurpleDnsQueryData *stun_query; /* later add stuff to handle TURN relays... */ - - /** - * The last server the user disco'd (or NULL) via the server discovery - * API. - */ - char *last_disco_server; }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *namespace); diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue May 26 02:27:49 2009 +0000 @@ -123,7 +123,6 @@ NULL, /* get_account_text_table */ jabber_initiate_media, /* initiate_media */ jabber_get_media_caps, /* get_media_caps */ - jabber_disco_get_list, /* disco_get_list */ }; static gboolean load_plugin(PurplePlugin *plugin) diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/prpl.h --- a/libpurple/prpl.h Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/prpl.h Tue May 26 02:27:49 2009 +0000 @@ -70,7 +70,6 @@ #include "proxy.h" #include "plugin.h" #include "roomlist.h" -#include "disco.h" #include "status.h" #include "whiteboard.h" @@ -482,11 +481,6 @@ */ PurpleMediaCaps (*get_media_caps)(PurpleAccount *account, const char *who); - - /** - * Service discovery prpl callbacks - */ - PurpleDiscoList *(*disco_get_list)(PurpleConnection *gc); }; #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \ diff -r fe1603ecf579 -r 08964b4b2fe4 libpurple/purple.h.in --- a/libpurple/purple.h.in Sun May 24 18:39:32 2009 +0000 +++ b/libpurple/purple.h.in Tue May 26 02:27:49 2009 +0000 @@ -79,7 +79,6 @@ #include #include #include -#include #include #include #include diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/Makefile.am --- a/pidgin/Makefile.am Sun May 24 18:39:32 2009 +0000 +++ b/pidgin/Makefile.am Tue May 26 02:27:49 2009 +0000 @@ -90,7 +90,6 @@ gtkconv.c \ gtkdebug.c \ gtkdialogs.c \ - gtkdisco.c \ gtkdnd-hints.c \ gtkdocklet.c \ gtkdocklet-x11.c \ @@ -148,7 +147,6 @@ gtkconvwin.h \ gtkdebug.h \ gtkdialogs.h \ - gtkdisco.h \ gtkdnd-hints.h \ gtkdocklet.h \ gtkeventloop.h \ diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/Makefile.mingw --- a/pidgin/Makefile.mingw Sun May 24 18:39:32 2009 +0000 +++ b/pidgin/Makefile.mingw Tue May 26 02:27:49 2009 +0000 @@ -65,7 +65,6 @@ gtkconv.c \ gtkdebug.c \ gtkdialogs.c \ - gtkdisco.c \ gtkdnd-hints.c \ gtkdocklet.c \ gtkeventloop.c \ diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sun May 24 18:39:32 2009 +0000 +++ b/pidgin/gtkblist.c Tue May 26 02:27:49 2009 +0000 @@ -49,7 +49,6 @@ #include "gtkconv.h" #include "gtkdebug.h" #include "gtkdialogs.h" -#include "gtkdisco.h" #include "gtkft.h" #include "gtklog.h" #include "gtkmenutray.h" @@ -3406,7 +3405,6 @@ { N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "", NULL }, { "/Tools/sep2", NULL, NULL, 0, "", NULL }, { N_("/Tools/_File Transfers"), "T", pidgin_xfer_dialog_show, 0, "", PIDGIN_STOCK_TOOLBAR_TRANSFER }, - { N_("/Tools/Service _Discovery"), NULL, pidgin_disco_dialog_show, 0, "", NULL }, { N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "", NULL }, { N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 3, "", NULL }, { "/Tools/sep3", NULL, NULL, 0, "", NULL }, @@ -4290,9 +4288,6 @@ widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List")); gtk_widget_set_sensitive(widget, pidgin_roomlist_is_showable()); - - widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Service Discovery")); - gtk_widget_set_sensitive(widget, pidgin_disco_is_showable()); } static void diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/gtkdisco.c --- a/pidgin/gtkdisco.c Sun May 24 18:39:32 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,582 +0,0 @@ -/** - * @file gtkdisco.c GTK+ Service Discovery UI - * @ingroup pidgin - */ - -/* pidgin - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 "gtkutils.h" -#include "debug.h" -#include "disco.h" - -#include "gtkdisco.h" - -typedef struct _PidginDiscoDialog { - GtkWidget *window; - GtkWidget *account_widget; - - GtkWidget *sw; - GtkWidget *progress; - - GtkWidget *stop_button; - GtkWidget *list_button; - GtkWidget *register_button; - GtkWidget *add_button; - GtkWidget *close_button; - - PurpleAccount *account; - PurpleDiscoList *discolist; -} PidginDiscoDialog; - -typedef struct _PidginDiscoList { - PidginDiscoDialog *dialog; - GtkTreeStore *model; - GtkWidget *tree; - GHashTable *services; -} PidginDiscoList; - -struct _menu_cb_info { - PurpleDiscoList *list; - PurpleDiscoService *service; -}; - -enum { - PIXBUF_COLUMN = 0, - NAME_COLUMN, - DESCRIPTION_COLUMN, - SERVICE_COLUMN, - NUM_OF_COLUMNS -}; - -static void pidgin_disco_create_tree(PidginDiscoList *pdl); - -static void dialog_select_account_cb(GObject *w, PurpleAccount *account, - PidginDiscoDialog *dialog) -{ - dialog->account = account; -} - -static void register_button_cb(GtkButton *button, PidginDiscoDialog *dialog) -{ - struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); - PurpleAccount *account; - PurpleConnection *gc; - - account = purple_disco_list_get_account(info->list); - gc = purple_account_get_connection(account); - - purple_disco_service_register(info->service); -} - -static void list_button_cb(GtkButton *button, PidginDiscoDialog *dialog) -{ - PurpleConnection *gc; - PidginDiscoList *pdl; - - gc = purple_account_get_connection(dialog->account); - if (!gc) - return; - - gtk_widget_set_sensitive(dialog->add_button, FALSE); - gtk_widget_set_sensitive(dialog->register_button, FALSE); - - if (dialog->discolist != NULL) { - pdl = purple_disco_list_get_ui_data(dialog->discolist); - gtk_widget_destroy(pdl->tree); - purple_disco_list_unref(dialog->discolist); - } - - dialog->discolist = purple_disco_get_list(gc); - if (!dialog->discolist) - return; - /* We keep a copy... */ - purple_disco_list_ref(dialog->discolist); - - pdl = purple_disco_list_get_ui_data(dialog->discolist); - pdl->dialog = dialog; - - pidgin_disco_create_tree(pdl); - - if (dialog->account_widget) - gtk_widget_set_sensitive(dialog->account_widget, FALSE); -} - -static void add_room_to_blist_cb(GtkButton *button, PidginDiscoDialog *dialog) -{ - struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); - PurpleAccount *account; - const char *name; - - g_return_if_fail(info != NULL); - - account = purple_disco_list_get_account(info->list); - name = purple_disco_service_get_name(info->service); - - if (purple_disco_service_get_type(info->service) == PURPLE_DISCO_SERVICE_TYPE_CHAT) - purple_blist_request_add_chat(account, NULL, NULL, name); - else - purple_blist_request_add_buddy(account, name, NULL, NULL); -} - -static void -selection_changed_cb(GtkTreeSelection *selection, PidginDiscoList *pdl) -{ - PurpleDiscoService *service; - PurpleDiscoServiceFlags flags; - GtkTreeIter iter; - GValue val; - static struct _menu_cb_info *info; - PidginDiscoDialog *dialog = pdl->dialog; - - if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { - val.g_type = 0; - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, &val); - service = g_value_get_pointer(&val); - if (!service) { - gtk_widget_set_sensitive(dialog->add_button, FALSE); - gtk_widget_set_sensitive(dialog->register_button, FALSE); - return; - } - - info = g_new0(struct _menu_cb_info, 1); - info->list = dialog->discolist; - info->service = service; - - g_object_set_data_full(G_OBJECT(dialog->add_button), "disco-info", - info, g_free); - g_object_set_data(G_OBJECT(dialog->register_button), "disco-info", info); - - flags = purple_disco_service_get_flags(service); - - gtk_widget_set_sensitive(dialog->add_button, flags & PURPLE_DISCO_ADD); - gtk_widget_set_sensitive(dialog->register_button, flags & PURPLE_DISCO_REGISTER); - } else { - gtk_widget_set_sensitive(dialog->add_button, FALSE); - gtk_widget_set_sensitive(dialog->register_button, FALSE); - } -} - -static void -row_expanded_cb(GtkTreeView *tree, GtkTreeIter *arg1, GtkTreePath *rg2, - gpointer user_data) -{ - PidginDiscoList *pdl; - PurpleDiscoService *service; - GValue val; - - pdl = user_data; - - val.g_type = 0; - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), arg1, SERVICE_COLUMN, - &val); - service = g_value_get_pointer(&val); - purple_disco_service_expand(service); -} - -static gint -delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) -{ - PidginDiscoDialog *dialog = d; - - if (dialog->discolist && purple_disco_list_get_in_progress(dialog->discolist)) - purple_disco_cancel_get_list(dialog->discolist); - - if (dialog->discolist) { - PidginDiscoList *pdl = purple_disco_list_get_ui_data(dialog->discolist); - - if (pdl) - pdl->dialog = NULL; - purple_disco_list_unref(dialog->discolist); - } - - g_free(dialog); - - return FALSE; -} - -static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog) -{ - purple_disco_cancel_get_list(dialog->discolist); - - if (dialog->account_widget) - gtk_widget_set_sensitive(dialog->account_widget, TRUE); - - gtk_widget_set_sensitive(dialog->stop_button, FALSE); - gtk_widget_set_sensitive(dialog->list_button, TRUE); - gtk_widget_set_sensitive(dialog->add_button, FALSE); -} - -static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog) -{ - GtkWidget *window = dialog->window; - - delete_win_cb(NULL, NULL, dialog); - gtk_widget_destroy(window); -} - -static gboolean account_filter_func(PurpleAccount *account) -{ - PurpleConnection *conn = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info = NULL; - - if (conn) - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl); - - return (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_get_list)); -} - -gboolean -pidgin_disco_is_showable() -{ - GList *c; - PurpleConnection *gc; - - for (c = purple_connections_get_all(); c != NULL; c = c->next) { - gc = c->data; - - if (account_filter_func(purple_connection_get_account(gc))) - return TRUE; - } - - return FALSE; -} - -static void pidgin_disco_create_tree(PidginDiscoList *pdl) -{ - GtkCellRenderer *text_renderer, *pixbuf_renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - - pdl->model = gtk_tree_store_new(NUM_OF_COLUMNS, - GDK_TYPE_PIXBUF, /* PIXBUF_COLUMN */ - G_TYPE_STRING, /* NAME_COLUMN */ - G_TYPE_STRING, /* DESCRIPTION_COLUMN */ - G_TYPE_POINTER /* SERVICE_COLUMN */ - ); - - pdl->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdl->model)); - gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pdl->tree), TRUE); - - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdl->tree)); - g_signal_connect(G_OBJECT(selection), "changed", - G_CALLBACK(selection_changed_cb), pdl); - - g_object_unref(pdl->model); - - gtk_container_add(GTK_CONTAINER(pdl->dialog->sw), pdl->tree); - gtk_widget_show(pdl->tree); - - text_renderer = gtk_cell_renderer_text_new(); - pixbuf_renderer = gtk_cell_renderer_pixbuf_new(); - - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, _("Name")); - - gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE); - gtk_tree_view_column_set_attributes(column, pixbuf_renderer, - "pixbuf", PIXBUF_COLUMN, NULL); - - gtk_tree_view_column_pack_start(column, text_renderer, TRUE); - gtk_tree_view_column_set_attributes(column, text_renderer, - "text", NAME_COLUMN, NULL); - - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), - GTK_TREE_VIEW_COLUMN_GROW_ONLY); - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN); - gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column); - - column = gtk_tree_view_column_new_with_attributes(_("Description"), text_renderer, - "text", DESCRIPTION_COLUMN, NULL); - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), - GTK_TREE_VIEW_COLUMN_GROW_ONLY); - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), DESCRIPTION_COLUMN); - gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); - gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column); - - g_signal_connect(G_OBJECT(pdl->tree), "row-expanded", G_CALLBACK(row_expanded_cb), pdl); -} - -static PidginDiscoDialog* -pidgin_disco_dialog_new_with_account(PurpleAccount *account) -{ - PidginDiscoDialog *dialog; - GtkWidget *window, *vbox, *vbox2, *bbox; - - dialog = g_new0(PidginDiscoDialog, 1); - dialog->account = account; - - /* Create the window. */ - dialog->window = window = pidgin_create_dialog(_("Service Discovery"), PIDGIN_HIG_BORDER, "service discovery", TRUE); - - g_signal_connect(G_OBJECT(window), "delete_event", - G_CALLBACK(delete_win_cb), dialog); - - /* Create the parent vbox for everything. */ - vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER); - - vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); - gtk_container_add(GTK_CONTAINER(vbox), vbox2); - gtk_widget_show(vbox2); - - /* accounts dropdown list */ - dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE, - G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog); - if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */ - dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget); - pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL); - - /* scrolled window */ - dialog->sw = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw), - GTK_SHADOW_IN); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0); - gtk_widget_set_size_request(dialog->sw, -1, 250); - gtk_widget_show(dialog->sw); - - /* progress bar */ - dialog->progress = gtk_progress_bar_new(); - gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1); - gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0); - gtk_widget_show(dialog->progress); - - - /* button box */ - bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window)); - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); - - /* stop button */ - dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, - G_CALLBACK(stop_button_cb), dialog); - gtk_widget_set_sensitive(dialog->stop_button, FALSE); - - /* list button */ - dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Browse"), GTK_STOCK_REFRESH, - PIDGIN_BUTTON_HORIZONTAL); - gtk_box_pack_start(GTK_BOX(bbox), dialog->list_button, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(dialog->list_button), "clicked", - G_CALLBACK(list_button_cb), dialog); - gtk_widget_show(dialog->list_button); - - /* register button */ - dialog->register_button = pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"), - G_CALLBACK(register_button_cb), dialog); - gtk_widget_set_sensitive(dialog->register_button, FALSE); - - /* add button */ - dialog->add_button = pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD, - PIDGIN_BUTTON_HORIZONTAL); - gtk_box_pack_start(GTK_BOX(bbox), dialog->add_button, FALSE, FALSE, 0); - g_signal_connect(G_OBJECT(dialog->add_button), "clicked", - G_CALLBACK(add_room_to_blist_cb), dialog); - gtk_widget_set_sensitive(dialog->add_button, FALSE); - gtk_widget_show(dialog->add_button); - - /* close button */ - dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, - G_CALLBACK(close_button_cb), dialog); - - /* show the dialog window and return the dialog */ - gtk_widget_show(dialog->window); - - return dialog; -} - -void -pidgin_disco_dialog_show(void) -{ - pidgin_disco_dialog_new_with_account(NULL); -} - -void -pidgin_disco_dialog_show_with_account(PurpleAccount* account) -{ - PidginDiscoDialog *dialog = pidgin_disco_dialog_new_with_account(account); - - if (!dialog) - return; - - list_button_cb(GTK_BUTTON(dialog->list_button), dialog); -} - -static void -pidgin_disco_create(PurpleDiscoList *list) -{ - PidginDiscoList *pdl = g_new0(PidginDiscoList, 1); - - purple_disco_list_set_ui_data(list, pdl); - - pdl->services = g_hash_table_new_full(NULL, NULL, NULL, - (GDestroyNotify)gtk_tree_row_reference_free); -} - - -static void -pidgin_disco_destroy(PurpleDiscoList *list) -{ - PidginDiscoList *pdl; - - pdl = purple_disco_list_get_ui_data(list); - - g_hash_table_destroy(pdl->services); - g_free(pdl); - - purple_disco_list_set_ui_data(list, NULL); -} - -static void pidgin_disco_in_progress(PurpleDiscoList *list, gboolean in_progress) -{ - PidginDiscoList *pdl; - PidginDiscoDialog *dialog; - - pdl = purple_disco_list_get_ui_data(list); - if (!pdl) - return; - - dialog = pdl->dialog; - if (!dialog) - return; - - if (in_progress) { - if (dialog->account_widget) - gtk_widget_set_sensitive(dialog->account_widget, FALSE); - gtk_widget_set_sensitive(dialog->stop_button, TRUE); - gtk_widget_set_sensitive(dialog->list_button, FALSE); - } else { - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0); - if (dialog->account_widget) - gtk_widget_set_sensitive(dialog->account_widget, TRUE); - gtk_widget_set_sensitive(dialog->stop_button, FALSE); - gtk_widget_set_sensitive(dialog->list_button, TRUE); - } -} - -static void pidgin_disco_add_service(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent) -{ - PidginDiscoList *pdl; - PidginDiscoDialog *dialog; - PurpleDiscoServiceType type; - const char *gateway_type; - GtkTreeIter iter, parent_iter, child; - char *filename = NULL; - GdkPixbuf *pixbuf = NULL; - gboolean append = TRUE; - - pdl = purple_disco_list_get_ui_data(list); - dialog = pdl->dialog; - - purple_debug_info("disco", "Add_service \"%s\"\n", purple_disco_service_get_name(service)); - - gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress)); - - if (parent) { - GtkTreeRowReference *rr; - GtkTreePath *path; - - rr = g_hash_table_lookup(pdl->services, parent); - path = gtk_tree_row_reference_get_path(rr); - if (path) { - gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &parent_iter, path); - gtk_tree_path_free(path); - - if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pdl->model), &child, - &parent_iter)) { - PurpleDiscoList *tmp; - gtk_tree_model_get(GTK_TREE_MODEL(pdl->model), &child, - SERVICE_COLUMN, &tmp, -1); - if (!tmp) - append = FALSE; - } - } - } - - if (append) - gtk_tree_store_append(pdl->model, &iter, (parent ? &parent_iter : NULL)); - else - iter = child; - - if (purple_disco_service_get_flags(service) & PURPLE_DISCO_BROWSE) { - GtkTreeRowReference *rr; - GtkTreePath *path; - - gtk_tree_store_append(pdl->model, &child, &iter); - - path = gtk_tree_model_get_path(GTK_TREE_MODEL(pdl->model), &iter); - rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(pdl->model), path); - g_hash_table_insert(pdl->services, service, rr); - gtk_tree_path_free(path); - } - - type = purple_disco_service_get_type(service); - gateway_type = purple_disco_service_get_gateway_type(service); - - if (type == PURPLE_DISCO_SERVICE_TYPE_GATEWAY && gateway_type) { - char *tmp = g_strconcat(gateway_type, ".png", NULL); - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", tmp, NULL); - g_free(tmp); -#if 0 - } else if (type == PURPLE_DISCO_SERVICE_TYPE_USER) { - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "person.png", NULL); -#endif - } else if (type == PURPLE_DISCO_SERVICE_TYPE_CHAT) - filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "chat.png", NULL); - - if (filename) { - pixbuf = gdk_pixbuf_new_from_file(filename, NULL); - g_free(filename); - } - - gtk_tree_store_set(pdl->model, &iter, - PIXBUF_COLUMN, pixbuf, - NAME_COLUMN, purple_disco_service_get_name(service), - DESCRIPTION_COLUMN, purple_disco_service_get_description(service), - SERVICE_COLUMN, service, - -1); - - if (pixbuf) - g_object_unref(pixbuf); -} - -static PurpleDiscoUiOps ops = { - pidgin_disco_dialog_show_with_account, - pidgin_disco_create, - pidgin_disco_destroy, - pidgin_disco_add_service, - pidgin_disco_in_progress, - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -void pidgin_disco_init() { - purple_disco_set_ui_ops(&ops); -} diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/gtkdisco.h --- a/pidgin/gtkdisco.h Sun May 24 18:39:32 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -/** - * @file gtkdisco.c GTK+ Service Discovery UI - * @ingroup pidgin - */ - -/* pidgin - * - * Pidgin is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * 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 - */ - -#ifndef _GTK_DISCO_H_ -#define _GTK_DISCO_H_ - - -/** - * Initializes the service discovery subsystem. - */ -void pidgin_disco_init(void); - -/** - * Determines if showing the service discovery dialog is a valid action. - * - * @return TRUE if there are accounts online that support service - * discovery. Otherwise return FALSE. - */ -gboolean pidgin_disco_is_showable(void); - -/** - * Shows a new service discovery dialog. - */ -void pidgin_disco_dialog_show(void); - -/** - * Shows a new service discovery dialog and fetches the list for the specified account. - * - * @param account The account to use. - */ -void pidgin_disco_dialog_show_with_account(PurpleAccount *account); - -#endif /* _GTK_DISCO_H_ */ diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/gtkmain.c --- a/pidgin/gtkmain.c Sun May 24 18:39:32 2009 +0000 +++ b/pidgin/gtkmain.c Tue May 26 02:27:49 2009 +0000 @@ -48,7 +48,6 @@ #include "gtkconv.h" #include "gtkdebug.h" #include "gtkdialogs.h" -#include "gtkdisco.h" #include "gtkdocklet.h" #include "gtkeventloop.h" #include "gtkft.h" @@ -309,7 +308,6 @@ pidgin_privacy_init(); pidgin_xfers_init(); pidgin_roomlist_init(); - pidgin_disco_init(); pidgin_log_init(); pidgin_docklet_init(); pidgin_smileys_init(); diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/Makefile.am --- a/pidgin/plugins/Makefile.am Sun May 24 18:39:32 2009 +0000 +++ b/pidgin/plugins/Makefile.am Tue May 26 02:27:49 2009 +0000 @@ -1,4 +1,4 @@ -DIST_SUBDIRS = cap gestures gevolution musicmessaging perl ticker +DIST_SUBDIRS = cap disco gestures gevolution musicmessaging perl ticker if BUILD_GEVOLUTION GEVOLUTION_DIR = gevolution @@ -26,6 +26,7 @@ $(GEVOLUTION_DIR) \ $(MUSICMESSAGING_DIR) \ $(PERL_DIR) \ + disco \ ticker plugindir = $(libdir)/pidgin diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/disco/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/Makefile.am Tue May 26 02:27:49 2009 +0000 @@ -0,0 +1,23 @@ +plugindir = $(libdir)/pidgin + +xmppdisco_la_LDFLAGS = -module -avoid-version + +if PLUGINS + +plugin_LTLIBRARIES = xmppdisco.la + +xmppdisco_la_SOURCES = \ + gtkdisco.c \ + xmppdisco.c + +xmppdisco_la_LIBADD = $(GTK_LIBS) + +endif + +AM_CPPFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -I$(top_srcdir)/libpurple \ + -I$(top_builddir)/libpurple \ + -I$(top_srcdir)/pidgin \ + $(DEBUG_CFLAGS) \ + $(GTK_CFLAGS) diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/disco/gtkdisco.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/gtkdisco.c Tue May 26 02:27:49 2009 +0000 @@ -0,0 +1,562 @@ +/** + * @file gtkdisco.c GTK+ Service Discovery UI + * @ingroup pidgin + */ + +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 "debug.h" +#include "gtkutils.h" +#include "pidgin.h" +#include "request.h" + +#include "gtkdisco.h" +#include "xmppdisco.h" + +GList *dialogs = NULL; + +struct _menu_cb_info { + PidginDiscoList *list; + XmppDiscoService *service; +}; + +enum { + PIXBUF_COLUMN = 0, + NAME_COLUMN, + DESCRIPTION_COLUMN, + SERVICE_COLUMN, + NUM_OF_COLUMNS +}; + +static void +pidgin_disco_list_destroy(PidginDiscoList *list) +{ + g_hash_table_destroy(list->services); + if (list->dialog && list->dialog->discolist == list) + list->dialog->discolist = NULL; + + if (list->tree) { + gtk_widget_destroy(list->tree); + list->tree = NULL; + } + + g_free((gchar*)list->server); + g_free(list); +} + +PidginDiscoList *pidgin_disco_list_ref(PidginDiscoList *list) +{ + g_return_val_if_fail(list != NULL, NULL); + + ++list->ref; + purple_debug_misc("xmppdisco", "reffing list, ref count now %d\n", list->ref); + + return list; +} + +void pidgin_disco_list_unref(PidginDiscoList *list) +{ + g_return_if_fail(list != NULL); + + --list->ref; + + purple_debug_misc("xmppdisco", "unreffing list, ref count now %d\n", list->ref); + if (list->ref == 0) + pidgin_disco_list_destroy(list); +} + +void pidgin_disco_list_set_in_progress(PidginDiscoList *list, gboolean in_progress) +{ + PidginDiscoDialog *dialog = list->dialog; + + if (!dialog) + return; + + list->in_progress = in_progress; + + if (in_progress) { + gtk_widget_set_sensitive(dialog->account_widget, FALSE); + gtk_widget_set_sensitive(dialog->stop_button, TRUE); + gtk_widget_set_sensitive(dialog->browse_button, FALSE); + } else { + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0); + + gtk_widget_set_sensitive(dialog->account_widget, TRUE); + + gtk_widget_set_sensitive(dialog->stop_button, FALSE); + gtk_widget_set_sensitive(dialog->browse_button, TRUE); +/* + gtk_widget_set_sensitive(dialog->register_button, FALSE); + gtk_widget_set_sensitive(dialog->add_button, FALSE); +*/ + } +} + +static void pidgin_disco_create_tree(PidginDiscoList *pdl); + +static void dialog_select_account_cb(GObject *w, PurpleAccount *account, + PidginDiscoDialog *dialog) +{ + dialog->account = account; + gtk_widget_set_sensitive(dialog->browse_button, account != NULL); +} + +static void register_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +{ + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); + + xmpp_disco_service_register(info->service); +} + +static void discolist_cancel_cb(PidginDiscoList *pdl, const char *server) +{ + pidgin_disco_list_set_in_progress(pdl, FALSE); + pidgin_disco_list_unref(pdl); +} + +static void discolist_ok_cb(PidginDiscoList *pdl, const char *server) +{ + gtk_widget_set_sensitive(pdl->dialog->browse_button, TRUE); + + if (!server || !*server) { + purple_notify_error(my_plugin, _("Invalid Server"), _("Invalid Server"), + NULL); + + pidgin_disco_list_set_in_progress(pdl, FALSE); + pidgin_disco_list_unref(pdl); + return; + } + + pdl->server = g_strdup(server); + pidgin_disco_list_set_in_progress(pdl, TRUE); + xmpp_disco_start(pdl); +} + +static void browse_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +{ + PurpleConnection *pc; + PidginDiscoList *pdl; + + pc = purple_account_get_connection(dialog->account); + if (!pc) + return; + + gtk_widget_set_sensitive(dialog->browse_button, FALSE); + gtk_widget_set_sensitive(dialog->add_button, FALSE); + gtk_widget_set_sensitive(dialog->register_button, FALSE); + + if (dialog->discolist != NULL) { + gtk_widget_destroy(dialog->discolist->tree); + pidgin_disco_list_unref(dialog->discolist); + } + + pdl = dialog->discolist = g_new0(PidginDiscoList, 1); + pdl->services = g_hash_table_new_full(NULL, NULL, NULL, + (GDestroyNotify)gtk_tree_row_reference_free); + pdl->pc = pc; + /* We keep a copy... */ + pidgin_disco_list_ref(pdl); + + pdl->dialog = dialog; + pidgin_disco_create_tree(pdl); + + if (dialog->account_widget) + gtk_widget_set_sensitive(dialog->account_widget, FALSE); + + purple_request_input(my_plugin, _("Server name request"), _("Enter an XMPP Server"), + _("Select an XMPP server to query"), + "jabber.org" /* FIXME */, FALSE, FALSE, NULL, + _("Find Services"), PURPLE_CALLBACK(discolist_ok_cb), + _("Cancel"), PURPLE_CALLBACK(discolist_cancel_cb), + purple_connection_get_account(pc), NULL, NULL, pdl); +} + +static void add_room_to_blist_cb(GtkButton *button, PidginDiscoDialog *dialog) +{ + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info"); + PurpleAccount *account; + const char *name; + + g_return_if_fail(info != NULL); + + account = purple_connection_get_account(info->list->pc); + name = info->service->name; + + if (info->service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) + purple_blist_request_add_chat(account, NULL, NULL, name); + else + purple_blist_request_add_buddy(account, name, NULL, NULL); +} + +static void +selection_changed_cb(GtkTreeSelection *selection, PidginDiscoList *pdl) +{ + XmppDiscoService *service; + GtkTreeIter iter; + GValue val; + static struct _menu_cb_info *info; + PidginDiscoDialog *dialog = pdl->dialog; + + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) { + val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, &val); + service = g_value_get_pointer(&val); + if (!service) { + gtk_widget_set_sensitive(dialog->add_button, FALSE); + gtk_widget_set_sensitive(dialog->register_button, FALSE); + return; + } + + info = g_new0(struct _menu_cb_info, 1); + info->list = dialog->discolist; + info->service = service; + + g_object_set_data_full(G_OBJECT(dialog->add_button), "disco-info", + info, g_free); + g_object_set_data(G_OBJECT(dialog->register_button), "disco-info", info); + + gtk_widget_set_sensitive(dialog->add_button, service->flags & XMPP_DISCO_ADD); + gtk_widget_set_sensitive(dialog->register_button, service->flags & XMPP_DISCO_REGISTER); + } else { + gtk_widget_set_sensitive(dialog->add_button, FALSE); + gtk_widget_set_sensitive(dialog->register_button, FALSE); + } +} + +static void +row_expanded_cb(GtkTreeView *tree, GtkTreeIter *arg1, GtkTreePath *rg2, + gpointer user_data) +{ + PidginDiscoList *pdl; + XmppDiscoService *service; + GValue val; + + pdl = user_data; + + val.g_type = 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), arg1, SERVICE_COLUMN, + &val); + service = g_value_get_pointer(&val); + xmpp_disco_service_expand(service); +} + +static void +destroy_win_cb(GtkWidget *window, gpointer d) +{ + PidginDiscoDialog *dialog = d; + PidginDiscoList *list = dialog->discolist; + + if (list) { + list->dialog = NULL; + + if (list->in_progress) + list->in_progress = FALSE; + + pidgin_disco_list_unref(list); + } + + dialogs = g_list_remove(dialogs, d); + g_free(dialog); +} + +static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +{ + pidgin_disco_list_set_in_progress(dialog->discolist, FALSE); +} + +static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog) +{ + GtkWidget *window = dialog->window; + + gtk_widget_destroy(window); +} + +static gboolean account_filter_func(PurpleAccount *account) +{ + return purple_strequal(purple_account_get_protocol_id(account), XMPP_PLUGIN_ID); +} + +static void pidgin_disco_create_tree(PidginDiscoList *pdl) +{ + GtkCellRenderer *text_renderer, *pixbuf_renderer; + GtkTreeViewColumn *column; + GtkTreeSelection *selection; + + pdl->model = gtk_tree_store_new(NUM_OF_COLUMNS, + GDK_TYPE_PIXBUF, /* PIXBUF_COLUMN */ + G_TYPE_STRING, /* NAME_COLUMN */ + G_TYPE_STRING, /* DESCRIPTION_COLUMN */ + G_TYPE_POINTER /* SERVICE_COLUMN */ + ); + + pdl->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdl->model)); + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(pdl->tree), TRUE); + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdl->tree)); + g_signal_connect(G_OBJECT(selection), "changed", + G_CALLBACK(selection_changed_cb), pdl); + + g_object_unref(pdl->model); + + gtk_container_add(GTK_CONTAINER(pdl->dialog->sw), pdl->tree); + gtk_widget_show(pdl->tree); + + text_renderer = gtk_cell_renderer_text_new(); + pixbuf_renderer = gtk_cell_renderer_pixbuf_new(); + + column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(column, _("Name")); + + gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE); + gtk_tree_view_column_set_attributes(column, pixbuf_renderer, + "pixbuf", PIXBUF_COLUMN, NULL); + + gtk_tree_view_column_pack_start(column, text_renderer, TRUE); + gtk_tree_view_column_set_attributes(column, text_renderer, + "text", NAME_COLUMN, NULL); + + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), + GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN); + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column); + + column = gtk_tree_view_column_new_with_attributes(_("Description"), text_renderer, + "text", DESCRIPTION_COLUMN, NULL); + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column), + GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), DESCRIPTION_COLUMN); + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column); + + g_signal_connect(G_OBJECT(pdl->tree), "row-expanded", G_CALLBACK(row_expanded_cb), pdl); +} + +void pidgin_disco_signed_off_cb(PurpleConnection *pc) +{ + GList *node; + + for (node = dialogs; node; node = node->next) { + PidginDiscoDialog *dialog = node->data; + PidginDiscoList *list = dialog->discolist; + + if (list && list->pc == pc) { + if (list->in_progress) + pidgin_disco_list_set_in_progress(list, FALSE); + + if (list->tree) { + gtk_widget_destroy(list->tree); + list->tree = NULL; + } + + pidgin_disco_list_unref(list); + dialog->discolist = NULL; + + gtk_widget_set_sensitive(dialog->browse_button, + pidgin_account_option_menu_get_selected(dialog->account_widget) != NULL); + + gtk_widget_set_sensitive(dialog->register_button, FALSE); + gtk_widget_set_sensitive(dialog->add_button, FALSE); + } + } +} + +void pidgin_disco_dialogs_destroy_all(void) +{ + while (dialogs) { + PidginDiscoDialog *dialog = dialogs->data; + + gtk_widget_destroy(dialog->window); + /* destroy_win_cb removes the dialog from the list */ + } +} + +PidginDiscoDialog *pidgin_disco_dialog_new(void) +{ + PidginDiscoDialog *dialog; + GtkWidget *window, *vbox, *vbox2, *bbox; + + dialog = g_new0(PidginDiscoDialog, 1); + dialogs = g_list_prepend(dialogs, dialog); + + /* Create the window. */ + dialog->window = window = pidgin_create_dialog(_("Service Discovery"), PIDGIN_HIG_BORDER, "service discovery", TRUE); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(destroy_win_cb), dialog); + + /* Create the parent vbox for everything. */ + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER); + + vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); + gtk_container_add(GTK_CONTAINER(vbox), vbox2); + gtk_widget_show(vbox2); + + /* accounts dropdown list */ + dialog->account_widget = pidgin_account_option_menu_new(NULL, FALSE, + G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog); + dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget); + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL); + + /* scrolled window */ + dialog->sw = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0); + gtk_widget_set_size_request(dialog->sw, -1, 250); + gtk_widget_show(dialog->sw); + + /* progress bar */ + dialog->progress = gtk_progress_bar_new(); + gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1); + gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0); + gtk_widget_show(dialog->progress); + + /* button box */ + bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window)); + gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + + /* stop button */ + dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP, + G_CALLBACK(stop_button_cb), dialog); + gtk_widget_set_sensitive(dialog->stop_button, FALSE); + + /* browse button */ + dialog->browse_button = pidgin_pixbuf_button_from_stock(_("_Browse"), GTK_STOCK_REFRESH, + PIDGIN_BUTTON_HORIZONTAL); + gtk_box_pack_start(GTK_BOX(bbox), dialog->browse_button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(dialog->browse_button), "clicked", + G_CALLBACK(browse_button_cb), dialog); + gtk_widget_set_sensitive(dialog->browse_button, dialog->account != NULL); + gtk_widget_show(dialog->browse_button); + + /* register button */ + dialog->register_button = pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"), + G_CALLBACK(register_button_cb), dialog); + gtk_widget_set_sensitive(dialog->register_button, FALSE); + + /* add button */ + dialog->add_button = pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD, + PIDGIN_BUTTON_HORIZONTAL); + gtk_box_pack_start(GTK_BOX(bbox), dialog->add_button, FALSE, FALSE, 0); + g_signal_connect(G_OBJECT(dialog->add_button), "clicked", + G_CALLBACK(add_room_to_blist_cb), dialog); + gtk_widget_set_sensitive(dialog->add_button, FALSE); + gtk_widget_show(dialog->add_button); + + /* close button */ + dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE, + G_CALLBACK(close_button_cb), dialog); + + /* show the dialog window and return the dialog */ + gtk_widget_show(dialog->window); + + return dialog; +} + +void pidgin_disco_add_service(PidginDiscoList *pdl, XmppDiscoService *service, XmppDiscoService *parent) +{ + PidginDiscoDialog *dialog; + GtkTreeIter iter, parent_iter, child; + char *filename = NULL; + GdkPixbuf *pixbuf = NULL; + gboolean append = TRUE; + + dialog = pdl->dialog; + g_return_if_fail(dialog != NULL); + + purple_debug_info("xmppdisco", "Adding service \"%s\"\n", service->name); + + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress)); + + if (parent) { + GtkTreeRowReference *rr; + GtkTreePath *path; + + rr = g_hash_table_lookup(pdl->services, parent); + path = gtk_tree_row_reference_get_path(rr); + if (path) { + gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &parent_iter, path); + gtk_tree_path_free(path); + + if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pdl->model), &child, + &parent_iter)) { + PidginDiscoList *tmp; + gtk_tree_model_get(GTK_TREE_MODEL(pdl->model), &child, + SERVICE_COLUMN, &tmp, -1); + if (!tmp) + append = FALSE; + } + } + } + + if (append) + gtk_tree_store_append(pdl->model, &iter, (parent ? &parent_iter : NULL)); + else + iter = child; + + if (service->flags & XMPP_DISCO_BROWSE) { + GtkTreeRowReference *rr; + GtkTreePath *path; + + gtk_tree_store_append(pdl->model, &child, &iter); + + path = gtk_tree_model_get_path(GTK_TREE_MODEL(pdl->model), &iter); + rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(pdl->model), path); + g_hash_table_insert(pdl->services, service, rr); + gtk_tree_path_free(path); + } + + if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY && service->gateway_type) { + char *tmp = g_strconcat(service->gateway_type, ".png", NULL); + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", tmp, NULL); + g_free(tmp); +#if 0 + } else if (service->type == XMPP_DISCO_SERVICE_TYPE_USER) { + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "person.png", NULL); +#endif + } else if (service->type == XMPP_DISCO_SERVICE_TYPE_CHAT) + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "chat.png", NULL); + + if (filename) { + pixbuf = gdk_pixbuf_new_from_file(filename, NULL); + g_free(filename); + } + + gtk_tree_store_set(pdl->model, &iter, + PIXBUF_COLUMN, pixbuf, + NAME_COLUMN, service->name, + DESCRIPTION_COLUMN, service->description, + SERVICE_COLUMN, service, + -1); + + if (pixbuf) + g_object_unref(pixbuf); +} + diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/disco/gtkdisco.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/gtkdisco.h Tue May 26 02:27:49 2009 +0000 @@ -0,0 +1,79 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + */ + +#ifndef PIDGIN_XMPP_DISCO_UI_H +#define PIDGIN_XMPP_DISCO_UI_H + +typedef struct _PidginDiscoDialog PidginDiscoDialog; +typedef struct _PidginDiscoList PidginDiscoList; + +#include "xmppdisco.h" + +struct _PidginDiscoDialog { + GtkWidget *window; + GtkWidget *account_widget; + + GtkWidget *sw; + GtkWidget *progress; + + GtkWidget *stop_button; + GtkWidget *browse_button; + GtkWidget *register_button; + GtkWidget *add_button; + GtkWidget *close_button; + + PurpleAccount *account; + PidginDiscoList *discolist; +}; + +struct _PidginDiscoList { + PurpleConnection *pc; + gboolean in_progress; + const gchar *server; + + gint ref; + guint fetch_count; + + PidginDiscoDialog *dialog; + GtkTreeStore *model; + GtkWidget *tree; + GHashTable *services; +}; + +/** + * Shows a new service discovery dialog. + */ +PidginDiscoDialog *pidgin_disco_dialog_new(void); + +/** + * Destroy all the open dialogs (called when unloading the plugin). + */ +void pidgin_disco_dialogs_destroy_all(void); +void pidgin_disco_signed_off_cb(PurpleConnection *pc); + +void pidgin_disco_add_service(PidginDiscoList *list, XmppDiscoService *service, + XmppDiscoService *parent); + +PidginDiscoList *pidgin_disco_list_ref(PidginDiscoList *list); +void pidgin_disco_list_unref(PidginDiscoList *list); + +void pidgin_disco_list_set_in_progress(PidginDiscoList *list, gboolean in_progress); +#endif /* PIDGIN_XMPP_DISCO_UI_H */ diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/disco/xmppdisco.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/xmppdisco.c Tue May 26 02:27:49 2009 +0000 @@ -0,0 +1,647 @@ +/* + * Purple - XMPP Service Disco Browser + * + * 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 "debug.h" +#include "signals.h" +#include "version.h" + +#include "gtkconv.h" +#include "gtkimhtml.h" +#include "gtkplugin.h" + +#include "xmppdisco.h" +#include "gtkdisco.h" + +/* Variables */ +PurplePlugin *my_plugin = NULL; +static GHashTable *iq_callbacks = NULL; +static gboolean iq_listening = FALSE; + +typedef void (*XmppIqCallback)(PurpleConnection *pc, const char *type, + const char *id, const char *from, xmlnode *iq, + gpointer data); + +struct xmpp_iq_cb_data +{ + gpointer context; + PurpleConnection *pc; + XmppIqCallback cb; +}; + +struct item_data { + PidginDiscoList *list; + XmppDiscoService *parent; + char *name; + char *node; /* disco#info replies don't always include the node */ +}; + +static char* +generate_next_id() +{ + static guint32 index = 0; + + if (index == 0) { + do { + index = g_random_int(); + } while (index == 0); + } + + return g_strdup_printf("purpledisco%x", index++); +} + +static gboolean +remove_iq_callbacks_by_pc(gpointer key, gpointer value, gpointer user_data) +{ + struct xmpp_iq_cb_data *cb_data = value; + + if (cb_data && cb_data->pc == user_data) { + /* + * This is a hack. All the IQ callback datas in this code are + * the same structure so that we can free them here. Ideally they'd + * be objects and this would be polymorphic. That's overkill, here. + */ + struct item_data *item_data = cb_data->context; + + if (item_data) { + pidgin_disco_list_unref(item_data->list); + g_free(item_data->name); + g_free(item_data->node); + g_free(item_data); + } + + return TRUE; + } else + return FALSE; +} + +static gboolean +xmpp_iq_received(PurpleConnection *pc, const char *type, const char *id, + const char *from, xmlnode *iq) +{ + struct xmpp_iq_cb_data *cb_data; + + cb_data = g_hash_table_lookup(iq_callbacks, id); + if (!cb_data) + return FALSE; + + cb_data->cb(cb_data->pc, type, id, from, iq, cb_data->context); + + g_hash_table_remove(iq_callbacks, id); + if (g_hash_table_size(iq_callbacks) == 0) { + PurplePlugin *prpl = purple_connection_get_prpl(pc); + iq_listening = FALSE; + purple_signal_disconnect(prpl, "jabber-receiving-iq", my_plugin, + PURPLE_CALLBACK(xmpp_iq_received)); + } + + /* Om nom nom nom */ + return TRUE; +} + +static void +xmpp_iq_register_callback(PurpleConnection *pc, gchar *id, gpointer data, + XmppIqCallback cb) +{ + struct xmpp_iq_cb_data *cbdata = g_new0(struct xmpp_iq_cb_data, 1); + + cbdata->context = data; + cbdata->cb = cb; + cbdata->pc = pc; + + g_hash_table_insert(iq_callbacks, id, cbdata); + + if (!iq_listening) { + PurplePlugin *prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); + iq_listening = TRUE; + purple_signal_connect(prpl, "jabber-receiving-iq", my_plugin, + PURPLE_CALLBACK(xmpp_iq_received), NULL); + } +} + +static void +xmpp_disco_info_do(PurpleConnection *pc, gpointer cbdata, const char *jid, + const char *node, XmppIqCallback cb) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + xmlnode *iq, *query; + char *id = generate_next_id(); + char *str; + + iq = xmlnode_new("iq"); + xmlnode_set_attrib(iq, "type", "get"); + xmlnode_set_attrib(iq, "to", jid); + xmlnode_set_attrib(iq, "id", id); + + query = xmlnode_new_child(iq, "query"); + xmlnode_set_namespace(query, NS_DISCO_INFO); + if (node) + xmlnode_set_attrib(query, "node", node); + + xmpp_iq_register_callback(pc, id, cbdata, cb); + + str = xmlnode_to_str(iq, NULL); + prpl = purple_connection_get_prpl(pc); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + prpl_info->send_raw(pc, str, -1); + g_free(str); +} + +static void +xmpp_disco_items_do(PurpleConnection *pc, gpointer cbdata, const char *jid, + const char *node, XmppIqCallback cb) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + xmlnode *iq, *query; + char *id = generate_next_id(); + char *str; + + iq = xmlnode_new("iq"); + xmlnode_set_attrib(iq, "type", "get"); + xmlnode_set_attrib(iq, "to", jid); + xmlnode_set_attrib(iq, "id", id); + + query = xmlnode_new_child(iq, "query"); + xmlnode_set_namespace(query, NS_DISCO_ITEMS); + if (node) + xmlnode_set_attrib(query, "node", node); + + xmpp_iq_register_callback(pc, id, cbdata, cb); + + str = xmlnode_to_str(iq, NULL); + prpl = purple_connection_get_prpl(pc); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + prpl_info->send_raw(pc, str, -1); + g_free(str); +} + +static XmppDiscoServiceType +disco_service_type_from_identity(xmlnode *identity) +{ + const char *category, *type; + + if (!identity) + return XMPP_DISCO_SERVICE_TYPE_OTHER; + + category = xmlnode_get_attrib(identity, "category"); + type = xmlnode_get_attrib(identity, "type"); + + if (!category) + return XMPP_DISCO_SERVICE_TYPE_OTHER; + + if (g_str_equal(category, "conference")) + return XMPP_DISCO_SERVICE_TYPE_CHAT; + else if (g_str_equal(category, "directory")) + return XMPP_DISCO_SERVICE_TYPE_DIRECTORY; + else if (g_str_equal(category, "gateway")) + return XMPP_DISCO_SERVICE_TYPE_GATEWAY; + else if (g_str_equal(category, "pubsub")) { + if (!type || g_str_equal(type, "collection")) + return XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION; + else if (g_str_equal(type, "leaf")) + return XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF; + else if (g_str_equal(type, "service")) { + purple_debug_error("xmppdisco", "here\n"); + return XMPP_DISCO_SERVICE_TYPE_OTHER; + } else { + purple_debug_warning("xmppdisco", "Unknown pubsub type '%s'\n", type); + return XMPP_DISCO_SERVICE_TYPE_OTHER; + } + } + + return XMPP_DISCO_SERVICE_TYPE_OTHER; +} + +static const struct { + const char *from; + const char *to; +} disco_type_mappings[] = { + { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ + { "sametime", "meanwhile" }, + { "myspaceim", "myspace" }, + { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ + { NULL, NULL } +}; + +static const gchar * +disco_type_from_string(const gchar *str) +{ + int i = 0; + + g_return_val_if_fail(str != NULL, ""); + + for ( ; disco_type_mappings[i].from; ++i) { + if (!strcasecmp(str, disco_type_mappings[i].from)) + return disco_type_mappings[i].to; + } + + /* fallback to the string itself */ + return str; +} + +static void +got_info_cb(PurpleConnection *pc, const char *type, const char *id, + const char *from, xmlnode *iq, gpointer data) +{ + struct item_data *item_data = data; + PidginDiscoList *list = item_data->list; + xmlnode *query; + + --list->fetch_count; + + if (!list->in_progress) + goto out; + + if (g_str_equal(type, "result") && + (query = xmlnode_get_child(iq, "query"))) { + xmlnode *identity = xmlnode_get_child(query, "identity"); + XmppDiscoService *service; + xmlnode *feature; + + service = g_new0(XmppDiscoService, 1); + service->list = item_data->list; + purple_debug_info("xmppdisco", "parent for %s is %p\n", from, item_data->parent); + service->parent = item_data->parent; + service->flags = XMPP_DISCO_ADD; + service->type = disco_service_type_from_identity(identity); + + if (item_data->node) { + if (item_data->name) { + service->name = item_data->name; + item_data->name = NULL; + } else + service->name = g_strdup(item_data->node); + + service->node = item_data->node; + item_data->node = NULL; + + if (service->type == XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION) + service->flags |= XMPP_DISCO_BROWSE; + } else + service->name = g_strdup(from); + + if (item_data->name) { + service->description = item_data->name; + item_data->name = NULL; + } else if (identity) + service->description = g_strdup(xmlnode_get_attrib(identity, "name")); + + /* TODO: Overlap with service->name a bit */ + service->jid = g_strdup(from); + + for (feature = xmlnode_get_child(query, "feature"); feature; + feature = xmlnode_get_next_twin(feature)) { + const char *var; + if (!(var = xmlnode_get_attrib(feature, "var"))) + continue; + + if (g_str_equal(var, NS_REGISTER)) + service->flags |= XMPP_DISCO_REGISTER; + else if (g_str_equal(var, NS_DISCO_ITEMS)) + service->flags |= XMPP_DISCO_BROWSE; + else if (g_str_equal(var, NS_MUC)) { + service->flags |= XMPP_DISCO_BROWSE; + service->type = XMPP_DISCO_SERVICE_TYPE_CHAT; + } + } + + if (service->type == XMPP_DISCO_SERVICE_TYPE_GATEWAY) + service->gateway_type = g_strdup(disco_type_from_string( + xmlnode_get_attrib(identity, "type"))); + + pidgin_disco_add_service(list, service, service->parent); + } + +out: + if (list->fetch_count == 0) + pidgin_disco_list_set_in_progress(list, FALSE); + + g_free(item_data->name); + g_free(item_data->node); + g_free(item_data); + pidgin_disco_list_unref(list); +} + +static void +got_items_cb(PurpleConnection *pc, const char *type, const char *id, + const char *from, xmlnode *iq, gpointer data) +{ + struct item_data *item_data = data; + PidginDiscoList *list = item_data->list; + xmlnode *query; + + --list->fetch_count; + + if (!list->in_progress) + goto out; + + if (g_str_equal(type, "result") && + (query = xmlnode_get_child(iq, "query"))) { + xmlnode *item; + + for (item = xmlnode_get_child(query, "item"); item; + item = xmlnode_get_next_twin(item)) { + const char *jid = xmlnode_get_attrib(item, "jid"); + const char *name = xmlnode_get_attrib(item, "name"); + const char *node = xmlnode_get_attrib(item, "node"); + + if (item_data->parent->type == XMPP_DISCO_SERVICE_TYPE_CHAT) { + /* This is a hacky first-order approximation. Any MUC + * component that has a >1 level hierarchy (a Yahoo MUC + * transport component probably does) will violate this. + * + * On the other hand, this is better than querying all the + * chats at conference.jabber.org to enumerate them. + */ + XmppDiscoService *service = g_new0(XmppDiscoService, 1); + service->list = item_data->list; + service->parent = item_data->parent; + service->flags = XMPP_DISCO_ADD; + service->type = XMPP_DISCO_SERVICE_TYPE_CHAT; + + service->name = g_strdup(name); + service->jid = g_strdup(jid); + service->node = g_strdup(node); + pidgin_disco_add_service(list, service, item_data->parent); + } else { + struct item_data *item_data2 = g_new0(struct item_data, 1); + + item_data2->list = item_data->list; + item_data2->parent = item_data->parent; + item_data2->name = g_strdup(name); + item_data2->node = g_strdup(node); + + ++list->fetch_count; + pidgin_disco_list_ref(list); + xmpp_disco_info_do(pc, item_data2, jid, node, got_info_cb); + } + } + } + +out: + if (list->fetch_count == 0) + pidgin_disco_list_set_in_progress(list, FALSE); + + g_free(item_data); + pidgin_disco_list_unref(list); +} + +static void +server_items_cb(PurpleConnection *pc, const char *type, const char *id, + const char *from, xmlnode *iq, gpointer data) +{ + struct item_data *cb_data = data; + PidginDiscoList *list = cb_data->list; + xmlnode *query; + + g_free(cb_data); + --list->fetch_count; + + if (g_str_equal(type, "result") && + (query = xmlnode_get_child(iq, "query"))) { + xmlnode *item; + + for (item = xmlnode_get_child(query, "item"); item; + item = xmlnode_get_next_twin(item)) { + const char *jid = xmlnode_get_attrib(item, "jid"); + struct item_data *item_data; + + if (!jid) + continue; + + item_data = g_new0(struct item_data, 1); + item_data->list = list; + + ++list->fetch_count; + pidgin_disco_list_ref(list); + xmpp_disco_info_do(pc, item_data, jid, NULL, got_info_cb); + } + } + + if (list->fetch_count == 0) + pidgin_disco_list_set_in_progress(list, FALSE); + + pidgin_disco_list_unref(list); +} + +static void +server_info_cb(PurpleConnection *pc, const char *type, const char *id, + const char *from, xmlnode *iq, gpointer data) +{ + struct item_data *cb_data = data; + PidginDiscoList *list = cb_data->list; + xmlnode *query; + gboolean items = FALSE; + + --list->fetch_count; + + if (g_str_equal(type, "result") && + (query = xmlnode_get_child(iq, "query"))) { + xmlnode *feature; + + for (feature = xmlnode_get_child(query, "feature"); feature; + feature = xmlnode_get_next_twin(feature)) { + const char *var = xmlnode_get_attrib(feature, "var"); + if (purple_strequal(var, NS_DISCO_ITEMS)) { + items = TRUE; + break; + } + } + } + + if (items) { + xmpp_disco_items_do(pc, cb_data, from, NULL /* node */, server_items_cb); + ++list->fetch_count; + pidgin_disco_list_ref(list); + } else { + purple_notify_error(my_plugin, _("Error"), + _("Server does not support service discovery"), + NULL); + pidgin_disco_list_set_in_progress(list, FALSE); + g_free(cb_data); + } + + pidgin_disco_list_unref(list); +} + +void xmpp_disco_start(PidginDiscoList *list) +{ + struct item_data *cb_data; + + g_return_if_fail(list != NULL); + + ++list->fetch_count; + pidgin_disco_list_ref(list); + + cb_data = g_new0(struct item_data, 1); + cb_data->list = list; + + xmpp_disco_info_do(list->pc, cb_data, list->server, NULL, server_info_cb); +} + +void xmpp_disco_service_expand(XmppDiscoService *service) +{ + struct item_data *item_data; + + g_return_if_fail(service != NULL); + + if (service->expanded) + return; + + item_data = g_new0(struct item_data, 1); + item_data->list = service->list; + item_data->parent = service; + + ++service->list->fetch_count; + pidgin_disco_list_ref(service->list); + + pidgin_disco_list_set_in_progress(service->list, TRUE); + + xmpp_disco_items_do(service->list->pc, item_data, service->jid, service->node, + got_items_cb); + service->expanded = TRUE; +} + +void xmpp_disco_service_register(XmppDiscoService *service) +{ + PurplePlugin *prpl; + PurplePluginProtocolInfo *prpl_info; + xmlnode *iq, *query; + char *id = generate_next_id(); + char *str; + + iq = xmlnode_new("iq"); + xmlnode_set_attrib(iq, "type", "get"); + xmlnode_set_attrib(iq, "to", service->jid); + xmlnode_set_attrib(iq, "id", id); + + query = xmlnode_new_child(iq, "query"); + xmlnode_set_namespace(query, NS_REGISTER); + + str = xmlnode_to_str(iq, NULL); + prpl = purple_connection_get_prpl(service->list->pc); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + prpl_info->send_raw(service->list->pc, str, -1); + g_free(str); + g_free(id); +} + +static void +create_dialog(PurplePluginAction *action) +{ + pidgin_disco_dialog_new(); +} + +static GList * +actions(PurplePlugin *plugin, gpointer context) +{ + GList *l = NULL; + PurplePluginAction *action = NULL; + + action = purple_plugin_action_new(_("XMPP Service Discovery"), + create_dialog); + l = g_list_prepend(l, action); + + return l; +} + +static void +signed_off_cb(PurpleConnection *pc, gpointer unused) +{ + /* Deal with any dialogs */ + pidgin_disco_signed_off_cb(pc); + + /* Remove all the IQ callbacks for this connection */ + g_hash_table_foreach_remove(iq_callbacks, remove_iq_callbacks_by_pc, pc); +} + +static gboolean +plugin_load(PurplePlugin *plugin) +{ + PurplePlugin *xmpp_prpl; + + my_plugin = plugin; + + xmpp_prpl = purple_plugins_find_with_id(XMPP_PLUGIN_ID); + if (NULL == xmpp_prpl) + return FALSE; + + purple_signal_connect(purple_connections_get_handle(), "signing-off", + plugin, PURPLE_CALLBACK(signed_off_cb), NULL); + + iq_callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + + return TRUE; +} + +static gboolean +plugin_unload(PurplePlugin *plugin) +{ + g_hash_table_destroy(iq_callbacks); + iq_callbacks = NULL; + + purple_signals_disconnect_by_handle(plugin); + pidgin_disco_dialogs_destroy_all(); + + return TRUE; +} + +static PurplePluginInfo info = +{ + PURPLE_PLUGIN_MAGIC, + PURPLE_MAJOR_VERSION, + PURPLE_MINOR_VERSION, + PURPLE_PLUGIN_STANDARD, + PIDGIN_PLUGIN_TYPE, + 0, + NULL, + PURPLE_PRIORITY_DEFAULT, + "gtk-xmppdisco", + N_("XMPP Service Discovery"), + DISPLAY_VERSION, + N_("Allows browsing and registering services."), + N_("This plugin is useful for registering with legacy transports or other " + "XMPP services."), + "Paul Aurich ", + PURPLE_WEBSITE, + plugin_load, + plugin_unload, + NULL, /**< destroy */ + NULL, /**< ui_info */ + NULL, /**< extra_info */ + NULL, /**< prefs_info */ + actions, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +init_plugin(PurplePlugin *plugin) +{ +} + +PURPLE_INIT_PLUGIN(xmppdisco, init_plugin, info) diff -r fe1603ecf579 -r 08964b4b2fe4 pidgin/plugins/disco/xmppdisco.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/plugins/disco/xmppdisco.h Tue May 26 02:27:49 2009 +0000 @@ -0,0 +1,107 @@ +/* pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 + */ + +#ifndef PIDGIN_XMPP_DISCO_H +#define PIDGIN_XMPP_DISCO_H + +typedef struct _XmppDiscoService XmppDiscoService; + +#include "gtkdisco.h" + +#define XMPP_PLUGIN_ID "prpl-jabber" +#define NS_DISCO_INFO "http://jabber.org/protocol/disco#info" +#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items" +#define NS_MUC "http://jabber.org/protocol/muc" +#define NS_REGISTER "jabber:iq:register" + +#include "plugin.h" +extern PurplePlugin *my_plugin; + +/** + * The types of services. + */ +typedef enum +{ + XMPP_DISCO_SERVICE_TYPE_UNSET, + /** + * A registerable gateway to another protocol. An example would be + * XMPP legacy transports. + */ + XMPP_DISCO_SERVICE_TYPE_GATEWAY, + + /** + * A directory (e.g. allows the user to search for other users). + */ + XMPP_DISCO_SERVICE_TYPE_DIRECTORY, + + /** + * A chat (multi-user conversation). + */ + XMPP_DISCO_SERVICE_TYPE_CHAT, + + /** + * A pubsub collection (contains nodes) + */ + XMPP_DISCO_SERVICE_TYPE_PUBSUB_COLLECTION, + + /** + * A pubsub leaf (contains stuff, not nodes). + */ + XMPP_DISCO_SERVICE_TYPE_PUBSUB_LEAF, + + /** + * Something else. Do we need more categories? + */ + XMPP_DISCO_SERVICE_TYPE_OTHER +} XmppDiscoServiceType; + +/** + * The flags of services. + */ +typedef enum +{ + XMPP_DISCO_NONE = 0x0000, + XMPP_DISCO_ADD = 0x0001, /**< Supports an 'add' operation */ + XMPP_DISCO_BROWSE = 0x0002, /**< Supports browsing */ + XMPP_DISCO_REGISTER = 0x0004 /**< Supports a 'register' operation */ +} XmppDiscoServiceFlags; + +struct _XmppDiscoService { + PidginDiscoList *list; + gchar *name; + gchar *description; + + gchar *gateway_type; + XmppDiscoServiceType type; + XmppDiscoServiceFlags flags; + + XmppDiscoService *parent; + gchar *jid; + gchar *node; + gboolean expanded; +}; + +void xmpp_disco_start(PidginDiscoList *list); + +void xmpp_disco_service_expand(XmppDiscoService *service); +void xmpp_disco_service_register(XmppDiscoService *service); + +#endif /* PIDGIN_XMPP_DISCO_H */