comparison libpurple/protocols/jabber/disco.c @ 26329:f5e613e05332

Applied disco-2.patch from nops with some modifications: * Alphabetized includes and Makefiles * Removed purple_disco_set_ui_ops(NULL) in finch; ops is NULL by default. * A few string changes * Removed DISCO_PREF_LAST_SERVER. Default to our server, but store the last requested in the JabberStream* and use it if available.
author Paul Aurich <paul@darkrain42.org>
date Sun, 29 Mar 2009 19:29:22 +0000
parents 5f9a24d1c25e
children f19c214201db
comparison
equal deleted inserted replaced
26328:c422c7b1bde7 26329:f5e613e05332
1 /* 1 /*
2 * purple - Jabber Protocol Plugin 2 * purple - Jabber Service Discovery
3 * 3 *
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> 4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com>
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * it under the terms of the GNU General Public License as published by
20 */ 20 */
21 21
22 #include "internal.h" 22 #include "internal.h"
23 #include "prefs.h" 23 #include "prefs.h"
24 #include "debug.h" 24 #include "debug.h"
25 #include "request.h"
26 #include "notify.h"
25 27
26 #include "buddy.h" 28 #include "buddy.h"
27 #include "google.h" 29 #include "google.h"
28 #include "iq.h" 30 #include "iq.h"
29 #include "disco.h" 31 #include "disco.h"
30 #include "jabber.h" 32 #include "jabber.h"
31 #include "presence.h" 33 #include "presence.h"
32 #include "roster.h" 34 #include "roster.h"
33 #include "pep.h" 35 #include "pep.h"
34 #include "adhoccommands.h" 36 #include "adhoccommands.h"
35 37 #include "xdata.h"
38 #include "libpurple/disco.h"
36 39
37 struct _jabber_disco_info_cb_data { 40 struct _jabber_disco_info_cb_data {
38 gpointer data; 41 gpointer data;
39 JabberDiscoInfoCallback *callback; 42 JabberDiscoInfoCallback *callback;
40 }; 43 };
266 capabilities |= JABBER_CAP_IQ_SEARCH; 269 capabilities |= JABBER_CAP_IQ_SEARCH;
267 else if(!strcmp(var, "jabber:iq:register")) 270 else if(!strcmp(var, "jabber:iq:register"))
268 capabilities |= JABBER_CAP_IQ_REGISTER; 271 capabilities |= JABBER_CAP_IQ_REGISTER;
269 else if(!strcmp(var, "http://www.xmpp.org/extensions/xep-0199.html#ns")) 272 else if(!strcmp(var, "http://www.xmpp.org/extensions/xep-0199.html#ns"))
270 capabilities |= JABBER_CAP_PING; 273 capabilities |= JABBER_CAP_PING;
274 else if(!strcmp(var, "http://jabber.org/protocol/disco#items"))
275 capabilities |= JABBER_CAP_ITEMS;
271 else if(!strcmp(var, "http://jabber.org/protocol/commands")) { 276 else if(!strcmp(var, "http://jabber.org/protocol/commands")) {
272 capabilities |= JABBER_CAP_ADHOC; 277 capabilities |= JABBER_CAP_ADHOC;
273 } 278 }
274 else if(!strcmp(var, "http://jabber.org/protocol/ibb")) { 279 else if(!strcmp(var, "http://jabber.org/protocol/ibb")) {
275 purple_debug_info("jabber", "remote supports IBB\n"); 280 purple_debug_info("jabber", "remote supports IBB\n");
309 jdicd->callback(js, from, capabilities, jdicd->data); 314 jdicd->callback(js, from, capabilities, jdicd->data);
310 g_hash_table_remove(js->disco_callbacks, from); 315 g_hash_table_remove(js->disco_callbacks, from);
311 } 316 }
312 } 317 }
313 318
314 void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) { 319 void jabber_disco_items_parse(JabberStream *js, xmlnode *packet)
320 {
315 const char *from = xmlnode_get_attrib(packet, "from"); 321 const char *from = xmlnode_get_attrib(packet, "from");
316 const char *type = xmlnode_get_attrib(packet, "type"); 322 const char *type = xmlnode_get_attrib(packet, "type");
317 323
318 if(type && !strcmp(type, "get")) { 324 if(type && !strcmp(type, "get")) {
319 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, 325 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
393 399
394 g_strfreev(ft_proxy_list); 400 g_strfreev(ft_proxy_list);
395 } 401 }
396 402
397 } 403 }
404
405 struct _disco_data {
406 PurpleDiscoList *list;
407 PurpleDiscoService *parent;
408 char *node;
409 };
398 410
399 static void 411 static void
400 jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data) 412 jabber_disco_server_info_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
401 { 413 {
402 xmlnode *query, *child; 414 xmlnode *query, *child;
556 xmlnode_set_attrib(iq->node, "to", who); 568 xmlnode_set_attrib(iq->node, "to", who);
557 569
558 jabber_iq_send(iq); 570 jabber_iq_send(iq);
559 } 571 }
560 572
561 573 static PurpleDiscoServiceCategory
574 jabber_disco_category_from_string(const gchar *str)
575 {
576 if (!strcasecmp(str, "gateway"))
577 return PURPLE_DISCO_SERVICE_CAT_GATEWAY;
578 else if (!strcasecmp(str, "directory"))
579 return PURPLE_DISCO_SERVICE_CAT_DIRECTORY;
580 else if (!strcasecmp(str, "conference"))
581 return PURPLE_DISCO_SERVICE_CAT_MUC;
582
583 return PURPLE_DISCO_SERVICE_CAT_NONE;
584 }
585
586 static PurpleDiscoServiceType
587 jabber_disco_type_from_string(const gchar *str)
588 {
589 if (!strcasecmp(str, "xmpp"))
590 return PURPLE_DISCO_SERVICE_TYPE_XMPP;
591 else if (!strcasecmp(str, "icq"))
592 return PURPLE_DISCO_SERVICE_TYPE_ICQ;
593 else if (!strcasecmp(str, "mrim"))
594 return PURPLE_DISCO_SERVICE_TYPE_MAIL;
595 else if (!strcasecmp(str, "user"))
596 return PURPLE_DISCO_SERVICE_TYPE_USER;
597 else if (!strcasecmp(str, "yahoo"))
598 return PURPLE_DISCO_SERVICE_TYPE_YAHOO;
599 else if (!strcasecmp(str, "irc"))
600 return PURPLE_DISCO_SERVICE_TYPE_IRC;
601 else if (!strcasecmp(str, "gadu-gadu"))
602 return PURPLE_DISCO_SERVICE_TYPE_GG;
603 else if (!strcasecmp(str, "aim"))
604 return PURPLE_DISCO_SERVICE_TYPE_AIM;
605 else if (!strcasecmp(str, "qq"))
606 return PURPLE_DISCO_SERVICE_TYPE_QQ;
607 else if (!strcasecmp(str, "msn"))
608 return PURPLE_DISCO_SERVICE_TYPE_MSN;
609
610 return PURPLE_DISCO_SERVICE_TYPE_NONE;
611 }
612
613 static void
614 jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data);
615
616 static void
617 jabber_disco_service_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
618 {
619 struct _disco_data *disco_data = data;
620 PurpleDiscoList *list = disco_data->list;
621 PurpleDiscoService *parent = disco_data->parent;
622 const char *parent_node = disco_data->node;
623 xmlnode *query = xmlnode_get_child(packet, "query");
624 const char *from = xmlnode_get_attrib(packet, "from");
625 const char *result = xmlnode_get_attrib(packet, "type");
626 xmlnode *child;
627 gboolean has_items = FALSE;
628
629 purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
630
631 if (!from || !result || !query || (strcmp(result, "result")
632 || !purple_disco_list_get_proto_data(list))) {
633 if (!purple_disco_list_get_fetch_count(list))
634 purple_disco_set_in_progress(list, FALSE);
635
636 purple_disco_list_unref(list);
637 return;
638 }
639
640 query = xmlnode_get_child(packet, "query");
641
642 for(child = xmlnode_get_child(query, "item"); child;
643 child = xmlnode_get_next_twin(child)) {
644 JabberIq *iq;
645 xmlnode *q;
646 const char *jid, *node;
647 struct _disco_data *disco_data;
648 char *full_node;
649
650 if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
651 continue;
652
653 node = xmlnode_get_attrib(child, "node");
654
655 if (parent_node) {
656 if (node) {
657 full_node = g_new0(char, strlen(parent_node) + 1 + strlen(node) + 1);
658 strcat(full_node, parent_node);
659 strcat(full_node, "/");
660 strcat(full_node, node);
661 } else {
662 continue;
663 }
664 } else {
665 full_node = g_strdup(node);
666 }
667
668 disco_data = g_new0(struct _disco_data, 1);
669 disco_data->list = list;
670 disco_data->parent = parent;
671 disco_data->node = full_node;
672
673 has_items = TRUE;
674 purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
675 purple_disco_list_ref(list);
676 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
677 xmlnode_set_attrib(iq->node, "to", jid);
678 if (full_node && (q = xmlnode_get_child(iq->node, "query")))
679 xmlnode_set_attrib(q, "node", full_node);
680 jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
681
682 jabber_iq_send(iq);
683 }
684
685 if (!purple_disco_list_get_fetch_count(list))
686 purple_disco_set_in_progress(list, FALSE);
687 purple_disco_list_unref(list);
688
689 g_free(disco_data->node);
690 g_free(disco_data);
691 }
692
693 static void
694 jabber_disco_service_info_cb(JabberStream *js, xmlnode *packet, gpointer data)
695 {
696 struct _disco_data *disco_data = data;
697 PurpleDiscoList *list = disco_data->list;
698 PurpleDiscoService *parent = disco_data->parent;
699 char *node = g_strdup(disco_data->node);
700 xmlnode *query, *ident, *child;
701 const char *from = xmlnode_get_attrib(packet, "from");
702 const char *result = xmlnode_get_attrib(packet, "type");
703 const char *acat, *atype, *adesc, *anode;
704 char *aname;
705 PurpleDiscoService *s;
706 PurpleDiscoServiceCategory cat;
707 PurpleDiscoServiceType type;
708 int flags = PURPLE_DISCO_FLAG_ADD;
709
710 g_free(disco_data->node);
711 g_free(disco_data);
712 purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) - 1);
713
714 if (!from || !result || (strcmp(result, "result") || !purple_disco_list_get_proto_data(list))
715 || (!(query = xmlnode_get_child(packet, "query")))
716 || (!(ident = xmlnode_get_child(query, "identity")))) {
717 if (!purple_disco_list_get_fetch_count(list))
718 purple_disco_set_in_progress(list, FALSE);
719
720 purple_disco_list_unref(list);
721 return;
722 }
723
724 acat = xmlnode_get_attrib(ident, "category");
725 atype = xmlnode_get_attrib(ident, "type");
726 adesc = xmlnode_get_attrib(ident, "name");
727 anode = xmlnode_get_attrib(query, "node");
728
729 if (anode) {
730 aname = g_new0(char, strlen(from) + strlen(anode) + 1);
731 strcat(aname, from);
732 strcat(aname, anode);
733 } else {
734 aname = g_strdup(from);
735 }
736
737 cat = jabber_disco_category_from_string(acat);
738 type = jabber_disco_type_from_string(atype);
739
740 for (child = xmlnode_get_child(query, "feature"); child;
741 child = xmlnode_get_next_twin(child)) {
742 const char *var;
743
744 if (!(var = xmlnode_get_attrib(child, "var")))
745 continue;
746
747 if (!strcmp(var, "jabber:iq:register"))
748 flags |= PURPLE_DISCO_FLAG_REGISTER;
749
750 if (!strcmp(var, "http://jabber.org/protocol/disco#items"))
751 flags |= PURPLE_DISCO_FLAG_BROWSE;
752
753 if (!strcmp(var, "http://jabber.org/protocol/muc"))
754 cat = PURPLE_DISCO_SERVICE_CAT_MUC;
755 }
756
757 purple_debug_info("disco", "service %s, category %s (%d), type %s (%d), description %s, flags %04x\n",
758 aname,
759 acat, cat,
760 atype, type,
761 adesc, flags);
762
763 s = purple_disco_list_service_new(cat, aname, type, adesc, flags);
764 purple_disco_list_service_add(list, s, parent);
765
766 /* if (flags & PURPLE_DISCO_FLAG_BROWSE) - not all browsable services has this future */
767 {
768 xmlnode *q;
769 JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
770
771 purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
772 purple_disco_list_ref(list);
773 disco_data = g_new0(struct _disco_data, 1);
774 disco_data->list = list;
775 disco_data->parent = s;
776
777 xmlnode_set_attrib(iq->node, "to", from);
778 jabber_iq_set_callback(iq, jabber_disco_service_items_cb, disco_data);
779 if (anode && (q = xmlnode_get_child(iq->node, "query")))
780 xmlnode_set_attrib(q, "node", node);
781 jabber_iq_send(iq);
782 }
783
784 if (!purple_disco_list_get_fetch_count(list))
785 purple_disco_set_in_progress(list, FALSE);
786
787 purple_disco_list_unref(list);
788
789 g_free(aname);
790 g_free(node);
791 }
792
793 static void
794 jabber_disco_server_items_cb(JabberStream *js, xmlnode *packet, gpointer data)
795 {
796 PurpleDiscoList *list = data;
797 xmlnode *query, *child;
798 const char *from = xmlnode_get_attrib(packet, "from");
799 const char *type = xmlnode_get_attrib(packet, "type");
800 gboolean has_items = FALSE;
801
802 if (!from || !type)
803 return;
804
805 if (strcmp(type, "result"))
806 return;
807
808 query = xmlnode_get_child(packet, "query");
809
810 for(child = xmlnode_get_child(query, "item"); child;
811 child = xmlnode_get_next_twin(child)) {
812 JabberIq *iq;
813 const char *jid;
814 struct _disco_data *disco_data;
815
816 if(!(jid = xmlnode_get_attrib(child, "jid")) || !purple_disco_list_get_proto_data(list))
817 continue;
818
819 disco_data = g_new0(struct _disco_data, 1);
820 disco_data->list = list;
821
822 has_items = TRUE;
823 purple_disco_list_set_fetch_count(list, purple_disco_list_get_fetch_count(list) + 1);
824 purple_disco_list_ref(list);
825 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#info");
826 xmlnode_set_attrib(iq->node, "to", jid);
827 jabber_iq_set_callback(iq, jabber_disco_service_info_cb, disco_data);
828
829 jabber_iq_send(iq);
830 }
831
832 if (!has_items)
833 purple_disco_set_in_progress(list, FALSE);
834
835 purple_disco_list_unref(list);
836 }
837
838 static void
839 jabber_disco_server_info_cb(JabberStream *js, const char *who, JabberCapabilities caps, gpointer data)
840 {
841 PurpleDiscoList *list = data;
842 JabberIq *iq;
843
844 if (caps & JABBER_CAP_ITEMS) {
845 iq = jabber_iq_new_query(js, JABBER_IQ_GET, "http://jabber.org/protocol/disco#items");
846 xmlnode_set_attrib(iq->node, "to", who);
847 jabber_iq_set_callback(iq, jabber_disco_server_items_cb, list);
848
849 if (purple_disco_list_get_proto_data(list))
850 jabber_iq_send(iq);
851 else
852 purple_disco_list_unref(list);
853
854 } else {
855 purple_notify_error(NULL, _("Error"), _("Server doesn't support service discovery"), NULL);
856 purple_disco_set_in_progress(list, FALSE);
857 purple_disco_list_unref(list);
858 }
859 }
860
861 static void
862 jabber_disco_server_cb(PurpleDiscoList *list, PurpleRequestFields *fields)
863 {
864 JabberStream *js;
865 const char *server_name;
866
867 server_name = purple_request_fields_get_string(fields, "server");
868
869 js = purple_disco_list_get_proto_data(list);
870 if (!js) {
871 purple_debug_error("jabber", "Service discovery requested for %s "
872 "without proto_data", server_name);
873 return;
874 }
875
876 purple_disco_set_in_progress(list, TRUE);
877 purple_debug_misc("jabber", "Service discovery for %s\n", server_name);
878 if (js->last_disco_server)
879 g_free(js->last_disco_server);
880 js->last_disco_server = g_strdup(server_name);
881
882 jabber_disco_info_do(js, server_name, jabber_disco_server_info_cb, list);
883 }
884
885 void
886 jabber_disco_get_list(PurpleConnection *gc, PurpleDiscoList *list)
887 {
888 PurpleRequestFields *fields;
889 PurpleRequestFieldGroup *g;
890 PurpleRequestField *f;
891 JabberStream *js;
892 const char *last_server;
893
894 purple_debug_misc("disco.c", "get_list\n");
895
896 js = purple_connection_get_protocol_data(gc);
897 purple_disco_list_set_proto_data(list, js);
898
899 last_server = js->last_disco_server;
900 if (last_server == NULL)
901 last_server = js->user->domain;
902
903
904 fields = purple_request_fields_new();
905 g = purple_request_field_group_new(NULL);
906 f = purple_request_field_string_new("server", _("Server"),
907 last_server ? last_server : js->user->domain, FALSE);
908
909 purple_request_field_group_add_field(g, f);
910 purple_request_fields_add_group(fields, g);
911
912 purple_disco_list_ref(list);
913
914 purple_request_fields(gc,
915 _("Server name request"),
916 _("Enter server name"),
917 NULL,
918 fields,
919 _("OK"), G_CALLBACK(jabber_disco_server_cb),
920 _("Cancel"), NULL,
921 purple_connection_get_account(gc), NULL, NULL, list);
922 }
923
924 void
925 jabber_disco_cancel(PurpleDiscoList *list)
926 {
927 purple_disco_list_set_proto_data(list, NULL);
928 purple_disco_set_in_progress(list, FALSE);
929 }
930
931 int
932 jabber_disco_service_register(PurpleConnection *gc, PurpleDiscoService *service)
933 {
934 JabberStream *js = gc->proto_data;
935
936 jabber_register_gateway(js, service->name);
937
938 return 0;
939 }
940