comparison libgaim/protocols/jabber/si.c @ 14192:60b1bc8dbf37

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