comparison libpurple/protocols/jabber/si.c @ 15373:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children e926951e61fe
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
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 *
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23 #include "blist.h"
24
25 #include "internal.h"
26 #include "cipher.h"
27 #include "debug.h"
28 #include "ft.h"
29 #include "network.h"
30 #include "notify.h"
31
32 #include "buddy.h"
33 #include "disco.h"
34 #include "jabber.h"
35 #include "iq.h"
36 #include "si.h"
37
38 #include "si.h"
39
40 struct bytestreams_streamhost {
41 char *jid;
42 char *host;
43 int port;
44 };
45
46 typedef struct _JabberSIXfer {
47 JabberStream *js;
48
49 GaimProxyConnectData *connect_data;
50 GaimNetworkListenData *listen_data;
51
52 gboolean accepted;
53
54 char *stream_id;
55 char *iq_id;
56
57 enum {
58 STREAM_METHOD_UNKNOWN = 0,
59 STREAM_METHOD_BYTESTREAMS = 2 << 1,
60 STREAM_METHOD_IBB = 2 << 2,
61 STREAM_METHOD_UNSUPPORTED = 2 << 31
62 } stream_method;
63
64 GList *streamhosts;
65 GaimProxyInfo *gpi;
66
67 char *rxqueue;
68 size_t rxlen;
69 gsize rxmaxlen;
70 } JabberSIXfer;
71
72 static GaimXfer*
73 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
74 {
75 GList *xfers;
76
77 if(!sid || !from)
78 return NULL;
79
80 for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
81 GaimXfer *xfer = xfers->data;
82 JabberSIXfer *jsx = xfer->data;
83 if(jsx->stream_id && xfer->who &&
84 !strcmp(jsx->stream_id, sid) && !strcmp(xfer->who, from))
85 return xfer;
86 }
87
88 return NULL;
89 }
90
91
92 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer);
93
94 static void
95 jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
96 {
97 GaimXfer *xfer = data;
98 JabberSIXfer *jsx = xfer->data;
99 JabberIq *iq;
100 xmlnode *query, *su;
101 struct bytestreams_streamhost *streamhost = jsx->streamhosts->data;
102
103 gaim_proxy_info_destroy(jsx->gpi);
104 jsx->connect_data = NULL;
105
106 if(source < 0) {
107 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
108 g_free(streamhost->jid);
109 g_free(streamhost->host);
110 g_free(streamhost);
111 jabber_si_bytestreams_attempt_connect(xfer);
112 return;
113 }
114
115 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, "http://jabber.org/protocol/bytestreams");
116 xmlnode_set_attrib(iq->node, "to", xfer->who);
117 jabber_iq_set_id(iq, jsx->iq_id);
118 query = xmlnode_get_child(iq->node, "query");
119 su = xmlnode_new_child(query, "streamhost-used");
120 xmlnode_set_attrib(su, "jid", streamhost->jid);
121
122 jabber_iq_send(iq);
123
124 gaim_xfer_start(xfer, source, NULL, -1);
125 }
126
127 static void jabber_si_bytestreams_attempt_connect(GaimXfer *xfer)
128 {
129 JabberSIXfer *jsx = xfer->data;
130 struct bytestreams_streamhost *streamhost;
131 char *dstaddr, *p;
132 int i;
133 unsigned char hashval[20];
134 JabberID *dstjid;
135
136 if(!jsx->streamhosts) {
137 JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR);
138 xmlnode *error, *inf;
139
140 if(jsx->iq_id)
141 jabber_iq_set_id(iq, jsx->iq_id);
142
143 xmlnode_set_attrib(iq->node, "to", xfer->who);
144 error = xmlnode_new_child(iq->node, "error");
145 xmlnode_set_attrib(error, "code", "404");
146 xmlnode_set_attrib(error, "type", "cancel");
147 inf = xmlnode_new_child(error, "item-not-found");
148 xmlnode_set_namespace(inf, "urn:ietf:params:xml:ns:xmpp-stanzas");
149
150 jabber_iq_send(iq);
151
152 gaim_xfer_cancel_local(xfer);
153
154 return;
155 }
156
157 streamhost = jsx->streamhosts->data;
158
159 dstjid = jabber_id_new(xfer->who);
160
161 if(dstjid != NULL) {
162 jsx->gpi = gaim_proxy_info_new();
163 gaim_proxy_info_set_type(jsx->gpi, GAIM_PROXY_SOCKS5);
164 gaim_proxy_info_set_host(jsx->gpi, streamhost->host);
165 gaim_proxy_info_set_port(jsx->gpi, streamhost->port);
166
167
168
169 dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource, jsx->js->user->node,
170 jsx->js->user->domain, jsx->js->user->resource);
171
172 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
173 sizeof(hashval), hashval, NULL);
174 g_free(dstaddr);
175 dstaddr = g_malloc(41);
176 p = dstaddr;
177 for(i=0; i<20; i++, p+=2)
178 snprintf(p, 3, "%02x", hashval[i]);
179
180 jsx->connect_data = gaim_proxy_connect_socks5(NULL, jsx->gpi,
181 dstaddr, 0,
182 jabber_si_bytestreams_connect_cb, xfer);
183 g_free(dstaddr);
184
185 jabber_id_free(dstjid);
186 }
187
188 if (jsx->connect_data == NULL)
189 {
190 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
191 g_free(streamhost->jid);
192 g_free(streamhost->host);
193 g_free(streamhost);
194 jabber_si_bytestreams_attempt_connect(xfer);
195 }
196 }
197
198 void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet)
199 {
200 GaimXfer *xfer;
201 JabberSIXfer *jsx;
202 xmlnode *query, *streamhost;
203 const char *sid, *from, *type;
204
205 if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "set"))
206 return;
207
208 if(!(from = xmlnode_get_attrib(packet, "from")))
209 return;
210
211 if(!(query = xmlnode_get_child(packet, "query")))
212 return;
213
214 if(!(sid = xmlnode_get_attrib(query, "sid")))
215 return;
216
217 if(!(xfer = jabber_si_xfer_find(js, sid, from)))
218 return;
219
220 jsx = xfer->data;
221
222 if(!jsx->accepted)
223 return;
224
225 if(jsx->iq_id)
226 g_free(jsx->iq_id);
227 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
228
229 for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
230 streamhost = xmlnode_get_next_twin(streamhost)) {
231 const char *jid, *host, *port;
232 int portnum;
233
234 if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
235 (host = xmlnode_get_attrib(streamhost, "host")) &&
236 (port = xmlnode_get_attrib(streamhost, "port")) &&
237 (portnum = atoi(port))) {
238 struct bytestreams_streamhost *sh = g_new0(struct bytestreams_streamhost, 1);
239 sh->jid = g_strdup(jid);
240 sh->host = g_strdup(host);
241 sh->port = portnum;
242 jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
243 }
244 }
245
246 jabber_si_bytestreams_attempt_connect(xfer);
247 }
248
249
250 static void
251 jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
252 GaimInputCondition cond)
253 {
254 GaimXfer *xfer = data;
255 JabberSIXfer *jsx = xfer->data;
256 int len;
257
258 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
259 if (len < 0 && errno == EAGAIN)
260 return;
261 else if (len < 0) {
262 gaim_input_remove(xfer->watcher);
263 xfer->watcher = 0;
264 g_free(jsx->rxqueue);
265 jsx->rxqueue = NULL;
266 close(source);
267 gaim_xfer_cancel_remote(xfer);
268 return;
269 }
270 jsx->rxlen += len;
271
272 if (jsx->rxlen < jsx->rxmaxlen)
273 return;
274
275 gaim_input_remove(xfer->watcher);
276 xfer->watcher = 0;
277 g_free(jsx->rxqueue);
278 jsx->rxqueue = NULL;
279
280 gaim_xfer_start(xfer, source, NULL, -1);
281 }
282
283 static void
284 jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
285 GaimInputCondition cond)
286 {
287 GaimXfer *xfer = data;
288 JabberSIXfer *jsx = xfer->data;
289 int i;
290 char buffer[256];
291 int len;
292 char *dstaddr, *p;
293 unsigned char hashval[20];
294 const char *host;
295
296 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
297
298 if(jsx->rxlen < 5) {
299 gaim_debug_info("jabber", "reading the first 5 bytes\n");
300 len = read(source, buffer, 5 - jsx->rxlen);
301 if(len < 0 && errno == EAGAIN)
302 return;
303 else if(len <= 0) {
304 gaim_input_remove(xfer->watcher);
305 xfer->watcher = 0;
306 close(source);
307 gaim_xfer_cancel_remote(xfer);
308 return;
309 }
310 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
311 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
312 jsx->rxlen += len;
313 return;
314 } else if(jsx->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 ||
315 jsx->rxqueue[3] != 0x03) {
316 gaim_debug_info("jabber", "invalid socks5 stuff\n");
317 gaim_input_remove(xfer->watcher);
318 xfer->watcher = 0;
319 close(source);
320 gaim_xfer_cancel_remote(xfer);
321 return;
322 } else if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2) {
323 gaim_debug_info("jabber", "reading umpteen more bytes\n");
324 len = read(source, buffer, jsx->rxqueue[4] + 5 + 2 - jsx->rxlen);
325 if(len < 0 && errno == EAGAIN)
326 return;
327 else if(len <= 0) {
328 gaim_input_remove(xfer->watcher);
329 xfer->watcher = 0;
330 close(source);
331 gaim_xfer_cancel_remote(xfer);
332 return;
333 }
334 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
335 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
336 jsx->rxlen += len;
337 }
338
339 if(jsx->rxlen - 5 < jsx->rxqueue[4] + 2)
340 return;
341
342 gaim_input_remove(xfer->watcher);
343 xfer->watcher = 0;
344
345 dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
346 jsx->js->user->node, jsx->js->user->domain,
347 jsx->js->user->resource, xfer->who);
348
349 gaim_cipher_digest_region("sha1", (guchar *)dstaddr, strlen(dstaddr),
350 sizeof(hashval), hashval, NULL);
351 g_free(dstaddr);
352 dstaddr = g_malloc(41);
353 p = dstaddr;
354 for(i=0; i<20; i++, p+=2)
355 snprintf(p, 3, "%02x", hashval[i]);
356
357 if(jsx->rxqueue[4] != 40 || strncmp(dstaddr, jsx->rxqueue+5, 40) ||
358 jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
359 gaim_debug_error("jabber", "someone connected with the wrong info!\n");
360 close(source);
361 gaim_xfer_cancel_remote(xfer);
362 return;
363 }
364
365 g_free(jsx->rxqueue);
366 host = gaim_network_get_my_ip(jsx->js->fd);
367
368 jsx->rxmaxlen = 5 + strlen(host) + 2;
369 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
370 jsx->rxlen = 0;
371
372 jsx->rxqueue[0] = 0x05;
373 jsx->rxqueue[1] = 0x00;
374 jsx->rxqueue[2] = 0x00;
375 jsx->rxqueue[3] = 0x03;
376 jsx->rxqueue[4] = strlen(host);
377 memcpy(jsx->rxqueue + 5, host, strlen(host));
378 jsx->rxqueue[5+strlen(host)] = 0x00;
379 jsx->rxqueue[6+strlen(host)] = 0x00;
380
381 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE,
382 jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer);
383 jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
384 GAIM_INPUT_WRITE);
385 }
386
387 static void
388 jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source,
389 GaimInputCondition cond)
390 {
391 GaimXfer *xfer = data;
392 JabberSIXfer *jsx = xfer->data;
393 int len;
394
395 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
396 if (len < 0 && errno == EAGAIN)
397 return;
398 else if (len < 0) {
399 gaim_input_remove(xfer->watcher);
400 xfer->watcher = 0;
401 g_free(jsx->rxqueue);
402 jsx->rxqueue = NULL;
403 close(source);
404 gaim_xfer_cancel_remote(xfer);
405 return;
406 }
407 jsx->rxlen += len;
408
409 if (jsx->rxlen < jsx->rxmaxlen)
410 return;
411
412 gaim_input_remove(xfer->watcher);
413 xfer->watcher = 0;
414
415 if (jsx->rxqueue[1] == 0x00) {
416 xfer->watcher = gaim_input_add(source, GAIM_INPUT_READ,
417 jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
418 g_free(jsx->rxqueue);
419 jsx->rxqueue = NULL;
420 } else {
421 close(source);
422 gaim_xfer_cancel_remote(xfer);
423 }
424 }
425
426 static void
427 jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
428 GaimInputCondition cond)
429 {
430 GaimXfer *xfer = data;
431 JabberSIXfer *jsx = xfer->data;
432 int i;
433 int len;
434 char buffer[256];
435
436 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n");
437
438 xfer->fd = source;
439
440 if(jsx->rxlen < 2) {
441 gaim_debug_info("jabber", "reading those first two bytes\n");
442 len = read(source, buffer, 2 - jsx->rxlen);
443 if(len < 0 && errno == EAGAIN)
444 return;
445 else if(len <= 0) {
446 gaim_input_remove(xfer->watcher);
447 xfer->watcher = 0;
448 close(source);
449 gaim_xfer_cancel_remote(xfer);
450 return;
451 }
452 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
453 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
454 jsx->rxlen += len;
455 return;
456 } else if(jsx->rxlen - 2 < jsx->rxqueue[1]) {
457 gaim_debug_info("jabber", "reading the next umpteen bytes\n");
458 len = read(source, buffer, jsx->rxqueue[1] + 2 - jsx->rxlen);
459 if(len < 0 && errno == EAGAIN)
460 return;
461 else if(len <= 0) {
462 gaim_input_remove(xfer->watcher);
463 xfer->watcher = 0;
464 close(source);
465 gaim_xfer_cancel_remote(xfer);
466 return;
467 }
468 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
469 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
470 jsx->rxlen += len;
471 }
472
473 if(jsx->rxlen -2 < jsx->rxqueue[1])
474 return;
475
476 gaim_input_remove(xfer->watcher);
477 xfer->watcher = 0;
478
479 gaim_debug_info("jabber", "checking to make sure we're socks FIVE\n");
480
481 if(jsx->rxqueue[0] != 0x05) {
482 close(source);
483 gaim_xfer_cancel_remote(xfer);
484 return;
485 }
486
487 gaim_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]);
488
489 for(i=0; i<jsx->rxqueue[1]; i++) {
490
491 gaim_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]);
492 if(jsx->rxqueue[i+2] == 0x00) {
493 g_free(jsx->rxqueue);
494 jsx->rxlen = 0;
495 jsx->rxmaxlen = 2;
496 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
497 jsx->rxqueue[0] = 0x05;
498 jsx->rxqueue[1] = 0x00;
499 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE,
500 jabber_si_xfer_bytestreams_send_read_response_cb,
501 xfer);
502 jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
503 source, GAIM_INPUT_WRITE);
504 jsx->rxqueue = NULL;
505 jsx->rxlen = 0;
506 return;
507 }
508 }
509
510 g_free(jsx->rxqueue);
511 jsx->rxlen = 0;
512 jsx->rxmaxlen = 2;
513 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
514 jsx->rxqueue[0] = 0x05;
515 jsx->rxqueue[1] = 0xFF;
516 xfer->watcher = gaim_input_add(source, GAIM_INPUT_WRITE,
517 jabber_si_xfer_bytestreams_send_read_response_cb, xfer);
518 jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
519 source, GAIM_INPUT_WRITE);
520 }
521
522 static void
523 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
524 GaimInputCondition cond)
525 {
526 GaimXfer *xfer = data;
527 int acceptfd;
528
529 gaim_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
530
531 acceptfd = accept(source, NULL, 0);
532 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
533 return;
534 else if(acceptfd == -1) {
535 gaim_debug_warning("jabber", "accept: %s\n", strerror(errno));
536 return;
537 }
538
539 gaim_input_remove(xfer->watcher);
540 close(source);
541
542 xfer->watcher = gaim_input_add(acceptfd, GAIM_INPUT_READ,
543 jabber_si_xfer_bytestreams_send_read_cb, xfer);
544 }
545
546 static void
547 jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
548 {
549 GaimXfer *xfer = data;
550 JabberSIXfer *jsx;
551 JabberIq *iq;
552 xmlnode *query, *streamhost;
553 char *jid, *port;
554
555 jsx = xfer->data;
556 jsx->listen_data = NULL;
557
558 if (gaim_xfer_get_status(xfer) == GAIM_XFER_STATUS_CANCEL_LOCAL) {
559 gaim_xfer_unref(xfer);
560 return;
561 }
562
563 gaim_xfer_unref(xfer);
564
565 if (sock < 0) {
566 gaim_xfer_cancel_local(xfer);
567 return;
568 }
569
570 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET,
571 "http://jabber.org/protocol/bytestreams");
572 xmlnode_set_attrib(iq->node, "to", xfer->who);
573 query = xmlnode_get_child(iq->node, "query");
574
575 xmlnode_set_attrib(query, "sid", jsx->stream_id);
576
577 streamhost = xmlnode_new_child(query, "streamhost");
578 jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
579 jsx->js->user->domain, jsx->js->user->resource);
580 xmlnode_set_attrib(streamhost, "jid", jid);
581 g_free(jid);
582
583 /* XXX: shouldn't we use the public IP or something? here */
584 xmlnode_set_attrib(streamhost, "host",
585 gaim_network_get_my_ip(jsx->js->fd));
586 xfer->local_port = gaim_network_get_port_from_fd(sock);
587 port = g_strdup_printf("%hu", xfer->local_port);
588 xmlnode_set_attrib(streamhost, "port", port);
589 g_free(port);
590
591 xfer->watcher = gaim_input_add(sock, GAIM_INPUT_READ,
592 jabber_si_xfer_bytestreams_send_connected_cb, xfer);
593
594 /* XXX: insert proxies here */
595
596 /* XXX: callback to find out which streamhost they used, or see if they
597 * screwed it up */
598 jabber_iq_send(iq);
599
600 }
601
602 static void
603 jabber_si_xfer_bytestreams_send_init(GaimXfer *xfer)
604 {
605 JabberSIXfer *jsx;
606
607 gaim_xfer_ref(xfer);
608
609 jsx = xfer->data;
610 jsx->listen_data = gaim_network_listen_range(0, 0, SOCK_STREAM,
611 jabber_si_xfer_bytestreams_listen_cb, xfer);
612 if (jsx->listen_data == NULL) {
613 gaim_xfer_unref(xfer);
614 /* XXX: couldn't open a port, we're fscked */
615 gaim_xfer_cancel_local(xfer);
616 return;
617 }
618
619 }
620
621 static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
622 gpointer data)
623 {
624 GaimXfer *xfer = data;
625 xmlnode *si, *feature, *x, *field, *value;
626
627 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
628 gaim_xfer_cancel_remote(xfer);
629 return;
630 }
631
632 if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
633 gaim_xfer_cancel_remote(xfer);
634 return;
635 }
636
637 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
638 gaim_xfer_cancel_remote(xfer);
639 return;
640 }
641
642 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
643 const char *var = xmlnode_get_attrib(field, "var");
644
645 if(var && !strcmp(var, "stream-method")) {
646 if((value = xmlnode_get_child(field, "value"))) {
647 char *val = xmlnode_get_data(value);
648 if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
649 jabber_si_xfer_bytestreams_send_init(xfer);
650 g_free(val);
651 return;
652 }
653 g_free(val);
654 }
655 }
656 }
657 gaim_xfer_cancel_remote(xfer);
658 }
659
660 static void jabber_si_xfer_send_request(GaimXfer *xfer)
661 {
662 JabberSIXfer *jsx = xfer->data;
663 JabberIq *iq;
664 xmlnode *si, *file, *feature, *x, *field, *option, *value;
665 char buf[32];
666
667 xfer->filename = g_path_get_basename(xfer->local_filename);
668
669 iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
670 xmlnode_set_attrib(iq->node, "to", xfer->who);
671 si = xmlnode_new_child(iq->node, "si");
672 xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
673 jsx->stream_id = jabber_get_next_id(jsx->js);
674 xmlnode_set_attrib(si, "id", jsx->stream_id);
675 xmlnode_set_attrib(si, "profile",
676 "http://jabber.org/protocol/si/profile/file-transfer");
677
678 file = xmlnode_new_child(si, "file");
679 xmlnode_set_namespace(file,
680 "http://jabber.org/protocol/si/profile/file-transfer");
681 xmlnode_set_attrib(file, "name", xfer->filename);
682 g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
683 xmlnode_set_attrib(file, "size", buf);
684 /* maybe later we'll do hash and date attribs */
685
686 feature = xmlnode_new_child(si, "feature");
687 xmlnode_set_namespace(feature,
688 "http://jabber.org/protocol/feature-neg");
689 x = xmlnode_new_child(feature, "x");
690 xmlnode_set_namespace(x, "jabber:x:data");
691 xmlnode_set_attrib(x, "type", "form");
692 field = xmlnode_new_child(x, "field");
693 xmlnode_set_attrib(field, "var", "stream-method");
694 xmlnode_set_attrib(field, "type", "list-single");
695 option = xmlnode_new_child(field, "option");
696 value = xmlnode_new_child(option, "value");
697 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams",
698 -1);
699 /*
700 option = xmlnode_new_child(field, "option");
701 value = xmlnode_new_child(option, "value");
702 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
703 */
704
705 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
706
707 jabber_iq_send(iq);
708 }
709
710 static void jabber_si_xfer_free(GaimXfer *xfer)
711 {
712 JabberSIXfer *jsx = xfer->data;
713 JabberStream *js = jsx->js;
714
715 js->file_transfers = g_list_remove(js->file_transfers, xfer);
716
717 if (jsx->connect_data != NULL)
718 gaim_proxy_connect_cancel(jsx->connect_data);
719 if (jsx->listen_data != NULL)
720 gaim_network_listen_cancel(jsx->listen_data);
721
722 g_free(jsx->stream_id);
723 g_free(jsx->iq_id);
724 /* XXX: free other stuff */
725 g_free(jsx->rxqueue);
726 g_free(jsx);
727 xfer->data = NULL;
728 }
729
730 static void jabber_si_xfer_cancel_send(GaimXfer *xfer)
731 {
732 jabber_si_xfer_free(xfer);
733 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_send\n");
734 }
735
736
737 static void jabber_si_xfer_request_denied(GaimXfer *xfer)
738 {
739 jabber_si_xfer_free(xfer);
740 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_request_denied\n");
741 }
742
743
744 static void jabber_si_xfer_cancel_recv(GaimXfer *xfer)
745 {
746 jabber_si_xfer_free(xfer);
747 gaim_debug(GAIM_DEBUG_INFO, "jabber", "in jabber_si_xfer_cancel_recv\n");
748 }
749
750
751 static void jabber_si_xfer_end(GaimXfer *xfer)
752 {
753 jabber_si_xfer_free(xfer);
754 }
755
756
757 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
758 JabberCapabilities capabilities, gpointer data)
759 {
760 GaimXfer *xfer = data;
761
762 if(capabilities & JABBER_CAP_SI_FILE_XFER) {
763 jabber_si_xfer_send_request(xfer);
764 } else {
765 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
766 gaim_notify_error(js->gc, _("File Send Failed"),
767 _("File Send Failed"), msg);
768 g_free(msg);
769 }
770 }
771
772 static void jabber_si_xfer_init(GaimXfer *xfer)
773 {
774 JabberSIXfer *jsx = xfer->data;
775 JabberIq *iq;
776 if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) {
777 JabberBuddy *jb;
778 JabberBuddyResource *jbr = NULL;
779
780 jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
781 /* XXX */
782 if(!jb)
783 return;
784
785 /* XXX: for now, send to the first resource available */
786 if(g_list_length(jb->resources) >= 1) {
787 char **who_v = g_strsplit(xfer->who, "/", 2);
788 char *who;
789
790 jbr = jabber_buddy_find_resource(jb, NULL);
791 who = g_strdup_printf("%s/%s", who_v[0], jbr->name);
792 g_strfreev(who_v);
793 g_free(xfer->who);
794 xfer->who = who;
795 jabber_disco_info_do(jsx->js, who,
796 jabber_si_xfer_send_disco_cb, xfer);
797 } else {
798 return; /* XXX: ick */
799 }
800 } else {
801 xmlnode *si, *feature, *x, *field, *value;
802
803 iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
804 xmlnode_set_attrib(iq->node, "to", xfer->who);
805 if(jsx->iq_id)
806 jabber_iq_set_id(iq, jsx->iq_id);
807
808 jsx->accepted = TRUE;
809
810 si = xmlnode_new_child(iq->node, "si");
811 xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
812
813 feature = xmlnode_new_child(si, "feature");
814 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
815
816 x = xmlnode_new_child(feature, "x");
817 xmlnode_set_namespace(x, "jabber:x:data");
818 xmlnode_set_attrib(x, "type", "submit");
819
820 field = xmlnode_new_child(x, "field");
821 xmlnode_set_attrib(field, "var", "stream-method");
822
823 value = xmlnode_new_child(field, "value");
824 if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS)
825 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
826 /*
827 else if(jsx->stream_method & STREAM_METHOD_IBB)
828 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
829 */
830
831 jabber_iq_send(iq);
832 }
833 }
834
835 GaimXfer *jabber_si_new_xfer(GaimConnection *gc, const char *who)
836 {
837 JabberStream *js;
838
839 GaimXfer *xfer;
840 JabberSIXfer *jsx;
841
842 js = gc->proto_data;
843
844 xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);
845 if (xfer)
846 {
847 xfer->data = jsx = g_new0(JabberSIXfer, 1);
848 jsx->js = js;
849
850 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
851 gaim_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
852 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
853
854 js->file_transfers = g_list_append(js->file_transfers, xfer);
855 }
856
857 return xfer;
858 }
859
860 void jabber_si_xfer_send(GaimConnection *gc, const char *who, const char *file)
861 {
862 JabberStream *js;
863
864 GaimXfer *xfer;
865
866 js = gc->proto_data;
867
868 if(!gaim_find_buddy(gc->account, who) || !jabber_buddy_find(js, who, FALSE))
869 return;
870
871 xfer = jabber_si_new_xfer(gc, who);
872
873 if (file)
874 gaim_xfer_request_accepted(xfer, file);
875 else
876 gaim_xfer_request(xfer);
877 }
878
879 void jabber_si_parse(JabberStream *js, xmlnode *packet)
880 {
881 JabberSIXfer *jsx;
882 GaimXfer *xfer;
883 xmlnode *si, *file, *feature, *x, *field, *option, *value;
884 const char *stream_id, *filename, *filesize_c, *profile, *from;
885 size_t filesize = 0;
886
887 if(!(si = xmlnode_get_child(packet, "si")))
888 return;
889
890 if(!(profile = xmlnode_get_attrib(si, "profile")) ||
891 strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
892 return;
893
894 if(!(stream_id = xmlnode_get_attrib(si, "id")))
895 return;
896
897 if(!(file = xmlnode_get_child(si, "file")))
898 return;
899
900 if(!(filename = xmlnode_get_attrib(file, "name")))
901 return;
902
903 if((filesize_c = xmlnode_get_attrib(file, "size")))
904 filesize = atoi(filesize_c);
905
906 if(!(feature = xmlnode_get_child(si, "feature")))
907 return;
908
909 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
910 return;
911
912 if(!(from = xmlnode_get_attrib(packet, "from")))
913 return;
914
915 /* if they've already sent us this file transfer with the same damn id
916 * then we're gonna ignore it, until I think of something better to do
917 * with it */
918 if((xfer = jabber_si_xfer_find(js, stream_id, from)))
919 return;
920
921 jsx = g_new0(JabberSIXfer, 1);
922
923 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
924 const char *var = xmlnode_get_attrib(field, "var");
925 if(var && !strcmp(var, "stream-method")) {
926 for(option = xmlnode_get_child(field, "option"); option;
927 option = xmlnode_get_next_twin(option)) {
928 if((value = xmlnode_get_child(option, "value"))) {
929 char *val;
930 if((val = xmlnode_get_data(value))) {
931 if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
932 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
933 /*
934 } else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
935 jsx->stream_method |= STREAM_METHOD_IBB;
936 */
937 }
938 g_free(val);
939 }
940 }
941 }
942 }
943 }
944
945 if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
946 g_free(jsx);
947 return;
948 }
949
950 jsx->js = js;
951 jsx->stream_id = g_strdup(stream_id);
952 jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
953
954 xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, from);
955 if (xfer)
956 {
957 xfer->data = jsx;
958
959 gaim_xfer_set_filename(xfer, filename);
960 if(filesize > 0)
961 gaim_xfer_set_size(xfer, filesize);
962
963 gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
964 gaim_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
965 gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
966 gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
967
968 js->file_transfers = g_list_append(js->file_transfers, xfer);
969
970 gaim_xfer_request(xfer);
971 }
972 }
973
974