1 /*
2 * purple - Jabber Protocol Plugin
3 *
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 *
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 *
23 */
24
25 #include "internal.h"
26
27 #include "blist.h"
28 #include "debug.h"
29 #include "ft.h"
30 #include "request.h"
31 #include "network.h"
32 #include "notify.h"
33
34 #include "buddy.h"
35 #include "data.h"
36 #include "disco.h"
37 #include "jabber.h"
38 #include "ibb.h"
39 #include "iq.h"
40 #include "si.h"
41
42 #define STREAMHOST_CONNECT_TIMEOUT 5
43 #define ENABLE_FT_THUMBNAILS 0
44
45 typedef struct _JabberSIXfer {
46 JabberStream *js;
47
48 PurpleProxyConnectData *connect_data;
49 PurpleNetworkListenData *listen_data;
50 guint connect_timeout;
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 << 30
62 } stream_method;
63
64 GList *streamhosts;
65 PurpleProxyInfo *gpi;
66
67 char *rxqueue;
68 size_t rxlen;
69 gsize rxmaxlen;
70 int local_streamhost_fd;
71
72 JabberIBBSession *ibb_session;
73 guint ibb_timeout_handle;
74 PurpleCircBuffer *ibb_buffer;
75 } JabberSIXfer;
76
77 /* some forward declarations */
78 static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
79
80 static PurpleXfer*
jabber_si_xfer_find(JabberStream * js,const char * sid,const char * from)81 jabber_si_xfer_find(JabberStream *js, const char *sid, const char *from)
82 {
83 GList *xfers;
84
85 if(!sid || !from)
86 return NULL;
87
88 for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
89 PurpleXfer *xfer = xfers->data;
90 JabberSIXfer *jsx = xfer->data;
91 if(jsx->stream_id && xfer->who &&
92 purple_strequal(jsx->stream_id, sid) && purple_strequal(xfer->who, from))
93 return xfer;
94 }
95
96 return NULL;
97 }
98
99 static void
jabber_si_free_streamhost(gpointer data,gpointer user_data)100 jabber_si_free_streamhost(gpointer data, gpointer user_data)
101 {
102 JabberBytestreamsStreamhost *sh = data;
103
104 if(!data)
105 return;
106
107 g_free(sh->jid);
108 g_free(sh->host);
109 g_free(sh->zeroconf);
110 g_free(sh);
111 }
112
113
114
115 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer);
116
117 static void
jabber_si_bytestreams_connect_cb(gpointer data,gint source,const gchar * error_message)118 jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
119 {
120 PurpleXfer *xfer = data;
121 JabberSIXfer *jsx = xfer->data;
122 JabberIq *iq;
123 xmlnode *query, *su;
124 JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
125
126 purple_proxy_info_destroy(jsx->gpi);
127 jsx->gpi = NULL;
128 jsx->connect_data = NULL;
129
130 if (jsx->connect_timeout > 0)
131 purple_timeout_remove(jsx->connect_timeout);
132 jsx->connect_timeout = 0;
133
134 if(source < 0) {
135 purple_debug_warning("jabber",
136 "si connection failed, jid was %s, host was %s, error was %s\n",
137 streamhost->jid, streamhost->host,
138 error_message ? error_message : "(null)");
139 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
140 jabber_si_free_streamhost(streamhost, NULL);
141 jabber_si_bytestreams_attempt_connect(xfer);
142 return;
143 }
144
145 /* unknown file transfer type is assumed to be RECEIVE */
146 if(xfer->type == PURPLE_XFER_SEND)
147 {
148 xmlnode *activate;
149 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
150 xmlnode_set_attrib(iq->node, "to", streamhost->jid);
151 query = xmlnode_get_child(iq->node, "query");
152 xmlnode_set_attrib(query, "sid", jsx->stream_id);
153 activate = xmlnode_new_child(query, "activate");
154 xmlnode_insert_data(activate, xfer->who, -1);
155
156 /* TODO: We need to wait for an activation result before starting */
157 }
158 else
159 {
160 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_RESULT, NS_BYTESTREAMS);
161 xmlnode_set_attrib(iq->node, "to", xfer->who);
162 jabber_iq_set_id(iq, jsx->iq_id);
163 query = xmlnode_get_child(iq->node, "query");
164 su = xmlnode_new_child(query, "streamhost-used");
165 xmlnode_set_attrib(su, "jid", streamhost->jid);
166 }
167
168 jabber_iq_send(iq);
169
170 purple_xfer_start(xfer, source, NULL, -1);
171 }
172
173 static gboolean
connect_timeout_cb(gpointer data)174 connect_timeout_cb(gpointer data)
175 {
176 PurpleXfer *xfer = data;
177 JabberSIXfer *jsx = xfer->data;
178
179 purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
180
181 jsx->connect_timeout = 0;
182
183 if (jsx->connect_data != NULL)
184 purple_proxy_connect_cancel(jsx->connect_data);
185 jsx->connect_data = NULL;
186
187 /* Trigger the connect error manually */
188 jabber_si_bytestreams_connect_cb(xfer, -1, "Timeout Exceeded.");
189
190 return FALSE;
191 }
192
193 static void
jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer * jsx)194 jabber_si_bytestreams_ibb_timeout_remove(JabberSIXfer *jsx)
195 {
196 if (jsx->ibb_timeout_handle) {
197 purple_timeout_remove(jsx->ibb_timeout_handle);
198 jsx->ibb_timeout_handle = 0;
199 }
200 }
201
202 static gboolean
jabber_si_bytestreams_ibb_timeout_cb(gpointer data)203 jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
204 {
205 PurpleXfer *xfer = (PurpleXfer *) data;
206 JabberSIXfer *jsx = xfer->data;
207
208 if (jsx && !jsx->ibb_session) {
209 purple_debug_info("jabber",
210 "jabber_si_bytestreams_ibb_timeout called and IBB session not set "
211 " up yet, cancel transfer");
212 jabber_si_bytestreams_ibb_timeout_remove(jsx);
213 purple_xfer_cancel_local(xfer);
214 }
215
216 return FALSE;
217 }
218
jabber_si_bytestreams_attempt_connect(PurpleXfer * xfer)219 static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
220 {
221 JabberSIXfer *jsx = xfer->data;
222 JabberBytestreamsStreamhost *streamhost;
223 JabberID *dstjid;
224
225 if(!jsx->streamhosts) {
226 JabberIq *iq = jabber_iq_new(jsx->js, JABBER_IQ_ERROR);
227 xmlnode *error, *inf;
228
229 if(jsx->iq_id)
230 jabber_iq_set_id(iq, jsx->iq_id);
231
232 xmlnode_set_attrib(iq->node, "to", xfer->who);
233 error = xmlnode_new_child(iq->node, "error");
234 xmlnode_set_attrib(error, "code", "404");
235 xmlnode_set_attrib(error, "type", "cancel");
236 inf = xmlnode_new_child(error, "item-not-found");
237 xmlnode_set_namespace(inf, NS_XMPP_STANZAS);
238
239 jabber_iq_send(iq);
240
241 /* if IBB is available, revert to that before giving up... */
242 if (jsx->stream_method & STREAM_METHOD_IBB) {
243 /* if we are the initializer, init IBB */
244 purple_debug_info("jabber",
245 "jabber_si_bytestreams_attempt_connect: "
246 "no streamhosts found, trying IBB\n");
247 /* if we are the sender, open an IBB session, but not if we already
248 did it, since we could have received the error <iq/> from the
249 receiver already... */
250 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
251 && !jsx->ibb_session) {
252 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
253 } else {
254 /* setup a timeout to cancel waiting for IBB open */
255 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
256 jabber_si_bytestreams_ibb_timeout_cb, xfer);
257 }
258 /* if we are the receiver, just wait for IBB open, callback is
259 already set up... */
260 } else {
261 purple_xfer_cancel_local(xfer);
262 }
263
264 return;
265 }
266
267 streamhost = jsx->streamhosts->data;
268
269 jsx->connect_data = NULL;
270 if (jsx->gpi != NULL)
271 purple_proxy_info_destroy(jsx->gpi);
272 jsx->gpi = NULL;
273
274 dstjid = jabber_id_new(xfer->who);
275
276 /* TODO: Deal with zeroconf */
277
278 if(dstjid != NULL && streamhost->host && streamhost->port > 0) {
279 char *dstaddr, *hash;
280 PurpleAccount *account;
281 jsx->gpi = purple_proxy_info_new();
282 purple_proxy_info_set_type(jsx->gpi, PURPLE_PROXY_SOCKS5);
283 purple_proxy_info_set_host(jsx->gpi, streamhost->host);
284 purple_proxy_info_set_port(jsx->gpi, streamhost->port);
285
286 /* unknown file transfer type is assumed to be RECEIVE */
287 if(xfer->type == PURPLE_XFER_SEND)
288 dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, jsx->js->user->node, jsx->js->user->domain,
289 jsx->js->user->resource, dstjid->node, dstjid->domain, dstjid->resource);
290 else
291 dstaddr = g_strdup_printf("%s%s@%s/%s%s@%s/%s", jsx->stream_id, dstjid->node, dstjid->domain, dstjid->resource,
292 jsx->js->user->node, jsx->js->user->domain, jsx->js->user->resource);
293
294 /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
295 hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
296
297 account = purple_connection_get_account(jsx->js->gc);
298 jsx->connect_data = purple_proxy_connect_socks5_account(NULL, account,
299 jsx->gpi, hash, 0,
300 jabber_si_bytestreams_connect_cb, xfer);
301 g_free(hash);
302 g_free(dstaddr);
303
304 /* When selecting a streamhost, timeout after STREAMHOST_CONNECT_TIMEOUT seconds, otherwise it takes forever */
305 if (xfer->type != PURPLE_XFER_SEND && jsx->connect_data != NULL)
306 jsx->connect_timeout = purple_timeout_add_seconds(
307 STREAMHOST_CONNECT_TIMEOUT, connect_timeout_cb, xfer);
308
309 jabber_id_free(dstjid);
310 }
311
312 if (jsx->connect_data == NULL)
313 {
314 jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
315 jabber_si_free_streamhost(streamhost, NULL);
316 jabber_si_bytestreams_attempt_connect(xfer);
317 }
318 }
319
jabber_bytestreams_parse(JabberStream * js,const char * from,JabberIqType type,const char * id,xmlnode * query)320 void jabber_bytestreams_parse(JabberStream *js, const char *from,
321 JabberIqType type, const char *id, xmlnode *query)
322 {
323 PurpleXfer *xfer;
324 JabberSIXfer *jsx;
325 xmlnode *streamhost;
326 const char *sid;
327
328 if(type != JABBER_IQ_SET)
329 return;
330
331 if(!from)
332 return;
333
334 if(!(sid = xmlnode_get_attrib(query, "sid")))
335 return;
336
337 if(!(xfer = jabber_si_xfer_find(js, sid, from)))
338 return;
339
340 jsx = xfer->data;
341
342 if(!jsx->accepted)
343 return;
344
345 if(jsx->iq_id)
346 g_free(jsx->iq_id);
347 jsx->iq_id = g_strdup(id);
348
349 for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
350 streamhost = xmlnode_get_next_twin(streamhost)) {
351 const char *jid, *host = NULL, *port, *zeroconf;
352 int portnum = 0;
353
354 if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
355 ((zeroconf = xmlnode_get_attrib(streamhost, "zeroconf")) ||
356 ((host = xmlnode_get_attrib(streamhost, "host")) &&
357 (port = xmlnode_get_attrib(streamhost, "port")) &&
358 (portnum = atoi(port))))) {
359 /* ignore 0.0.0.0 */
360 if(purple_strequal(host, "0.0.0.0") == FALSE) {
361 JabberBytestreamsStreamhost *sh = g_new0(JabberBytestreamsStreamhost, 1);
362 sh->jid = g_strdup(jid);
363 sh->host = g_strdup(host);
364 sh->port = portnum;
365 sh->zeroconf = g_strdup(zeroconf);
366
367 /* If there were a lot of these, it'd be worthwhile to prepend and reverse. */
368 jsx->streamhosts = g_list_append(jsx->streamhosts, sh);
369 }
370 }
371 }
372
373 jabber_si_bytestreams_attempt_connect(xfer);
374 }
375
376
377 static void
jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data,gint source,PurpleInputCondition cond)378 jabber_si_xfer_bytestreams_send_read_again_resp_cb(gpointer data, gint source,
379 PurpleInputCondition cond)
380 {
381 PurpleXfer *xfer = data;
382 JabberSIXfer *jsx = xfer->data;
383 int len;
384
385 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
386 if (len < 0 && errno == EAGAIN)
387 return;
388 else if (len < 0) {
389 purple_input_remove(xfer->watcher);
390 xfer->watcher = 0;
391 g_free(jsx->rxqueue);
392 jsx->rxqueue = NULL;
393 close(source);
394 purple_xfer_cancel_remote(xfer);
395 return;
396 }
397 jsx->rxlen += len;
398
399 if (jsx->rxlen < jsx->rxmaxlen)
400 return;
401
402 purple_input_remove(xfer->watcher);
403 xfer->watcher = 0;
404 g_free(jsx->rxqueue);
405 jsx->rxqueue = NULL;
406
407 /* Before actually starting sending the file, we need to wait until the
408 * recipient sends the IQ result with <streamhost-used/>
409 */
410 purple_debug_info("jabber", "SOCKS5 connection negotiation completed. "
411 "Waiting for IQ result to start file transfer.\n");
412 }
413
414 static void
jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data,gint source,PurpleInputCondition cond)415 jabber_si_xfer_bytestreams_send_read_again_cb(gpointer data, gint source,
416 PurpleInputCondition cond)
417 {
418 PurpleXfer *xfer = data;
419 JabberSIXfer *jsx = xfer->data;
420 char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
421 int len;
422 char *dstaddr, *hash;
423 const char *host;
424
425 purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
426
427 if(jsx->rxlen < 5) {
428 purple_debug_info("jabber", "reading the first 5 bytes\n");
429 len = read(source, buffer, 5 - jsx->rxlen);
430 if(len < 0 && errno == EAGAIN)
431 return;
432 else if(len <= 0) {
433 purple_input_remove(xfer->watcher);
434 xfer->watcher = 0;
435 close(source);
436 purple_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->rxqueue[0] != 0x05 || jsx->rxqueue[1] != 0x01 ||
444 jsx->rxqueue[3] != 0x03 || jsx->rxqueue[4] != 40) {
445 purple_debug_info("jabber", "Invalid socks5 conn req. header[0x%x,0x%x,0x%x,0x%x,0x%x]\n",
446 jsx->rxqueue[0], jsx->rxqueue[1], jsx->rxqueue[2],
447 jsx->rxqueue[3], jsx->rxqueue[4]);
448 purple_input_remove(xfer->watcher);
449 xfer->watcher = 0;
450 close(source);
451 purple_xfer_cancel_remote(xfer);
452 return;
453 } else if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2) {
454 /* Upper-bound of 257 (jsx->rxlen = 5, jsx->rxqueue[4] = 0xFF) */
455 unsigned short to_read = jsx->rxqueue[4] + 2 - (jsx->rxlen - 5);
456 purple_debug_info("jabber", "reading %u bytes for DST.ADDR + port num (trying to read %hu now)\n",
457 jsx->rxqueue[4] + 2, to_read);
458 len = read(source, buffer, to_read);
459 if(len < 0 && errno == EAGAIN)
460 return;
461 else if(len <= 0) {
462 purple_input_remove(xfer->watcher);
463 xfer->watcher = 0;
464 close(source);
465 purple_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 /* Have we not read all of DST.ADDR and the following 2-byte port number? */
474 if(jsx->rxlen - 5 < (size_t)jsx->rxqueue[4] + 2)
475 return;
476
477 purple_input_remove(xfer->watcher);
478 xfer->watcher = 0;
479
480 dstaddr = g_strdup_printf("%s%s@%s/%s%s", jsx->stream_id,
481 jsx->js->user->node, jsx->js->user->domain,
482 jsx->js->user->resource, xfer->who);
483
484 /* Per XEP-0065, the 'host' must be SHA1(SID + from JID + to JID) */
485 hash = jabber_calculate_data_hash(dstaddr, strlen(dstaddr), "sha1");
486
487 if(strncmp(hash, jsx->rxqueue + 5, 40) ||
488 jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00) {
489 if (jsx->rxqueue[45] != 0x00 || jsx->rxqueue[46] != 0x00)
490 purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.PORT"
491 " (must be 0 - got[0x%x,0x%x]).\n",
492 jsx->rxqueue[45], jsx->rxqueue[46]);
493 else
494 purple_debug_error("jabber", "Got SOCKS5 BS conn with the wrong DST.ADDR"
495 " (expected '%s' - got '%.40s').\n",
496 hash, jsx->rxqueue + 5);
497 close(source);
498 purple_xfer_cancel_remote(xfer);
499 g_free(hash);
500 g_free(dstaddr);
501 return;
502 }
503
504 g_free(hash);
505 g_free(dstaddr);
506
507 g_free(jsx->rxqueue);
508 host = purple_network_get_my_ip(jsx->js->fd);
509
510 jsx->rxmaxlen = 5 + strlen(host) + 2;
511 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
512 jsx->rxlen = 0;
513
514 jsx->rxqueue[0] = 0x05;
515 jsx->rxqueue[1] = 0x00;
516 jsx->rxqueue[2] = 0x00;
517 jsx->rxqueue[3] = 0x03;
518 jsx->rxqueue[4] = strlen(host);
519 memcpy(jsx->rxqueue + 5, host, strlen(host));
520 jsx->rxqueue[5+strlen(host)] = 0x00;
521 jsx->rxqueue[6+strlen(host)] = 0x00;
522
523 xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
524 jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer);
525 jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
526 PURPLE_INPUT_WRITE);
527 }
528
529 static void
jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data,gint source,PurpleInputCondition cond)530 jabber_si_xfer_bytestreams_send_read_response_cb(gpointer data, gint source,
531 PurpleInputCondition cond)
532 {
533 PurpleXfer *xfer = data;
534 JabberSIXfer *jsx = xfer->data;
535 int len;
536
537 len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
538 if (len < 0 && errno == EAGAIN)
539 return;
540 else if (len < 0) {
541 purple_input_remove(xfer->watcher);
542 xfer->watcher = 0;
543 g_free(jsx->rxqueue);
544 jsx->rxqueue = NULL;
545 close(source);
546 purple_xfer_cancel_remote(xfer);
547 return;
548 }
549 jsx->rxlen += len;
550
551 if (jsx->rxlen < jsx->rxmaxlen)
552 return;
553
554 purple_input_remove(xfer->watcher);
555 xfer->watcher = 0;
556
557 /* If we sent a "Success", wait for a response, otherwise give up and cancel */
558 if (jsx->rxqueue[1] == 0x00) {
559 xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
560 jabber_si_xfer_bytestreams_send_read_again_cb, xfer);
561 g_free(jsx->rxqueue);
562 jsx->rxqueue = NULL;
563 jsx->rxlen = 0;
564 } else {
565 close(source);
566 purple_xfer_cancel_remote(xfer);
567 }
568 }
569
570 static void
jabber_si_xfer_bytestreams_send_read_cb(gpointer data,gint source,PurpleInputCondition cond)571 jabber_si_xfer_bytestreams_send_read_cb(gpointer data, gint source,
572 PurpleInputCondition cond)
573 {
574 PurpleXfer *xfer = data;
575 JabberSIXfer *jsx = xfer->data;
576 int i;
577 int len;
578 char buffer[256];
579
580 purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_cb\n");
581
582 xfer->fd = source;
583
584 /** Try to read the SOCKS5 header */
585 if(jsx->rxlen < 2) {
586 purple_debug_info("jabber", "reading those first two bytes\n");
587 len = read(source, buffer, 2 - jsx->rxlen);
588 if(len < 0 && errno == EAGAIN)
589 return;
590 else if(len <= 0) {
591 purple_input_remove(xfer->watcher);
592 xfer->watcher = 0;
593 close(source);
594 purple_xfer_cancel_remote(xfer);
595 return;
596 }
597 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
598 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
599 jsx->rxlen += len;
600 return;
601 } else if(jsx->rxlen - 2 < (size_t)jsx->rxqueue[1]) {
602 /* Has a maximum value of 255 (jsx->rxlen = 2, jsx->rxqueue[1] = 0xFF) */
603 unsigned short to_read = jsx->rxqueue[1] - (jsx->rxlen - 2);
604 purple_debug_info("jabber", "reading %u bytes for auth methods (trying to read %hu now)\n",
605 jsx->rxqueue[1], to_read);
606 len = read(source, buffer, to_read);
607 if(len < 0 && errno == EAGAIN)
608 return;
609 else if(len <= 0) {
610 purple_input_remove(xfer->watcher);
611 xfer->watcher = 0;
612 close(source);
613 purple_xfer_cancel_remote(xfer);
614 return;
615 }
616 jsx->rxqueue = g_realloc(jsx->rxqueue, len + jsx->rxlen);
617 memcpy(jsx->rxqueue + jsx->rxlen, buffer, len);
618 jsx->rxlen += len;
619 }
620
621 /* Have we not read all the auth. method bytes? */
622 if(jsx->rxlen -2 < (size_t)jsx->rxqueue[1])
623 return;
624
625 purple_input_remove(xfer->watcher);
626 xfer->watcher = 0;
627
628 purple_debug_info("jabber", "checking to make sure we're socks FIVE\n");
629
630 if(jsx->rxqueue[0] != 0x05) {
631 close(source);
632 purple_xfer_cancel_remote(xfer);
633 return;
634 }
635
636 purple_debug_info("jabber", "going to test %hhu different methods\n", jsx->rxqueue[1]);
637
638 for(i=0; i<jsx->rxqueue[1]; i++) {
639
640 purple_debug_info("jabber", "testing %hhu\n", jsx->rxqueue[i+2]);
641 if(jsx->rxqueue[i+2] == 0x00) {
642 g_free(jsx->rxqueue);
643 jsx->rxlen = 0;
644 jsx->rxmaxlen = 2;
645 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
646 jsx->rxqueue[0] = 0x05;
647 jsx->rxqueue[1] = 0x00;
648 xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
649 jabber_si_xfer_bytestreams_send_read_response_cb,
650 xfer);
651 jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
652 source, PURPLE_INPUT_WRITE);
653 jsx->rxqueue = NULL;
654 jsx->rxlen = 0;
655 return;
656 }
657 }
658
659 g_free(jsx->rxqueue);
660 jsx->rxlen = 0;
661 jsx->rxmaxlen = 2;
662 jsx->rxqueue = g_malloc(jsx->rxmaxlen);
663 jsx->rxqueue[0] = 0x05;
664 jsx->rxqueue[1] = 0xFF;
665 xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
666 jabber_si_xfer_bytestreams_send_read_response_cb, xfer);
667 jabber_si_xfer_bytestreams_send_read_response_cb(xfer,
668 source, PURPLE_INPUT_WRITE);
669 }
670
671 static gint
jabber_si_compare_jid(gconstpointer a,gconstpointer b)672 jabber_si_compare_jid(gconstpointer a, gconstpointer b)
673 {
674 const JabberBytestreamsStreamhost *sh = a;
675
676 if(!a)
677 return -1;
678
679 return strcmp(sh->jid, (char *)b);
680 }
681
682 static void
jabber_si_xfer_bytestreams_send_connected_cb(gpointer data,gint source,PurpleInputCondition cond)683 jabber_si_xfer_bytestreams_send_connected_cb(gpointer data, gint source,
684 PurpleInputCondition cond)
685 {
686 PurpleXfer *xfer = data;
687 JabberSIXfer *jsx = xfer->data;
688 int acceptfd;
689
690 purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
691
692 acceptfd = accept(source, NULL, 0);
693 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
694 return;
695 else if(acceptfd == -1) {
696 purple_debug_warning("jabber", "accept: %s\n", g_strerror(errno));
697 /* Don't cancel the ft - allow it to fall to the next streamhost.*/
698 return;
699 }
700
701 purple_input_remove(xfer->watcher);
702 close(source);
703 jsx->local_streamhost_fd = -1;
704
705 _purple_network_set_common_socket_flags(acceptfd);
706
707 xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
708 jabber_si_xfer_bytestreams_send_read_cb, xfer);
709 }
710
711 static void
jabber_si_connect_proxy_cb(JabberStream * js,const char * from,JabberIqType type,const char * id,xmlnode * packet,gpointer data)712 jabber_si_connect_proxy_cb(JabberStream *js, const char *from,
713 JabberIqType type, const char *id,
714 xmlnode *packet, gpointer data)
715 {
716 PurpleXfer *xfer = data;
717 JabberSIXfer *jsx;
718 xmlnode *query, *streamhost_used;
719 const char *jid;
720 GList *matched;
721
722 /* TODO: This need to send errors if we don't see what we're looking for */
723
724 /* Make sure that the xfer is actually still valid and we're not just receiving an old iq response */
725 if (!g_list_find(js->file_transfers, xfer)) {
726 purple_debug_error("jabber", "Got bytestreams response for no longer existing xfer (%p)\n", xfer);
727 return;
728 }
729
730 /* In the case of a direct file transfer, this is expected to return */
731 if(!xfer->data)
732 return;
733
734 jsx = xfer->data;
735
736 if(type != JABBER_IQ_RESULT) {
737 purple_debug_info("jabber",
738 "jabber_si_xfer_connect_proxy_cb: type = error\n");
739 /* if IBB is available, open IBB session */
740 purple_debug_info("jabber",
741 "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
742 jsx->stream_method);
743 if (jsx->stream_method & STREAM_METHOD_IBB) {
744 purple_debug_info("jabber", "IBB is possible, try it\n");
745 /* if we are the sender and haven't already opened an IBB
746 session, do so now (we might already have failed to open
747 the bytestream proxy ourselves when receiving this <iq/> */
748 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND
749 && !jsx->ibb_session) {
750 jabber_si_xfer_ibb_send_init(js, xfer);
751 } else {
752 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
753 jabber_si_bytestreams_ibb_timeout_cb, xfer);
754 }
755 /* if we are receiver, just wait for IBB open stanza, callback
756 is already set up */
757 } else {
758 purple_xfer_cancel_remote(xfer);
759 }
760 return;
761 }
762
763 if (!from)
764 return;
765
766 if(!(query = xmlnode_get_child(packet, "query")))
767 return;
768
769 if(!(streamhost_used = xmlnode_get_child(query, "streamhost-used")))
770 return;
771
772 if(!(jid = xmlnode_get_attrib(streamhost_used, "jid")))
773 return;
774
775 purple_debug_info("jabber", "jabber_si_connect_proxy_cb() will be looking at jsx %p: jsx->streamhosts is %p and jid is %s\n",
776 jsx, jsx->streamhosts, jid);
777
778 if(!(matched = g_list_find_custom(jsx->streamhosts, jid, jabber_si_compare_jid)))
779 {
780 gchar *my_jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
781 jsx->js->user->domain, jsx->js->user->resource);
782 if (purple_strequal(jid, my_jid)) {
783 purple_debug_info("jabber", "Got local SOCKS5 streamhost-used.\n");
784 purple_xfer_start(xfer, xfer->fd, NULL, -1);
785 } else {
786 /* if available, try to revert to IBB... */
787 if (jsx->stream_method & STREAM_METHOD_IBB) {
788 purple_debug_info("jabber",
789 "jabber_si_connect_proxy_cb: trying to revert to IBB\n");
790 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
791 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
792 } else {
793 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
794 jabber_si_bytestreams_ibb_timeout_cb, xfer);
795 }
796 /* if we are the receiver, we are already set up...*/
797 } else {
798 purple_debug_info("jabber",
799 "streamhost-used does not match any proxy that was offered to target\n");
800 purple_xfer_cancel_local(xfer);
801 }
802 }
803 g_free(my_jid);
804 return;
805 }
806
807 /* Clean up the local streamhost - it isn't going to be used.*/
808 if (xfer->watcher > 0) {
809 purple_input_remove(xfer->watcher);
810 xfer->watcher = 0;
811 }
812 if (jsx->local_streamhost_fd >= 0) {
813 close(jsx->local_streamhost_fd);
814 jsx->local_streamhost_fd = -1;
815 }
816
817 jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
818 g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
819 g_list_free(jsx->streamhosts);
820
821 jsx->streamhosts = matched;
822
823 jabber_si_bytestreams_attempt_connect(xfer);
824 }
825
826 static void
jabber_si_xfer_bytestreams_listen_cb(int sock,gpointer data)827 jabber_si_xfer_bytestreams_listen_cb(int sock, gpointer data)
828 {
829 PurpleXfer *xfer = data;
830 JabberSIXfer *jsx;
831 JabberIq *iq;
832 xmlnode *query, *streamhost;
833 char port[6];
834 GList *tmp;
835 JabberBytestreamsStreamhost *sh, *sh2;
836 int streamhost_count = 0;
837
838 jsx = xfer->data;
839 jsx->listen_data = NULL;
840
841 /* I'm not sure under which conditions this can happen
842 * (it seems like it shouldn't be possible */
843 if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) {
844 purple_xfer_unref(xfer);
845 return;
846 }
847
848 purple_xfer_unref(xfer);
849
850 iq = jabber_iq_new_query(jsx->js, JABBER_IQ_SET, NS_BYTESTREAMS);
851 xmlnode_set_attrib(iq->node, "to", xfer->who);
852 query = xmlnode_get_child(iq->node, "query");
853
854 xmlnode_set_attrib(query, "sid", jsx->stream_id);
855
856 /* If we successfully started listening locally */
857 if (sock >= 0) {
858 gchar *jid;
859 GList *local_ips =
860 purple_network_get_all_local_system_ips();
861 const char *public_ip;
862 gboolean has_public_ip = FALSE;
863
864 jsx->local_streamhost_fd = sock;
865
866 jid = g_strdup_printf("%s@%s/%s", jsx->js->user->node,
867 jsx->js->user->domain, jsx->js->user->resource);
868 xfer->local_port = purple_network_get_port_from_fd(sock);
869 g_snprintf(port, sizeof(port), "%hu", (guint16)xfer->local_port);
870
871 public_ip = purple_network_get_my_ip(jsx->js->fd);
872
873 /* Include the localhost's IPs (for in-network transfers) */
874 while (local_ips) {
875 gchar *local_ip = local_ips->data;
876 streamhost_count++;
877 streamhost = xmlnode_new_child(query, "streamhost");
878 xmlnode_set_attrib(streamhost, "jid", jid);
879 xmlnode_set_attrib(streamhost, "host", local_ip);
880 xmlnode_set_attrib(streamhost, "port", port);
881 if (purple_strequal(local_ip, public_ip))
882 has_public_ip = TRUE;
883 g_free(local_ip);
884 local_ips = g_list_delete_link(local_ips, local_ips);
885 }
886
887 /* Include the public IP (assuming that there is a port mapped somehow) */
888 if (!has_public_ip && !purple_strequal(public_ip, "0.0.0.0")) {
889 streamhost_count++;
890 streamhost = xmlnode_new_child(query, "streamhost");
891 xmlnode_set_attrib(streamhost, "jid", jid);
892 xmlnode_set_attrib(streamhost, "host", public_ip);
893 xmlnode_set_attrib(streamhost, "port", port);
894 }
895
896 g_free(jid);
897
898 /* The listener for the local proxy */
899 xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
900 jabber_si_xfer_bytestreams_send_connected_cb, xfer);
901 }
902
903 for (tmp = jsx->js->bs_proxies; tmp; tmp = tmp->next) {
904 sh = tmp->data;
905
906 /* TODO: deal with zeroconf proxies */
907
908 if (!(sh->jid && sh->host && sh->port > 0))
909 continue;
910
911 purple_debug_info("jabber", "jabber_si_xfer_bytestreams_listen_cb() will be looking at jsx %p: jsx->streamhosts %p and sh->jid %p\n",
912 jsx, jsx->streamhosts, sh->jid);
913 if(g_list_find_custom(jsx->streamhosts, sh->jid, jabber_si_compare_jid) != NULL)
914 continue;
915
916 streamhost_count++;
917 streamhost = xmlnode_new_child(query, "streamhost");
918 xmlnode_set_attrib(streamhost, "jid", sh->jid);
919 xmlnode_set_attrib(streamhost, "host", sh->host);
920 g_snprintf(port, sizeof(port), "%hu", (guint16)sh->port);
921 xmlnode_set_attrib(streamhost, "port", port);
922
923 sh2 = g_new0(JabberBytestreamsStreamhost, 1);
924 sh2->jid = g_strdup(sh->jid);
925 sh2->host = g_strdup(sh->host);
926 /*sh2->zeroconf = g_strdup(sh->zeroconf);*/
927 sh2->port = sh->port;
928
929 jsx->streamhosts = g_list_prepend(jsx->streamhosts, sh2);
930 }
931
932 /* We have no way of transferring, cancel the transfer */
933 if (streamhost_count == 0) {
934 jabber_iq_free(iq);
935
936 /* if available, revert to IBB */
937 if (jsx->stream_method & STREAM_METHOD_IBB) {
938 purple_debug_info("jabber",
939 "jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
940 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
941 /* if we are the sender, init the IBB session... */
942 jabber_si_xfer_ibb_send_init(jsx->js, xfer);
943 } else {
944 jsx->ibb_timeout_handle = purple_timeout_add_seconds(30,
945 jabber_si_bytestreams_ibb_timeout_cb, xfer);
946 }
947 /* if we are the receiver, we should just wait... the IBB open
948 handler has already been set up... */
949 } else {
950 /* We should probably notify the target,
951 but this really shouldn't ever happen */
952 purple_xfer_cancel_local(xfer);
953 }
954
955 return;
956 }
957
958 jabber_iq_set_callback(iq, jabber_si_connect_proxy_cb, xfer);
959
960 jabber_iq_send(iq);
961
962 }
963
964 static void
jabber_si_xfer_bytestreams_send_init(PurpleXfer * xfer)965 jabber_si_xfer_bytestreams_send_init(PurpleXfer *xfer)
966 {
967 JabberSIXfer *jsx;
968 PurpleProxyType proxy_type;
969
970 purple_xfer_ref(xfer);
971
972 jsx = xfer->data;
973
974 /* TODO: This should probably be done with an account option instead of
975 * piggy-backing on the TOR proxy type. */
976 proxy_type = purple_proxy_info_get_type(
977 purple_proxy_get_setup(purple_connection_get_account(jsx->js->gc)));
978 if (proxy_type == PURPLE_PROXY_TOR) {
979 purple_debug_info("jabber", "Skipping attempting local streamhost.\n");
980 jsx->listen_data = NULL;
981 } else
982 jsx->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
983 jabber_si_xfer_bytestreams_listen_cb, xfer);
984
985 if (jsx->listen_data == NULL) {
986 /* We couldn't open a local port. Perhaps we can use a proxy. */
987 jabber_si_xfer_bytestreams_listen_cb(-1, xfer);
988 }
989
990 }
991
992 static void
jabber_si_xfer_ibb_error_cb(JabberIBBSession * sess)993 jabber_si_xfer_ibb_error_cb(JabberIBBSession *sess)
994 {
995 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
996
997 purple_debug_error("jabber", "an error occurred during IBB file transfer\n");
998 purple_xfer_cancel_remote(xfer);
999 }
1000
1001 static void
jabber_si_xfer_ibb_closed_cb(JabberIBBSession * sess)1002 jabber_si_xfer_ibb_closed_cb(JabberIBBSession *sess)
1003 {
1004 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1005
1006 purple_debug_info("jabber", "the remote user closed the transfer\n");
1007 if (purple_xfer_get_bytes_remaining(xfer) > 0) {
1008 purple_xfer_cancel_remote(xfer);
1009 } else {
1010 purple_xfer_set_completed(xfer, TRUE);
1011 purple_xfer_end(xfer);
1012 }
1013 }
1014
1015 static void
jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession * sess,gpointer data,gsize size)1016 jabber_si_xfer_ibb_recv_data_cb(JabberIBBSession *sess, gpointer data,
1017 gsize size)
1018 {
1019 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1020 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1021
1022 if (size <= purple_xfer_get_bytes_remaining(xfer)) {
1023 purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
1024 size);
1025 purple_circ_buffer_append(jsx->ibb_buffer, data, size);
1026 purple_xfer_prpl_ready(xfer);
1027 } else {
1028 /* trying to write past size of file transfers negotiated size,
1029 reject transfer to protect against malicious behaviour */
1030 purple_debug_error("jabber",
1031 "IBB file transfer send more data than expected\n");
1032 purple_xfer_cancel_remote(xfer);
1033 }
1034
1035 }
1036
1037 static gssize
jabber_si_xfer_ibb_read(guchar ** out_buffer,PurpleXfer * xfer)1038 jabber_si_xfer_ibb_read(guchar **out_buffer, PurpleXfer *xfer)
1039 {
1040 JabberSIXfer *jsx = xfer->data;
1041 guchar *buffer;
1042 gsize size;
1043 gsize tmp;
1044
1045 size = jsx->ibb_buffer->bufused;
1046 *out_buffer = buffer = g_malloc(size);
1047 while ((tmp = purple_circ_buffer_get_max_read(jsx->ibb_buffer))) {
1048 memcpy(buffer, jsx->ibb_buffer->outptr, tmp);
1049 buffer += tmp;
1050 purple_circ_buffer_mark_read(jsx->ibb_buffer, tmp);
1051 }
1052
1053 return size;
1054 }
1055
1056 static gboolean
jabber_si_xfer_ibb_open_cb(JabberStream * js,const char * who,const char * id,xmlnode * open)1057 jabber_si_xfer_ibb_open_cb(JabberStream *js, const char *who, const char *id,
1058 xmlnode *open)
1059 {
1060 const gchar *sid = xmlnode_get_attrib(open, "sid");
1061 PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
1062 if (xfer) {
1063 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1064 JabberIBBSession *sess =
1065 jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer);
1066
1067 jabber_si_bytestreams_ibb_timeout_remove(jsx);
1068
1069 if (sess) {
1070 /* setup callbacks here...*/
1071 jabber_ibb_session_set_data_received_callback(sess,
1072 jabber_si_xfer_ibb_recv_data_cb);
1073 jabber_ibb_session_set_closed_callback(sess,
1074 jabber_si_xfer_ibb_closed_cb);
1075 jabber_ibb_session_set_error_callback(sess,
1076 jabber_si_xfer_ibb_error_cb);
1077
1078 jsx->ibb_session = sess;
1079 /* we handle up to block-size bytes of decoded data, to handle
1080 clients interpreting the block-size attribute as that
1081 (see also remark in ibb.c) */
1082 jsx->ibb_buffer =
1083 purple_circ_buffer_new(jabber_ibb_session_get_block_size(sess));
1084
1085 /* set up read function */
1086 purple_xfer_set_read_fnc(xfer, jabber_si_xfer_ibb_read);
1087
1088 /* start the transfer */
1089 purple_xfer_start(xfer, -1, NULL, 0);
1090 return TRUE;
1091 } else {
1092 /* failed to create IBB session */
1093 purple_debug_error("jabber", "failed to create IBB session\n");
1094 purple_xfer_cancel_remote(xfer);
1095 return FALSE;
1096 }
1097 } else {
1098 /* we got an IBB <open/> for an unknown file transfer, pass along... */
1099 purple_debug_info("jabber",
1100 "IBB open did not match any SI file transfer\n");
1101 return FALSE;
1102 }
1103 }
1104
1105 static gssize
jabber_si_xfer_ibb_write(const guchar * buffer,size_t len,PurpleXfer * xfer)1106 jabber_si_xfer_ibb_write(const guchar *buffer, size_t len, PurpleXfer *xfer)
1107 {
1108 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1109 JabberIBBSession *sess = jsx->ibb_session;
1110 gsize packet_size = len < jabber_ibb_session_get_max_data_size(sess) ?
1111 len : jabber_ibb_session_get_max_data_size(sess);
1112
1113 jabber_ibb_session_send_data(sess, buffer, packet_size);
1114
1115 return packet_size;
1116 }
1117
1118 static void
jabber_si_xfer_ibb_sent_cb(JabberIBBSession * sess)1119 jabber_si_xfer_ibb_sent_cb(JabberIBBSession *sess)
1120 {
1121 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1122 gsize remaining = purple_xfer_get_bytes_remaining(xfer);
1123
1124 if (remaining == 0) {
1125 /* close the session */
1126 jabber_ibb_session_close(sess);
1127 purple_xfer_set_completed(xfer, TRUE);
1128 purple_xfer_end(xfer);
1129 } else {
1130 /* send more... */
1131 purple_xfer_prpl_ready(xfer);
1132 }
1133 }
1134
1135 static void
jabber_si_xfer_ibb_opened_cb(JabberIBBSession * sess)1136 jabber_si_xfer_ibb_opened_cb(JabberIBBSession *sess)
1137 {
1138 PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
1139
1140 if (jabber_ibb_session_get_state(sess) == JABBER_IBB_SESSION_OPENED) {
1141 purple_xfer_start(xfer, -1, NULL, 0);
1142 purple_xfer_prpl_ready(xfer);
1143 } else {
1144 /* error */
1145 purple_xfer_end(xfer);
1146 }
1147 }
1148
1149 static void
jabber_si_xfer_ibb_send_init(JabberStream * js,PurpleXfer * xfer)1150 jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
1151 {
1152 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1153
1154 jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
1155 purple_xfer_get_remote_user(xfer), xfer);
1156
1157 if (jsx->ibb_session) {
1158 /* should set callbacks here... */
1159 jabber_ibb_session_set_opened_callback(jsx->ibb_session,
1160 jabber_si_xfer_ibb_opened_cb);
1161 jabber_ibb_session_set_data_sent_callback(jsx->ibb_session,
1162 jabber_si_xfer_ibb_sent_cb);
1163 jabber_ibb_session_set_closed_callback(jsx->ibb_session,
1164 jabber_si_xfer_ibb_closed_cb);
1165 jabber_ibb_session_set_error_callback(jsx->ibb_session,
1166 jabber_si_xfer_ibb_error_cb);
1167
1168 purple_xfer_set_write_fnc(xfer, jabber_si_xfer_ibb_write);
1169
1170 jsx->ibb_buffer =
1171 purple_circ_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session));
1172
1173 /* open the IBB session */
1174 jabber_ibb_session_open(jsx->ibb_session);
1175
1176 } else {
1177 /* failed to create IBB session */
1178 purple_debug_error("jabber",
1179 "failed to initiate IBB session for file transfer\n");
1180 purple_xfer_cancel_local(xfer);
1181 }
1182 }
1183
jabber_si_xfer_send_method_cb(JabberStream * js,const char * from,JabberIqType type,const char * id,xmlnode * packet,gpointer data)1184 static void jabber_si_xfer_send_method_cb(JabberStream *js, const char *from,
1185 JabberIqType type, const char *id,
1186 xmlnode *packet, gpointer data)
1187 {
1188 PurpleXfer *xfer = data;
1189 xmlnode *si, *feature, *x, *field, *value;
1190 gboolean found_method = FALSE;
1191
1192 if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
1193 purple_xfer_cancel_remote(xfer);
1194 return;
1195 }
1196
1197 if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
1198 purple_xfer_cancel_remote(xfer);
1199 return;
1200 }
1201
1202 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
1203 purple_xfer_cancel_remote(xfer);
1204 return;
1205 }
1206
1207 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
1208 const char *var = xmlnode_get_attrib(field, "var");
1209 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1210
1211 if(purple_strequal(var, "stream-method")) {
1212 if((value = xmlnode_get_child(field, "value"))) {
1213 char *val = xmlnode_get_data(value);
1214 if(purple_strequal(val, NS_BYTESTREAMS)) {
1215 jabber_si_xfer_bytestreams_send_init(xfer);
1216 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
1217 found_method = TRUE;
1218 } else if (purple_strequal(val, NS_IBB)) {
1219 jsx->stream_method |= STREAM_METHOD_IBB;
1220 if (!found_method) {
1221 /* we haven't tried to init a bytestream session, yet
1222 start IBB right away... */
1223 jabber_si_xfer_ibb_send_init(js, xfer);
1224 found_method = TRUE;
1225 }
1226 }
1227 g_free(val);
1228 }
1229 }
1230 }
1231
1232 if (!found_method) {
1233 purple_xfer_cancel_remote(xfer);
1234 }
1235
1236 }
1237
jabber_si_xfer_send_request(PurpleXfer * xfer)1238 static void jabber_si_xfer_send_request(PurpleXfer *xfer)
1239 {
1240 JabberSIXfer *jsx = xfer->data;
1241 JabberIq *iq;
1242 xmlnode *si, *file, *feature, *x, *field, *option, *value;
1243 char buf[32];
1244 #if ENABLE_FT_THUMBNAILS
1245 gconstpointer thumb;
1246 gsize thumb_size;
1247
1248 purple_xfer_prepare_thumbnail(xfer, "jpeg,png");
1249 #endif
1250 xfer->filename = g_path_get_basename(xfer->local_filename);
1251
1252 iq = jabber_iq_new(jsx->js, JABBER_IQ_SET);
1253 xmlnode_set_attrib(iq->node, "to", xfer->who);
1254 si = xmlnode_new_child(iq->node, "si");
1255 xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
1256 jsx->stream_id = jabber_get_next_id(jsx->js);
1257 xmlnode_set_attrib(si, "id", jsx->stream_id);
1258 xmlnode_set_attrib(si, "profile", NS_SI_FILE_TRANSFER);
1259
1260 file = xmlnode_new_child(si, "file");
1261 xmlnode_set_namespace(file, NS_SI_FILE_TRANSFER);
1262 xmlnode_set_attrib(file, "name", xfer->filename);
1263 g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
1264 xmlnode_set_attrib(file, "size", buf);
1265 /* maybe later we'll do hash and date attribs */
1266
1267 #if ENABLE_FT_THUMBNAILS
1268 /* add thumbnail, if appropriate */
1269 if ((thumb = purple_xfer_get_thumbnail(xfer, &thumb_size))) {
1270 const gchar *mimetype = purple_xfer_get_thumbnail_mimetype(xfer);
1271 JabberData *thumbnail_data =
1272 jabber_data_create_from_data(thumb, thumb_size,
1273 mimetype, TRUE, jsx->js);
1274 xmlnode *thumbnail = xmlnode_new_child(file, "thumbnail");
1275 xmlnode_set_namespace(thumbnail, NS_THUMBS);
1276 xmlnode_set_attrib(thumbnail, "cid",
1277 jabber_data_get_cid(thumbnail_data));
1278 xmlnode_set_attrib(thumbnail, "mime-type", mimetype);
1279 /* cache data */
1280 jabber_data_associate_local(thumbnail_data, NULL);
1281 }
1282 #endif
1283
1284 feature = xmlnode_new_child(si, "feature");
1285 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
1286 x = xmlnode_new_child(feature, "x");
1287 xmlnode_set_namespace(x, "jabber:x:data");
1288 xmlnode_set_attrib(x, "type", "form");
1289 field = xmlnode_new_child(x, "field");
1290 xmlnode_set_attrib(field, "var", "stream-method");
1291 xmlnode_set_attrib(field, "type", "list-single");
1292 /* maybe we should add an option to always skip bytestreams for people
1293 behind troublesome firewalls */
1294 option = xmlnode_new_child(field, "option");
1295 value = xmlnode_new_child(option, "value");
1296 xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
1297 option = xmlnode_new_child(field, "option");
1298 value = xmlnode_new_child(option, "value");
1299 xmlnode_insert_data(value, NS_IBB, -1);
1300
1301 jabber_iq_set_callback(iq, jabber_si_xfer_send_method_cb, xfer);
1302
1303 /* Store the IQ id so that we can cancel the callback */
1304 g_free(jsx->iq_id);
1305 jsx->iq_id = g_strdup(iq->id);
1306
1307 jabber_iq_send(iq);
1308 }
1309
jabber_si_xfer_free(PurpleXfer * xfer)1310 static void jabber_si_xfer_free(PurpleXfer *xfer)
1311 {
1312 JabberSIXfer *jsx = xfer->data;
1313
1314 if (jsx) {
1315 JabberStream *js = jsx->js;
1316
1317 js->file_transfers = g_list_remove(js->file_transfers, xfer);
1318
1319 if (jsx->connect_data != NULL)
1320 purple_proxy_connect_cancel(jsx->connect_data);
1321 if (jsx->listen_data != NULL)
1322 purple_network_listen_cancel(jsx->listen_data);
1323 if (jsx->iq_id != NULL)
1324 jabber_iq_remove_callback_by_id(js, jsx->iq_id);
1325 if (jsx->local_streamhost_fd >= 0)
1326 close(jsx->local_streamhost_fd);
1327 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND &&
1328 xfer->fd >= 0) {
1329 purple_debug_info("jabber", "remove port mapping\n");
1330 purple_network_remove_port_mapping(xfer->fd);
1331 }
1332 if (jsx->connect_timeout > 0)
1333 purple_timeout_remove(jsx->connect_timeout);
1334 if (jsx->ibb_timeout_handle > 0)
1335 purple_timeout_remove(jsx->ibb_timeout_handle);
1336
1337 if (jsx->streamhosts) {
1338 g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
1339 g_list_free(jsx->streamhosts);
1340 }
1341
1342 if (jsx->ibb_session) {
1343 purple_debug_info("jabber",
1344 "jabber_si_xfer_free: destroying IBB session\n");
1345 jabber_ibb_session_destroy(jsx->ibb_session);
1346 }
1347
1348 if (jsx->ibb_buffer) {
1349 purple_circ_buffer_destroy(jsx->ibb_buffer);
1350 }
1351
1352 purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
1353
1354 g_free(jsx->stream_id);
1355 g_free(jsx->iq_id);
1356 /* XXX: free other stuff */
1357 g_free(jsx->rxqueue);
1358 g_free(jsx);
1359 xfer->data = NULL;
1360 }
1361 }
1362
1363 /*
1364 * These four functions should only be called from the PurpleXfer functions
1365 * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or
1366 * purple_xfer_request_denied.
1367 */
jabber_si_xfer_cancel_send(PurpleXfer * xfer)1368 static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
1369 {
1370 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1371
1372 /* if there is an IBB session active, send close on that */
1373 if (jsx->ibb_session) {
1374 jabber_ibb_session_close(jsx->ibb_session);
1375 }
1376 jabber_si_xfer_free(xfer);
1377 purple_debug_info("jabber", "in jabber_si_xfer_cancel_send\n");
1378 }
1379
1380
jabber_si_xfer_request_denied(PurpleXfer * xfer)1381 static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
1382 {
1383 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1384 JabberStream *js = jsx->js;
1385
1386 /*
1387 * TODO: It's probably an error if jsx->iq_id == NULL. g_return_if_fail
1388 * might be warranted.
1389 */
1390 if (jsx->iq_id && !jsx->accepted) {
1391 JabberIq *iq;
1392 xmlnode *error, *child;
1393 iq = jabber_iq_new(js, JABBER_IQ_ERROR);
1394 xmlnode_set_attrib(iq->node, "to", xfer->who);
1395 jabber_iq_set_id(iq, jsx->iq_id);
1396
1397 error = xmlnode_new_child(iq->node, "error");
1398 xmlnode_set_attrib(error, "type", "cancel");
1399 child = xmlnode_new_child(error, "forbidden");
1400 xmlnode_set_namespace(child, NS_XMPP_STANZAS);
1401 child = xmlnode_new_child(error, "text");
1402 xmlnode_set_namespace(child, NS_XMPP_STANZAS);
1403 xmlnode_insert_data(child, "Offer Declined", -1);
1404
1405 jabber_iq_send(iq);
1406 }
1407
1408 jabber_si_xfer_free(xfer);
1409 purple_debug_info("jabber", "in jabber_si_xfer_request_denied\n");
1410 }
1411
1412
jabber_si_xfer_cancel_recv(PurpleXfer * xfer)1413 static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
1414 {
1415 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1416 /* if there is an IBB session active, send close */
1417 if (jsx->ibb_session) {
1418 jabber_ibb_session_close(jsx->ibb_session);
1419 }
1420 jabber_si_xfer_free(xfer);
1421 purple_debug_info("jabber", "in jabber_si_xfer_cancel_recv\n");
1422 }
1423
1424
jabber_si_xfer_end(PurpleXfer * xfer)1425 static void jabber_si_xfer_end(PurpleXfer *xfer)
1426 {
1427 jabber_si_xfer_free(xfer);
1428 }
1429
1430
jabber_si_xfer_send_disco_cb(JabberStream * js,const char * who,JabberCapabilities capabilities,gpointer data)1431 static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
1432 JabberCapabilities capabilities, gpointer data)
1433 {
1434 PurpleXfer *xfer = (PurpleXfer *) data;
1435 JabberSIXfer *jsx = (JabberSIXfer *) xfer->data;
1436
1437 if (capabilities & JABBER_CAP_IBB) {
1438 purple_debug_info("jabber",
1439 "jabber_si_xfer_send_disco_cb: remote JID supports IBB\n");
1440 jsx->stream_method |= STREAM_METHOD_IBB;
1441 }
1442
1443 if (capabilities & JABBER_CAP_SI_FILE_XFER) {
1444 jabber_si_xfer_send_request(xfer);
1445 } else {
1446 char *msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
1447 purple_notify_error(js->gc, _("File Send Failed"),
1448 _("File Send Failed"), msg);
1449 g_free(msg);
1450 purple_xfer_cancel_local(xfer);
1451 }
1452 }
1453
resource_select_cancel_cb(PurpleXfer * xfer,PurpleRequestFields * fields)1454 static void resource_select_cancel_cb(PurpleXfer *xfer, PurpleRequestFields *fields)
1455 {
1456 purple_xfer_cancel_local(xfer);
1457 }
1458
do_transfer_send(PurpleXfer * xfer,const char * resource)1459 static void do_transfer_send(PurpleXfer *xfer, const char *resource)
1460 {
1461 JabberSIXfer *jsx = xfer->data;
1462 char **who_v = g_strsplit(xfer->who, "/", 2);
1463 char *who;
1464 JabberBuddy *jb;
1465 JabberBuddyResource *jbr = NULL;
1466
1467 jb = jabber_buddy_find(jsx->js, who_v[0], FALSE);
1468 if (jb) {
1469 jbr = jabber_buddy_find_resource(jb, resource);
1470 }
1471
1472 who = g_strdup_printf("%s/%s", who_v[0], resource);
1473 g_strfreev(who_v);
1474 g_free(xfer->who);
1475 xfer->who = who;
1476
1477 if (jbr && jabber_resource_know_capabilities(jbr)) {
1478 char *msg;
1479
1480 if (jabber_resource_has_capability(jbr, NS_IBB))
1481 jsx->stream_method |= STREAM_METHOD_IBB;
1482 if (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)) {
1483 jabber_si_xfer_send_request(xfer);
1484 return;
1485 }
1486
1487 msg = g_strdup_printf(_("Unable to send file to %s, user does not support file transfers"), who);
1488 purple_notify_error(jsx->js->gc, _("File Send Failed"),
1489 _("File Send Failed"), msg);
1490 g_free(msg);
1491 purple_xfer_cancel_local(xfer);
1492 } else {
1493 jabber_disco_info_do(jsx->js, who,
1494 jabber_si_xfer_send_disco_cb, xfer);
1495 }
1496 }
1497
resource_select_ok_cb(PurpleXfer * xfer,PurpleRequestFields * fields)1498 static void resource_select_ok_cb(PurpleXfer *xfer, PurpleRequestFields *fields)
1499 {
1500 PurpleRequestField *field = purple_request_fields_get_field(fields, "resource");
1501 int selected_id = purple_request_field_choice_get_value(field);
1502 GList *labels = purple_request_field_choice_get_labels(field);
1503
1504 const char *selected_label = g_list_nth_data(labels, selected_id);
1505
1506 do_transfer_send(xfer, selected_label);
1507 }
1508
jabber_si_xfer_init(PurpleXfer * xfer)1509 static void jabber_si_xfer_init(PurpleXfer *xfer)
1510 {
1511 JabberSIXfer *jsx = xfer->data;
1512 JabberIq *iq;
1513 if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
1514 JabberBuddy *jb;
1515 JabberBuddyResource *jbr = NULL;
1516 char *resource;
1517 GList *resources = NULL;
1518
1519 if(NULL != (resource = jabber_get_resource(xfer->who))) {
1520 /* they've specified a resource, no need to ask or
1521 * default or anything, just do it */
1522
1523 do_transfer_send(xfer, resource);
1524 g_free(resource);
1525 return;
1526 }
1527
1528 jb = jabber_buddy_find(jsx->js, xfer->who, TRUE);
1529
1530 if (jb) {
1531 GList *l;
1532
1533 for (l = jb->resources ; l ; l = g_list_next(l)) {
1534 jbr = l->data;
1535
1536 if (!jabber_resource_know_capabilities(jbr) ||
1537 (jabber_resource_has_capability(jbr, NS_SI_FILE_TRANSFER)
1538 && (jabber_resource_has_capability(jbr, NS_BYTESTREAMS)
1539 || jabber_resource_has_capability(jbr, NS_IBB)))) {
1540 resources = g_list_append(resources, jbr);
1541 }
1542 }
1543 }
1544
1545 if (!resources) {
1546 /* no resources online, we're trying to send to someone
1547 * whose presence we're not subscribed to, or
1548 * someone who is offline. Let's inform the user */
1549 char *msg;
1550
1551 if(!jb) {
1552 msg = g_strdup_printf(_("Unable to send file to %s, invalid JID"), xfer->who);
1553 } else if(jb->subscription & JABBER_SUB_TO) {
1554 msg = g_strdup_printf(_("Unable to send file to %s, user is not online"), xfer->who);
1555 } else {
1556 msg = g_strdup_printf(_("Unable to send file to %s, not subscribed to user presence"), xfer->who);
1557 }
1558
1559 purple_notify_error(jsx->js->gc, _("File Send Failed"), _("File Send Failed"), msg);
1560 g_free(msg);
1561 } else if (g_list_length(resources) == 1) {
1562 /* only 1 resource online (probably our most common case)
1563 * so no need to ask who to send to */
1564 jbr = resources->data;
1565 do_transfer_send(xfer, jbr->name);
1566 } else {
1567 /* we've got multiple resources, we need to pick one to send to */
1568 GList *l;
1569 char *msg = g_strdup_printf(_("Please select the resource of %s to which you would like to send a file"), xfer->who);
1570 PurpleRequestFields *fields = purple_request_fields_new();
1571 PurpleRequestField *field = purple_request_field_choice_new("resource", _("Resource"), 0);
1572 PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
1573
1574 for(l = resources; l; l = l->next) {
1575 jbr = l->data;
1576 purple_request_field_choice_add(field, jbr->name);
1577 }
1578
1579 purple_request_field_group_add_field(group, field);
1580
1581 purple_request_fields_add_group(fields, group);
1582
1583 purple_request_fields(jsx->js->gc, _("Select a Resource"), msg, NULL, fields,
1584 _("Send File"), G_CALLBACK(resource_select_ok_cb), _("Cancel"), G_CALLBACK(resource_select_cancel_cb),
1585 jsx->js->gc->account, xfer->who, NULL, xfer);
1586
1587 g_free(msg);
1588 }
1589
1590 g_list_free(resources);
1591 } else {
1592 xmlnode *si, *feature, *x, *field, *value;
1593
1594 iq = jabber_iq_new(jsx->js, JABBER_IQ_RESULT);
1595 xmlnode_set_attrib(iq->node, "to", xfer->who);
1596 if(jsx->iq_id)
1597 jabber_iq_set_id(iq, jsx->iq_id);
1598 else
1599 purple_debug_error("jabber", "Sending SI result with new IQ id.\n");
1600
1601 jsx->accepted = TRUE;
1602
1603 si = xmlnode_new_child(iq->node, "si");
1604 xmlnode_set_namespace(si, "http://jabber.org/protocol/si");
1605
1606 feature = xmlnode_new_child(si, "feature");
1607 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
1608
1609 x = xmlnode_new_child(feature, "x");
1610 xmlnode_set_namespace(x, "jabber:x:data");
1611 xmlnode_set_attrib(x, "type", "submit");
1612 field = xmlnode_new_child(x, "field");
1613 xmlnode_set_attrib(field, "var", "stream-method");
1614
1615 /* we should maybe "remember" if bytestreams has failed before (in the
1616 same session) with this JID, and only present IBB as an option to
1617 avoid unnessesary timeout */
1618 /* maybe we should have an account option to always just try IBB
1619 for people who know their firewalls are very restrictive */
1620 if (jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
1621 value = xmlnode_new_child(field, "value");
1622 xmlnode_insert_data(value, NS_BYTESTREAMS, -1);
1623 } else if(jsx->stream_method & STREAM_METHOD_IBB) {
1624 value = xmlnode_new_child(field, "value");
1625 xmlnode_insert_data(value, NS_IBB, -1);
1626 }
1627
1628 jabber_iq_send(iq);
1629 }
1630 }
1631
jabber_si_new_xfer(PurpleConnection * gc,const char * who)1632 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who)
1633 {
1634 JabberStream *js;
1635
1636 PurpleXfer *xfer;
1637 JabberSIXfer *jsx;
1638
1639 js = gc->proto_data;
1640
1641 xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
1642 if (xfer)
1643 {
1644 xfer->data = jsx = g_new0(JabberSIXfer, 1);
1645 jsx->js = js;
1646 jsx->local_streamhost_fd = -1;
1647
1648 jsx->ibb_session = NULL;
1649
1650 purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
1651 purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
1652 purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
1653
1654 js->file_transfers = g_list_append(js->file_transfers, xfer);
1655 }
1656
1657 return xfer;
1658 }
1659
jabber_si_xfer_send(PurpleConnection * gc,const char * who,const char * file)1660 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file)
1661 {
1662 PurpleXfer *xfer;
1663
1664 xfer = jabber_si_new_xfer(gc, who);
1665
1666 if (file)
1667 purple_xfer_request_accepted(xfer, file);
1668 else
1669 purple_xfer_request(xfer);
1670 }
1671
1672 #if ENABLE_FT_THUMBNAILS
1673 static void
jabber_si_thumbnail_cb(JabberData * data,gchar * alt,gpointer userdata)1674 jabber_si_thumbnail_cb(JabberData *data, gchar *alt, gpointer userdata)
1675 {
1676 PurpleXfer *xfer = (PurpleXfer *) userdata;
1677
1678 if (data) {
1679 purple_xfer_set_thumbnail(xfer, jabber_data_get_data(data),
1680 jabber_data_get_size(data), jabber_data_get_type(data));
1681 /* data is ephemeral, get rid of now (the xfer re-owned the thumbnail */
1682 jabber_data_destroy(data);
1683 }
1684
1685 purple_xfer_request(xfer);
1686 }
1687 #endif
1688
jabber_si_parse(JabberStream * js,const char * from,JabberIqType type,const char * id,xmlnode * si)1689 void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
1690 const char *id, xmlnode *si)
1691 {
1692 JabberSIXfer *jsx;
1693 PurpleXfer *xfer;
1694 xmlnode *file, *feature, *x, *field, *option, *value;
1695 #if ENABLE_FT_THUMBNAILS
1696 xmlnode *thumbnail;
1697 #endif
1698 const char *stream_id, *filename, *filesize_c, *profile;
1699 guint64 filesize_64 = 0;
1700 size_t filesize = 0;
1701
1702 if(!(profile = xmlnode_get_attrib(si, "profile")) ||
1703 !purple_strequal(profile, NS_SI_FILE_TRANSFER))
1704 return;
1705
1706 if(!(stream_id = xmlnode_get_attrib(si, "id")))
1707 return;
1708
1709 if(!(file = xmlnode_get_child(si, "file")))
1710 return;
1711
1712 if(!(filename = xmlnode_get_attrib(file, "name")))
1713 return;
1714
1715 if((filesize_c = xmlnode_get_attrib(file, "size")))
1716 filesize_64 = g_ascii_strtoull(filesize_c, NULL, 10);
1717
1718 #ifndef __COVERITY__
1719 /* TODO 3.0.0: When the core uses a guint64, this is redundant.
1720 * See #8477.
1721 *
1722 * It may not be necessary on 64-bit machine.
1723 * It raises result_independent_of_operands coverity false positive.
1724 */
1725 if (filesize_64 > G_MAXSIZE) {
1726 /* Should this pop up a warning? */
1727 purple_debug_warning("jabber", "Unable to transfer file (too large)"
1728 " -- see #8477 for more details.");
1729 return;
1730 }
1731 #endif
1732 filesize = filesize_64;
1733
1734 if(!(feature = xmlnode_get_child(si, "feature")))
1735 return;
1736
1737 if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
1738 return;
1739
1740 if(!from)
1741 return;
1742
1743 /* if they've already sent us this file transfer with the same damn id
1744 * then we're gonna ignore it, until I think of something better to do
1745 * with it */
1746 if(jabber_si_xfer_find(js, stream_id, from) != NULL)
1747 return;
1748
1749 jsx = g_new0(JabberSIXfer, 1);
1750 jsx->local_streamhost_fd = -1;
1751
1752 jsx->ibb_session = NULL;
1753
1754 for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
1755 const char *var = xmlnode_get_attrib(field, "var");
1756 if(purple_strequal(var, "stream-method")) {
1757 for(option = xmlnode_get_child(field, "option"); option;
1758 option = xmlnode_get_next_twin(option)) {
1759 if((value = xmlnode_get_child(option, "value"))) {
1760 char *val;
1761 if((val = xmlnode_get_data(value))) {
1762 if(purple_strequal(val, NS_BYTESTREAMS)) {
1763 jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
1764 } else if(purple_strequal(val, NS_IBB)) {
1765 jsx->stream_method |= STREAM_METHOD_IBB;
1766 }
1767 g_free(val);
1768 }
1769 }
1770 }
1771 }
1772 }
1773
1774 if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
1775 g_free(jsx);
1776 return;
1777 }
1778
1779 jsx->js = js;
1780 jsx->stream_id = g_strdup(stream_id);
1781 jsx->iq_id = g_strdup(id);
1782
1783 xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
1784 g_return_if_fail(xfer != NULL);
1785
1786 xfer->data = jsx;
1787
1788 purple_xfer_set_filename(xfer, filename);
1789 if(filesize > 0)
1790 purple_xfer_set_size(xfer, filesize);
1791
1792 purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
1793 purple_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
1794 purple_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
1795 purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
1796
1797 js->file_transfers = g_list_append(js->file_transfers, xfer);
1798
1799 #if ENABLE_FT_THUMBNAILS
1800 /* if there is a thumbnail, we should request it... */
1801 if ((thumbnail = xmlnode_get_child_with_namespace(file, "thumbnail",
1802 NS_THUMBS))) {
1803 const char *cid = xmlnode_get_attrib(thumbnail, "cid");
1804 if (cid) {
1805 jabber_data_request(js, cid, purple_xfer_get_remote_user(xfer),
1806 NULL, TRUE, jabber_si_thumbnail_cb, xfer);
1807 return;
1808 }
1809 }
1810 #endif
1811
1812 purple_xfer_request(xfer);
1813 }
1814
1815 void
jabber_si_init(void)1816 jabber_si_init(void)
1817 {
1818 jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);
1819
1820 jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
1821 }
1822
1823 void
jabber_si_uninit(void)1824 jabber_si_uninit(void)
1825 {
1826 jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
1827 }
1828
1829