Mercurial > pidgin
annotate src/protocols/jabber/si.c @ 9709:4d05b6e9e9cd
[gaim-migrate @ 10570]
This patch is freaking massive.
Renamed ui.h to gtkdialogs.h
Renamed dialogs.c to gtkdialogs.c
sed'ed the hell out of the .po files
These files are similar to gtkutil.c/.h. They are meant to contain
dialogs such as the "New Instant Message" window, which does not
belong in gtkblist.c or gtkconv.c, and is called from both places.
Eventually the functions in gtkdialogs.c/.h should be changed to
conform to Gaim's naming convention.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 08 Aug 2004 00:48:19 +0000 |
parents | d27156c9c876 |
children | 1a91e814e9d8 |
rev | line source |
---|---|
7395 | 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 */ | |
9030 | 21 |
22 #include "blist.h" | |
23 | |
7395 | 24 #include "internal.h" |
25 #include "debug.h" | |
26 #include "ft.h" | |
8231
f50c059b6384
[gaim-migrate @ 8954]
Christian Hammond <chipx86@chipx86.com>
parents:
8135
diff
changeset
|
27 #include "network.h" |
7395 | 28 #include "notify.h" |
8262 | 29 #include "sha.h" |
7395 | 30 #include "util.h" |
31 | |
32 #include "buddy.h" | |
8312 | 33 #include "disco.h" |
7395 | 34 #include "jabber.h" |
35 #include "iq.h" | |
36 #include "si.h" | |
37 | |
38 #include "si.h" | |
39 | |
8262 | 40 struct bytestreams_streamhost { |
41 char *jid; | |
42 char *host; | |
43 int port; | |
44 }; | |
45 | |
46 typedef struct _JabberSIXfer { | |
47 JabberStream *js; | |
48 | |
49 char *stream_id; | |
50 char *iq_id; | |
51 | |
52 enum { | |
53 STREAM_METHOD_UNKNOWN = 0, | |
54 STREAM_METHOD_BYTESTREAMS = 2 << 1, | |
55 STREAM_METHOD_IBB = 2 << 2, | |
56 STREAM_METHOD_UNSUPPORTED = 2 << 31 | |
57 } stream_method; | |
58 | |
59 GList *streamhosts; | |
60 GaimProxyInfo *gpi; | |
8316 | 61 |
62 char *rxqueue; | |
63 size_t rxlen; | |
8262 | 64 } JabberSIXfer; |
65 | |
66 static GaimXfer* | |
67 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from) | |
7395 | 68 { |
69 GList *xfers; | |
70 | |
8262 | 71 if(!sid || !from) |
7395 | 72 return NULL; |
73 | |
74 for(xfers = js->file_transfers; xfers; xfers = xfers->next) { | |
75 GaimXfer *xfer = xfers->data; | |
76 JabberSIXfer *jsx = xfer->data; | |
8316 | 77 if(jsx->stream_id && xfer->who && |
78 !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from)) | |
7395 | 79 return xfer; |
80 } | |
81 | |
82 return NULL; | |
83 } | |
84 | |
8262 | 85 |
86 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer); | |
87 | |
88 static void jabber_si_bytestreams_connect_cb(gpointer data, gint source, GaimInputCondition cond) | |
89 { | |
7395 | 90 GaimXfer *xfer = data; |
91 JabberSIXfer *jsx = xfer->data; | |
8262 | 92 JabberIq *iq; |
93 xmlnode *query, *su; | |
94 struct bytestreams_streamhost *streamhost = jsx->streamhosts->data; | |
7395 | 95 |
8262 | 96 gaim_proxy_info_destroy(jsx->gpi); |
97 | |
98 if(source < 0) { | |
99 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost); | |
100 g_free(streamhost->jid); | |
101 g_free(streamhost->host); | |
102 g_free(streamhost); | |
103 jabber_si_bytestreams_attempt_connect(xfer); | |
104 return; | |
105 } | |
106 | |
107 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams"); | |
108 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
109 jabber_iq_set_id(iq, jsx->iq_id); | |
110 query = xmlnode_get_child(iq->node, "query"); | |
111 su = xmlnode_new_child(query, "streamhost-used"); | |
112 xmlnode_set_attrib(su, "jid", streamhost->jid); | |
113 | |
114 jabber_iq_send(iq); | |
115 | |
116 gaim_xfer_start(xfer, source, NULL, -1); | |
117 } | |
118 | |
119 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer) | |
120 { | |
121 JabberSIXfer *jsx = xfer->data; | |
122 struct bytestreams_streamhost *streamhost; | |
123 char *dstaddr, *p; | |
124 int i; | |
125 unsigned char hashval[20]; | |
126 | |
127 if(!jsx->streamhosts) { | |
128 JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR); | |
129 xmlnode *error, *condition; | |
130 | |
131 if(jsx->iq_id) | |
132 jabber_iq_set_id(iq, jsx->iq_id); | |
133 | |
134 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
135 error = xmlnode_new_child(iq->node, "error"); | |
136 xmlnode_set_attrib(error, "code", "404"); | |
137 xmlnode_set_attrib(error, "type", "cancel"); | |
138 condition = xmlnode_new_child(error, "condition"); | |
139 xmlnode_set_attrib(condition, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas"); | |
140 xmlnode_new_child(condition, "item-not-found"); | |
141 | |
142 jabber_iq_send(iq); | |
143 | |
144 gaim_xfer_cancel_local(xfer); | |
145 | |
146 return; | |
147 } | |
148 | |
149 streamhost = jsx->streamhosts->data; | |
7395 | 150 |
8262 | 151 jsx->gpi = gaim_proxy_info_new(); |
152 gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5); | |
153 gaim_proxy_info_set_host(jsx->gpi, streamhost->host); | |
154 gaim_proxy_info_set_port(jsx->gpi, streamhost->port); | |
155 | |
156 dstaddr = g_strdup_printf("%s%s%s@%s/%s", jsx->stream_id, xfer->who, jsx->js->user->node, | |
157 jsx->js->user->domain, jsx->js->user->resource); | |
158 shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval); | |
159 g_free(dstaddr); | |
160 dstaddr = g_malloc(41); | |
161 p = dstaddr; | |
162 for(i=0; i<20; i++, p+=2) | |
163 snprintf(p, 3, "%02x", hashval[i]); | |
164 | |
165 gaim_proxy_connect_socks5(jsx->gpi, dstaddr, 0, jabber_si_bytestreams_connect_cb, xfer); | |
166 g_free(dstaddr); | |
167 } | |
168 | |
169 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet) | |
170 { | |
171 GaimXfer *xfer; | |
172 JabberSIXfer *jsx; | |
173 xmlnode *query, *streamhost; | |
174 const char *sid, *from; | |
175 | |
176 if(!(from = xmlnode_get_attrib(packet, "from"))) | |
177 return; | |
178 | |
179 if(!(query = xmlnode_get_child(packet, "query"))) | |
180 return; | |
181 | |
182 if(!(sid = xmlnode_get_attrib(query, "sid"))) | |
183 return; | |
184 | |
185 if(!(xfer = jabber_si_xfer_find(js, sid, from))) | |
186 return; | |
187 | |
188 jsx = xfer->data; | |
189 if(jsx->iq_id) | |
190 g_free(jsx->iq_id); | |
191 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
192 | |
193 for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost; | |
194 streamhost = xmlnode_get_next_twin(streamhost)) { | |
195 const char *jid, *host, *port; | |
196 int portnum; | |
197 | |
198 if((jid = xmlnode_get_attrib(streamhost, "jid")) && | |
199 (host = xmlnode_get_attrib(streamhost, "host")) && | |
200 (port = xmlnode_get_attrib(streamhost, "port")) && | |
201 (portnum = atoi(port))) { | |
202 struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1); | |
203 sh->jid = g_strdup(jid); | |
204 sh->host = g_strdup(host); | |
205 sh->port = portnum; | |
206 jsx->streamhosts = g_list_append(jsx->streamhosts, sh); | |
207 } | |
208 } | |
209 | |
210 jabber_si_bytestreams_attempt_connect(xfer); | |
7395 | 211 } |
212 | |
8312 | 213 static void |
8316 | 214 jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source, |
8312 | 215 GaimInputCondition cond) |
216 { | |
217 GaimXfer *xfer = data; | |
8316 | 218 JabberSIXfer *jsx = xfer->data; |
8312 | 219 int i; |
8316 | 220 char buffer[256]; |
221 int len; | |
222 char *dstaddr, *p; | |
223 unsigned char hashval[20]; | |
224 const char *host; | |
225 | |
226 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n"); | |
8312 | 227 |
8316 | 228 if(jsx->rxlen < 5) { |
229 gaim_debug_info("jabber", "reading the first 5 bytes\n"); | |
230 if((len = read(source, buffer, 5 - jsx->rxlen)) <= 0) { | |
231 gaim_input_remove(xfer->watcher); | |
232 xfer->watcher = 0; | |
233 close(source); | |
234 gaim_xfer_cancel_remote(xfer); | |
235 return; | |
236 } | |
237 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
238 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
239 jsx->rxlen += len; | |
240 return; | |
241 } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 || | |
242 jsx->rxqueue[3] != 0x03) { | |
243 gaim_debug_info("jabber", "invalid socks5 stuff\n"); | |
244 gaim_input_remove(xfer->watcher); | |
245 xfer->watcher = 0; | |
246 close(source); | |
247 gaim_xfer_cancel_remote(xfer); | |
248 return; | |
249 } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) { | |
250 gaim_debug_info("jabber", "reading umpteen more bytes\n"); | |
251 if((len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen)) <= 0) { | |
252 gaim_input_remove(xfer->watcher); | |
253 xfer->watcher = 0; | |
254 close(source); | |
255 gaim_xfer_cancel_remote(xfer); | |
256 return; | |
257 } | |
258 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
259 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
260 jsx->rxlen += len; | |
261 } | |
8312 | 262 |
8316 | 263 if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) |
264 return; | |
265 | |
266 gaim_input_remove(xfer->watcher); | |
267 xfer->watcher = 0; | |
8312 | 268 |
8316 | 269 dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id, |
270 jsx->js->user->node, jsx->js->user->domain, | |
271 jsx->js->user->resource, xfer->who); | |
272 shaBlock((unsigned char *)dstaddr, strlen(dstaddr), hashval); | |
273 g_free(dstaddr); | |
274 dstaddr = g_malloc(41); | |
275 p = dstaddr; | |
276 for(i=0; i<20; i++, p+=2) | |
277 snprintf(p, 3, "%02x", hashval[i]); | |
278 | |
279 if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) || | |
280 jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) { | |
281 gaim_debug_error("jabber", "someone connected with the wrong info!\n"); | |
8312 | 282 close(source); |
283 gaim_xfer_cancel_remote(xfer); | |
284 return; | |
285 } | |
286 | |
8838 | 287 host = gaim_network_get_my_ip(jsx->js->fd); |
8316 | 288 |
289 buffer[0] = 0x05; | |
290 buffer[1] = 0x00; | |
291 buffer[2] = 0x00; | |
292 buffer[3] = 0x03; | |
293 buffer[4] = strlen(host); | |
294 memcpy(buffer + 5, host, strlen(host)); | |
295 buffer[5+strlen(host)] = 0x00; | |
296 buffer[6+strlen(host)] = 0x00; | |
297 | |
298 write(source, buffer, strlen(host)+7); | |
299 | |
300 gaim_xfer_start(xfer, source, NULL, -1); | |
301 } | |
302 | |
303 static void | |
304 jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source, | |
305 GaimInputCondition cond) | |
306 { | |
307 GaimXfer *xfer = data; | |
308 JabberSIXfer *jsx = xfer->data; | |
309 int i; | |
310 int len; | |
311 char buffer[256]; | |
312 | |
313 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n"); | |
314 | |
315 xfer->fd = source; | |
316 | |
317 if(jsx->rxlen < 2) { | |
318 gaim_debug_info("jabber", "reading those first two bytes\n"); | |
319 if((len = read(source, buffer, 2 - jsx->rxlen)) <= 0) { | |
320 gaim_input_remove(xfer->watcher); | |
321 xfer->watcher = 0; | |
322 close(source); | |
323 gaim_xfer_cancel_remote(xfer); | |
324 return; | |
325 } | |
326 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
327 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
328 jsx->rxlen += len; | |
329 return; | |
330 } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) { | |
331 gaim_debug_info("jabber", "reading the next umpteen bytes\n"); | |
332 if((len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen)) <= 0) { | |
333 gaim_input_remove(xfer->watcher); | |
334 xfer->watcher = 0; | |
335 close(source); | |
336 gaim_xfer_cancel_remote(xfer); | |
337 return; | |
338 } | |
339 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen); | |
340 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len); | |
341 jsx->rxlen += len; | |
342 } | |
343 | |
344 if(jsx->rxlen -2 < jsx->rxqueue[1]) | |
345 return; | |
346 | |
347 gaim_input_remove(xfer->watcher); | |
348 xfer->watcher = 0; | |
349 | |
350 | |
351 gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n"); | |
352 | |
353 if(jsx->rxqueue[0] != 0x05) { | |
354 close(source); | |
355 gaim_xfer_cancel_remote(xfer); | |
356 return; | |
357 } | |
358 | |
359 gaim_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]); | |
360 | |
361 for(i=0; i<jsx->rxqueue[1]; i++) { | |
362 | |
363 gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]); | |
364 if(jsx->rxqueue[i+2] == 0x00) { | |
365 buffer[0] = 0x05; | |
366 buffer[1] = 0x00; | |
367 write(source, buffer, 2); | |
368 xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ, | |
369 jabber_si_xfer_bytestreams_send_read_again_cb, xfer); | |
370 g_free(jsx->rxqueue); | |
371 jsx->rxqueue = NULL; | |
372 jsx->rxlen = 0; | |
8312 | 373 return; |
374 } | |
375 } | |
376 | |
8316 | 377 buffer[0] = 0x05; |
378 buffer[1] = 0xFF; | |
379 write(source, buffer, 2); | |
380 close(source); | |
381 g_free(jsx->rxqueue); | |
382 jsx->rxqueue = NULL; | |
383 jsx->rxlen = 0; | |
8312 | 384 gaim_xfer_cancel_remote(xfer); |
385 } | |
386 | |
387 static void | |
8316 | 388 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source, |
389 GaimInputCondition cond) | |
390 { | |
391 GaimXfer *xfer = data; | |
392 int acceptfd; | |
393 | |
394 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n"); | |
395 | |
396 if((acceptfd = accept(source, NULL, 0)) == -1) { | |
397 gaim_debug_warning("jabber", "accept: %s\n", strerror(errno)); | |
398 return; | |
399 } | |
400 | |
401 gaim_input_remove(xfer->watcher); | |
402 close(source); | |
403 | |
404 xfer->watcher = gaim_input_add(acceptfd, GAIM_INPUT_READ, | |
405 jabber_si_xfer_bytestreams_send_read_cb, xfer); | |
406 } | |
407 | |
408 | |
409 static void | |
8312 | 410 jabber_si_xfer_bytestreams_send_init(GaimXfer *xfer) |
411 { | |
412 JabberSIXfer *jsx = xfer->data; | |
413 JabberIq *iq; | |
414 xmlnode *query, *streamhost; | |
415 char *jid, *port; | |
416 int fd; | |
417 | |
418 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, | |
419 "http://jabber.org/protocol/bytestreams"); | |
420 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
421 query = xmlnode_get_child(iq->node, "query"); | |
422 | |
423 xmlnode_set_attrib(query, "sid", jsx->stream_id); | |
424 | |
425 streamhost = xmlnode_new_child(query, "streamhost"); | |
426 jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource); | |
427 xmlnode_set_attrib(streamhost, "jid", jid); | |
428 g_free(jid); | |
429 | |
430 if((fd = gaim_network_listen_range(0, 0)) < 0) { | |
431 /* XXX: couldn't open a port, we're fscked */ | |
432 return; | |
433 } | |
434 | |
8838 | 435 xmlnode_set_attrib(streamhost, "host", gaim_network_get_my_ip(jsx->js->fd)); |
8312 | 436 xfer->local_port = gaim_network_get_port_from_fd(fd); |
8316 | 437 port = g_strdup_printf("%hu", xfer->local_port); |
8312 | 438 xmlnode_set_attrib(streamhost, "port", port); |
439 g_free(port); | |
440 | |
441 xfer->watcher = gaim_input_add(fd, GAIM_INPUT_READ, | |
442 jabber_si_xfer_bytestreams_send_connected_cb, xfer); | |
443 | |
444 /* XXX: insert proxies here */ | |
445 | |
446 /* XXX: callback to find out which streamhost they used, or see if they | |
447 * screwed it up */ | |
448 jabber_iq_send(iq); | |
449 } | |
450 | |
451 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet, | |
452 gpointer data) | |
453 { | |
454 GaimXfer *xfer = data; | |
455 xmlnode *si, *feature, *x, *field, *value; | |
456 | |
457 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) { | |
458 gaim_xfer_cancel_remote(xfer); | |
459 return; | |
460 } | |
461 | |
462 if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) { | |
463 gaim_xfer_cancel_remote(xfer); | |
464 return; | |
465 } | |
466 | |
467 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) { | |
468 gaim_xfer_cancel_remote(xfer); | |
469 return; | |
470 } | |
471 | |
472 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
473 const char *var = xmlnode_get_attrib(field, "var"); | |
474 | |
475 if(var && !strcmp(var, "stream-method")) { | |
476 if((value = xmlnode_get_child(field, "value"))) { | |
477 char *val = xmlnode_get_data(value); | |
478 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
479 jabber_si_xfer_bytestreams_send_init(xfer); | |
480 g_free(val); | |
481 return; | |
482 } | |
483 g_free(val); | |
484 } | |
485 } | |
486 } | |
487 gaim_xfer_cancel_remote(xfer); | |
488 } | |
489 | |
490 static void jabber_si_xfer_send_request(GaimXfer *xfer) | |
491 { | |
492 JabberSIXfer *jsx = xfer->data; | |
493 JabberIq *iq; | |
494 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
495 char buf[32]; | |
496 | |
497 xfer->filename = g_path_get_basename(xfer->local_filename); | |
498 | |
499 iq = jabber_iq_new(jsx->js, JABBER_IQ_SET); | |
500 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
501 si = xmlnode_new_child(iq->node, "si"); | |
502 xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); | |
503 jsx->stream_id = jabber_get_next_id(jsx->js); | |
504 xmlnode_set_attrib(si, "id", jsx->stream_id); | |
505 xmlnode_set_attrib(si, "profile", | |
506 "http://jabber.org/protocol/si/profile/file-transfer"); | |
507 | |
508 file = xmlnode_new_child(si, "file"); | |
509 xmlnode_set_attrib(file, "xmlns", | |
510 "http://jabber.org/protocol/si/profile/file-transfer"); | |
511 xmlnode_set_attrib(file, "name", xfer->filename); | |
512 g_snprintf(buf, sizeof(buf), "%d", xfer->size); | |
513 xmlnode_set_attrib(file, "size", buf); | |
514 /* maybe later we'll do hash and date attribs */ | |
515 | |
516 feature = xmlnode_new_child(si, "feature"); | |
517 xmlnode_set_attrib(feature, "xmlns", | |
518 "http://jabber.org/protocol/feature-neg"); | |
519 x = xmlnode_new_child(feature, "x"); | |
520 xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
521 xmlnode_set_attrib(x, "type", "form"); | |
522 field = xmlnode_new_child(x, "field"); | |
523 xmlnode_set_attrib(field, "var", "stream-method"); | |
524 xmlnode_set_attrib(field, "type", "list-single"); | |
525 option = xmlnode_new_child(field, "option"); | |
526 value = xmlnode_new_child(option, "value"); | |
527 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", | |
528 -1); | |
529 /* | |
530 option = xmlnode_new_child(field, "option"); | |
531 value = xmlnode_new_child(option, "value"); | |
532 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); | |
533 */ | |
534 | |
535 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer); | |
536 | |
537 jabber_iq_send(iq); | |
538 } | |
539 | |
8316 | 540 static void jabber_si_xfer_free(GaimXfer *xfer) |
8312 | 541 { |
8316 | 542 JabberSIXfer *jsx = xfer->data; |
543 JabberStream *js = jsx->js; | |
544 | |
545 js->file_transfers = g_list_remove(js->file_transfers, xfer); | |
546 | |
547 g_free(jsx->stream_id); | |
548 g_free(jsx->iq_id); | |
549 /* XXX: free other stuff */ | |
550 g_free(jsx); | |
551 xfer->data = NULL; | |
552 } | |
553 | |
554 static void jabber_si_xfer_cancel_send(GaimXfer *xfer) | |
555 { | |
556 jabber_si_xfer_free(xfer); | |
8312 | 557 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n"); |
558 } | |
559 | |
560 | |
8316 | 561 static void jabber_si_xfer_cancel_recv(GaimXfer *xfer) |
8312 | 562 { |
8316 | 563 jabber_si_xfer_free(xfer); |
8312 | 564 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n"); |
565 } | |
566 | |
567 | |
8316 | 568 static void jabber_si_xfer_end(GaimXfer *xfer) |
569 { | |
570 jabber_si_xfer_free(xfer); | |
571 } | |
572 | |
573 | |
8312 | 574 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who, |
575 JabberCapabilities capabilities, gpointer data) | |
576 { | |
577 GaimXfer *xfer = data; | |
578 | |
579 if(capabilities & JABBER_CAP_SI_FILE_XFER) { | |
580 jabber_si_xfer_send_request(xfer); | |
581 } else { | |
582 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who); | |
583 gaim_notify_error(js->gc, _("File Send Failed"), | |
584 _("File Send Failed"), msg); | |
585 g_free(msg); | |
586 } | |
587 } | |
588 | |
8262 | 589 static void jabber_si_xfer_init(GaimXfer *xfer) |
590 { | |
591 JabberSIXfer *jsx = xfer->data; | |
592 JabberIq *iq; | |
8312 | 593 if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { |
594 JabberBuddy *jb; | |
595 JabberBuddyResource *jbr = NULL; | |
596 | |
597 jb = jabber_buddy_find(jsx->js, xfer->who, TRUE); | |
598 /* XXX */ | |
599 if(!jb) | |
600 return; | |
8262 | 601 |
8312 | 602 /* XXX: for now, send to the first resource available */ |
603 if(g_list_length(jb->resources) >= 1) { | |
604 char *who; | |
8316 | 605 jbr = jabber_buddy_find_resource(jb, NULL); |
8312 | 606 who = g_strdup_printf("%s/%s", xfer->who, jbr->name); |
607 g_free(xfer->who); | |
608 xfer->who = who; | |
609 jabber_disco_info_do(jsx->js, who, | |
610 jabber_si_xfer_send_disco_cb, xfer); | |
611 } else { | |
612 return; /* XXX: ick */ | |
613 } | |
614 } else { | |
615 xmlnode *si, *feature, *x, *field, *value; | |
8262 | 616 |
8312 | 617 iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT); |
618 xmlnode_set_attrib(iq->node, "to", xfer->who); | |
619 if(jsx->iq_id) | |
620 jabber_iq_set_id(iq, jsx->iq_id); | |
621 | |
622 si = xmlnode_new_child(iq->node, "si"); | |
623 xmlnode_set_attrib(si, "xmlns", "http://jabber.org/protocol/si"); | |
624 | |
625 feature = xmlnode_new_child(si, "feature"); | |
626 xmlnode_set_attrib(feature, "xmlns", "http://jabber.org/protocol/feature-neg"); | |
8262 | 627 |
8312 | 628 x = xmlnode_new_child(feature, "x"); |
629 xmlnode_set_attrib(x, "xmlns", "jabber:x:data"); | |
8343 | 630 xmlnode_set_attrib(x, "type", "submit"); |
8262 | 631 |
8312 | 632 field = xmlnode_new_child(x, "field"); |
633 xmlnode_set_attrib(field, "var", "stream-method"); | |
634 | |
635 value = xmlnode_new_child(field, "value"); | |
636 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) | |
637 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1); | |
638 /* | |
639 else if(jsx->stream_method & STREAM_METHOD_IBB) | |
8262 | 640 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1); |
641 */ | |
642 | |
8312 | 643 jabber_iq_send(iq); |
644 } | |
8262 | 645 } |
646 | |
9466 | 647 void jabber_si_xfer_send(GaimConnection *gc, const char *who, const char *file) |
8312 | 648 { |
9030 | 649 JabberStream *js; |
650 | |
8312 | 651 GaimXfer *xfer; |
652 JabberSIXfer *jsx; | |
653 | |
9030 | 654 js = gc->proto_data; |
655 | |
9466 | 656 if(!gaim_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE)) |
8312 | 657 return; |
658 | |
9466 | 659 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who); |
8262 | 660 |
8312 | 661 xfer->data = jsx = g_new0(JabberSIXfer, 1); |
662 jsx->js = js; | |
8262 | 663 |
8312 | 664 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); |
665 gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send); | |
8316 | 666 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); |
8312 | 667 |
668 js->file_transfers = g_list_append(js->file_transfers, xfer); | |
669 | |
9466 | 670 if (file) |
671 gaim_xfer_request_accepted(xfer, file); | |
672 else | |
673 gaim_xfer_request(xfer); | |
8262 | 674 } |
675 | |
676 void jabber_si_parse(JabberStream *js, xmlnode *packet) | |
677 { | |
678 JabberSIXfer *jsx; | |
679 GaimXfer *xfer; | |
680 xmlnode *si, *file, *feature, *x, *field, *option, *value; | |
681 const char *stream_id, *filename, *filesize_c, *profile; | |
682 size_t filesize = 0; | |
683 | |
684 if(!(si = xmlnode_get_child(packet, "si"))) | |
685 return; | |
686 | |
687 if(!(profile = xmlnode_get_attrib(si, "profile")) || | |
688 strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) | |
689 return; | |
690 | |
691 if(!(stream_id = xmlnode_get_attrib(si, "id"))) | |
692 return; | |
693 | |
694 if(!(file = xmlnode_get_child(si, "file"))) | |
695 return; | |
696 | |
697 if(!(filename = xmlnode_get_attrib(file, "name"))) | |
698 return; | |
699 | |
700 if((filesize_c = xmlnode_get_attrib(file, "size"))) | |
701 filesize = atoi(filesize_c); | |
702 | |
703 if(!(feature = xmlnode_get_child(si, "feature"))) | |
704 return; | |
705 | |
706 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) | |
707 return; | |
708 | |
709 jsx = g_new0(JabberSIXfer, 1); | |
710 | |
711 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) { | |
712 const char *var = xmlnode_get_attrib(field, "var"); | |
713 if(var && !strcmp(var, "stream-method")) { | |
714 for(option = xmlnode_get_child(field, "option"); option; | |
715 option = xmlnode_get_next_twin(option)) { | |
716 if((value = xmlnode_get_child(option, "value"))) { | |
717 char *val; | |
718 if((val = xmlnode_get_data(value))) { | |
719 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) { | |
720 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS; | |
721 /* | |
722 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) { | |
723 jsx->stream_method |= STREAM_METHOD_IBB; | |
724 */ | |
725 } | |
726 g_free(val); | |
727 } | |
728 } | |
729 } | |
730 } | |
731 } | |
732 | |
733 if(jsx->stream_method == STREAM_METHOD_UNKNOWN) { | |
734 g_free(jsx); | |
735 return; | |
736 } | |
737 | |
738 jsx->js = js; | |
739 jsx->stream_id = g_strdup(stream_id); | |
740 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id")); | |
741 | |
742 xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, | |
743 xmlnode_get_attrib(packet, "from")); | |
744 xfer->data = jsx; | |
745 | |
746 gaim_xfer_set_filename(xfer, filename); | |
747 if(filesize > 0) | |
748 gaim_xfer_set_size(xfer, filesize); | |
749 | |
750 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init); | |
8316 | 751 gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv); |
752 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end); | |
8262 | 753 |
754 js->file_transfers = g_list_append(js->file_transfers, xfer); | |
755 | |
756 gaim_xfer_request(xfer); | |
757 } | |
758 | |
7395 | 759 |