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