| |
1 /* |
| |
2 * gaim - Jabber Protocol Plugin |
| |
3 * |
| |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> |
| |
5 * |
| |
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 |
| |
8 * the Free Software Foundation; either version 2 of the License, or |
| |
9 * (at your option) any later version. |
| |
10 * |
| |
11 * This program is distributed in the hope that it will be useful, |
| |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
14 * GNU General Public License for more details. |
| |
15 * |
| |
16 * You should have received a copy of the GNU General Public License |
| |
17 * along with this program; if not, write to the Free Software |
| |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
19 * |
| |
20 */ |
| |
21 #include "internal.h" |
| |
22 #include "debug.h" |
| |
23 #include "prefs.h" |
| |
24 #include "util.h" |
| |
25 |
| |
26 #include "buddy.h" |
| |
27 #include "disco.h" |
| |
28 #include "google.h" |
| |
29 #include "iq.h" |
| |
30 #include "oob.h" |
| |
31 #include "roster.h" |
| |
32 #include "si.h" |
| |
33 |
| |
34 #ifdef _WIN32 |
| |
35 #include "utsname.h" |
| |
36 #endif |
| |
37 |
| |
38 GHashTable *iq_handlers = NULL; |
| |
39 |
| |
40 |
| |
41 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type) |
| |
42 { |
| |
43 JabberIq *iq; |
| |
44 |
| |
45 iq = g_new0(JabberIq, 1); |
| |
46 |
| |
47 iq->type = type; |
| |
48 |
| |
49 iq->node = xmlnode_new("iq"); |
| |
50 switch(iq->type) { |
| |
51 case JABBER_IQ_SET: |
| |
52 xmlnode_set_attrib(iq->node, "type", "set"); |
| |
53 break; |
| |
54 case JABBER_IQ_GET: |
| |
55 xmlnode_set_attrib(iq->node, "type", "get"); |
| |
56 break; |
| |
57 case JABBER_IQ_ERROR: |
| |
58 xmlnode_set_attrib(iq->node, "type", "error"); |
| |
59 break; |
| |
60 case JABBER_IQ_RESULT: |
| |
61 xmlnode_set_attrib(iq->node, "type", "result"); |
| |
62 break; |
| |
63 case JABBER_IQ_NONE: |
| |
64 /* this shouldn't ever happen */ |
| |
65 break; |
| |
66 } |
| |
67 |
| |
68 iq->js = js; |
| |
69 |
| |
70 if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) { |
| |
71 iq->id = jabber_get_next_id(js); |
| |
72 xmlnode_set_attrib(iq->node, "id", iq->id); |
| |
73 } |
| |
74 |
| |
75 return iq; |
| |
76 } |
| |
77 |
| |
78 JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type, |
| |
79 const char *xmlns) |
| |
80 { |
| |
81 JabberIq *iq = jabber_iq_new(js, type); |
| |
82 xmlnode *query; |
| |
83 |
| |
84 query = xmlnode_new_child(iq->node, "query"); |
| |
85 xmlnode_set_namespace(query, xmlns); |
| |
86 |
| |
87 return iq; |
| |
88 } |
| |
89 |
| |
90 typedef struct _JabberCallbackData { |
| |
91 JabberIqCallback *callback; |
| |
92 gpointer data; |
| |
93 } JabberCallbackData; |
| |
94 |
| |
95 void |
| |
96 jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data) |
| |
97 { |
| |
98 iq->callback = callback; |
| |
99 iq->callback_data = data; |
| |
100 } |
| |
101 |
| |
102 void jabber_iq_set_id(JabberIq *iq, const char *id) |
| |
103 { |
| |
104 if(iq->id) |
| |
105 g_free(iq->id); |
| |
106 |
| |
107 if(id) { |
| |
108 xmlnode_set_attrib(iq->node, "id", id); |
| |
109 iq->id = g_strdup(id); |
| |
110 } else { |
| |
111 xmlnode_remove_attrib(iq->node, "id"); |
| |
112 iq->id = NULL; |
| |
113 } |
| |
114 } |
| |
115 |
| |
116 void jabber_iq_send(JabberIq *iq) |
| |
117 { |
| |
118 JabberCallbackData *jcd; |
| |
119 g_return_if_fail(iq != NULL); |
| |
120 |
| |
121 jabber_send(iq->js, iq->node); |
| |
122 |
| |
123 if(iq->id && iq->callback) { |
| |
124 jcd = g_new0(JabberCallbackData, 1); |
| |
125 jcd->callback = iq->callback; |
| |
126 jcd->data = iq->callback_data; |
| |
127 g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd); |
| |
128 } |
| |
129 |
| |
130 jabber_iq_free(iq); |
| |
131 } |
| |
132 |
| |
133 void jabber_iq_free(JabberIq *iq) |
| |
134 { |
| |
135 g_return_if_fail(iq != NULL); |
| |
136 |
| |
137 g_free(iq->id); |
| |
138 xmlnode_free(iq->node); |
| |
139 g_free(iq); |
| |
140 } |
| |
141 |
| |
142 static void jabber_iq_last_parse(JabberStream *js, xmlnode *packet) |
| |
143 { |
| |
144 JabberIq *iq; |
| |
145 const char *type; |
| |
146 const char *from; |
| |
147 const char *id; |
| |
148 xmlnode *query; |
| |
149 char *idle_time; |
| |
150 |
| |
151 type = xmlnode_get_attrib(packet, "type"); |
| |
152 from = xmlnode_get_attrib(packet, "from"); |
| |
153 id = xmlnode_get_attrib(packet, "id"); |
| |
154 |
| |
155 if(type && !strcmp(type, "get")) { |
| |
156 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:last"); |
| |
157 jabber_iq_set_id(iq, id); |
| |
158 xmlnode_set_attrib(iq->node, "to", from); |
| |
159 |
| |
160 query = xmlnode_get_child(iq->node, "query"); |
| |
161 |
| |
162 idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0); |
| |
163 xmlnode_set_attrib(query, "seconds", idle_time); |
| |
164 g_free(idle_time); |
| |
165 |
| |
166 jabber_iq_send(iq); |
| |
167 } |
| |
168 } |
| |
169 |
| |
170 static void jabber_iq_time_parse(JabberStream *js, xmlnode *packet) |
| |
171 { |
| |
172 const char *type, *from, *id; |
| |
173 JabberIq *iq; |
| |
174 xmlnode *query; |
| |
175 time_t now_t; |
| |
176 struct tm *now; |
| |
177 |
| |
178 time(&now_t); |
| |
179 now = localtime(&now_t); |
| |
180 |
| |
181 type = xmlnode_get_attrib(packet, "type"); |
| |
182 from = xmlnode_get_attrib(packet, "from"); |
| |
183 id = xmlnode_get_attrib(packet, "id"); |
| |
184 |
| |
185 if(type && !strcmp(type, "get")) { |
| |
186 const char *date; |
| |
187 |
| |
188 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:time"); |
| |
189 jabber_iq_set_id(iq, id); |
| |
190 xmlnode_set_attrib(iq->node, "to", from); |
| |
191 |
| |
192 query = xmlnode_get_child(iq->node, "query"); |
| |
193 |
| |
194 date = gaim_utf8_strftime("%Y%m%dT%T", now); |
| |
195 xmlnode_insert_data(xmlnode_new_child(query, "utc"), date, -1); |
| |
196 |
| |
197 date = gaim_utf8_strftime("%Z", now); |
| |
198 xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1); |
| |
199 |
| |
200 date = gaim_utf8_strftime("%d %b %Y %T", now); |
| |
201 xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1); |
| |
202 |
| |
203 jabber_iq_send(iq); |
| |
204 } |
| |
205 } |
| |
206 |
| |
207 static void jabber_iq_version_parse(JabberStream *js, xmlnode *packet) |
| |
208 { |
| |
209 JabberIq *iq; |
| |
210 const char *type, *from, *id; |
| |
211 xmlnode *query; |
| |
212 char *os = NULL; |
| |
213 |
| |
214 type = xmlnode_get_attrib(packet, "type"); |
| |
215 |
| |
216 if(type && !strcmp(type, "get")) { |
| |
217 |
| |
218 if(!gaim_prefs_get_bool("/plugins/prpl/jabber/hide_os")) { |
| |
219 struct utsname osinfo; |
| |
220 |
| |
221 uname(&osinfo); |
| |
222 os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release, |
| |
223 osinfo.machine); |
| |
224 } |
| |
225 |
| |
226 from = xmlnode_get_attrib(packet, "from"); |
| |
227 id = xmlnode_get_attrib(packet, "id"); |
| |
228 |
| |
229 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version"); |
| |
230 xmlnode_set_attrib(iq->node, "to", from); |
| |
231 jabber_iq_set_id(iq, id); |
| |
232 |
| |
233 query = xmlnode_get_child(iq->node, "query"); |
| |
234 |
| |
235 xmlnode_insert_data(xmlnode_new_child(query, "name"), PACKAGE, -1); |
| |
236 xmlnode_insert_data(xmlnode_new_child(query, "version"), VERSION, -1); |
| |
237 if(os) { |
| |
238 xmlnode_insert_data(xmlnode_new_child(query, "os"), os, -1); |
| |
239 g_free(os); |
| |
240 } |
| |
241 |
| |
242 jabber_iq_send(iq); |
| |
243 } |
| |
244 } |
| |
245 |
| |
246 void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id) |
| |
247 { |
| |
248 g_hash_table_remove(js->iq_callbacks, id); |
| |
249 } |
| |
250 |
| |
251 void jabber_iq_parse(JabberStream *js, xmlnode *packet) |
| |
252 { |
| |
253 JabberCallbackData *jcd; |
| |
254 xmlnode *query, *error, *x; |
| |
255 const char *xmlns; |
| |
256 const char *type, *id, *from; |
| |
257 JabberIqHandler *jih; |
| |
258 |
| |
259 query = xmlnode_get_child(packet, "query"); |
| |
260 type = xmlnode_get_attrib(packet, "type"); |
| |
261 from = xmlnode_get_attrib(packet, "from"); |
| |
262 id = xmlnode_get_attrib(packet, "id"); |
| |
263 |
| |
264 /* First, lets see if a special callback got registered */ |
| |
265 |
| |
266 if(type && (!strcmp(type, "result") || !strcmp(type, "error"))) { |
| |
267 if(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) { |
| |
268 jcd->callback(js, packet, jcd->data); |
| |
269 jabber_iq_remove_callback_by_id(js, id); |
| |
270 return; |
| |
271 } |
| |
272 } |
| |
273 |
| |
274 /* Apparently not, so lets see if we have a pre-defined handler */ |
| |
275 |
| |
276 if(type && query && (xmlns = xmlnode_get_namespace(query))) { |
| |
277 if((jih = g_hash_table_lookup(iq_handlers, xmlns))) { |
| |
278 jih(js, packet); |
| |
279 return; |
| |
280 } |
| |
281 } |
| |
282 |
| |
283 if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { |
| |
284 jabber_si_parse(js, packet); |
| |
285 return; |
| |
286 } |
| |
287 |
| |
288 if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { |
| |
289 jabber_gmail_poke(js, packet); |
| |
290 return; |
| |
291 } |
| |
292 |
| |
293 /* If we get here, send the default error reply mandated by XMPP-CORE */ |
| |
294 if(type && (!strcmp(type, "set") || !strcmp(type, "get"))) { |
| |
295 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); |
| |
296 |
| |
297 xmlnode_free(iq->node); |
| |
298 iq->node = xmlnode_copy(packet); |
| |
299 xmlnode_set_attrib(iq->node, "to", from); |
| |
300 xmlnode_remove_attrib(iq->node, "from"); |
| |
301 xmlnode_set_attrib(iq->node, "type", "error"); |
| |
302 error = xmlnode_new_child(iq->node, "error"); |
| |
303 xmlnode_set_attrib(error, "type", "cancel"); |
| |
304 xmlnode_set_attrib(error, "code", "501"); |
| |
305 x = xmlnode_new_child(error, "feature-not-implemented"); |
| |
306 xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas"); |
| |
307 |
| |
308 jabber_iq_send(iq); |
| |
309 } |
| |
310 } |
| |
311 |
| |
312 void jabber_iq_register_handler(const char *xmlns, JabberIqHandler handlerfunc) |
| |
313 { |
| |
314 g_hash_table_replace(iq_handlers, g_strdup(xmlns), handlerfunc); |
| |
315 } |
| |
316 |
| |
317 void jabber_iq_init(void) |
| |
318 { |
| |
319 iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
| |
320 |
| |
321 jabber_iq_register_handler("jabber:iq:roster", jabber_roster_parse); |
| |
322 jabber_iq_register_handler("jabber:iq:oob", jabber_oob_parse); |
| |
323 jabber_iq_register_handler("http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse); |
| |
324 jabber_iq_register_handler("jabber:iq:last", jabber_iq_last_parse); |
| |
325 jabber_iq_register_handler("jabber:iq:time", jabber_iq_time_parse); |
| |
326 jabber_iq_register_handler("jabber:iq:version", jabber_iq_version_parse); |
| |
327 jabber_iq_register_handler("http://jabber.org/protocol/disco#info", jabber_disco_info_parse); |
| |
328 jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse); |
| |
329 jabber_iq_register_handler("jabber:iq:register", jabber_register_parse); |
| |
330 } |
| |
331 |
| |
332 void jabber_iq_uninit(void) |
| |
333 { |
| |
334 g_hash_table_destroy(iq_handlers); |
| |
335 } |
| |
336 |