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