filetransfer: extracted usage of purple_network_listen_range() into
[siplcs.git] / src / core / sipe-ft.c
blob9004de25ad24d882ea6f1a1e26d50e1e14a9d7e1
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 #define SIPE_FILE_TRANSFER_PUBLIC ((struct sipe_file_transfer *) ft_private)
91 #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_private *) ft)
93 static void raise_ft_error(struct sipe_file_transfer_private *ft_private,
94 const gchar *errmsg)
96 gchar *tmp = g_strdup_printf("%s: %s", errmsg,
97 sipe_backend_ft_get_error(SIPE_FILE_TRANSFER_PUBLIC));
98 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, tmp);
99 g_free(tmp);
102 static void raise_ft_error_and_cancel(struct sipe_file_transfer_private *ft_private,
103 const gchar *errmsg)
105 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, errmsg);
106 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER_PUBLIC);
109 static void raise_ft_socket_read_error_and_cancel(struct sipe_file_transfer_private *ft_private)
111 raise_ft_error_and_cancel(ft_private, _("Socket read failed"));
114 static void raise_ft_socket_write_error_and_cancel(struct sipe_file_transfer_private *ft_private)
116 raise_ft_error_and_cancel(ft_private, _("Socket write failed"));
119 static gboolean read_exact(struct sipe_file_transfer_private *ft_private,
120 guchar *data,
121 gsize size)
123 const gulong READ_TIMEOUT = 10000000;
124 gulong time_spent = 0;
126 while (size) {
127 gssize bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
128 data, size);
129 if (bytes_read == 0) {
130 g_usleep(100000);
131 time_spent += 100000;
132 } else if (bytes_read < 0 || time_spent > READ_TIMEOUT) {
133 return FALSE;
134 } else {
135 size -= bytes_read;
136 data += bytes_read;
137 time_spent = 0;
140 return TRUE;
143 static gboolean read_line(struct sipe_file_transfer_private *ft_private,
144 guchar *data,
145 gsize size)
147 gsize pos = 0;
149 if (size < 2) return FALSE;
151 memset(data, 0, size--);
152 do {
153 if (!read_exact(ft_private, data + pos, 1))
154 return FALSE;
155 } while ((data[pos] != '\n') && (++pos < size));
157 /* Buffer too short? */
158 if ((pos == size) && (data[pos - 1] != '\n')) {
159 return FALSE;
162 return TRUE;
165 static gboolean write_exact(struct sipe_file_transfer_private *ft_private,
166 const guchar *data,
167 gsize size)
169 gssize bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
170 data, size);
171 if ((bytes_written < 0) || ((gsize) bytes_written != size))
172 return FALSE;
173 return TRUE;
176 static void generate_key(guchar *buffer, gsize size)
178 gsize i = 0;
179 while (i < size) buffer[i++] = rand();
182 static gpointer sipe_cipher_context_init(const guchar *enc_key)
185 * Decryption of file from SIPE file transfer
187 * Decryption:
188 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
189 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
192 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
194 /* 1.) SHA1 sum */
195 sipe_digest_sha1(enc_key, SIPE_FT_KEY_LENGTH, k2);
197 /* 2.) RC4 decryption */
198 return sipe_crypt_ft_start(k2);
201 static gpointer sipe_hmac_context_init(const guchar *hash_key)
204 * Count MAC digest
206 * HMAC digest:
207 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
208 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
211 guchar k2[SIPE_DIGEST_SHA1_LENGTH];
213 /* 1.) SHA1 sum */
214 sipe_digest_sha1(hash_key, SIPE_FT_KEY_LENGTH, k2);
216 /* 2.) HMAC (initialization only) */
217 return sipe_digest_ft_start(k2);
220 static gchar *sipe_hmac_finalize(gpointer hmac_context)
222 guchar hmac_digest[SIPE_DIGEST_FILETRANSFER_LENGTH];
224 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
225 sipe_digest_ft_end(hmac_context, hmac_digest);
227 return g_base64_encode(hmac_digest, sizeof (hmac_digest));
230 struct sipe_file_transfer *sipe_core_ft_allocate(struct sipe_core_public *sipe_public)
232 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
233 struct sipe_file_transfer_private *ft_private =
234 g_new0(struct sipe_file_transfer_private, 1);
236 ft_private->sipe_private = sipe_private;
237 ft_private->invitation_cookie = g_strdup_printf("%u", rand() % 1000000000);
239 return(SIPE_FILE_TRANSFER_PUBLIC);
242 static void sipe_ft_deallocate(struct sipe_file_transfer *ft)
244 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
246 if (ft->backend_private)
247 sipe_backend_ft_deallocate(ft);
249 if (ft_private->cipher_context)
250 sipe_crypt_ft_destroy(ft_private->cipher_context);
252 if (ft_private->hmac_context)
253 sipe_digest_ft_destroy(ft_private->hmac_context);
255 g_free(ft_private->invitation_cookie);
256 g_free(ft_private->encrypted_outbuf);
257 g_free(ft_private);
260 void sipe_core_ft_deallocate(struct sipe_file_transfer *ft)
262 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
263 struct sip_dialog *dialog = ft_private->dialog;
265 if (dialog)
266 dialog->filetransfers = g_slist_remove(dialog->filetransfers, ft_private);
268 sipe_ft_deallocate(ft);
271 static void sipe_ft_request(struct sipe_file_transfer_private *ft_private,
272 const gchar *body)
274 struct sip_dialog *dialog = ft_private->dialog;
275 sip_transport_request(ft_private->sipe_private,
276 "MESSAGE",
277 dialog->with,
278 dialog->with,
279 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
280 body,
281 dialog,
282 NULL);
285 void sipe_core_ft_cancel(struct sipe_file_transfer *ft)
287 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
289 gchar *body = g_strdup_printf("Invitation-Command: CANCEL\r\n"
290 "Invitation-Cookie: %s\r\n"
291 "Cancel-Code: REJECT\r\n",
292 ft_private->invitation_cookie);
293 sipe_ft_request(ft_private, body);
294 g_free(body);
297 void sipe_core_ft_incoming_init(struct sipe_file_transfer *ft)
299 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
300 gchar *b64_encryption_key = g_base64_encode(ft_private->encryption_key,
301 SIPE_FT_KEY_LENGTH);
302 gchar *b64_hash_key = g_base64_encode(ft_private->hash_key,
303 SIPE_FT_KEY_LENGTH);
305 gchar *body = g_strdup_printf("Invitation-Command: ACCEPT\r\n"
306 "Request-Data: IP-Address:\r\n"
307 "Invitation-Cookie: %s\r\n"
308 "Encryption-Key: %s\r\n"
309 "Hash-Key: %s\r\n"
310 /*"IP-Address: %s\r\n"
311 "Port: 6900\r\n"
312 "PortX: 11178\r\n"
313 "Auth-Cookie: 11111111\r\n"
314 "Sender-Connect: TRUE\r\n"*/,
315 ft_private->invitation_cookie,
316 b64_encryption_key,
317 b64_hash_key
318 /*,sipe_backend_network_ip_address()*/
320 sipe_ft_request(ft_private, body);
322 g_free(body);
323 g_free(b64_hash_key);
324 g_free(b64_encryption_key);
327 void sipe_core_ft_incoming_accept(struct sipe_file_transfer *ft,
328 const gchar *who,
329 unsigned short port)
331 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
332 gchar *body;
334 ft_private->auth_cookie = rand() % 1000000000;
336 body = g_strdup_printf("Invitation-Command: ACCEPT\r\n"
337 "Invitation-Cookie: %s\r\n"
338 "IP-Address: %s\r\n"
339 "Port: %u\r\n"
340 "PortX: 11178\r\n"
341 "AuthCookie: %u\r\n"
342 "Request-Data: IP-Address:\r\n",
343 ft_private->invitation_cookie,
344 sipe_utils_get_suitable_local_ip(-1),
345 port,
346 ft_private->auth_cookie);
348 if (!ft_private->dialog) {
349 struct sip_session *session = sipe_session_find_or_add_im(ft_private->sipe_private,
350 who);
351 ft_private->dialog = sipe_dialog_find(session, who);
354 if (ft_private->dialog) {
355 sipe_ft_request(ft_private, body);
357 g_free(body);
360 void sipe_core_ft_incoming_start(struct sipe_file_transfer *ft,
361 gsize total_size)
363 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
364 static const guchar TFR[] = "TFR\r\n";
365 const gsize FILE_SIZE_OFFSET = 4;
367 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
368 guchar buf[BUFFER_SIZE];
369 gchar *request;
370 gsize file_size;
372 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
373 raise_ft_socket_read_error_and_cancel(ft_private);
374 return;
376 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
377 raise_ft_socket_read_error_and_cancel(ft_private);
378 return;
381 request = g_strdup_printf("USR %s %u\r\n",
382 ft_private->sipe_private->username,
383 ft_private->auth_cookie);
384 if (!write_exact(ft_private, (guchar *)request, strlen(request))) {
385 raise_ft_socket_write_error_and_cancel(ft_private);
386 g_free(request);
387 return;
389 g_free(request);
391 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
392 raise_ft_socket_read_error_and_cancel(ft_private);
393 return;
396 file_size = g_ascii_strtoull((gchar *) buf + FILE_SIZE_OFFSET, NULL, 10);
397 if (file_size != total_size) {
398 raise_ft_error_and_cancel(ft_private,
399 _("File size is different from the advertised value."));
400 return;
403 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, TFR, sizeof(TFR) - 1)) {
404 raise_ft_socket_write_error_and_cancel(ft_private);
405 return;
408 ft_private->bytes_remaining_chunk = 0;
409 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
410 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
413 gboolean sipe_core_ft_incoming_stop(struct sipe_file_transfer *ft)
415 static const guchar BYE[] = "BYE 16777989\r\n";
416 const gsize MAC_OFFSET = 4;
418 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
419 gchar buffer[BUFFER_SIZE];
420 gsize mac_len;
421 gchar *mac;
422 gchar *mac1;
424 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, BYE, sizeof(BYE) - 1)) {
425 raise_ft_socket_write_error_and_cancel(ft_private);
426 return FALSE;
429 if (!read_line(ft_private, (guchar *) buffer, BUFFER_SIZE)) {
430 raise_ft_socket_read_error_and_cancel(ft_private);
431 return FALSE;
434 mac_len = strlen(buffer);
435 if (mac_len < (MAC_OFFSET)) {
436 raise_ft_error_and_cancel(ft_private,
437 _("Received MAC is corrupted"));
438 return FALSE;
441 /* Check MAC */
442 mac = g_strndup(buffer + MAC_OFFSET, mac_len - MAC_OFFSET);
443 mac1 = sipe_hmac_finalize(ft_private->hmac_context);
444 if (!sipe_strequal(mac, mac1)) {
445 g_free(mac1);
446 g_free(mac);
447 raise_ft_error_and_cancel(ft_private,
448 _("Received file is corrupted"));
449 return(FALSE);
451 g_free(mac1);
452 g_free(mac);
454 return(TRUE);
457 void sipe_core_ft_outgoing_init(struct sipe_file_transfer *ft,
458 const gchar *filename, gsize size,
459 const gchar *who)
461 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
462 struct sipe_core_private *sipe_private = ft_private->sipe_private;
463 struct sip_dialog *dialog;
465 gchar *body = g_strdup_printf("Application-Name: File Transfer\r\n"
466 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
467 "Invitation-Command: INVITE\r\n"
468 "Invitation-Cookie: %s\r\n"
469 "Application-File: %s\r\n"
470 "Application-FileSize: %" G_GSIZE_FORMAT "\r\n"
471 //"Connectivity: N\r\n" TODO
472 "Encryption: R\r\n", // TODO: non encrypted file transfer support
473 ft_private->invitation_cookie,
474 filename,
475 size);
477 struct sip_session *session = sipe_session_find_or_add_im(sipe_private, who);
479 // Queue the message
480 sipe_session_enqueue_message(session, body, "text/x-msmsgsinvite");
482 dialog = sipe_dialog_find(session, who);
483 if (dialog && !dialog->outgoing_invite) {
484 sipe_im_process_queue(sipe_private, session);
485 } else if (!dialog || !dialog->outgoing_invite) {
486 // Need to send the INVITE to get the outgoing dialog setup
487 sipe_im_invite(sipe_private, session, who, body, "text/x-msmsgsinvite", NULL, FALSE);
488 dialog = sipe_dialog_find(session, who);
491 dialog->filetransfers = g_slist_append(dialog->filetransfers, ft_private);
492 ft_private->dialog = dialog;
494 g_free(body);
497 void sipe_core_ft_outgoing_start(struct sipe_file_transfer *ft,
498 gsize total_size)
500 static const guchar VER[] = "VER MSN_SECURE_FTP\r\n";
502 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
503 guchar buf[BUFFER_SIZE];
504 gchar **parts;
505 unsigned auth_cookie_received;
506 gboolean users_match;
508 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
509 raise_ft_socket_read_error_and_cancel(ft_private);
510 return;
513 if (!sipe_strequal((gchar *)buf, (gchar *)VER)) {
514 raise_ft_error_and_cancel(ft_private,
515 _("File transfer initialization failed."));
516 SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s",
517 buf, VER);
518 return;
521 if (!write_exact(ft_private, VER, sizeof(VER) - 1)) {
522 raise_ft_socket_write_error_and_cancel(ft_private);
523 return;
526 if (!read_line(ft_private, buf, BUFFER_SIZE)) {
527 raise_ft_socket_read_error_and_cancel(ft_private);
528 return;
531 parts = g_strsplit((gchar *)buf, " ", 3);
532 auth_cookie_received = g_ascii_strtoull(parts[2], NULL, 10);
533 /* dialog->with has 'sip:' prefix, skip these four characters */
534 users_match = sipe_strcase_equal(parts[1],
535 (ft_private->dialog->with + 4));
536 g_strfreev(parts);
538 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
539 buf,
540 ft_private->dialog->with + 4,
541 ft_private->auth_cookie);
543 if (!users_match ||
544 (ft_private->auth_cookie != auth_cookie_received)) {
545 raise_ft_error_and_cancel(ft_private,
546 _("File transfer authentication failed."));
547 return;
550 g_sprintf((gchar *)buf, "FIL %" G_GSIZE_FORMAT "\r\n", total_size);
551 if (!write_exact(ft_private, buf, strlen((gchar *)buf))) {
552 raise_ft_socket_write_error_and_cancel(ft_private);
553 return;
556 /* TFR */
557 if (!read_line(ft_private ,buf, BUFFER_SIZE)) {
558 raise_ft_socket_read_error_and_cancel(ft_private);
559 return;
562 ft_private->bytes_remaining_chunk = 0;
563 ft_private->cipher_context = sipe_cipher_context_init(ft_private->encryption_key);
564 ft_private->hmac_context = sipe_hmac_context_init(ft_private->hash_key);
567 gboolean sipe_core_ft_outgoing_stop(struct sipe_file_transfer *ft)
569 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
570 guchar buffer[BUFFER_SIZE];
571 gchar *mac;
572 gsize mac_len;
574 /* BYE */
575 if (!read_line(ft_private, buffer, BUFFER_SIZE)) {
576 raise_ft_socket_read_error_and_cancel(ft_private);
577 return FALSE;
580 mac = sipe_hmac_finalize(ft_private->hmac_context);
581 g_sprintf((gchar *)buffer, "MAC %s \r\n", mac);
582 g_free(mac);
584 mac_len = strlen((gchar *)buffer);
585 /* There must be this zero byte between mac and \r\n */
586 buffer[mac_len - 3] = 0;
588 if (!write_exact(ft_private, buffer, mac_len)) {
589 raise_ft_socket_write_error_and_cancel(ft_private);
590 return FALSE;
593 return TRUE;
596 gssize sipe_core_ft_read(struct sipe_file_transfer *ft, guchar **buffer,
597 gsize bytes_remaining, gsize bytes_available)
599 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
600 gsize bytes_to_read;
601 gssize bytes_read;
603 if (ft_private->bytes_remaining_chunk == 0) {
604 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
606 /* read chunk header */
607 if (!read_exact(ft_private, hdr_buf, sizeof(hdr_buf))) {
608 raise_ft_error(ft_private, _("Socket read failed"));
609 return -1;
612 /* chunk header format:
614 * 0: 00 unknown (always zero?)
615 * 1: LL chunk size in bytes (low byte)
616 * 2: HH chunk size in bytes (high byte)
618 * Convert size from little endian to host order
620 ft_private->bytes_remaining_chunk =
621 hdr_buf[1] + (hdr_buf[2] << 8);
624 bytes_to_read = MIN(bytes_remaining, bytes_available);
625 bytes_to_read = MIN(bytes_to_read, ft_private->bytes_remaining_chunk);
627 *buffer = g_malloc(bytes_to_read);
628 if (!*buffer) {
629 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
630 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for receive buffer",
631 bytes_to_read);
632 return -1;
635 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC, *buffer, bytes_to_read);
636 if (bytes_read < 0) {
637 raise_ft_error(ft_private, _("Socket read failed"));
638 g_free(*buffer);
639 *buffer = NULL;
640 return -1;
643 if (bytes_read > 0) {
644 guchar *decrypted = g_malloc(bytes_read);
646 if (!decrypted) {
647 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC, _("Out of memory"));
648 SIPE_DEBUG_ERROR("sipe_core_ft_read: can't allocate %" G_GSIZE_FORMAT " bytes for decryption buffer",
649 (gsize)bytes_read);
650 g_free(*buffer);
651 *buffer = NULL;
652 return -1;
654 sipe_crypt_ft_stream(ft_private->cipher_context,
655 *buffer, bytes_read, decrypted);
656 g_free(*buffer);
657 *buffer = decrypted;
659 sipe_digest_ft_update(ft_private->hmac_context,
660 decrypted, bytes_read);
662 ft_private->bytes_remaining_chunk -= bytes_read;
665 return(bytes_read);
668 gssize sipe_core_ft_write(struct sipe_file_transfer *ft,
669 const guchar *buffer, gsize size)
671 struct sipe_file_transfer_private *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
672 gssize bytes_written;
674 /* When sending data via server with ForeFront installed, block bigger than
675 * this default causes ending of transmission. Hard limit block to this value
676 * when libpurple sends us more data. */
677 const gsize DEFAULT_BLOCK_SIZE = 2045;
678 if (size > DEFAULT_BLOCK_SIZE)
679 size = DEFAULT_BLOCK_SIZE;
681 if (ft_private->bytes_remaining_chunk == 0) {
682 gssize bytes_read;
683 guchar local_buf[16];
684 guchar hdr_buf[SIPE_FT_CHUNK_HEADER_LENGTH];
686 memset(local_buf, 0, sizeof local_buf);
688 /* Check if receiver did not cancel the transfer
689 before it is finished */
690 bytes_read = sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC,
691 local_buf,
692 sizeof(local_buf));
693 if (bytes_read < 0) {
694 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
695 _("Socket read failed"));
696 return -1;
697 } else if ((bytes_read > 0) &&
698 (g_str_has_prefix((gchar *)local_buf, "CCL\r\n") ||
699 g_str_has_prefix((gchar *)local_buf, "BYE 2164261682\r\n"))) {
700 return -1;
703 if (ft_private->outbuf_size < size) {
704 g_free(ft_private->encrypted_outbuf);
705 ft_private->outbuf_size = size;
706 ft_private->encrypted_outbuf = g_malloc(ft_private->outbuf_size);
707 if (!ft_private->encrypted_outbuf) {
708 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
709 _("Out of memory"));
710 SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT " bytes for send buffer",
711 ft_private->outbuf_size);
712 return -1;
716 ft_private->bytes_remaining_chunk = size;
717 ft_private->outbuf_ptr = ft_private->encrypted_outbuf;
718 sipe_crypt_ft_stream(ft_private->cipher_context,
719 buffer, size,
720 ft_private->encrypted_outbuf);
721 sipe_digest_ft_update(ft_private->hmac_context,
722 buffer, size);
724 /* chunk header format:
726 * 0: 00 unknown (always zero?)
727 * 1: LL chunk size in bytes (low byte)
728 * 2: HH chunk size in bytes (high byte)
730 * Convert size from host order to little endian
732 hdr_buf[0] = 0;
733 hdr_buf[1] = (ft_private->bytes_remaining_chunk & 0x00FF);
734 hdr_buf[2] = (ft_private->bytes_remaining_chunk & 0xFF00) >> 8;
736 /* write chunk header */
737 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC, hdr_buf, sizeof(hdr_buf))) {
738 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC,
739 _("Socket write failed"));
740 return -1;
744 bytes_written = sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC,
745 ft_private->outbuf_ptr,
746 ft_private->bytes_remaining_chunk);
747 if (bytes_written < 0) {
748 raise_ft_error(ft_private, _("Socket write failed"));
749 } else if (bytes_written > 0) {
750 ft_private->bytes_remaining_chunk -= bytes_written;
751 ft_private->outbuf_ptr += bytes_written;
754 return bytes_written;
757 void sipe_ft_incoming_transfer(struct sipe_core_private *sipe_private,
758 struct sip_dialog *dialog,
759 const GSList *body)
761 struct sipe_file_transfer_private *ft_private;
762 gsize file_size;
764 ft_private = g_new0(struct sipe_file_transfer_private, 1);
765 ft_private->sipe_private = sipe_private;
767 generate_key(ft_private->encryption_key, SIPE_FT_KEY_LENGTH);
768 generate_key(ft_private->hash_key, SIPE_FT_KEY_LENGTH);
770 ft_private->invitation_cookie = g_strdup(sipe_utils_nameval_find(body, "Invitation-Cookie"));
772 ft_private->dialog = dialog;
774 file_size = g_ascii_strtoull(sipe_utils_nameval_find(body,
775 "Application-FileSize"),
776 NULL, 10);
777 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC,
778 SIPE_FILE_TRANSFER_PUBLIC,
779 dialog->with,
780 sipe_utils_nameval_find(body, "Application-File"),
781 file_size);
783 if (ft_private->public.backend_private != NULL) {
784 ft_private->dialog->filetransfers = g_slist_append(ft_private->dialog->filetransfers, ft_private);
785 } else {
786 sipe_ft_deallocate(SIPE_FILE_TRANSFER_PUBLIC);
790 static struct sipe_file_transfer_private *
791 sipe_find_ft(const struct sip_dialog *dialog, const gchar *inv_cookie)
793 GSList *ftlist = dialog->filetransfers;
794 for (; ftlist != NULL; ftlist = ftlist->next) {
795 struct sipe_file_transfer_private *ft_private = ftlist->data;
796 if (sipe_strequal(ft_private->invitation_cookie, inv_cookie))
797 return ft_private;
799 return NULL;
802 void sipe_ft_incoming_accept(struct sip_dialog *dialog, const GSList *body)
804 const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie");
805 struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie);
807 if (ft_private) {
808 const gchar *ip = sipe_utils_nameval_find(body, "IP-Address");
809 const gchar *port_str = sipe_utils_nameval_find(body, "Port");
810 const gchar *auth_cookie = sipe_utils_nameval_find(body, "AuthCookie");
811 const gchar *enc_key_b64 = sipe_utils_nameval_find(body, "Encryption-Key");
812 const gchar *hash_key_b64 = sipe_utils_nameval_find(body, "Hash-Key");
814 if (auth_cookie)
815 ft_private->auth_cookie = g_ascii_strtoull(auth_cookie,
816 NULL, 10);
817 if (enc_key_b64) {
818 gsize ret_len;
819 guchar *enc_key = g_base64_decode(enc_key_b64,
820 &ret_len);
821 if (ret_len == SIPE_FT_KEY_LENGTH) {
822 memcpy(ft_private->encryption_key,
823 enc_key, SIPE_FT_KEY_LENGTH);
824 } else {
825 raise_ft_error_and_cancel(ft_private,
826 _("Received encryption key has wrong size."));
827 g_free(enc_key);
828 return;
830 g_free(enc_key);
832 if (hash_key_b64) {
833 gsize ret_len;
834 guchar *hash_key = g_base64_decode(hash_key_b64,
835 &ret_len);
836 if (ret_len == SIPE_FT_KEY_LENGTH) {
837 memcpy(ft_private->hash_key,
838 hash_key, SIPE_FT_KEY_LENGTH);
839 } else {
840 raise_ft_error_and_cancel(ft_private,
841 _("Received hash key has wrong size."));
842 g_free(hash_key);
843 return;
845 g_free(hash_key);
849 if (ip && port_str) {
850 unsigned short port = g_ascii_strtoull(port_str,
851 NULL, 10);
853 sipe_backend_ft_incoming_accept(SIPE_FILE_TRANSFER_PUBLIC,
855 port,
856 port);
857 } else {
858 if (!sipe_backend_ft_incoming_accept(SIPE_FILE_TRANSFER_PUBLIC,
859 NULL,
860 SIPE_FT_TCP_PORT_MIN,
861 SIPE_FT_TCP_PORT_MAX)) {
862 raise_ft_error_and_cancel(ft_private,
863 _("Could not create listen socket"));
864 return;
870 void sipe_ft_incoming_cancel(struct sip_dialog *dialog, const GSList *body)
872 const gchar *inv_cookie = sipe_utils_nameval_find(body, "Invitation-Cookie");
873 struct sipe_file_transfer_private *ft_private = sipe_find_ft(dialog, inv_cookie);
875 if (ft_private)
876 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC);
879 GSList *sipe_ft_parse_msg_body(const gchar *body)
881 GSList *list = NULL;
882 gchar **lines = g_strsplit(body, "\r\n", 0);
883 if (sipe_utils_parse_lines(&list, lines, ":") == FALSE) {
884 sipe_utils_nameval_free(list);
885 list = NULL;
887 g_strfreev(lines);
888 return list;
892 Local Variables:
893 mode: c
894 c-file-style: "bsd"
895 indent-tabs-mode: t
896 tab-width: 8
897 End: