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
33 #include <glib/gprintf.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"
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
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
];
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
;
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
,
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
);
104 static void raise_ft_error_and_cancel(struct sipe_file_transfer_private
*ft_private
,
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
,
125 const gulong READ_TIMEOUT
= 10000000;
126 gulong time_spent
= 0;
129 gssize bytes_read
= sipe_backend_ft_read(SIPE_FILE_TRANSFER_PUBLIC
,
131 if (bytes_read
== 0) {
133 time_spent
+= 100000;
134 } else if (bytes_read
< 0 || time_spent
> READ_TIMEOUT
) {
145 static gboolean
read_line(struct sipe_file_transfer_private
*ft_private
,
151 if (size
< 2) return FALSE
;
153 memset(data
, 0, size
--);
155 if (!read_exact(ft_private
, data
+ pos
, 1))
157 } while ((data
[pos
] != '\n') && (++pos
< size
));
159 /* Buffer too short? */
160 if ((pos
== size
) && (data
[pos
- 1] != '\n')) {
167 static gboolean
write_exact(struct sipe_file_transfer_private
*ft_private
,
171 gssize bytes_written
= sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
,
173 if ((bytes_written
< 0) || ((gsize
) bytes_written
!= size
))
178 static void generate_key(guchar
*buffer
, gsize size
)
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
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
];
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
)
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
];
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
);
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
;
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
,
279 struct sip_dialog
*dialog
= ft_private
->dialog
;
280 sip_transport_request(ft_private
->sipe_private
,
284 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
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
);
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
,
307 gchar
*b64_hash_key
= g_base64_encode(ft_private
->hash_key
,
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"
315 /*"IP-Address: %s\r\n"
318 "Auth-Cookie: 11111111\r\n"
319 "Sender-Connect: TRUE\r\n"*/,
320 ft_private
->invitation_cookie
,
323 /*,sipe_backend_network_ip_address()*/
325 sipe_ft_request(ft_private
, 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
,
336 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
339 ft_private
->auth_cookie
= rand() % 1000000000;
341 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
342 "Invitation-Cookie: %s\r\n"
347 "Request-Data: IP-Address:\r\n",
348 ft_private
->invitation_cookie
,
349 sipe_utils_get_suitable_local_ip(-1),
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
,
356 ft_private
->dialog
= sipe_dialog_find(session
, who
);
359 if (ft_private
->dialog
) {
360 sipe_ft_request(ft_private
, body
);
365 void sipe_core_ft_incoming_start(struct sipe_file_transfer
*ft
,
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
];
377 if (!write_exact(ft_private
, VER
, sizeof(VER
) - 1)) {
378 raise_ft_socket_read_error_and_cancel(ft_private
);
381 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
382 raise_ft_socket_read_error_and_cancel(ft_private
);
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
);
396 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
397 raise_ft_socket_read_error_and_cancel(ft_private
);
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."));
408 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
, TFR
, sizeof(TFR
) - 1)) {
409 raise_ft_socket_write_error_and_cancel(ft_private
);
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
];
429 if (!sipe_backend_ft_write(SIPE_FILE_TRANSFER_PUBLIC
, BYE
, sizeof(BYE
) - 1)) {
430 raise_ft_socket_write_error_and_cancel(ft_private
);
434 if (!read_line(ft_private
, (guchar
*) buffer
, BUFFER_SIZE
)) {
435 raise_ft_socket_read_error_and_cancel(ft_private
);
439 mac_len
= strlen(buffer
);
440 if (mac_len
< (MAC_OFFSET
)) {
441 raise_ft_error_and_cancel(ft_private
,
442 _("Received MAC is corrupted"));
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
)) {
452 raise_ft_error_and_cancel(ft_private
,
453 _("Received file is corrupted"));
462 void sipe_core_ft_outgoing_init(struct sipe_file_transfer
*ft
,
463 const gchar
*filename
, gsize size
,
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
,
482 struct sip_session
*session
= sipe_session_find_or_add_im(sipe_private
, who
);
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
;
502 void sipe_core_ft_outgoing_start(struct sipe_file_transfer
*ft
,
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
];
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
);
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",
526 if (!write_exact(ft_private
, VER
, sizeof(VER
) - 1)) {
527 raise_ft_socket_write_error_and_cancel(ft_private
);
531 if (!read_line(ft_private
, buf
, BUFFER_SIZE
)) {
532 raise_ft_socket_read_error_and_cancel(ft_private
);
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));
543 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
545 ft_private
->dialog
->with
+ 4,
546 ft_private
->auth_cookie
);
549 (ft_private
->auth_cookie
!= auth_cookie_received
)) {
550 raise_ft_error_and_cancel(ft_private
,
551 _("File transfer authentication failed."));
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
);
562 if (!read_line(ft_private
,buf
, BUFFER_SIZE
)) {
563 raise_ft_socket_read_error_and_cancel(ft_private
);
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
];
580 if (!read_line(ft_private
, buffer
, BUFFER_SIZE
)) {
581 raise_ft_socket_read_error_and_cancel(ft_private
);
585 mac
= sipe_hmac_finalize(ft_private
->hmac_context
);
586 g_sprintf((gchar
*)buffer
, "MAC %s \r\n", 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
);
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
;
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"));
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
);
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",
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"));
648 if (bytes_read
> 0) {
649 guchar
*decrypted
= g_malloc(bytes_read
);
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",
659 sipe_crypt_ft_stream(ft_private
->cipher_context
,
660 *buffer
, bytes_read
, decrypted
);
664 sipe_digest_ft_update(ft_private
->hmac_context
,
665 decrypted
, bytes_read
);
667 ft_private
->bytes_remaining_chunk
-= 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) {
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
,
698 if (bytes_read
< 0) {
699 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
,
700 _("Socket read failed"));
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"))) {
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
,
715 SIPE_DEBUG_ERROR("sipe_core_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer",
716 ft_private
->outbuf_size
);
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
,
725 ft_private
->encrypted_outbuf
);
726 sipe_digest_ft_update(ft_private
->hmac_context
,
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
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"));
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
,
766 struct sipe_file_transfer_private
*ft_private
;
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"),
782 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC
,
783 SIPE_FILE_TRANSFER_PUBLIC
,
785 sipe_utils_nameval_find(body
, "Application-File"),
788 if (ft_private
->public.backend_private
!= NULL
) {
789 ft_private
->dialog
->filetransfers
= g_slist_append(ft_private
->dialog
->filetransfers
, ft_private
);
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
))
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
);
816 client_connected_cb(gint fd
, gpointer data
)
818 struct sipe_file_transfer
*ft
= data
;
820 SIPE_FILE_TRANSFER_PRIVATE
->listendata
= NULL
;
823 sipe_backend_ft_error(ft
, _("Socket read failed"));
824 sipe_backend_ft_cancel_local(ft
);
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
);
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");
843 ft_private
->auth_cookie
= g_ascii_strtoull(auth_cookie
,
847 guchar
*enc_key
= g_base64_decode(enc_key_b64
,
849 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
850 memcpy(ft_private
->encryption_key
,
851 enc_key
, SIPE_FT_KEY_LENGTH
);
853 raise_ft_error_and_cancel(ft_private
,
854 _("Received encryption key has wrong size."));
862 guchar
*hash_key
= g_base64_decode(hash_key_b64
,
864 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
865 memcpy(ft_private
->hash_key
,
866 hash_key
, SIPE_FT_KEY_LENGTH
);
868 raise_ft_error_and_cancel(ft_private
,
869 _("Received hash key has wrong size."));
877 if (ip
&& port_str
) {
878 sipe_backend_ft_start(SIPE_FILE_TRANSFER_PUBLIC
, -1, ip
,
879 g_ascii_strtoull(port_str
, NULL
, 10));
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
,
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
);
900 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC
);
903 GSList
*sipe_ft_parse_msg_body(const gchar
*body
)
906 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
907 if (sipe_utils_parse_lines(&list
, lines
, ":") == FALSE
) {
908 sipe_utils_nameval_free(list
);