updated on Wed Jan 25 16:08:47 UTC 2012
[aur-mirror.git] / pidgin-disco / disco-2.patch
blobf1c5035e94da28937f51bd8299a6ce8c112a50da
1 --- libpurple/disco.c 7b08cba2694eab268ca987664f70a6b9ff9738e5
2 +++ libpurple/disco.c 7b08cba2694eab268ca987664f70a6b9ff9738e5
3 @@ -0,0 +1,257 @@
4 +/**
5 + * @file disco.c Service Discovery API
6 + * @ingroup core
7 + */
9 +/* purple
10 + *
11 + * Purple is the legal property of its developers, whose names are too numerous
12 + * to list here. Please refer to the COPYRIGHT file distributed with this
13 + * source distribution.
14 + *
15 + * This program is free software; you can redistribute it and/or modify
16 + * it under the terms of the GNU General Public License as published by
17 + * the Free Software Foundation; either version 2 of the License, or
18 + * (at your option) any later version.
19 + *
20 + * This program is distributed in the hope that it will be useful,
21 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 + * GNU General Public License for more details.
24 + *
25 + * You should have received a copy of the GNU General Public License
26 + * along with this program; if not, write to the Free Software
27 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
28 + */
30 +#include "internal.h"
31 +#include "debug.h"
33 +#include "disco.h"
35 +static PurpleDiscoUiOps *ops = NULL;
37 +PurpleDiscoList *purple_disco_list_new(PurpleAccount *account, void *ui_data)
39 + PurpleDiscoList *list;
41 + g_return_val_if_fail(account != NULL, NULL);
43 + list = g_new0(PurpleDiscoList, 1);
44 + list->account = account;
45 + list->ref = 1;
46 + list->ui_data = ui_data;
48 + if (ops && ops->create)
49 + ops->create(list);
51 + return list;
54 +void purple_disco_list_ref(PurpleDiscoList *list)
56 + g_return_if_fail(list != NULL);
58 + list->ref++;
59 + purple_debug_misc("disco", "reffing list, ref count now %d\n", list->ref);
62 +static void purple_disco_list_service_destroy(PurpleDiscoList *list, PurpleDiscoService *r)
64 + g_free(r->name);
65 + g_free(r->description);
66 + g_free(r);
69 +static void purple_disco_list_destroy(PurpleDiscoList *list)
71 + GList *l;
73 + purple_debug_misc("disco", "destroying list %p\n", list);
75 + if (ops && ops->destroy)
76 + ops->destroy(list);
78 + for (l = list->services; l; l = l->next) {
79 + PurpleDiscoService *s = l->data;
80 + purple_disco_list_service_destroy(list, s);
81 + }
82 + g_list_free(list->services);
84 + g_free(list);
87 +void purple_disco_list_unref(PurpleDiscoList *list)
89 + g_return_if_fail(list != NULL);
90 + g_return_if_fail(list->ref > 0);
92 + list->ref--;
94 + purple_debug_misc("disco", "unreffing list, ref count now %d\n", list->ref);
95 + if (list->ref == 0)
96 + purple_disco_list_destroy(list);
99 +void purple_disco_list_service_add(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent)
101 + g_return_if_fail(list != NULL);
102 + g_return_if_fail(service != NULL);
104 + list->services = g_list_append(list->services, service);
105 + service->list = list;
107 + if (ops && ops->add_service)
108 + ops->add_service(list, service, parent);
111 +PurpleDiscoService *purple_disco_list_service_new(PurpleDiscoServiceCategory category, const gchar *name,
112 + PurpleDiscoServiceType type, const gchar *description, int flags)
114 + PurpleDiscoService *s;
116 + g_return_val_if_fail(name != NULL, NULL);
118 + s = g_new0(PurpleDiscoService, 1);
119 + s->category = category;
120 + s->name = g_strdup(name);
121 + s->type = type;
122 + s->description = g_strdup(description);
123 + s->flags = flags;
125 + return s;
128 +void purple_disco_get_list(PurpleConnection *gc, PurpleDiscoList *list)
130 + PurplePlugin *prpl = NULL;
131 + PurplePluginProtocolInfo *prpl_info = NULL;
133 + g_return_if_fail(gc != NULL);
135 + prpl = purple_connection_get_prpl(gc);
137 + if (prpl != NULL)
138 + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
140 + if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_get_list))
141 + prpl_info->disco_get_list(gc, list);
144 +void purple_disco_cancel_get_list(PurpleDiscoList *list)
146 + PurplePlugin *prpl = NULL;
147 + PurplePluginProtocolInfo *prpl_info = NULL;
148 + PurpleConnection *gc;
150 + g_return_if_fail(list != NULL);
152 + gc = purple_account_get_connection(list->account);
154 + g_return_if_fail(gc != NULL);
156 + if (gc)
157 + prpl = purple_connection_get_prpl(gc);
159 + if (prpl)
160 + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
162 + if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_cancel))
163 + prpl_info->disco_cancel(list);
166 +int purple_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service)
168 + PurplePlugin *prpl = NULL;
169 + PurplePluginProtocolInfo *prpl_info = NULL;
171 + g_return_val_if_fail(gc != NULL, -EINVAL);
173 + prpl = purple_connection_get_prpl(gc);
175 + if (prpl != NULL)
176 + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
178 + if (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_service_register))
179 + return prpl_info->disco_service_register(gc, service);
181 + return -EINVAL;
184 +void purple_disco_set_in_progress(PurpleDiscoList *list, gboolean in_progress)
186 + g_return_if_fail(list != NULL);
188 + list->in_progress = in_progress;
190 + if (ops && ops->in_progress)
191 + ops->in_progress(list, in_progress);
194 +gboolean purple_disco_get_in_progress(PurpleDiscoList *list)
196 + g_return_val_if_fail(list != NULL, FALSE);
198 + return list->in_progress;
201 +void purple_disco_list_set_account(PurpleDiscoList *list, PurpleAccount *account)
203 + list->account = account;
206 +PurpleAccount* purple_disco_list_get_account(PurpleDiscoList *list)
208 + return list->account;
211 +GList* spurple_disco_list_get_services(PurpleDiscoList *dl)
213 + return dl->services;
216 +void purple_disco_list_set_ui_data(PurpleDiscoList *list, gpointer ui_data)
218 + list->ui_data = ui_data;
221 +gpointer purple_disco_list_get_ui_data(PurpleDiscoList *list)
223 + return list->ui_data;
226 +void purple_disco_list_set_in_progress(PurpleDiscoList *list, gboolean in_progress)
228 + list->in_progress = in_progress;
231 +gboolean purple_disco_list_get_in_progress(PurpleDiscoList *list)
233 + return list->in_progress;
236 +void purple_disco_list_set_fetch_count(PurpleDiscoList *list, gint fetch_count)
238 + list->fetch_count = fetch_count;
239 + purple_debug_info("disco", "fetch_count = %d\n", fetch_count);
242 +gint purple_disco_list_get_fetch_count(PurpleDiscoList *list)
244 + return list->fetch_count;
247 +void purple_disco_list_set_proto_data(PurpleDiscoList *list, gpointer proto_data)
249 + list->proto_data = proto_data;
252 +gpointer purple_disco_list_get_proto_data(PurpleDiscoList *list)
254 + return list->proto_data;
257 +void purple_disco_set_ui_ops(PurpleDiscoUiOps *ui_ops)
259 + ops = ui_ops;
262 --- libpurple/disco.h 12a480589341ae81d963f919dbf030d7a6ef689b
263 +++ libpurple/disco.h 12a480589341ae81d963f919dbf030d7a6ef689b
264 @@ -0,0 +1,232 @@
265 +/**
266 + * @file disco.h Service Discovery API
267 + * @ingroup core
268 + */
270 +/* purple
272 + * Purple is the legal property of its developers, whose names are too numerous
273 + * to list here. Please refer to the COPYRIGHT file distributed with this
274 + * source distribution.
276 + * This program is free software; you can redistribute it and/or modify
277 + * it under the terms of the GNU General Public License as published by
278 + * the Free Software Foundation; either version 2 of the License, or
279 + * (at your option) any later version.
281 + * This program is distributed in the hope that it will be useful,
282 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
283 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
284 + * GNU General Public License for more details.
286 + * You should have received a copy of the GNU General Public License
287 + * along with this program; if not, write to the Free Software
288 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
289 + */
291 +#ifndef _PURPLE_DISCO_H_
292 +#define _PURPLE_DISCO_H_
295 +typedef struct _PurpleDiscoList PurpleDiscoList;
296 +typedef struct _PurpleDiscoService PurpleDiscoService;
297 +typedef struct _PurpleDiscoUiOps PurpleDiscoUiOps;
299 +#include "account.h"
301 +/**
302 + * Represents a list of services for a given connection on a given protocol.
303 + */
304 +struct _PurpleDiscoList {
305 + PurpleAccount *account; /**< The account this list belongs to. */
306 + GList *services; /**< The list of services. */
307 + gpointer *ui_data; /**< UI private data. */
308 + gboolean in_progress;
309 + gint fetch_count; /**< Uses in fetch processes */
310 + gpointer proto_data; /** Prpl private data. */
311 + guint ref; /**< The reference count. */
314 +/**
315 + * The categories of services.
316 + */
317 +typedef enum
319 + PURPLE_DISCO_SERVICE_CAT_NONE, /**< Other category */
320 + PURPLE_DISCO_SERVICE_CAT_GATEWAY,
321 + PURPLE_DISCO_SERVICE_CAT_DIRECTORY,
322 + PURPLE_DISCO_SERVICE_CAT_MUC
323 +} PurpleDiscoServiceCategory;
325 +/**
326 + * The types of services.
327 + */
328 +typedef enum
330 + PURPLE_DISCO_SERVICE_TYPE_NONE,
331 + PURPLE_DISCO_SERVICE_TYPE_XMPP,
332 + PURPLE_DISCO_SERVICE_TYPE_ICQ,
333 + PURPLE_DISCO_SERVICE_TYPE_MAIL,
334 + PURPLE_DISCO_SERVICE_TYPE_USER,
335 + PURPLE_DISCO_SERVICE_TYPE_YAHOO,
336 + PURPLE_DISCO_SERVICE_TYPE_GTALK,
337 + PURPLE_DISCO_SERVICE_TYPE_IRC,
338 + PURPLE_DISCO_SERVICE_TYPE_GG,
339 + PURPLE_DISCO_SERVICE_TYPE_AIM,
340 + PURPLE_DISCO_SERVICE_TYPE_QQ,
341 + PURPLE_DISCO_SERVICE_TYPE_MSN
342 +} PurpleDiscoServiceType;
344 +/**
345 + * The flags of services.
346 + */
347 +#define PURPLE_DISCO_FLAG_NONE 0
348 +#define PURPLE_DISCO_FLAG_ADD 1 << 0
349 +#define PURPLE_DISCO_FLAG_BROWSE 1 << 1
350 +#define PURPLE_DISCO_FLAG_REGISTER 1 << 2
352 +/**
353 + * Represents a list of services for a given connection on a given protocol.
354 + */
355 +struct _PurpleDiscoService {
356 + PurpleDiscoList *list;
357 + PurpleDiscoServiceCategory category; /**< The category of service. */
358 + gchar *name; /**< The name of the service. */
359 + PurpleDiscoServiceType type; /**< The type of service. */
360 + guint flags;
361 + gchar *description; /**< The name of the service. */
364 +struct _PurpleDiscoUiOps {
365 + void (*dialog_show_with_account)(PurpleAccount* account); /**< Force the ui to pop up a dialog */
366 + void (*create)(PurpleDiscoList *list); /**< Init ui resources */
367 + void (*destroy)(PurpleDiscoList *list); /**< Free ui resources */
368 + void (*add_service)(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent); /**< Add service to dialog */
369 + void (*in_progress)(PurpleDiscoList *list, gboolean in_progress); /**< Set progress to dialog */
372 +#ifdef __cplusplus
373 +extern "C" {
374 +#endif
376 +/**
377 + * Returns a newly created service discovery object.
379 + * It has an initial reference count of 1.
381 + * @param account The account that's listing rooms.
382 + * @return The new service discovery list handle.
383 + */
384 +PurpleDiscoList *purple_disco_list_new(PurpleAccount *account, void *ui_data);
386 +/**
387 + * Increases the reference count on the service discovery list.
389 + * @param list The object to ref.
390 + */
391 +void purple_disco_list_ref(PurpleDiscoList *list);
393 +/**
394 + * Decreases the reference count on the service discovery list.
396 + * The room list will be destroyed when this reaches 0.
398 + * @param list The room list object to unref and possibly
399 + * destroy.
400 + */
401 +void purple_disco_list_unref(PurpleDiscoList *list);
403 +/**
404 + * Instructs the prpl to start fetching the list.
406 + * @param gc The PurpleConnection to have get a list.
408 + */
409 +void purple_disco_get_list(PurpleConnection *gc, PurpleDiscoList *list);
411 +/**
412 + * Tells the prpl to stop fetching the list.
413 + * If this is possible and done, the prpl will
414 + * call set_in_progress with @c FALSE and possibly
415 + * unref the list if it took a reference.
417 + * @param list The service list to cancel a get_list on.
418 + */
419 +void purple_disco_cancel_get_list(PurpleDiscoList *list);
421 +/**
422 + * Create new service object
423 + */
424 +PurpleDiscoService *purple_disco_list_service_new(PurpleDiscoServiceCategory category, const gchar *name,
425 + PurpleDiscoServiceType type, const gchar *description, int flags);
427 +/**
428 + * Add service to list
429 + */
430 +void purple_disco_list_service_add(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent);
432 +/**
433 + * Set the "in progress" state of the Service Discovery.
435 + * The UI is encouraged to somehow hint to the user
436 + * whether or not we're busy downloading a service list or not.
438 + * @param list The service list.
439 + * @param in_progress We're downloading it, or we're not.
440 + */
441 +void purple_disco_set_in_progress(PurpleDiscoList *list, gboolean in_progress);
443 +/**
444 + * Gets the "in progress" state of the Service Discovery.
446 + * The UI is encouraged to somehow hint to the user
447 + * whether or not we're busy downloading a service list or not.
449 + * @param list The service list.
450 + * @return True if we're downloading it, or false if we're not.
451 + */
452 +gboolean purple_disco_get_in_progress(PurpleDiscoList *list);
455 +/**
456 + * Sets the UI operations structure to be used in all purple service discovery.
458 + * @param ops The UI operations structure.
459 + */
460 +void purple_disco_set_ui_ops(PurpleDiscoUiOps *ui_ops);
462 +/**
463 + * Register service
464 + * @param gc Connection
465 + * @param service The service that will be registered
466 + */
467 +int purple_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service);
469 +/**< Set/Get the account this list belongs to. */
470 +void purple_disco_list_set_account(PurpleDiscoList *list, PurpleAccount *account);
471 +PurpleAccount* purple_disco_list_get_account(PurpleDiscoList *list);
473 +/**< The list of services. */
474 +GList* spurple_disco_list_get_services(PurpleDiscoList *dl);
476 +/**< Set/Get UI private data. */
477 +void purple_disco_list_set_ui_data(PurpleDiscoList *list, gpointer ui_data);
478 +gpointer purple_disco_list_get_ui_data(PurpleDiscoList *list);
480 +/** Set/Get in progress flag */
481 +void purple_disco_list_set_in_progress(PurpleDiscoList *list, gboolean in_progress);
482 +gboolean purple_disco_list_get_in_progress(PurpleDiscoList *list);
484 +/** Set/Get fetch counter */
485 +void purple_disco_list_set_fetch_count(PurpleDiscoList *list, gint fetch_count);
486 +gint purple_disco_list_get_fetch_count(PurpleDiscoList *list);
488 +/** Set/Get prpl private data. */
489 +void purple_disco_list_set_proto_data(PurpleDiscoList *list, gpointer proto_data);
490 +gpointer purple_disco_list_get_proto_data(PurpleDiscoList *list);
492 +#ifdef __cplusplus
494 +#endif
496 +#endif /* _PURPLE_DISCO_H_ */
498 --- pidgin/gtkdisco.c cb47049a7b18d4f4e5c065df6877ae84839e1804
499 +++ pidgin/gtkdisco.c cb47049a7b18d4f4e5c065df6877ae84839e1804
500 @@ -0,0 +1,486 @@
501 +/**
502 + * @file gtkdisco.c GTK+ Service Discovery UI
503 + * @ingroup pidgin
504 + */
506 +/* pidgin
508 + * Pidgin is the legal property of its developers, whose names are too numerous
509 + * to list here. Please refer to the COPYRIGHT file distributed with this
510 + * source distribution.
512 + * This program is free software; you can redistribute it and/or modify
513 + * it under the terms of the GNU General Public License as published by
514 + * the Free Software Foundation; either version 2 of the License, or
515 + * (at your option) any later version.
517 + * This program is distributed in the hope that it will be useful,
518 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
519 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
520 + * GNU General Public License for more details.
522 + * You should have received a copy of the GNU General Public License
523 + * along with this program; if not, write to the Free Software
524 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
525 + */
527 +#include "internal.h"
528 +#include "pidgin.h"
529 +#include "gtkutils.h"
530 +#include "debug.h"
531 +#include "disco.h"
533 +#include "gtkdisco.h"
535 +typedef struct _PidginDiscoDialog {
536 + GtkWidget *window;
537 + GtkWidget *account_widget;
539 + GtkWidget *sw;
540 + GtkWidget *progress;
541 + GtkTreeStore *model;
542 + GtkWidget *tree;
543 + GHashTable *cats; /** Meow. */
545 + GtkWidget *stop_button;
546 + GtkWidget *list_button;
547 + GtkWidget *register_button;
548 + GtkWidget *add_button;
549 + GtkWidget *close_button;
551 + PurpleAccount *account;
552 + PurpleDiscoList *discolist;
553 +} PidginDiscoDialog;
555 +struct _menu_cb_info {
556 + PurpleDiscoList *list;
557 + PurpleDiscoService *service;
560 +enum {
561 + PIXBUF_COLUMN = 0,
562 + NAME_COLUMN,
563 + DESCRIPTION_COLUMN,
564 + SERVICE_COLUMN,
565 + NUM_OF_COLUMNS
568 +static void dialog_select_account_cb(GObject *w, PurpleAccount *account,
569 + PidginDiscoDialog *dialog)
571 + dialog->account = account;
574 +static void register_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
576 + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info");
577 + PurpleConnection *gc = purple_account_get_connection(info->list->account);
579 + purple_disco_service_register(gc, info->service);
582 +static void list_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
584 + PurpleConnection *gc;
586 + gc = purple_account_get_connection(dialog->account);
587 + if (!gc)
588 + return;
590 + if (dialog->discolist != NULL)
591 + purple_disco_list_unref(dialog->discolist);
593 + dialog->discolist = purple_disco_list_new(dialog->account, (void*) dialog);
595 + purple_disco_get_list(gc, dialog->discolist);
598 +static void add_room_to_blist_cb(GtkButton *button, PidginDiscoDialog *dialog)
600 + struct _menu_cb_info *info = g_object_get_data(G_OBJECT(button), "disco-info");
602 + if (info) {
603 + if (info->service->category == PURPLE_DISCO_SERVICE_CAT_MUC)
604 + purple_blist_request_add_chat(info->list->account, NULL, NULL, info->service->name);
605 + else
606 + purple_blist_request_add_buddy(info->list->account, info->service->name, NULL, NULL);
610 +static void
611 +selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog)
613 + PurpleDiscoService *service;
614 + GtkTreeIter iter;
615 + GValue val;
616 + static struct _menu_cb_info *info;
618 + if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
619 + val.g_type = 0;
620 + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog-> model), &iter, SERVICE_COLUMN, &val);
621 + service = g_value_get_pointer(&val);
622 + if (!service) {
623 + gtk_widget_set_sensitive(dialog->add_button, FALSE);
624 + gtk_widget_set_sensitive(dialog->register_button, FALSE);
625 + return;
628 + info = g_new0(struct _menu_cb_info, 1);
629 + info->list = dialog->discolist;
630 + info->service = service;
632 + g_object_set_data(G_OBJECT(dialog->add_button), "disco-info", info);
633 + g_object_set_data(G_OBJECT(dialog->register_button), "disco-info", info);
635 + gtk_widget_set_sensitive(dialog->add_button, service->flags & PURPLE_DISCO_FLAG_ADD);
636 + gtk_widget_set_sensitive(dialog->register_button, service->flags & PURPLE_DISCO_FLAG_REGISTER);
637 + } else {
638 + gtk_widget_set_sensitive(dialog->add_button, FALSE);
639 + gtk_widget_set_sensitive(dialog->register_button, FALSE);
643 +static gint
644 +delete_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d)
646 + PidginDiscoDialog *dialog = d;
648 + if (dialog->discolist)
649 + purple_disco_list_unref(dialog->discolist);
651 + g_free(dialog);
653 + return FALSE;
656 +static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
658 + purple_disco_cancel_get_list(dialog->discolist);
660 + if (dialog->account_widget)
661 + gtk_widget_set_sensitive(dialog->account_widget, TRUE);
663 + gtk_widget_set_sensitive(dialog->stop_button, FALSE);
664 + gtk_widget_set_sensitive(dialog->list_button, TRUE);
665 + gtk_widget_set_sensitive(dialog->add_button, FALSE);
668 +static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
670 + GtkWidget *window = dialog->window;
672 + delete_win_cb(NULL, NULL, dialog);
673 + gtk_widget_destroy(window);
676 +static gboolean account_filter_func(PurpleAccount *account)
678 + PurpleConnection *conn = purple_account_get_connection(account);
679 + PurplePluginProtocolInfo *prpl_info = NULL;
681 + if (conn)
682 + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(conn->prpl);
684 + return (prpl_info && PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, disco_get_list));
687 +gboolean
688 +pidgin_disco_is_showable()
690 + GList *c;
691 + PurpleConnection *gc;
693 + for (c = purple_connections_get_all(); c != NULL; c = c->next) {
694 + gc = c->data;
696 + if (account_filter_func(purple_connection_get_account(gc)))
697 + return TRUE;
700 + return FALSE;
703 +static void pidgin_disco_create_tree(PidginDiscoDialog *dialog)
705 + GtkCellRenderer *text_renderer, *pixbuf_renderer;
706 + GtkTreeViewColumn *column;
707 + GtkTreeSelection *selection;
709 + dialog->model = gtk_tree_store_new(NUM_OF_COLUMNS,
710 + GDK_TYPE_PIXBUF, /* PIXBUF_COLUMN */
711 + G_TYPE_STRING, /* NAME_COLUMN */
712 + G_TYPE_STRING, /* DESCRIPTION_COLUMN */
713 + G_TYPE_POINTER /* SERVICE_COLUMN */
714 + );
716 + dialog->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dialog->model));
717 + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(dialog->tree), TRUE);
719 + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->tree));
720 + g_signal_connect(G_OBJECT(selection), "changed",
721 + G_CALLBACK(selection_changed_cb), dialog);
723 + g_object_unref(dialog->model);
725 + gtk_container_add(GTK_CONTAINER(dialog->sw), dialog->tree);
726 + gtk_widget_show(dialog->tree);
728 + text_renderer = gtk_cell_renderer_text_new();
729 + pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
731 + column = gtk_tree_view_column_new();
732 + gtk_tree_view_column_set_title(column, _("Name"));
734 + gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
735 + gtk_tree_view_column_set_attributes(column, pixbuf_renderer,
736 + "pixbuf", PIXBUF_COLUMN, NULL);
738 + gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
739 + gtk_tree_view_column_set_attributes(column, text_renderer,
740 + "text", NAME_COLUMN, NULL);
742 + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
743 + GTK_TREE_VIEW_COLUMN_GROW_ONLY);
744 + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
745 + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN);
746 + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
747 + gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->tree), column);
749 + column = gtk_tree_view_column_new_with_attributes(_("Description"), text_renderer,
750 + "text", DESCRIPTION_COLUMN, NULL);
751 + gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
752 + GTK_TREE_VIEW_COLUMN_GROW_ONLY);
753 + gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
754 + gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), DESCRIPTION_COLUMN);
755 + gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
756 + gtk_tree_view_append_column(GTK_TREE_VIEW(dialog->tree), column);
759 +static PidginDiscoDialog*
760 +pidgin_disco_dialog_new_with_account(PurpleAccount *account)
762 + PidginDiscoDialog *dialog;
763 + GtkWidget *window, *vbox, *vbox2, *bbox;
765 + dialog = g_new0(PidginDiscoDialog, 1);
766 + dialog->account = account;
768 + /* Create the window. */
769 + dialog->window = window = pidgin_create_dialog(_("Service Discovery"), PIDGIN_HIG_BORDER, "service discovery", TRUE);
771 + g_signal_connect(G_OBJECT(window), "delete_event",
772 + G_CALLBACK(delete_win_cb), dialog);
774 + /* Create the parent vbox for everything. */
775 + vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
777 + vbox2 = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER);
778 + gtk_container_add(GTK_CONTAINER(vbox), vbox2);
779 + gtk_widget_show(vbox2);
781 + /* accounts dropdown list */
782 + dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE,
783 + G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog);
784 + if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */
785 + dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget);
786 + pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
788 + /* scrolled window */
789 + dialog->sw = gtk_scrolled_window_new(NULL, NULL);
790 + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(dialog->sw),
791 + GTK_SHADOW_IN);
792 + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dialog->sw),
793 + GTK_POLICY_AUTOMATIC,
794 + GTK_POLICY_AUTOMATIC);
795 + gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0);
796 + gtk_widget_set_size_request(dialog->sw, -1, 250);
797 + gtk_widget_show(dialog->sw);
799 + /* progress bar */
800 + dialog->progress = gtk_progress_bar_new();
801 + gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1);
802 + gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0);
803 + gtk_widget_show(dialog->progress);
806 + /* button box */
807 + bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
808 + gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
809 + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
811 + /* stop button */
812 + dialog->stop_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_STOP,
813 + G_CALLBACK(stop_button_cb), dialog);
814 + gtk_widget_set_sensitive(dialog->stop_button, FALSE);
816 + /* list button */
817 + dialog->list_button = pidgin_pixbuf_button_from_stock(_("_Get List"), GTK_STOCK_REFRESH,
818 + PIDGIN_BUTTON_HORIZONTAL);
819 + gtk_box_pack_start(GTK_BOX(bbox), dialog->list_button, FALSE, FALSE, 0);
820 + g_signal_connect(G_OBJECT(dialog->list_button), "clicked",
821 + G_CALLBACK(list_button_cb), dialog);
822 + gtk_widget_show(dialog->list_button);
824 + /* register button */
825 + dialog->register_button = pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"),
826 + G_CALLBACK(register_button_cb), dialog);
827 + gtk_widget_set_sensitive(dialog->register_button, FALSE);
829 + /* add button */
830 + dialog->add_button = pidgin_pixbuf_button_from_stock(_("_Add"), GTK_STOCK_ADD,
831 + PIDGIN_BUTTON_HORIZONTAL);
832 + gtk_box_pack_start(GTK_BOX(bbox), dialog->add_button, FALSE, FALSE, 0);
833 + g_signal_connect(G_OBJECT(dialog->add_button), "clicked",
834 + G_CALLBACK(add_room_to_blist_cb), dialog);
835 + gtk_widget_set_sensitive(dialog->add_button, FALSE);
836 + gtk_widget_show(dialog->add_button);
838 + /* close button */
839 + dialog->close_button = pidgin_dialog_add_button(GTK_DIALOG(window), GTK_STOCK_CLOSE,
840 + G_CALLBACK(close_button_cb), dialog);
842 + pidgin_disco_create_tree(dialog);
844 + /* show the dialog window and return the dialog */
845 + gtk_widget_show(dialog->window);
847 + return dialog;
850 +void
851 +pidgin_disco_dialog_show(void)
853 + pidgin_disco_dialog_new_with_account(NULL);
856 +void
857 +pidgin_disco_dialog_show_with_account(PurpleAccount* account)
859 + PidginDiscoDialog *dialog = pidgin_disco_dialog_new_with_account(account);
861 + if (!dialog)
862 + return;
864 + list_button_cb(GTK_BUTTON(dialog->list_button), dialog);
867 +static void
868 +pidgin_disco_create(PurpleDiscoList *list)
870 + PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data;
872 + dialog->cats = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)gtk_tree_row_reference_free);
876 +static void
877 +pidgin_disco_destroy(PurpleDiscoList *list)
879 + PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data;
881 + g_hash_table_destroy(dialog->cats);
884 +static void pidgin_disco_in_progress(PurpleDiscoList *list, gboolean in_progress)
886 + PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data;
888 + if (!dialog)
889 + return;
891 + if (in_progress) {
892 + gtk_tree_store_clear(dialog->model);
893 + if (dialog->account_widget)
894 + gtk_widget_set_sensitive(dialog->account_widget, FALSE);
895 + gtk_widget_set_sensitive(dialog->stop_button, TRUE);
896 + gtk_widget_set_sensitive(dialog->list_button, FALSE);
897 + } else {
898 + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0);
899 + if (dialog->account_widget)
900 + gtk_widget_set_sensitive(dialog->account_widget, TRUE);
901 + gtk_widget_set_sensitive(dialog->stop_button, FALSE);
902 + gtk_widget_set_sensitive(dialog->list_button, TRUE);
906 +static void pidgin_disco_add_service(PurpleDiscoList *list, PurpleDiscoService *service, PurpleDiscoService *parent)
908 + PidginDiscoDialog *dialog = (PidginDiscoDialog *) list->ui_data;
909 + GtkTreeIter iter, parent_iter;
910 + GtkTreeRowReference *rr;
911 + GtkTreePath *path;
912 + char *filename = NULL;
913 + GdkPixbuf *pixbuf = NULL;
915 + purple_debug_info("disco", "Add_service \"%s\"\n", service->name);
917 + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(dialog->progress));
919 + if (parent) {
920 + rr = g_hash_table_lookup(dialog->cats, parent);
921 + path = gtk_tree_row_reference_get_path(rr);
922 + if (path) {
923 + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &parent_iter, path);
924 + gtk_tree_path_free(path);
928 + gtk_tree_store_append(dialog->model, &iter, (parent ? &parent_iter : NULL));
930 + if (service->type == PURPLE_DISCO_SERVICE_TYPE_XMPP)
931 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "jabber.png", NULL);
932 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_ICQ)
933 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "icq.png", NULL);
934 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_YAHOO)
935 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "yahoo.png", NULL);
936 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_GTALK)
937 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "google-talk.png", NULL);
938 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_IRC)
939 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "irc.png", NULL);
940 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_GG)
941 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "gadu-gadu.png", NULL);
942 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_AIM)
943 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "aim.png", NULL);
944 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_QQ)
945 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "qq.png", NULL);
946 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_MSN)
947 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "protocols", "22", "msn.png", NULL);
948 + else if (service->type == PURPLE_DISCO_SERVICE_TYPE_USER)
949 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "person.png", NULL);
950 + else if (service->category == PURPLE_DISCO_SERVICE_CAT_MUC)
951 + filename = g_build_filename(DATADIR, "pixmaps", "pidgin", "status", "22", "chat.png", NULL);
953 + if (filename) {
954 + pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
955 + g_free(filename);
958 + gtk_tree_store_set(dialog->model, &iter,
959 + PIXBUF_COLUMN, pixbuf,
960 + NAME_COLUMN, service->name,
961 + DESCRIPTION_COLUMN, service->description,
962 + SERVICE_COLUMN, service,
963 + -1);
965 + path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model), &iter);
967 + rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model), path);
968 + g_hash_table_insert(dialog->cats, service, rr);
970 + gtk_tree_path_free(path);
972 + if (pixbuf)
973 + g_object_unref(pixbuf);
976 +static PurpleDiscoUiOps ops = {
977 + pidgin_disco_dialog_show_with_account,
978 + pidgin_disco_create,
979 + pidgin_disco_destroy,
980 + pidgin_disco_add_service,
981 + pidgin_disco_in_progress
984 +void pidgin_disco_init() {
985 + purple_disco_set_ui_ops(&ops);
988 --- pidgin/gtkdisco.h 1e643fc20a98b6c11e0038da0d8e9270b56dde33
989 +++ pidgin/gtkdisco.h 1e643fc20a98b6c11e0038da0d8e9270b56dde33
990 @@ -0,0 +1,56 @@
991 +/**
992 + * @file gtkdisco.c GTK+ Service Discovery UI
993 + * @ingroup pidgin
994 + */
996 +/* pidgin
998 + * Pidgin is the legal property of its developers, whose names are too numerous
999 + * to list here. Please refer to the COPYRIGHT file distributed with this
1000 + * source distribution.
1002 + * This program is free software; you can redistribute it and/or modify
1003 + * it under the terms of the GNU General Public License as published by
1004 + * the Free Software Foundation; either version 2 of the License, or
1005 + * (at your option) any later version.
1007 + * This program is distributed in the hope that it will be useful,
1008 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1009 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1010 + * GNU General Public License for more details.
1012 + * You should have received a copy of the GNU General Public License
1013 + * along with this program; if not, write to the Free Software
1014 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
1015 + */
1017 +#ifndef _GTK_DISCO_H_
1018 +#define _GTK_DISCO_H_
1021 +/**
1022 + * Initializes the service discovery subsystem.
1023 + */
1024 +void pidgin_disco_init(void);
1026 +/**
1027 + * Determines if showing the service discovery dialog is a valid action.
1029 + * @return TRUE if there are accounts online that support service
1030 + * discovery. Otherwise return FALSE.
1031 + */
1032 +gboolean pidgin_disco_is_showable(void);
1034 +/**
1035 + * Shows a new service discovery dialog.
1036 + */
1037 +void pidgin_disco_dialog_show(void);
1039 +/**
1040 + * Shows a new service discovery dialog and fetches the list for the specified account.
1042 + * @param account The account to use.
1043 + */
1044 +void pidgin_disco_dialog_show_with_account(PurpleAccount *account);
1046 +#endif /* _GTK_DISCO_H_ */
1048 --- finch/gntui.c 0a3dd5b122adf23040adadd21949a5ea7e51683d
1049 +++ finch/gntui.c d118c20ec0349bdc2b40f90379365804dec6a830
1050 @@ -91,6 +91,9 @@
1051 finch_roomlist_init();
1052 purple_roomlist_set_ui_ops(finch_roomlist_get_ui_ops());
1054 + /* Finch don't have service discovery UI */
1055 + purple_disco_set_ui_ops(NULL);
1057 gnt_register_action(_("Accounts"), finch_accounts_show_all);
1058 gnt_register_action(_("Buddy List"), finch_blist_show);
1059 gnt_register_action(_("Buddy Pounces"), finch_pounces_manager_show);
1061 --- libpurple/Makefile.am ea431ecfb45e9b1e546b7f8dd0d012a98bb1aaf7
1062 +++ libpurple/Makefile.am 687239120dd3f71d520ee0bae26095744e228b20
1063 @@ -65,6 +65,7 @@
1064 prpl.c \
1065 request.c \
1066 roomlist.c \
1067 + disco.c \
1068 savedstatuses.c \
1069 server.c \
1070 signals.c \
1071 @@ -118,6 +119,7 @@
1072 prpl.h \
1073 request.h \
1074 roomlist.h \
1075 + disco.h \
1076 savedstatuses.h \
1077 server.h \
1078 signals.h \
1079 @@ -155,7 +157,7 @@
1080 dbus_headers = dbus-bindings.h dbus-purple.h dbus-server.h dbus-useful.h dbus-define-api.h dbus-types.h
1082 dbus_exported = dbus-useful.h dbus-define-api.h account.h blist.h buddyicon.h \
1083 - connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h \
1084 + connection.h conversation.h core.h ft.h log.h notify.h prefs.h roomlist.h disco.h \
1085 savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h
1087 purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
1088 --- libpurple/Makefile.mingw b2ff98bfc1a3f857f03e3b3c6f337ed251d07635
1089 +++ libpurple/Makefile.mingw 677c5b3e5408027f5a1d8411705aa9e4840ce100
1090 @@ -42,6 +42,7 @@
1091 debug.c \
1092 dnsquery.c \
1093 dnssrv.c \
1094 + disco.c \
1095 eventloop.c \
1096 ft.c \
1097 circbuffer.c \
1099 --- libpurple/protocols/jabber/disco.c 15e05aeaa2b1e64bc8b77794391c734d82691a3e
1100 +++ libpurple/protocols/jabber/disco.c 4e8b89e320ee1d783536d45979aa37777b650239
1101 @@ -1,5 +1,5 @@
1103 - * purple - Jabber Protocol Plugin
1104 + * purple - Jabber Service Discovery
1106 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
1108 @@ -22,6 +22,8 @@
1109 #include "internal.h"
1110 #include "prefs.h"
1111 #include "debug.h"
1112 +#include "request.h"
1113 +#include "notify.h"
1115 #include "buddy.h"
1116 #include "google.h"
1117 @@ -32,8 +34,9 @@
1118 #include "roster.h"
1119 #include "pep.h"
1120 #include "adhoccommands.h"
1121 +#include "xdata.h"
1122 +#include "libpurple/disco.h"
1125 struct _jabber_disco_info_cb_data {
1126 gpointer data;
1127 JabberDiscoInfoCallback *callback;
1128 @@ -270,6 +273,8 @@
1129 capabilities |= JABBER_CAP_IQ_REGISTER;
1130 else if(!strcmp(var, "http://www.xmpp.org/extensions/xep-0199.html#ns"))
1131 capabilities |= JABBER_CAP_PING;
1132 + else if(!strcmp(var, "http://jabber.org/protocol/disco#items"))
1133 + capabilities |= JABBER_CAP_ITEMS;
1134 else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
1135 capabilities |= JABBER_CAP_ADHOC;
1137 @@ -309,7 +314,8 @@
1141 -void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) {
1142 +void jabber_disco_items_parse(JabberStream *js, xmlnode *packet)
1144 const char *from = xmlnode_get_attrib(packet, "from");
1145 const char *type = xmlnode_get_attrib(packet, "type");
1147 @@ -394,6 +400,12 @@
1151 +struct _disco_data {
1152 + PurpleDiscoList *list;
1153 + PurpleDiscoService *parent;
1154 + char *node;
1157 static void
1158 jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
1160 @@ -556,4 +568,366 @@
1161 jabber_iq_send(iq);
1164 +static PurpleDiscoServiceCategory
1165 +jabber_disco_category_from_string(const gchar *str)
1167 + if (!strcasecmp(str, "gateway"))
1168 + return PURPLE_DISCO_SERVICE_CAT_GATEWAY;
1169 + else if (!strcasecmp(str, "directory"))
1170 + return PURPLE_DISCO_SERVICE_CAT_DIRECTORY;
1171 + else if (!strcasecmp(str, "conference"))
1172 + return PURPLE_DISCO_SERVICE_CAT_MUC;
1174 + return PURPLE_DISCO_SERVICE_CAT_NONE;
1177 +static PurpleDiscoServiceType
1178 +jabber_disco_type_from_string(const gchar *str)
1180 + if (!strcasecmp(str, "xmpp"))
1181 + return PURPLE_DISCO_SERVICE_TYPE_XMPP;
1182 + else if (!strcasecmp(str, "icq"))
1183 + return PURPLE_DISCO_SERVICE_TYPE_ICQ;
1184 + else if (!strcasecmp(str, "mrim"))
1185 + return PURPLE_DISCO_SERVICE_TYPE_MAIL;
1186 + else if (!strcasecmp(str, "user"))
1187 + return PURPLE_DISCO_SERVICE_TYPE_USER;
1188 + else if (!strcasecmp(str, "yahoo"))
1189 + return PURPLE_DISCO_SERVICE_TYPE_YAHOO;
1190 + else if (!strcasecmp(str, "irc"))
1191 + return PURPLE_DISCO_SERVICE_TYPE_IRC;
1192 + else if (!strcasecmp(str, "gadu-gadu"))
1193 + return PURPLE_DISCO_SERVICE_TYPE_GG;
1194 + else if (!strcasecmp(str, "aim"))
1195 + return PURPLE_DISCO_SERVICE_TYPE_AIM;
1196 + else if (!strcasecmp(str, "qq"))
1197 + return PURPLE_DISCO_SERVICE_TYPE_QQ;
1198 + else if (!strcasecmp(str, "msn"))
1199 + return PURPLE_DISCO_SERVICE_TYPE_MSN;
1201 + return PURPLE_DISCO_SERVICE_TYPE_NONE;
1204 +static void
1205 +jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data);
1207 +static void
1208 +jabber_disco_service_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
1210 + struct _disco_data *disco_data = data;
1211 + PurpleDiscoList *list = disco_data->list;
1212 + PurpleDiscoService *parent = disco_data->parent;
1213 + const char *parent_node = disco_data->node;
1214 + xmlnode *query = xmlnode_get_child(packet, "query");
1215 + const char *from = xmlnode_get_attrib(packet, "from");
1216 + const char *result = xmlnode_get_attrib(packet, "type");
1217 + xmlnode *child;
1218 + gboolean has_items = FALSE;
1220 + purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
1222 + if (!from || !result || !query || (strcmp(result, "result")
1223 + || !purple_disco_list_get_proto_data(list))) {
1224 + if (!purple_disco_list_get_fetch_count(list))
1225 + purple_disco_set_in_progress(list, FALSE);
1227 + purple_disco_list_unref(list);
1228 + return;
1231 + query = xmlnode_get_child(packet, "query");
1233 + for(child = xmlnode_get_child(query, "item"); child;
1234 + child = xmlnode_get_next_twin(child)) {
1235 + JabberIq *iq;
1236 + xmlnode *q;
1237 + const char *jid, *node;
1238 + struct _disco_data *disco_data;
1239 + char *full_node;
1241 + if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
1242 + continue;
1244 + node = xmlnode_get_attrib(child, "node");
1246 + if (parent_node) {
1247 + if (node) {
1248 + full_node = g_new0(char, strlen(parent_node) + 1 + strlen(node) + 1);
1249 + strcat(full_node, parent_node);
1250 + strcat(full_node, "/");
1251 + strcat(full_node, node);
1252 + } else {
1253 + continue;
1255 + } else {
1256 + full_node = g_strdup(node);
1259 + disco_data = g_new0(struct _disco_data, 1);
1260 + disco_data->list = list;
1261 + disco_data->parent = parent;
1262 + disco_data->node = full_node;
1264 + has_items = TRUE;
1265 + purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
1266 + purple_disco_list_ref(list);
1267 + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
1268 + xmlnode_set_attrib(iq->node, "to", jid);
1269 + if (full_node && (q = xmlnode_get_child(iq->node, "query")))
1270 + xmlnode_set_attrib(q, "node", full_node);
1271 + jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
1273 + jabber_iq_send(iq);
1276 + if (!purple_disco_list_get_fetch_count(list))
1277 + purple_disco_set_in_progress(list, FALSE);
1278 + purple_disco_list_unref(list);
1280 + g_free(disco_data->node);
1281 + g_free(disco_data);
1284 +static void
1285 +jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data)
1287 + struct _disco_data *disco_data = data;
1288 + PurpleDiscoList *list = disco_data->list;
1289 + PurpleDiscoService *parent = disco_data->parent;
1290 + char *node = g_strdup(disco_data->node);
1291 + xmlnode *query, *ident, *child;
1292 + const char *from = xmlnode_get_attrib(packet, "from");
1293 + const char *result = xmlnode_get_attrib(packet, "type");
1294 + const char *acat, *atype, *adesc, *anode;
1295 + char *aname;
1296 + PurpleDiscoService *s;
1297 + PurpleDiscoServiceCategory cat;
1298 + PurpleDiscoServiceType type;
1299 + int flags = PURPLE_DISCO_FLAG_ADD;
1301 + g_free(disco_data->node);
1302 + g_free(disco_data);
1303 + purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
1305 + if (!from || !result || (strcmp(result, "result") || !purple_disco_list_get_proto_data(list))
1306 + || (!(query = xmlnode_get_child(packet, "query")))
1307 + || (!(ident = xmlnode_get_child(query, "identity")))) {
1308 + if (!purple_disco_list_get_fetch_count(list))
1309 + purple_disco_set_in_progress(list, FALSE);
1311 + purple_disco_list_unref(list);
1312 + return;
1315 + acat = xmlnode_get_attrib(ident, "category");
1316 + atype = xmlnode_get_attrib(ident, "type");
1317 + adesc = xmlnode_get_attrib(ident, "name");
1318 + anode = xmlnode_get_attrib(query, "node");
1320 + if (anode) {
1321 + aname = g_new0(char, strlen(from) + strlen(anode) + 1);
1322 + strcat(aname, from);
1323 + strcat(aname, anode);
1324 + } else {
1325 + aname = g_strdup(from);
1328 + cat = jabber_disco_category_from_string(acat);
1329 + type = jabber_disco_type_from_string(atype);
1331 + for (child = xmlnode_get_child(query, "feature"); child;
1332 + child = xmlnode_get_next_twin(child)) {
1333 + const char *var;
1335 + if (!(var = xmlnode_get_attrib(child, "var")))
1336 + continue;
1338 + if (!strcmp(var, "jabber:iq:register"))
1339 + flags |= PURPLE_DISCO_FLAG_REGISTER;
1341 + if (!strcmp(var, "http://jabber.org/protocol/disco#items"))
1342 + flags |= PURPLE_DISCO_FLAG_BROWSE;
1344 + if (!strcmp(var, "http://jabber.org/protocol/muc"))
1345 + cat = PURPLE_DISCO_SERVICE_CAT_MUC;
1348 + purple_debug_info("disco", "service %s, category %s (%d), type %s (%d), description %s, flags %04x\n",
1349 + aname,
1350 + acat, cat,
1351 + atype, type,
1352 + adesc, flags);
1354 + s = purple_disco_list_service_new(cat, aname, type, adesc, flags);
1355 + purple_disco_list_service_add(list, s, parent);
1357 + /* if (flags & PURPLE_DISCO_FLAG_BROWSE) - not all browsable services has this future */
1359 + xmlnode *q;
1360 + JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
1362 + purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
1363 + purple_disco_list_ref(list);
1364 + disco_data = g_new0(struct _disco_data, 1);
1365 + disco_data->list = list;
1366 + disco_data->parent = s;
1368 + xmlnode_set_attrib(iq->node, "to", from);
1369 + jabber_iq_set_callback(iq, jabber_disco_service_items_cb, disco_data);
1370 + if (anode && (q = xmlnode_get_child(iq->node, "query")))
1371 + xmlnode_set_attrib(q, "node", node);
1372 + jabber_iq_send(iq);
1375 + if (!purple_disco_list_get_fetch_count(list))
1376 + purple_disco_set_in_progress(list, FALSE);
1378 + purple_disco_list_unref(list);
1380 + g_free(aname);
1381 + g_free(node);
1384 +static void
1385 +jabber_disco_server_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
1387 + PurpleDiscoList *list = data;
1388 + xmlnode *query, *child;
1389 + const char *from = xmlnode_get_attrib(packet, "from");
1390 + const char *type = xmlnode_get_attrib(packet, "type");
1391 + gboolean has_items = FALSE;
1393 + if (!from || !type)
1394 + return;
1396 + if (strcmp(type, "result"))
1397 + return;
1399 + query = xmlnode_get_child(packet, "query");
1401 + for(child = xmlnode_get_child(query, "item"); child;
1402 + child = xmlnode_get_next_twin(child)) {
1403 + JabberIq *iq;
1404 + const char *jid;
1405 + struct _disco_data *disco_data;
1407 + if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
1408 + continue;
1410 + disco_data = g_new0(struct _disco_data, 1);
1411 + disco_data->list = list;
1413 + has_items = TRUE;
1414 + purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
1415 + purple_disco_list_ref(list);
1416 + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
1417 + xmlnode_set_attrib(iq->node, "to", jid);
1418 + jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
1420 + jabber_iq_send(iq);
1423 + if (!has_items)
1424 + purple_disco_set_in_progress(list, FALSE);
1426 + purple_disco_list_unref(list);
1429 +static void
1430 +jabber_disco_server_info_cb(JabberStream *js, const char *who, JabberCapabilities caps, gpointer data)
1432 + PurpleDiscoList *list = data;
1433 + JabberIq *iq;
1435 + if (caps & JABBER_CAP_ITEMS) {
1436 + iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
1437 + xmlnode_set_attrib(iq->node, "to", who);
1438 + jabber_iq_set_callback(iq, jabber_disco_server_items_cb, list);
1440 + if (purple_disco_list_get_proto_data(list))
1441 + jabber_iq_send(iq);
1442 + else
1443 + purple_disco_list_unref(list);
1445 + } else {
1446 + purple_notify_error(NULL, _("Error"), _("Server don't support service discovery"), NULL);
1447 + purple_disco_set_in_progress(list, FALSE);
1448 + purple_disco_list_unref(list);
1452 +static void
1453 +jabber_disco_server_cb(PurpleDiscoList *list, PurpleRequestFields *fields)
1455 + const char *server_name;
1457 + purple_disco_set_in_progress(list, TRUE);
1458 + server_name = purple_request_fields_get_string(fields, "server");
1459 + purple_debug_misc("jabber", "Service discovery for %s\n", server_name);
1461 + purple_prefs_set_string(DISCO_PREF_LAST_SERVER, server_name);
1463 + if (purple_disco_list_get_proto_data(list))
1464 + jabber_disco_info_do(purple_disco_list_get_proto_data(list),
1465 + server_name, jabber_disco_server_info_cb, list);
1468 +void
1469 +jabber_disco_get_list(PurpleConnection *gc, PurpleDiscoList *list)
1471 + PurpleRequestFields *fields;
1472 + PurpleRequestFieldGroup *g;
1473 + PurpleRequestField *f;
1474 + JabberStream *js;
1475 + const char *last_server = purple_prefs_get_string(DISCO_PREF_LAST_SERVER);
1477 + purple_debug_misc("disco.c", "get_list\n");
1479 + js = gc->proto_data;
1480 + purple_disco_list_set_proto_data(list, js);
1482 + fields = purple_request_fields_new();
1483 + g = purple_request_field_group_new(NULL);
1484 + f = purple_request_field_string_new("server", _("Server"),
1485 + last_server ? last_server : js->user->domain, FALSE);
1487 + purple_request_field_group_add_field(g, f);
1488 + purple_request_fields_add_group(fields, g);
1490 + purple_disco_list_ref(list);
1492 + purple_request_fields(gc,
1493 + _("Server name request"),
1494 + _("Enter server name"),
1495 + NULL,
1496 + fields,
1497 + _("OK"), G_CALLBACK(jabber_disco_server_cb),
1498 + _("Cancel"), NULL,
1499 + purple_connection_get_account(gc), NULL, NULL, list);
1502 +void
1503 +jabber_disco_cancel(PurpleDiscoList *list)
1505 + purple_disco_list_set_proto_data(list, NULL);
1506 + purple_disco_set_in_progress(list, FALSE);
1509 +int
1510 +jabber_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service)
1512 + JabberStream *js = gc->proto_data;
1514 + jabber_register_gateway(js, service->name);
1516 + return 0;
1519 +void jabber_disco_init(void)
1521 + purple_prefs_add_none("/purple/jabber");
1522 + purple_prefs_add_none("/purple/jabber/disco");
1524 + purple_prefs_add_string(DISCO_PREF_LAST_SERVER, NULL);
1528 --- libpurple/protocols/jabber/disco.h f30f10709e0282c73ff287b1c373440f9ae41e81
1529 +++ libpurple/protocols/jabber/disco.h 29da953b418e804fce3861879702bd64066b496a
1530 @@ -1,5 +1,5 @@
1532 - * @file iq.h JabberID handlers
1533 + * @file disco.h Jabber Service Discovery
1535 * purple
1537 @@ -24,6 +24,10 @@
1539 #include "jabber.h"
1541 +#define DISCO_PREF_LAST_SERVER "/purple/jabber/disco/last_server"
1543 +void jabber_disco_init(void);
1545 typedef void (JabberDiscoInfoCallback)(JabberStream *js, const char *who,
1546 JabberCapabilities capabilities, gpointer data);
1548 @@ -35,4 +39,10 @@
1549 void jabber_disco_info_do(JabberStream *js, const char *who,
1550 JabberDiscoInfoCallback *callback, gpointer data);
1552 +void jabber_disco_get_list(PurpleConnection *gc, PurpleDiscoList* list);
1553 +void jabber_disco_cancel(PurpleDiscoList *list);
1554 +void jabber_disco_get_childs(PurpleConnection *gc, PurpleDiscoService *service);
1556 +int jabber_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service);
1558 #endif /* _PURPLE_JABBER_DISCO_H_ */
1560 --- libpurple/protocols/jabber/jabber.c ffa2b86a187b53e8e165af133466d0a7e5251046
1561 +++ libpurple/protocols/jabber/jabber.c bf9735fec8a40e324451d21e28bcdb2dc4440588
1562 @@ -1074,22 +1074,24 @@
1563 group = purple_request_field_group_new(NULL);
1564 purple_request_fields_add_group(fields, group);
1566 - if(js->registration)
1567 - field = purple_request_field_string_new("username", _("Username"), js->user->node, FALSE);
1568 - else
1569 - field = purple_request_field_string_new("username", _("Username"), NULL, FALSE);
1570 + if(xmlnode_get_child(query, "username")) {
1571 + if(js->registration)
1572 + field = purple_request_field_string_new("username", _("Username"), js->user->node, FALSE);
1573 + else
1574 + field = purple_request_field_string_new("username", _("Username"), NULL, FALSE);
1576 - purple_request_field_group_add_field(group, field);
1577 + purple_request_field_group_add_field(group, field);
1579 + if(xmlnode_get_child(query, "password")) {
1580 + if(js->registration)
1581 + field = purple_request_field_string_new("password", _("Password"),
1582 + purple_connection_get_password(js->gc), FALSE);
1583 + else
1584 + field = purple_request_field_string_new("password", _("Password"), NULL, FALSE);
1586 - if(js->registration)
1587 - field = purple_request_field_string_new("password", _("Password"),
1588 - purple_connection_get_password(js->gc), FALSE);
1589 - else
1590 - field = purple_request_field_string_new("password", _("Password"), NULL, FALSE);
1592 - purple_request_field_string_set_masked(field, TRUE);
1593 - purple_request_field_group_add_field(group, field);
1595 + purple_request_field_string_set_masked(field, TRUE);
1596 + purple_request_field_group_add_field(group, field);
1598 if(xmlnode_get_child(query, "name")) {
1599 if(js->registration)
1600 field = purple_request_field_string_new("name", _("Name"),
1602 --- libpurple/protocols/jabber/jabber.h df574ab651cdebe290c7ce7f0a329167c562919a
1603 +++ libpurple/protocols/jabber/jabber.h b7df523dfc92963b33d884cc339f430c5ca0fd19
1604 @@ -44,6 +44,8 @@
1605 JABBER_CAP_ADHOC = 1 << 12,
1606 JABBER_CAP_BLOCKING = 1 << 13,
1608 + JABBER_CAP_ITEMS = 1 << 14,
1610 JABBER_CAP_RETRIEVED = 1 << 31
1611 } JabberCapabilities;
1614 --- libpurple/protocols/jabber/libxmpp.c 57be32934eb9f7a99243045cadba2059bcc3a4f4
1615 +++ libpurple/protocols/jabber/libxmpp.c e937ed9f40bff9fe19aa8524179a0ab63883f1ae
1616 @@ -34,6 +34,7 @@
1617 #include "iq.h"
1618 #include "jabber.h"
1619 #include "chat.h"
1620 +#include "disco.h"
1621 #include "message.h"
1622 #include "roster.h"
1623 #include "si.h"
1624 @@ -118,7 +119,11 @@
1625 jabber_attention_types, /* attention_types */
1627 sizeof(PurplePluginProtocolInfo), /* struct_size */
1628 - NULL
1629 + NULL,
1630 + jabber_disco_get_list, /* disco_get_list */
1631 + jabber_disco_cancel, /* disco_cancel */
1632 + jabber_disco_service_register /* disco_service_register */
1636 static gboolean load_plugin(PurplePlugin *plugin)
1637 @@ -280,7 +285,8 @@
1638 jabber_caps_init();
1640 jabber_data_init();
1642 + jabber_disco_init();
1644 jabber_add_feature("avatarmeta", AVATARNAMESPACEMETA, jabber_pep_namespace_only_when_pep_enabled_cb);
1645 jabber_add_feature("avatardata", AVATARNAMESPACEDATA, jabber_pep_namespace_only_when_pep_enabled_cb);
1646 jabber_add_feature("buzz", "http://www.xmpp.org/extensions/xep-0224.html#ns",
1647 --- libpurple/prpl.h f93c6c9088ab8fd3d2984da02ace7121f7dab5f5
1648 +++ libpurple/prpl.h f8e7474e9d1788e2fb97d9e6ebfd2734f6f054a2
1649 @@ -69,6 +69,7 @@
1650 #include "proxy.h"
1651 #include "plugin.h"
1652 #include "roomlist.h"
1653 +#include "disco.h"
1654 #include "status.h"
1655 #include "whiteboard.h"
1657 @@ -449,6 +450,21 @@
1658 * destroyed by the caller when it's no longer needed.
1660 GHashTable *(*get_account_text_table)(PurpleAccount *account);
1662 + /**
1663 + * Service discovery prpl callbacks
1664 + */
1665 + void (*disco_get_list)(PurpleConnection *gc, PurpleDiscoList *list);
1667 + /**
1668 + * Cancel fetching service list
1669 + */
1670 + void (*disco_cancel)(PurpleDiscoList *list);
1672 + /**
1673 + * Register service
1674 + */
1675 + int (*disco_service_register)(PurpleConnection *gc, PurpleDiscoService *service);
1678 #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
1679 --- libpurple/purple.h.in 1fa94bd111d8190501cf48321c9b4fd6d2e5261d
1680 +++ libpurple/purple.h.in dea46f95559f744f817499d5d4e480c9a8bd4bce
1681 @@ -79,6 +79,7 @@
1682 #include <prpl.h>
1683 #include <request.h>
1684 #include <roomlist.h>
1685 +#include <disco.h>
1686 #include <savedstatuses.h>
1687 #include <server.h>
1688 #include <signals.h>
1690 --- pidgin/Makefile.am d51a79bde1a00dc66f425cfc3fa80792c80656af
1691 +++ pidgin/Makefile.am 688c7b357b147c28ce57416eb995c79b5ce0375e
1692 @@ -108,6 +108,7 @@
1693 gtkprivacy.c \
1694 gtkrequest.c \
1695 gtkroomlist.c \
1696 + gtkdisco.c \
1697 gtksavedstatuses.c \
1698 gtkscrollbook.c \
1699 gtksession.c \
1700 @@ -161,6 +162,7 @@
1701 gtkpounce.h \
1702 gtkrequest.h \
1703 gtkroomlist.h \
1704 + gtkdisco.h \
1705 gtksavedstatuses.h \
1706 gtkscrollbook.h \
1707 gtksession.h \
1709 --- pidgin/Makefile.mingw cd6e78bb6c96ba89595feeffa33c29f7f4d74c26
1710 +++ pidgin/Makefile.mingw 4ee397e1e56cd14e0f6c8a4a7f128b8db05400fa
1711 @@ -63,6 +63,7 @@
1712 gtkconv.c \
1713 gtkdebug.c \
1714 gtkdialogs.c \
1715 + gtkdisco.c \
1716 gtkdnd-hints.c \
1717 gtkdocklet.c \
1718 gtkeventloop.c \
1720 --- pidgin/gtkblist.c e8f765b444cae9e40419e924045e4e59b67a1e18
1721 +++ pidgin/gtkblist.c 7d7b2fee41ab9307afe901a2b43863c373f93e55
1722 @@ -55,6 +55,7 @@
1723 #include "gtkprefs.h"
1724 #include "gtkprivacy.h"
1725 #include "gtkroomlist.h"
1726 +#include "gtkdisco.h"
1727 #include "gtkstatusbox.h"
1728 #include "gtkscrollbook.h"
1729 #include "gtksmiley.h"
1730 @@ -3293,6 +3294,7 @@
1731 { N_("/Tools/Smile_y"), "<CTL>Y", pidgin_smiley_manager_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_SMILEY },
1732 { "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL },
1733 { N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER },
1734 + { N_("/Tools/Service _Discovery"), NULL, pidgin_disco_dialog_show, 0, "<Item>", NULL },
1735 { N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL },
1736 { N_("/Tools/System _Log"), NULL, gtk_blist_show_systemlog_cb, 3, "<Item>", NULL },
1737 { "/Tools/sep3", NULL, NULL, 0, "<Separator>", NULL },
1738 @@ -4153,6 +4155,9 @@
1740 widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Room List"));
1741 gtk_widget_set_sensitive(widget, pidgin_roomlist_is_showable());
1743 + widget = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Service Discovery"));
1744 + gtk_widget_set_sensitive(widget, pidgin_disco_is_showable());
1747 static void
1749 --- pidgin/gtkmain.c 9969c0180c98f26e1312f35b33092667914b9dea
1750 +++ pidgin/gtkmain.c 13fad327dafb8746dfc36ab5c8f6d2a08dec78b9
1751 @@ -60,6 +60,7 @@
1752 #include "gtkprivacy.h"
1753 #include "gtkrequest.h"
1754 #include "gtkroomlist.h"
1755 +#include "gtkdisco.h"
1756 #include "gtksavedstatuses.h"
1757 #include "gtksession.h"
1758 #include "gtksmiley.h"
1759 @@ -307,6 +308,7 @@
1760 pidgin_privacy_init();
1761 pidgin_xfers_init();
1762 pidgin_roomlist_init();
1763 + pidgin_disco_init();
1764 pidgin_log_init();
1765 pidgin_docklet_init();
1766 pidgin_smileys_init();