1 /*
2 * Copyright (C) 2007,2008,2009 Colin DIDIER
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 /* XEP-0030: Service Discovery */
19
20 #include <string.h>
21
22 #include "module.h"
23 #include "signals.h"
24
25 #include "xmpp-servers.h"
26 #include "tools.h"
27 #include "disco.h"
28
29 #define XMLNS_DISCO "http://jabber.org/protocol/disco#info"
30
31 static GSList *my_features;
32
33 void
disco_add_feature(char * feature)34 disco_add_feature(char *feature)
35 {
36 g_return_if_fail(feature != NULL && *feature != '\0');
37 my_features = g_slist_insert_sorted(my_features, feature,
38 (GCompareFunc)strcmp);
39 }
40
41 gboolean
disco_have_feature(GSList * list,const char * feature)42 disco_have_feature(GSList *list, const char *feature)
43 {
44 GSList *tmp;
45
46 for (tmp = list; tmp != NULL; tmp = tmp->next)
47 if (strcmp(feature, tmp->data) == 0)
48 return TRUE;
49 return FALSE;
50 }
51
52 static void
cleanup_features(GSList * list)53 cleanup_features(GSList *list)
54 {
55 GSList *tmp, *next;
56
57 for (tmp = list; tmp != NULL; tmp = next) {
58 next = tmp->next;
59 g_free(tmp->data);
60 list = g_slist_remove(list, tmp->data);
61 }
62 }
63
64 void
disco_request(XMPP_SERVER_REC * server,const char * dest)65 disco_request(XMPP_SERVER_REC *server, const char *dest)
66 {
67 LmMessage *lmsg;
68 LmMessageNode *node;
69 char *recoded;
70
71 g_return_if_fail(IS_XMPP_SERVER(server));
72 g_return_if_fail(dest != NULL && dest != '\0');
73 recoded = xmpp_recode_out(dest);
74 lmsg = lm_message_new_with_sub_type(recoded, LM_MESSAGE_TYPE_IQ,
75 LM_MESSAGE_SUB_TYPE_GET);
76 g_free(recoded);
77 node = lm_message_node_add_child(lmsg->node, "query", NULL);
78 lm_message_node_set_attribute(node, XMLNS, XMLNS_DISCO);
79 signal_emit("xmpp send iq", 2, server, lmsg);
80 lm_message_unref(lmsg);
81 }
82
83 static void
send_disco(XMPP_SERVER_REC * server,const char * dest)84 send_disco(XMPP_SERVER_REC *server, const char *dest)
85 {
86 LmMessage *lmsg;
87 LmMessageNode *node, *child;
88 GSList *tmp;
89 char *recoded;
90
91 recoded = xmpp_recode_out(dest);
92 lmsg = lm_message_new_with_sub_type(recoded, LM_MESSAGE_TYPE_IQ,
93 LM_MESSAGE_SUB_TYPE_RESULT);
94 g_free(recoded);
95 node = lm_message_node_add_child(lmsg->node, "query", NULL);
96 lm_message_node_set_attribute(node, XMLNS, XMLNS_DISCO);
97 child = lm_message_node_add_child(node, "identity", NULL);
98 lm_message_node_set_attribute(child, "category", "client");
99 lm_message_node_set_attribute(child, "type", "console");
100 lm_message_node_set_attribute(child, "name", IRSSI_XMPP_PACKAGE);
101 for (tmp = my_features; tmp != NULL; tmp = tmp->next) {
102 child = lm_message_node_add_child(node, "feature", NULL);
103 lm_message_node_set_attribute(child, "var", tmp->data);
104 }
105 signal_emit("xmpp send iq", 2, server, lmsg);
106 lm_message_unref(lmsg);
107 }
108
109 static void
sig_recv_iq(XMPP_SERVER_REC * server,LmMessage * lmsg,const int type,const char * id,const char * from,const char * to)110 sig_recv_iq(XMPP_SERVER_REC *server, LmMessage *lmsg, const int type,
111 const char *id, const char *from, const char *to)
112 {
113 LmMessageNode *node;
114 GSList *features;
115
116 if (type == LM_MESSAGE_SUB_TYPE_RESULT) {
117 node = lm_find_node(lmsg->node, "query", XMLNS, XMLNS_DISCO);
118 if (node == NULL)
119 return;
120 features = NULL;
121 for (node = node->children; node != NULL; node = node->next) {
122 if (strcmp(node->name, "feature") == 0) {
123 features = g_slist_prepend(features,
124 xmpp_recode_in(
125 lm_message_node_get_attribute(node, "var")));
126 }
127 }
128 signal_emit("xmpp features", 3, server, from, features);
129 if (strcmp(from, server->domain) == 0) {
130 cleanup_features(server->server_features);
131 server->server_features = features;
132 signal_emit("xmpp server features", 1, server);
133 } else
134 cleanup_features(features);
135 } else if (type == LM_MESSAGE_SUB_TYPE_GET) {
136 node = lm_find_node(lmsg->node, "query", XMLNS, XMLNS_DISCO);
137 if (node != NULL)
138 send_disco(server, from);
139 }
140 }
141
142 static void
sig_connected(XMPP_SERVER_REC * server)143 sig_connected(XMPP_SERVER_REC *server)
144 {
145 if (IS_XMPP_SERVER(server))
146 disco_request(server, server->domain);
147 }
148
149 static void
sig_disconnected(XMPP_SERVER_REC * server)150 sig_disconnected(XMPP_SERVER_REC *server)
151 {
152 if (!IS_XMPP_SERVER(server))
153 return;
154 cleanup_features(server->server_features);
155 server->server_features = NULL;
156 }
157
158 void
disco_init(void)159 disco_init(void)
160 {
161 my_features = NULL;
162 disco_add_feature(XMLNS_DISCO);
163 signal_add("server connected", sig_connected);
164 signal_add("server disconnected", sig_disconnected);
165 signal_add("xmpp recv iq", sig_recv_iq);
166 }
167
168 void
disco_deinit(void)169 disco_deinit(void)
170 {
171 signal_remove("server connected", sig_connected);
172 signal_remove("server disconnected", sig_disconnected);
173 signal_remove("xmpp recv iq", sig_recv_iq);
174 g_slist_free(my_features);
175 }
176