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