filetransfer: move sipe_backend_network_listen_range() usage to core
[siplcs.git] / src / core / sipe-ft.c
blobbeb866b8db626ca7579370413416a8948f1b0e86
1 /**
2 * @file sipe-ft.c
4 * pidgin-sipe
6 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
8 * Copyright (C) 2010 Tomáš Hrabčík <tomas.hrabcik@tieto.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
29 #include <stdlib.h>
30 #include <string.h>
32 #include <glib.h>
33 #include <glib/gprintf.h>
35 #include "sipmsg.h"
36 #include "sip-transport.h"
37 #include "sipe-backend.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-crypt.h"
41 #include "sipe-dialog.h"
42 #include "sipe-digest.h"
43 #include "sipe-ft.h"
44 #include "sipe-im.h"
45 #include "sipe-nls.h"
46 #include "sipe-session.h"
47 #include "sipe-utils.h"
49 #define SIPE_FT_KEY_LENGTH 24
50 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
51 #define BUFFER_SIZE 50
54 * DO NOT CHANGE THE FOLLOWING CONSTANTS!!!
56 * It seems that Microsoft Office Communicator client will accept
57 * file transfer invitations *only* within this port range!
59 * If a firewall is active on your system you need to open these ports if
60 * you want to *send* files to other users. Receiving files uses an outgoing
61 * connection and should therefore automatically penetrate your firewall.
63 #define SIPE_FT_TCP_PORT_MIN 6891
64 #define SIPE_FT_TCP_PORT_MAX 6901
66 /**
67 * File transport (private part)
69 struct sipe_file_transfer_private {
70 struct sipe_file_transfer public;
72 struct sipe_core_private *sipe_private;
74 guchar encryption_key[SIPE_FT_KEY_LENGTH];
75 guchar hash_key[SIPE_FT_KEY_LENGTH];
76 unsigned auth_cookie;
77 gchar *invitation_cookie;
79 struct sip_dialog *dialog;
81 gpointer cipher_context;
82 gpointer hmac_context;
84 gsize bytes_remaining_chunk;
86 guchar *encrypted_outbuf;
87 guchar *outbuf_ptr;
88 gsize outbuf_size;
90 struct sipe_backend_listendata *listendata;
92 #define SIPE_FILE_TRANSFER_PUBLIC ((struct sipe_file_transfer *) ft_private)
93 #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_private *) ft)
95 static void raise_ft_error(struct sipe_file_transfer_private *ft_private,
96 const gchar *errmsg)
98 gchar *tmp = g_strdup_printf("%s: %s", errmsg,
99 sipe_backend_ft_get_error(SIPE_FILE_TRANSFER_PUBLIC));
100 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, tmp);
101 g_free(tmp);
104 static void raise_ft_error_and_cancel(struct sipe_file_transfer_private *ft_private,
105 const gchar *errmsg)
107 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, errmsg);
108 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER_PUBLIC);
111 static void raise_ft_socket_read_error_and_cancel(struct sipe_file_transfer_private *ft_private)
113 raise_ft_error_and_cancel(ft_private, _("Socket read failed"));
116 static void raise_ft_socket_write_error_and_cancel(struct sipe_file_transfer_private *ft_private)
118 raise_ft_error_and_cancel(ft_private, _("Socket write failed"));
121 static gboolean read_exact(struct sipe_file_transfer_private *ft_private,
122 guchar *data,
123 gsize size)
125 const gulong READ_TIMEOUT = 10000000;
126 gulong time_spent = 0;
128 while (size) {
129 gssize bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
130 data, size);
131 if (bytes_read == 0) {
132 g_usleep(100000);
133 time_spent += 100000;
134 } else if (bytes_read < 0 || time_spent > READ_TIMEOUT) {
135 return FALSE;
136 } else {
137 size -= bytes_read;
138 data += bytes_read;
139 time_spent = 0;
142 return TRUE;
145 static gboolean read_line(struct sipe_file_transfer_private *ft_private,
146 guchar *data,
147 gsize size)
149 gsize pos = 0;
151 if (size < 2) return FALSE;
153 memset(data, 0, size--);
154 do {
155 if (!read_exact(ft_private, data + pos, 1))
156 return FALSE;
157 } while ((data[pos] != '\n') && (++pos < size));
159 /* Buffer too short? */
160 if ((pos == size) && (data[pos - 1] != '\n')) {
161 return FALSE;
164 return TRUE;
167 static gboolean write_exact(struct sipe_file_transfer_private *ft_private,
168 const guchar *data,
169 gsize size)
171 gssize bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
172 data, size);
173 if ((bytes_written < 0) || ((gsize) bytes_written != size))
174 return FALSE;
175 return TRUE;
178 static void generate_key(guchar *buffer, gsize size)
180 gsize i = 0;
181 while (i < size) buffer[i++] = rand();
184 static gpointer sipe_cipher_context_init(const guchar *enc_key)
187 * Decryption of file from SIPE file transfer
189 * Decryption:
190 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
191 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
194 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
196 /* 1.) SHA1 sum */
197 sipe_digest_sha1(enc_key, SIPE_FT_KEY_LENGTH, k2);
199 /* 2.) RC4 decryption */
200 return sipe_crypt_ft_start(k2);
203 static gpointer sipe_hmac_context_init(const guchar *hash_key)
206 * Count MAC digest
208 * HMAC digest:
209 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
210 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
213 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
215 /* 1.) SHA1 sum */
216 sipe_digest_sha1(hash_key, SIPE_FT_KEY_LENGTH, k2);
218 /* 2.) HMAC (initialization only) */
219 return sipe_digest_ft_start(k2);
222 static gchar *sipe_hmac_finalize(gpointer hmac_context)
224 guchar hmac_digest[SIPE_DIGEST_FILETRANSFER_LENGTH];
226 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
227 sipe_digest_ft_end(hmac_context, hmac_digest);
229 return g_base64_encode(hmac_digest, sizeof (hmac_digest));
232 struct sipe_file_transfer *sipe_core_ft_allocate(struct sipe_core_public *sipe_public)
234 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
235 struct sipe_file_transfer_private *ft_private =
236 g_new0(struct sipe_file_transfer_private, 1);
238 ft_private->sipe_private = sipe_private;
239 ft_private->invitation_cookie = g_strdup_printf("%u", rand() % 1000000000);
241 return(SIPE_FILE_TRANSFER_PUBLIC);
244 static void sipe_ft_deallocate(struct sipe_file_transfer *ft)
246 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
248 if (ft->backend_private)
249 sipe_backend_ft_deallocate(ft);
251 if (ft_private->listendata)
252 sipe_backend_network_listen_cancel(ft_private->listendata);
254 if (ft_private->cipher_context)
255 sipe_crypt_ft_destroy(ft_private->cipher_context);
257 if (ft_private->hmac_context)
258 sipe_digest_ft_destroy(ft_private->hmac_context);
260 g_free(ft_private->invitation_cookie);
261 g_free(ft_private->encrypted_outbuf);
262 g_free(ft_private);
265 void sipe_core_ft_deallocate(struct sipe_file_transfer *ft)
267 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
268 struct sip_dialog *dialog = ft_private->dialog;
270 if (dialog)
271 dialog->filetransfers = g_slist_remove(dialog->filetransfers, ft_private);
273 sipe_ft_deallocate(ft);
276 static void sipe_ft_request(struct sipe_file_transfer_private *ft_private,
277 const gchar *body)
279 struct sip_dialog *dialog = ft_private->dialog;
280 sip_transport_request(ft_private->sipe_private,
281 "MESSAGE",
282 dialog->with,
283 dialog->with,
284 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
285 body,
286 dialog,
287 NULL);
290 void sipe_core_ft_cancel(struct sipe_file_transfer *ft)
292 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
294 gchar *body = g_strdup_printf("Invitation-Command: CANCEL\r\n"
295 "Invitation-Cookie: %s\r\n"
296 "Cancel-Code: REJECT\r\n",
297 ft_private->invitation_cookie);
298 sipe_ft_request(ft_private, body);
299 g_free(body);
302 void sipe_core_ft_incoming_init(struct sipe_file_transfer *ft)
304 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
305 gchar *b64_encryption_key = g_base64_encode(ft_private->encryption_key,
306 SIPE_FT_KEY_LENGTH);
307 gchar *b64_hash_key = g_base64_encode(ft_private->hash_key,
308 SIPE_FT_KEY_LENGTH);
310 gchar *body = g_strdup_printf("Invitation-Command: ACCEPT\r\n"
311 "Request-Data: IP-Address:\r\n"
312 "Invitation-Cookie: %s\r\n"
313 "Encryption-Key: %s\r\n"
314 "Hash-Key: %s\r\n"
315 /*"IP-Address: %s\r\n"
316 "Port: 6900\r\n"
317 "PortX: 11178\r\n"
318 "Auth-Cookie: 11111111\r\n"
319 "Sender-Connect: TRUE\r\n"*/,
320 ft_private->invitation_cookie,
321 b64_encryption_key,
322 b64_hash_key
323 /*,sipe_backend_network_ip_address()*/
325 sipe_ft_request(ft_private, body);
327 g_free(body);
328 g_free(b64_hash_key);
329 g_free(b64_encryption_key);
332 void sipe_core_ft_incoming_accept(struct sipe_file_transfer *ft,
333 const gchar *who,
334 unsigned short port)
336 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
337 gchar *body;
339 ft_private->auth_cookie = rand() % 1000000000;
341 body = g_strdup_printf("Invitation-Command: ACCEPT\r\n"
342 "Invitation-Cookie: %s\r\n"
343 "IP-Address: %s\r\n"
344 "Port: %u\r\n"
345 "PortX: 11178\r\n"
346 "AuthCookie: %u\r\n"
347 "Request-Data: IP-Address:\r\n",
348 ft_private->invitation_cookie,
349 sipe_utils_get_suitable_local_ip(-1),
350 port,
351 ft_private->auth_cookie);
353 if (!ft_private->dialog) {
354 struct sip_session *session = sipe_session_find_or_add_im(ft_private->sipe_private,
355 who);
356 ft_private->dialog = sipe_dialog_find(session, who);
359 if (ft_private->dialog) {
360 sipe_ft_request(ft_private, body);
362 g_free(body);
365 void sipe_core_ft_incoming_start(struct sipe_file_transfer *ft,
366 gsize total_size)
368 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
369 static const guchar TFR[] = "TFR\r\n";
370 const gsize FILE_SIZE_OFFSET = 4;
372 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
373 guchar buf[BUFFER_SIZE];
374 gchar *request;
375 gsize file_size;
377 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
378 raise_ft_socket_read_error_and_cancel(ft_private);
379 return;
381 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
382 raise_ft_socket_read_error_and_cancel(ft_private);
383 return;
386 request = g_strdup_printf("USR %s %u\r\n",
387 ft_private->sipe_private->username,
388 ft_private->auth_cookie);
389 if (!write_exact(ft_private, (guchar *)request, strlen(request))) {
390 raise_ft_socket_write_error_and_cancel(ft_private);
391 g_free(request);
392 return;
394 g_free(request);
396 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
397 raise_ft_socket_read_error_and_cancel(ft_private);
398 return;
401 file_size = g_ascii_strtoull((gchar *) buf + FILE_SIZE_OFFSET, NULL, 10);
402 if (file_size != total_size) {
403 raise_ft_error_and_cancel(ft_private,
404 _("File size is different from the advertised value."));
405 return;
408 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, TFR, sizeof(TFR) - 1)) {
409 raise_ft_socket_write_error_and_cancel(ft_private);
410 return;
413 ft_private->bytes_remaining_chunk = 0;
414 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
415 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
418 gboolean sipe_core_ft_incoming_stop(struct sipe_file_transfer *ft)
420 static const guchar BYE[] = "BYE 16777989\r\n";
421 const gsize MAC_OFFSET = 4;
423 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
424 gchar buffer[BUFFER_SIZE];
425 gsize mac_len;
426 gchar *mac;
427 gchar *mac1;
429 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, BYE, sizeof(BYE) - 1)) {
430 raise_ft_socket_write_error_and_cancel(ft_private);
431 return FALSE;
434 if (!read_line(ft_private, (guchar *) buffer, BUFFER_SIZE)) {
435 raise_ft_socket_read_error_and_cancel(ft_private);
436 return FALSE;
439 mac_len = strlen(buffer);
440 if (mac_len < (MAC_OFFSET)) {
441 raise_ft_error_and_cancel(ft_private,
442 _("Received MAC is corrupted"));
443 return FALSE;
446 /* Check MAC */
447 mac = g_strndup(buffer + MAC_OFFSET, mac_len - MAC_OFFSET);
448 mac1 = sipe_hmac_finalize(ft_private->hmac_context);
449 if (!sipe_strequal(mac, mac1)) {
450 g_free(mac1);
451 g_free(mac);
452 raise_ft_error_and_cancel(ft_private,
453 _("Received file is corrupted"));
454 return(FALSE);
456 g_free(mac1);
457 g_free(mac);
459 return(TRUE);
462 void sipe_core_ft_outgoing_init(struct sipe_file_transfer *ft,
463 const gchar *filename, gsize size,
464 const gchar *who)
466 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
467 struct sipe_core_private *sipe_private = ft_private->sipe_private;
468 struct sip_dialog *dialog;
470 gchar *body = g_strdup_printf("Application-Name: File Transfer\r\n"
471 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
472 "Invitation-Command: INVITE\r\n"
473 "Invitation-Cookie: %s\r\n"
474 "Application-File: %s\r\n"
475 "Application-FileSize: %" G_GSIZE_FORMAT "\r\n"
476 //"Connectivity: N\r\n" TODO
477 "Encryption: R\r\n", // TODO: non encrypted file transfer support
478 ft_private->invitation_cookie,
479 filename,
480 size);
482 struct sip_session *session = sipe_session_find_or_add_im(sipe_private, who);
484 // Queue the message
485 sipe_session_enqueue_message(session, body, "text/x-msmsgsinvite");
487 dialog = sipe_dialog_find(session, who);
488 if (dialog && !dialog->outgoing_invite) {
489 sipe_im_process_queue(sipe_private, session);
490 } else if (!dialog || !dialog->outgoing_invite) {
491 // Need to send the INVITE to get the outgoing dialog setup
492 sipe_im_invite(sipe_private, session, who, body, "text/x-msmsgsinvite", NULL, FALSE);
493 dialog = sipe_dialog_find(session, who);
496 dialog->filetransfers = g_slist_append(dialog->filetransfers, ft_private);
497 ft_private->dialog = dialog;
499 g_free(body);
502 void sipe_core_ft_outgoing_start(struct sipe_file_transfer *ft,
503 gsize total_size)
505 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
507 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
508 guchar buf[BUFFER_SIZE];
509 gchar **parts;
510 unsigned auth_cookie_received;
511 gboolean users_match;
513 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
514 raise_ft_socket_read_error_and_cancel(ft_private);
515 return;
518 if (!sipe_strequal((gchar *)buf, (gchar *)VER)) {
519 raise_ft_error_and_cancel(ft_private,
520 _("File transfer initialization failed."));
521 SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s",
522 buf, VER);
523 return;
526 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
527 raise_ft_socket_write_error_and_cancel(ft_private);
528 return;
531 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
532 raise_ft_socket_read_error_and_cancel(ft_private);
533 return;
536 parts = g_strsplit((gchar *)buf, " ", 3);
537 auth_cookie_received = g_ascii_strtoull(parts[2], NULL, 10);
538 /* dialog->with has 'sip:' prefix, skip these four characters */
539 users_match = sipe_strcase_equal(parts[1],
540 (ft_private->dialog->with + 4));
541 g_strfreev(parts);
543 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
544 buf,
545 ft_private->dialog->with + 4,
546 ft_private->auth_cookie);
548 if (!users_match ||
549 (ft_private->auth_cookie != auth_cookie_received)) {
550 raise_ft_error_and_cancel(ft_private,
551 _("File transfer authentication failed."));
552 return;
555 g_sprintf((gchar *)buf, "FIL %" G_GSIZE_FORMAT "\r\n", total_size);
556 if (!write_exact(ft_private, buf, strlen((gchar *)buf))) {
557 raise_ft_socket_write_error_and_cancel(ft_private);
558 return;
561 /* TFR */
562 if (!read_line(ft_private ,buf, BUFFER_SIZE)) {
563 raise_ft_socket_read_error_and_cancel(ft_private);
564 return;
567 ft_private->bytes_remaining_chunk = 0;
568 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
569 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
572 gboolean sipe_core_ft_outgoing_stop(struct sipe_file_transfer *ft)
574 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
575 guchar buffer[BUFFER_SIZE];
576 gchar *mac;
577 gsize mac_len;
579 /* BYE */
580 if (!read_line(ft_private, buffer, BUFFER_SIZE)) {
581 raise_ft_socket_read_error_and_cancel(ft_private);
582 return FALSE;
585 mac = sipe_hmac_finalize(ft_private->hmac_context);
586 g_sprintf((gchar *)buffer, "MAC %s \r\n", mac);
587 g_free(mac);
589 mac_len = strlen((gchar *)buffer);
590 /* There must be this zero byte between mac and \r\n */
591 buffer[mac_len - 3] = 0;
593 if (!write_exact(ft_private, buffer, mac_len)) {
594 raise_ft_socket_write_error_and_cancel(ft_private);
595 return FALSE;
598 return TRUE;
601 gssize sipe_core_ft_read(struct sipe_file_transfer *ft, guchar **buffer,
602 gsize bytes_remaining, gsize bytes_available)
604 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
605 gsize bytes_to_read;
606 gssize bytes_read;
608 if (ft_private->bytes_remaining_chunk == 0) {
609 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
611 /* read chunk header */
612 if (!read_exact(ft_private, hdr_buf, sizeof(hdr_buf))) {
613 raise_ft_error(ft_private, _("Socket read failed"));
614 return -1;
617 /* chunk header format:
619 * 0: 00 unknown (always zero?)
620 * 1: LL chunk size in bytes (low byte)
621 * 2: HH chunk size in bytes (high byte)
623 * Convert size from little endian to host order
625 ft_private->bytes_remaining_chunk =
626 hdr_buf[1] + (hdr_buf[2] << 8);
629 bytes_to_read = MIN(bytes_remaining, bytes_available);
630 bytes_to_read = MIN(bytes_to_read, ft_private->bytes_remaining_chunk);
632 *buffer = g_malloc(bytes_to_read);
633 if (!*buffer) {
634 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
635 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for receive buffer",
636 bytes_to_read);
637 return -1;
640 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, *buffer, bytes_to_read);
641 if (bytes_read < 0) {
642 raise_ft_error(ft_private, _("Socket read failed"));
643 g_free(*buffer);
644 *buffer = NULL;
645 return -1;
648 if (bytes_read > 0) {
649 guchar *decrypted = g_malloc(bytes_read);
651 if (!decrypted) {
652 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
653 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for decryption buffer",
654 (gsize)bytes_read);
655 g_free(*buffer);
656 *buffer = NULL;
657 return -1;
659 sipe_crypt_ft_stream(ft_private->cipher_context,
660 *buffer, bytes_read, decrypted);
661 g_free(*buffer);
662 *buffer = decrypted;
664 sipe_digest_ft_update(ft_private->hmac_context,
665 decrypted, bytes_read);
667 ft_private->bytes_remaining_chunk -= bytes_read;
670 return(bytes_read);
673 gssize sipe_core_ft_write(struct sipe_file_transfer *ft,
674 const guchar *buffer, gsize size)
676 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
677 gssize bytes_written;
679 /* When sending data via server with ForeFront installed, block bigger than
680 * this default causes ending of transmission. Hard limit block to this value
681 * when libpurple sends us more data. */
682 const gsize DEFAULT_BLOCK_SIZE = 2045;
683 if (size > DEFAULT_BLOCK_SIZE)
684 size = DEFAULT_BLOCK_SIZE;
686 if (ft_private->bytes_remaining_chunk == 0) {
687 gssize bytes_read;
688 guchar local_buf[16];
689 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
691 memset(local_buf, 0, sizeof local_buf);
693 /* Check if receiver did not cancel the transfer
694 before it is finished */
695 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
696 local_buf,
697 sizeof(local_buf));
698 if (bytes_read < 0) {
699 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
700 _("Socket read failed"));
701 return -1;
702 } else if ((bytes_read > 0) &&
703 (g_str_has_prefix((gchar *)local_buf, "CCL\r\n") ||
704 g_str_has_prefix((gchar *)local_buf, "BYE 2164261682\r\n"))) {
705 return -1;
708 if (ft_private->outbuf_size < size) {
709 g_free(ft_private->encrypted_outbuf);
710 ft_private->outbuf_size = size;
711 ft_private->encrypted_outbuf = g_malloc(ft_private->outbuf_size);
712 if (!ft_private->encrypted_outbuf) {
713 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
714 _("Out of memory"));
715 SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT " bytes for send buffer",
716 ft_private->outbuf_size);
717 return -1;
721 ft_private->bytes_remaining_chunk = size;
722 ft_private->outbuf_ptr = ft_private->encrypted_outbuf;
723 sipe_crypt_ft_stream(ft_private->cipher_context,
724 buffer, size,
725 ft_private->encrypted_outbuf);
726 sipe_digest_ft_update(ft_private->hmac_context,
727 buffer, size);
729 /* chunk header format:
731 * 0: 00 unknown (always zero?)
732 * 1: LL chunk size in bytes (low byte)
733 * 2: HH chunk size in bytes (high byte)
735 * Convert size from host order to little endian
737 hdr_buf[0] = 0;
738 hdr_buf[1] = (ft_private->bytes_remaining_chunk & 0x00FF);
739 hdr_buf[2] = (ft_private->bytes_remaining_chunk & 0xFF00) >> 8;
741 /* write chunk header */
742 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, hdr_buf, sizeof(hdr_buf))) {
743 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
744 _("Socket write failed"));
745 return -1;
749 bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
750 ft_private->outbuf_ptr,
751 ft_private->bytes_remaining_chunk);
752 if (bytes_written < 0) {
753 raise_ft_error(ft_private, _("Socket write failed"));
754 } else if (bytes_written > 0) {
755 ft_private->bytes_remaining_chunk -= bytes_written;
756 ft_private->outbuf_ptr += bytes_written;
759 return bytes_written;
762 void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private,
763 struct sip_dialog *dialog,
764 const GSList *body)
766 struct sipe_file_transfer_private *ft_private;
767 gsize file_size;
769 ft_private = g_new0(struct sipe_file_transfer_private, 1);
770 ft_private->sipe_private = sipe_private;
772 generate_key(ft_private->encryption_key, SIPE_FT_KEY_LENGTH);
773 generate_key(ft_private->hash_key, SIPE_FT_KEY_LENGTH);
775 ft_private->invitation_cookie = g_strdup(sipe_utils_nameval_find(body, "Invitation-Cookie"));
777 ft_private->dialog = dialog;
779 file_size = g_ascii_strtoull(sipe_utils_nameval_find(body,
780 "Application-FileSize"),
781 NULL, 10);
782 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC,
783 SIPE_FILE_TRANSFER_PUBLIC,
784 dialog->with,
785 sipe_utils_nameval_find(body, "Application-File"),
786 file_size);
788 if (ft_private->public.backend_private != NULL) {
789 ft_private->dialog->filetransfers = g_slist_append(ft_private->dialog->filetransfers, ft_private);
790 } else {
791 sipe_ft_deallocate(SIPE_FILE_TRANSFER_PUBLIC);
795 static struct sipe_file_transfer_private *
796 sipe_find_ft(const struct sip_dialog *dialog, const gchar *inv_cookie)
798 GSList *ftlist = dialog->filetransfers;
799 for (; ftlist != NULL; ftlist = ftlist->next) {
800 struct sipe_file_transfer_private *ft_private = ftlist->data;
801 if (sipe_strequal(ft_private->invitation_cookie, inv_cookie))
802 return ft_private;
804 return NULL;
807 static void
808 listen_socket_created_cb(unsigned short port, gpointer data)
810 struct sipe_file_transfer_private *ft_private = data;
811 sipe_core_ft_incoming_accept(SIPE_FILE_TRANSFER_PUBLIC,
812 ft_private->dialog->with, port);
815 static void
816 client_connected_cb(gint fd, gpointer data)
818 struct sipe_file_transfer *ft = data;
820 SIPE_FILE_TRANSFER_PRIVATE->listendata = NULL;
822 if (fd < 0) {
823 sipe_backend_ft_error(ft, _("Socket read failed"));
824 sipe_backend_ft_cancel_local(ft);
825 } else {
826 sipe_backend_ft_start(ft, fd, NULL, 0);
830 void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body)
832 const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie");
833 struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie);
835 if (ft_private) {
836 const gchar *ip = sipe_utils_nameval_find(body, "IP-Address");
837 const gchar *port_str = sipe_utils_nameval_find(body, "Port");
838 const gchar *auth_cookie = sipe_utils_nameval_find(body, "AuthCookie");
839 const gchar *enc_key_b64 = sipe_utils_nameval_find(body, "Encryption-Key");
840 const gchar *hash_key_b64 = sipe_utils_nameval_find(body, "Hash-Key");
842 if (auth_cookie)
843 ft_private->auth_cookie = g_ascii_strtoull(auth_cookie,
844 NULL, 10);
845 if (enc_key_b64) {
846 gsize ret_len;
847 guchar *enc_key = g_base64_decode(enc_key_b64,
848 &ret_len);
849 if (ret_len == SIPE_FT_KEY_LENGTH) {
850 memcpy(ft_private->encryption_key,
851 enc_key, SIPE_FT_KEY_LENGTH);
852 } else {
853 raise_ft_error_and_cancel(ft_private,
854 _("Received encryption key has wrong size."));
855 g_free(enc_key);
856 return;
858 g_free(enc_key);
860 if (hash_key_b64) {
861 gsize ret_len;
862 guchar *hash_key = g_base64_decode(hash_key_b64,
863 &ret_len);
864 if (ret_len == SIPE_FT_KEY_LENGTH) {
865 memcpy(ft_private->hash_key,
866 hash_key, SIPE_FT_KEY_LENGTH);
867 } else {
868 raise_ft_error_and_cancel(ft_private,
869 _("Received hash key has wrong size."));
870 g_free(hash_key);
871 return;
873 g_free(hash_key);
877 if (ip && port_str) {
878 sipe_backend_ft_start(SIPE_FILE_TRANSFER_PUBLIC, -1, ip,
879 g_ascii_strtoull(port_str, NULL, 10));
880 } else {
881 ft_private->listendata =
882 sipe_backend_network_listen_range(SIPE_FT_TCP_PORT_MIN,
883 SIPE_FT_TCP_PORT_MAX,
884 listen_socket_created_cb,
885 client_connected_cb,
886 ft_private);
887 if (!ft_private->listendata)
888 raise_ft_error_and_cancel(ft_private,
889 _("Could not create listen socket"));
894 void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body)
896 const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie");
897 struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie);
899 if (ft_private)
900 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC);
903 GSList *sipe_ft_parse_msg_body(const gchar *body)
905 GSList *list = NULL;
906 gchar **lines = g_strsplit(body, "\r\n", 0);
907 if (sipe_utils_parse_lines(&list, lines, ":") == FALSE) {
908 sipe_utils_nameval_free(list);
909 list = NULL;
911 g_strfreev(lines);
912 return list;
916 Local Variables:
917 mode: c
918 c-file-style: "bsd"
919 indent-tabs-mode: t
920 tab-width: 8
921 End: