Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / protocols / bonjour / bonjour_ft.c
blobc38fdb88d19703ca99489b5f7a99a9368922c36c
1 /*
2 * purple - Bonjour Protocol Plugin
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.
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.
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 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "internal.h"
23 #include "util.h"
24 #include "debug.h"
25 #include "notify.h"
26 #include "proxy.h"
27 #include "ft.h"
28 #include "buddy.h"
29 #include "bonjour.h"
30 #include "bonjour_ft.h"
31 #include "cipher.h"
33 static void
34 bonjour_bytestreams_init(PurpleXfer *xfer);
35 static void
36 bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb);
37 static void
38 bonjour_xfer_init(PurpleXfer *xfer);
39 static void
40 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
41 const int filesize, const char *filename, int option);
42 static void bonjour_free_xfer(PurpleXfer *xfer);
44 /* Look for specific xfer handle */
45 static unsigned int next_id = 0;
47 static void
48 xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type)
50 xmlnode *error_node;
51 XepIq *iq;
53 g_return_if_fail(error_code != NULL);
54 g_return_if_fail(error_type != NULL);
56 if(!to || !id)
57 return;
59 purple_debug_info("bonjour", "xep file transfer stream initialization error.\n");
60 iq = xep_iq_new(bd, XEP_IQ_ERROR, to, purple_account_get_username(bd->jabber_data->account), id);
61 if(iq == NULL)
62 return;
64 error_node = xmlnode_new_child(iq->node, "error");
65 xmlnode_set_attrib(error_node, "code", error_code);
66 xmlnode_set_attrib(error_node, "type", error_type);
68 /* TODO: Make this better */
69 if (!strcmp(error_code, "403")) {
70 xmlnode *tmp_node = xmlnode_new_child(error_node, "forbidden");
71 xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
73 tmp_node = xmlnode_new_child(error_node, "text");
74 xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
75 xmlnode_insert_data(tmp_node, "Offer Declined", -1);
76 } else if (!strcmp(error_code, "404")) {
77 xmlnode *tmp_node = xmlnode_new_child(error_node, "item-not-found");
78 xmlnode_set_namespace(tmp_node, "urn:ietf:params:xml:ns:xmpp-stanzas");
81 xep_iq_send_and_free(iq);
84 static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
86 purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
87 bonjour_free_xfer(xfer);
90 static void bonjour_xfer_request_denied(PurpleXfer *xfer)
92 XepXfer *xf = xfer->data;
94 purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
96 if(xf)
97 xep_ft_si_reject(xf->data, xf->sid, xfer->who, "403", "cancel");
99 bonjour_free_xfer(xfer);
102 static void bonjour_xfer_cancel_recv(PurpleXfer *xfer)
104 purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
105 bonjour_free_xfer(xfer);
108 struct socket_cleanup {
109 int fd;
110 guint handle;
113 static void
114 _wait_for_socket_close(gpointer data, gint source, PurpleInputCondition cond)
116 struct socket_cleanup *sc = data;
117 char buf[1];
118 int ret;
120 ret = recv(source, buf, 1, 0);
122 if (ret == 0 || (ret == -1 && !(errno == EAGAIN || errno == EWOULDBLOCK))) {
123 purple_debug_info("bonjour", "Client completed recieving; closing server socket.\n");
124 purple_input_remove(sc->handle);
125 close(sc->fd);
126 g_free(sc);
130 static void bonjour_xfer_end(PurpleXfer *xfer)
132 purple_debug_info("bonjour", "Bonjour-xfer-end.\n");
134 /* We can't allow the server side to close the connection until the client is complete,
135 * otherwise there is a RST resulting in an error on the client side */
136 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND && purple_xfer_is_completed(xfer)) {
137 struct socket_cleanup *sc = g_new0(struct socket_cleanup, 1);
138 sc->fd = xfer->fd;
139 xfer->fd = -1;
140 sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ,
141 _wait_for_socket_close, sc);
144 bonjour_free_xfer(xfer);
147 static PurpleXfer*
148 bonjour_si_xfer_find(BonjourData *bd, const char *sid, const char *from)
150 GSList *xfers;
151 PurpleXfer *xfer;
152 XepXfer *xf;
154 if(!sid || !from || !bd)
155 return NULL;
157 purple_debug_info("bonjour", "Look for sid=%s from=%s xferlists.\n",
158 sid, from);
160 for(xfers = bd->xfer_lists; xfers; xfers = xfers->next) {
161 xfer = xfers->data;
162 if(xfer == NULL)
163 break;
164 xf = xfer->data;
165 if(xf == NULL)
166 break;
167 if(xf->sid && xfer->who && !strcmp(xf->sid, sid) &&
168 !strcmp(xfer->who, from))
169 return xfer;
172 purple_debug_info("bonjour", "Look for xfer list fail\n");
174 return NULL;
177 static void
178 xep_ft_si_offer(PurpleXfer *xfer, const gchar *to)
180 xmlnode *si_node, *feature, *field, *file, *x;
181 XepIq *iq;
182 XepXfer *xf = xfer->data;
183 BonjourData *bd = NULL;
184 char buf[32];
186 if(!xf)
187 return;
189 bd = xf->data;
190 if(!bd)
191 return;
193 purple_debug_info("bonjour", "xep file transfer stream initialization offer-id=%d.\n", next_id);
195 /* Assign stream id. */
196 g_free(xf->iq_id);
197 xf->iq_id = g_strdup_printf("%u", next_id++);
198 iq = xep_iq_new(xf->data, XEP_IQ_SET, to, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
199 if(iq == NULL)
200 return;
202 /*Construct Stream initialization offer message.*/
203 si_node = xmlnode_new_child(iq->node, "si");
204 xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
205 xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");
206 g_free(xf->sid);
207 xf->sid = g_strdup(xf->iq_id);
208 xmlnode_set_attrib(si_node, "id", xf->sid);
210 file = xmlnode_new_child(si_node, "file");
211 xmlnode_set_namespace(file, "http://jabber.org/protocol/si/profile/file-transfer");
212 xmlnode_set_attrib(file, "name", xfer->filename);
213 g_snprintf(buf, sizeof(buf), "%" G_GSIZE_FORMAT, xfer->size);
214 xmlnode_set_attrib(file, "size", buf);
216 feature = xmlnode_new_child(si_node, "feature");
217 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
219 x = xmlnode_new_child(feature, "x");
220 xmlnode_set_namespace(x, "jabber:x:data");
221 xmlnode_set_attrib(x, "type", "form");
223 field = xmlnode_new_child(x, "field");
224 xmlnode_set_attrib(field, "var", "stream-method");
225 xmlnode_set_attrib(field, "type", "list-single");
227 if (xf->mode & XEP_BYTESTREAMS) {
228 xmlnode *option = xmlnode_new_child(field, "option");
229 xmlnode *value = xmlnode_new_child(option, "value");
230 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
232 if (xf->mode & XEP_IBB) {
233 xmlnode *option = xmlnode_new_child(field, "option");
234 xmlnode *value = xmlnode_new_child(option, "value");
235 xmlnode_insert_data(value, "http://jabber.org/protocol/ibb", -1);
238 xep_iq_send_and_free(iq);
241 static void
242 xep_ft_si_result(PurpleXfer *xfer, char *to)
244 xmlnode *si_node, *feature, *field, *value, *x;
245 XepIq *iq;
246 XepXfer *xf;
247 BonjourData *bd;
249 if(!to || !xfer)
250 return;
251 xf = xfer->data;
252 if(!xf)
253 return;
255 bd = xf->data;
257 purple_debug_info("bonjour", "xep file transfer stream initialization result.\n");
258 iq = xep_iq_new(bd, XEP_IQ_RESULT, to, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
259 if(iq == NULL)
260 return;
262 si_node = xmlnode_new_child(iq->node, "si");
263 xmlnode_set_namespace(si_node, "http://jabber.org/protocol/si");
264 /*xmlnode_set_attrib(si_node, "profile", "http://jabber.org/protocol/si/profile/file-transfer");*/
266 feature = xmlnode_new_child(si_node, "feature");
267 xmlnode_set_namespace(feature, "http://jabber.org/protocol/feature-neg");
269 x = xmlnode_new_child(feature, "x");
270 xmlnode_set_namespace(x, "jabber:x:data");
271 xmlnode_set_attrib(x, "type", "submit");
273 field = xmlnode_new_child(x, "field");
274 xmlnode_set_attrib(field, "var", "stream-method");
276 value = xmlnode_new_child(field, "value");
277 xmlnode_insert_data(value, "http://jabber.org/protocol/bytestreams", -1);
279 xep_iq_send_and_free(iq);
282 static void
283 bonjour_free_xfer(PurpleXfer *xfer)
285 XepXfer *xf;
287 if(xfer == NULL) {
288 purple_debug_info("bonjour", "bonjour-free-xfer-null.\n");
289 return;
292 purple_debug_info("bonjour", "bonjour-free-xfer-%p.\n", xfer);
294 xf = (XepXfer*)xfer->data;
295 if(xf != NULL) {
296 BonjourData *bd = (BonjourData*)xf->data;
297 if(bd != NULL) {
298 bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
299 purple_debug_info("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
301 if (xf->proxy_connection != NULL)
302 purple_proxy_connect_cancel(xf->proxy_connection);
303 if (xf->proxy_info != NULL)
304 purple_proxy_info_destroy(xf->proxy_info);
305 if (xf->listen_data != NULL)
306 purple_network_listen_cancel(xf->listen_data);
307 g_free(xf->iq_id);
308 g_free(xf->jid);
309 g_free(xf->proxy_host);
310 g_free(xf->buddy_ip);
311 g_free(xf->sid);
312 g_free(xf);
313 xfer->data = NULL;
316 purple_debug_info("bonjour", "Need close socket=%d.\n", xfer->fd);
319 PurpleXfer *
320 bonjour_new_xfer(PurpleConnection *gc, const char *who)
322 PurpleXfer *xfer;
323 XepXfer *xep_xfer;
324 BonjourData *bd;
326 if(who == NULL || gc == NULL)
327 return NULL;
329 purple_debug_info("bonjour", "Bonjour-new-xfer to %s.\n", who);
330 bd = (BonjourData*) gc->proto_data;
331 if(bd == NULL)
332 return NULL;
334 /* Build the file transfer handle */
335 xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who);
336 xfer->data = xep_xfer = g_new0(XepXfer, 1);
337 xep_xfer->data = bd;
339 purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
341 /* We don't support IBB yet */
342 /*xep_xfer->mode = XEP_BYTESTREAMS | XEP_IBB;*/
343 xep_xfer->mode = XEP_BYTESTREAMS;
344 xep_xfer->sid = NULL;
346 purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
347 purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
348 purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
350 bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
352 return xfer;
355 void
356 bonjour_send_file(PurpleConnection *gc, const char *who, const char *file)
358 PurpleXfer *xfer;
360 g_return_if_fail(gc != NULL);
361 g_return_if_fail(who != NULL);
363 purple_debug_info("bonjour", "Bonjour-send-file to=%s.\n", who);
365 xfer = bonjour_new_xfer(gc, who);
367 if (file)
368 purple_xfer_request_accepted(xfer, file);
369 else
370 purple_xfer_request(xfer);
374 static void
375 bonjour_xfer_init(PurpleXfer *xfer)
377 PurpleBuddy *buddy;
378 BonjourBuddy *bb;
379 XepXfer *xf;
381 xf = (XepXfer*)xfer->data;
382 if(xf == NULL)
383 return;
385 purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
387 buddy = purple_find_buddy(xfer->account, xfer->who);
388 /* this buddy is offline. */
389 if (buddy == NULL || buddy->proto_data == NULL)
390 return;
392 bb = (BonjourBuddy *)buddy->proto_data;
393 /* Assume it is the first IP. We could do something like keep track of which one is in use or something. */
394 if (bb->ips)
395 xf->buddy_ip = g_strdup(bb->ips->data);
396 if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
397 /* initiate file transfer, send SI offer. */
398 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_SEND.\n");
399 xep_ft_si_offer(xfer, xfer->who);
400 } else {
401 /* accept file transfer request, send SI result. */
402 xep_ft_si_result(xfer, xfer->who);
403 purple_debug_info("bonjour", "Bonjour xfer type is PURPLE_XFER_RECEIVE.\n");
407 void
408 xep_si_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
410 const char *type, *id;
411 BonjourData *bd;
412 PurpleXfer *xfer;
414 g_return_if_fail(pc != NULL);
415 g_return_if_fail(packet != NULL);
416 g_return_if_fail(pb != NULL);
418 bd = (BonjourData*) pc->proto_data;
419 if(bd == NULL)
420 return;
422 purple_debug_info("bonjour", "xep-si-parse.\n");
424 type = xmlnode_get_attrib(packet, "type");
425 id = xmlnode_get_attrib(packet, "id");
426 if(type) {
427 if(!strcmp(type, "set")) {
428 const char *profile;
429 xmlnode *si;
430 gboolean parsed_receive = FALSE;
432 si = xmlnode_get_child(packet, "si");
434 purple_debug_info("bonjour", "si offer Message type - SET.\n");
435 if (si && (profile = xmlnode_get_attrib(si, "profile"))
436 && !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) {
437 const char *filename = NULL, *filesize_str = NULL;
438 int filesize = 0;
439 xmlnode *file;
441 const char *sid = xmlnode_get_attrib(si, "id");
443 if ((file = xmlnode_get_child(si, "file"))) {
444 filename = xmlnode_get_attrib(file, "name");
445 if((filesize_str = xmlnode_get_attrib(file, "size")))
446 filesize = atoi(filesize_str);
449 /* TODO: Make sure that it is advertising a bytestreams transfer */
451 if (filename) {
452 bonjour_xfer_receive(pc, id, sid, pb->name, filesize, filename, XEP_BYTESTREAMS);
454 parsed_receive = TRUE;
458 if (!parsed_receive) {
459 purple_debug_info("bonjour", "rejecting unrecognized si SET offer.\n");
460 xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
461 /*TODO: Send Cancel (501) */
463 } else if(!strcmp(type, "result")) {
464 purple_debug_info("bonjour", "si offer Message type - RESULT.\n");
466 xfer = bonjour_si_xfer_find(bd, id, pb->name);
468 if(xfer == NULL) {
469 purple_debug_info("bonjour", "xfer find fail.\n");
470 xep_ft_si_reject((BonjourData *)pc->proto_data, id, pb->name, "403", "cancel");
471 } else
472 bonjour_bytestreams_init(xfer);
474 } else if(!strcmp(type, "error")) {
475 purple_debug_info("bonjour", "si offer Message type - ERROR.\n");
477 xfer = bonjour_si_xfer_find(bd, id, pb->name);
479 if(xfer == NULL)
480 purple_debug_info("bonjour", "xfer find fail.\n");
481 else
482 purple_xfer_cancel_remote(xfer);
483 } else
484 purple_debug_info("bonjour", "si offer Message type - Unknown-%s.\n", type);
488 void
489 xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb)
491 const char *type, *from;
492 xmlnode *query;
493 BonjourData *bd;
495 g_return_if_fail(pc != NULL);
496 g_return_if_fail(packet != NULL);
497 g_return_if_fail(pb != NULL);
499 bd = (BonjourData*) pc->proto_data;
500 if(bd == NULL)
501 return;
503 purple_debug_info("bonjour", "xep-bytestreams-parse.\n");
505 type = xmlnode_get_attrib(packet, "type");
506 from = pb->name;
507 query = xmlnode_get_child(packet,"query");
508 if(type) {
509 if(!strcmp(type, "set")) {
510 const char *iq_id, *sid;
511 gboolean found = FALSE;
512 PurpleXfer *xfer;
514 purple_debug_info("bonjour", "bytestream offer Message type - SET.\n");
516 iq_id = xmlnode_get_attrib(packet, "id");
518 sid = xmlnode_get_attrib(query, "sid");
519 xfer = bonjour_si_xfer_find(bd, sid, from);
521 if(xfer) {
522 const char *jid, *host, *port;
523 xmlnode *streamhost;
524 int portnum;
525 XepXfer *xf = NULL;
527 xf = (XepXfer*)xfer->data;
528 for(streamhost = xmlnode_get_child(query, "streamhost");
529 streamhost;
530 streamhost = xmlnode_get_next_twin(streamhost)) {
532 if((jid = xmlnode_get_attrib(streamhost, "jid")) &&
533 (host = xmlnode_get_attrib(streamhost, "host")) &&
534 (port = xmlnode_get_attrib(streamhost, "port")) &&
535 (portnum = atoi(port))) {
537 if(!strcmp(host, xf->buddy_ip)) {
538 g_free(xf->iq_id);
539 xf->iq_id = g_strdup(iq_id);
540 xf->jid = g_strdup(jid);
541 xf->proxy_host = g_strdup(host);
542 xf->proxy_port = portnum;
543 purple_debug_info("bonjour", "bytestream offer parse"
544 "jid=%s host=%s port=%d.\n", jid, host, portnum);
545 bonjour_bytestreams_connect(xfer, pb);
546 found = TRUE;
547 break;
549 } else {
550 purple_debug_info("bonjour", "bytestream offer Message parse error.\n");
553 } else {
557 if (!found) {
558 purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n");
560 if (iq_id && xfer != NULL)
561 xep_ft_si_reject(bd, iq_id, xfer->who, "404", "cancel");
564 } else {
565 purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type);
570 static void
571 bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
572 const int filesize, const char *filename, int option)
574 PurpleXfer *xfer;
575 XepXfer *xf;
576 BonjourData *bd;
578 if(pc == NULL || id == NULL || from == NULL)
579 return;
581 bd = (BonjourData*) pc->proto_data;
582 if(bd == NULL)
583 return;
585 purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
587 /* Build the file transfer handle */
588 xfer = purple_xfer_new(pc->account, PURPLE_XFER_RECEIVE, from);
589 xfer->data = xf = g_new0(XepXfer, 1);
590 xf->data = bd;
591 purple_xfer_set_filename(xfer, filename);
592 xf->iq_id = g_strdup(id);
593 xf->sid = g_strdup(sid);
595 if(filesize > 0)
596 purple_xfer_set_size(xfer, filesize);
597 purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
598 purple_xfer_set_request_denied_fnc(xfer, bonjour_xfer_request_denied);
599 purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
600 purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
602 bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
604 purple_xfer_request(xfer);
607 static void
608 bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
610 PurpleXfer *xfer = data;
611 XepXfer *xf = xfer->data;
612 int acceptfd;
613 int len = 0;
615 if(xf == NULL)
616 return;
618 purple_debug_info("bonjour", "bonjour_sock5_request_cb - req_state = 0x%x\n", xf->sock5_req_state);
620 switch(xf->sock5_req_state){
621 case 0x00:
622 acceptfd = accept(source, NULL, 0);
623 if(acceptfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
625 } else if(acceptfd == -1) {
626 /* This should cancel the ft */
627 purple_debug_error("bonjour", "Error accepting incoming SOCKS5 connection. (%d)\n", errno);
629 purple_input_remove(xfer->watcher);
630 xfer->watcher = 0;
631 close(source);
632 purple_xfer_cancel_remote(xfer);
633 return;
634 } else {
635 int flags;
637 purple_debug_info("bonjour", "Accepted SOCKS5 ft connection - fd=%d\n", acceptfd);
639 flags = fcntl(acceptfd, F_GETFL);
640 fcntl(acceptfd, F_SETFL, flags | O_NONBLOCK);
641 #ifndef _WIN32
642 fcntl(acceptfd, F_SETFD, FD_CLOEXEC);
643 #endif
645 purple_input_remove(xfer->watcher);
646 close(source);
647 xfer->watcher = purple_input_add(acceptfd, PURPLE_INPUT_READ,
648 bonjour_sock5_request_cb, xfer);
649 xf->sock5_req_state++;
650 xf->rxlen = 0;
652 break;
653 case 0x01:
654 xfer->fd = source;
655 len = read(source, xf->rx_buf + xf->rxlen, 3);
656 if(len < 0 && errno == EAGAIN)
657 return;
658 else if(len <= 0){
659 purple_input_remove(xfer->watcher);
660 xfer->watcher = 0;
661 close(source);
662 purple_xfer_cancel_remote(xfer);
663 return;
664 } else {
665 purple_input_remove(xfer->watcher);
666 xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
667 bonjour_sock5_request_cb, xfer);
668 xf->sock5_req_state++;
669 xf->rxlen = 0;
670 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
672 break;
673 case 0x02:
674 xf->tx_buf[0] = 0x05;
675 xf->tx_buf[1] = 0x00;
676 len = write(source, xf->tx_buf, 2);
677 if (len < 0 && errno == EAGAIN)
678 return;
679 else if (len < 0) {
680 purple_input_remove(xfer->watcher);
681 xfer->watcher = 0;
682 close(source);
683 purple_xfer_cancel_remote(xfer);
684 return;
685 } else {
686 purple_input_remove(xfer->watcher);
687 xfer->watcher = purple_input_add(source, PURPLE_INPUT_READ,
688 bonjour_sock5_request_cb, xfer);
689 xf->sock5_req_state++;
690 xf->rxlen = 0;
692 break;
693 case 0x03:
694 len = read(source, xf->rx_buf + xf->rxlen, 20);
695 if(len<=0){
696 } else {
697 purple_input_remove(xfer->watcher);
698 xfer->watcher = purple_input_add(source, PURPLE_INPUT_WRITE,
699 bonjour_sock5_request_cb, xfer);
700 xf->sock5_req_state++;
701 xf->rxlen = 0;
702 bonjour_sock5_request_cb(xfer, source, PURPLE_INPUT_WRITE);
704 break;
705 case 0x04:
706 xf->tx_buf[0] = 0x05;
707 xf->tx_buf[1] = 0x00;
708 xf->tx_buf[2] = 0x00;
709 xf->tx_buf[3] = 0x03;
710 xf->tx_buf[4] = strlen(xf->buddy_ip);
711 memcpy(xf->tx_buf + 5, xf->buddy_ip, strlen(xf->buddy_ip));
712 xf->tx_buf[5+strlen(xf->buddy_ip)] = 0x00;
713 xf->tx_buf[6+strlen(xf->buddy_ip)] = 0x00;
714 len = write(source, xf->tx_buf, 7 + strlen(xf->buddy_ip));
715 if (len < 0 && errno == EAGAIN) {
716 return;
717 } else if (len < 0) {
718 purple_input_remove(xfer->watcher);
719 xfer->watcher = 0;
720 close(source);
721 purple_xfer_cancel_remote(xfer);
722 return;
723 } else {
724 purple_input_remove(xfer->watcher);
725 xfer->watcher = 0;
726 xf->rxlen = 0;
727 /*close(source);*/
728 purple_xfer_start(xfer, source, NULL, -1);
730 break;
731 default:
732 break;
734 return;
737 static void
738 bonjour_bytestreams_listen(int sock, gpointer data)
740 PurpleXfer *xfer = data;
741 XepXfer *xf;
742 XepIq *iq;
743 xmlnode *query, *streamhost;
744 gchar *port;
745 const char *next_ip, *local_ip;
746 const char token [] = ";";
747 BonjourData *bd;
749 purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock);
750 if (sock < 0 || xfer == NULL) {
751 /*purple_xfer_cancel_local(xfer);*/
752 return;
755 xfer->watcher = purple_input_add(sock, PURPLE_INPUT_READ,
756 bonjour_sock5_request_cb, xfer);
757 xf = (XepXfer*)xfer->data;
758 xf->listen_data = NULL;
760 bd = xf->data;
762 iq = xep_iq_new(bd, XEP_IQ_SET, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->sid);
764 query = xmlnode_new_child(iq->node, "query");
765 xmlnode_set_namespace(query, "http://jabber.org/protocol/bytestreams");
766 xmlnode_set_attrib(query, "sid", xf->sid);
767 xmlnode_set_attrib(query, "mode", "tcp");
769 xfer->local_port = purple_network_get_port_from_fd(sock);
771 local_ip = purple_network_get_my_ip_ext2(sock);
772 /* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */
773 next_ip = strtok((char *)local_ip, token);
775 port = g_strdup_printf("%hu", xfer->local_port);
776 while(next_ip != NULL) {
777 streamhost = xmlnode_new_child(query, "streamhost");
778 xmlnode_set_attrib(streamhost, "jid", xf->sid);
779 xmlnode_set_attrib(streamhost, "host", next_ip);
780 xmlnode_set_attrib(streamhost, "port", port);
781 next_ip = strtok(NULL, token);
783 g_free(port);
785 xep_iq_send_and_free(iq);
788 static void
789 bonjour_bytestreams_init(PurpleXfer *xfer)
791 XepXfer *xf;
792 if(xfer == NULL)
793 return;
794 purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
795 xf = xfer->data;
796 purple_network_listen_map_external(FALSE);
797 xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM,
798 bonjour_bytestreams_listen, xfer);
799 purple_network_listen_map_external(TRUE);
800 if (xf->listen_data == NULL) {
801 purple_xfer_cancel_local(xfer);
803 return;
806 static void
807 bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
809 PurpleXfer *xfer = data;
810 XepXfer *xf = xfer->data;
811 XepIq *iq;
812 xmlnode *q_node, *tmp_node;
813 BonjourData *bd;
815 xf->proxy_connection = NULL;
817 if(source < 0) {
818 purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n",
819 error_message ? error_message : "(null)");
820 xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
821 /* Cancel the connection */
822 purple_xfer_cancel_local(xfer);
823 return;
826 purple_debug_info("bonjour", "Connected successfully via SOCKS5, starting transfer.\n");
828 bd = xf->data;
830 /* Here, start the file transfer.*/
832 /* Notify Initiator of Connection */
833 iq = xep_iq_new(bd, XEP_IQ_RESULT, xfer->who, purple_account_get_username(bd->jabber_data->account), xf->iq_id);
834 q_node = xmlnode_new_child(iq->node, "query");
835 xmlnode_set_namespace(q_node, "http://jabber.org/protocol/bytestreams");
836 tmp_node = xmlnode_new_child(q_node, "streamhost-used");
837 xmlnode_set_attrib(tmp_node, "jid", xf->jid);
838 xep_iq_send_and_free(iq);
840 purple_xfer_start(xfer, source, NULL, -1);
843 static void
844 bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb)
846 XepXfer *xf;
847 char dstaddr[41];
848 unsigned char hashval[20];
849 char *p;
850 int i;
852 if(xfer == NULL)
853 return;
855 purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
857 xf = (XepXfer*)xfer->data;
858 if(!xf)
859 return;
861 p = g_strdup_printf("%s%s%s", xf->sid, pb->name, purple_account_get_username(pb->account));
862 purple_cipher_digest_region("sha1", (guchar *)p, strlen(p),
863 sizeof(hashval), hashval, NULL);
864 g_free(p);
866 memset(dstaddr, 0, 41);
867 p = dstaddr;
868 for(i = 0; i < 20; i++, p += 2)
869 snprintf(p, 3, "%02x", hashval[i]);
871 xf->proxy_info = purple_proxy_info_new();
872 purple_proxy_info_set_type(xf->proxy_info, PURPLE_PROXY_SOCKS5);
873 purple_proxy_info_set_host(xf->proxy_info, xf->proxy_host);
874 purple_proxy_info_set_port(xf->proxy_info, xf->proxy_port);
875 xf->proxy_connection = purple_proxy_connect_socks5(NULL, xf->proxy_info,
876 dstaddr, 0,
877 bonjour_bytestreams_connect_cb, xfer);
879 if(xf->proxy_connection == NULL) {
880 xep_ft_si_reject(xf->data, xf->iq_id, xfer->who, "404", "cancel");
881 /* Cancel the connection */
882 purple_xfer_cancel_local(xfer);