6 * Copyright (C) 2010 Jakub Adam <jakub.adam@tieto.com>
7 * Copyright (C) 2010 Tomáš Hrabčík <tomas.hrabcik@tieto.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <glib/gprintf.h>
36 #include "connection.h"
37 #include "eventloop.h"
44 #include "libc_interface.h"
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
51 #include <arpa/inet.h>
52 #ifdef HAVE_SYS_SOCKIO_H
53 #include <sys/sockio.h> /* SIOCGIFCONF for Solaris */
57 #include "core-depurple.h" /* Temporary for the core de-purple transition */
59 #include "sipe-common.h"
61 #include "sip-transport.h"
62 #include "sipe-backend.h"
63 #include "sipe-core.h"
64 #include "sipe-core-private.h"
65 #include "sipe-crypt.h"
66 #include "sipe-dialog.h"
67 #include "sipe-digest.h"
70 #include "sipe-session.h"
71 #include "sipe-utils.h"
74 #define SIPE_FT_KEY_LENGTH 24
75 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
78 * DO NOT CHANGE THE FOLLOWING CONSTANTS!!!
80 * It seems that Microsoft Office Communicator client will accept
81 * file transfer invitations *only* within this port range!
83 * If a firewall is active on your system you need to open these ports if
84 * you want to *send* files to other users. Receiving files uses an ougoing
85 * connection and should therefore automatically penetrate your firewall.
87 #define SIPE_FT_TCP_PORT_MIN 6891
88 #define SIPE_FT_TCP_PORT_MAX 6901
90 struct _sipe_file_transfer
{
91 guchar encryption_key
[SIPE_FT_KEY_LENGTH
];
92 guchar hash_key
[SIPE_FT_KEY_LENGTH
];
93 gchar
*invitation_cookie
;
95 struct sipe_account_data
*sip
;
96 struct sip_dialog
*dialog
;
97 gpointer cipher_context
;
98 gpointer hmac_context
;
100 PurpleNetworkListenData
*listener
;
103 gsize bytes_remaining_chunk
;
104 guchar
* encrypted_outbuf
;
108 typedef struct _sipe_file_transfer sipe_file_transfer
;
110 static void send_filetransfer_accept(PurpleXfer
* xfer
);
111 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
112 static ssize_t
do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
113 static gboolean
read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
114 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
);
115 static gpointer
sipe_cipher_context_init(const guchar
*enc_key
);
116 static gpointer
sipe_hmac_context_init(const guchar
*hash_key
);
117 static gchar
*sipe_hmac_finalize(gpointer hmac_context
);
118 static void generate_key(guchar
*buffer
, gsize size
);
119 static void set_socket_nonblock(int fd
, gboolean state
);
120 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
122 //******************************************************************************
123 // I/O operations for PurpleXfer structure
124 //******************************************************************************
127 sipe_ft_incoming_init(PurpleXfer
*xfer
)
129 send_filetransfer_accept(xfer
);
133 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
135 sipe_file_transfer
*ft
= xfer
->data
;
137 struct sipe_account_data
*sip
= PURPLE_XFER_TO_SIPE_ACCOUNT_DATA
;
139 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
142 purple_input_remove(xfer
->watcher
);
145 if (ft
->listenfd
>= 0) {
146 SIPE_DEBUG_INFO("sipe_ft_free_xfer_struct: closing listening socket %d", ft
->listenfd
);
150 purple_network_listen_cancel(ft
->listener
);
151 if (ft
->cipher_context
)
152 sipe_crypt_ft_destroy(ft
->cipher_context
);
154 if (ft
->hmac_context
)
155 sipe_digest_ft_destroy(ft
->hmac_context
);
157 g_free(ft
->encrypted_outbuf
);
158 g_free(ft
->invitation_cookie
);
165 sipe_ft_request_denied(PurpleXfer
*xfer
)
167 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
168 send_filetransfer_cancel(xfer
);
169 sipe_ft_free_xfer_struct(xfer
);
173 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
175 purple_xfer_error(purple_xfer_get_type(xfer
),
176 xfer
->account
, xfer
->who
,
181 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
183 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
184 raise_ft_error(xfer
, tmp
);
189 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
191 raise_ft_error(xfer
, errmsg
);
192 purple_xfer_cancel_local(xfer
);
196 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
198 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
202 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
204 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
208 sipe_ft_incoming_start(PurpleXfer
*xfer
)
210 sipe_file_transfer
*ft
;
211 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
212 static const gchar TFR
[] = "TFR\r\n";
213 const gsize BUFFER_SIZE
= 50;
214 gchar buf
[BUFFER_SIZE
];
215 struct sipe_account_data
*sip
;
217 const gsize FILE_SIZE_OFFSET
= 4;
222 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
223 raise_ft_socket_write_error_and_cancel(xfer
);
226 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
227 raise_ft_socket_read_error_and_cancel(xfer
);
231 sip
= PURPLE_XFER_TO_SIPE_ACCOUNT_DATA
;
233 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
234 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
235 raise_ft_socket_write_error_and_cancel(xfer
);
241 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
242 raise_ft_socket_read_error_and_cancel(xfer
);
246 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
247 if (file_size
!= xfer
->size
) {
248 raise_ft_error_and_cancel(xfer
,
249 _("File size is different from the advertised value."));
253 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
254 raise_ft_socket_write_error_and_cancel(xfer
);
258 ft
->bytes_remaining_chunk
= 0;
260 ft
->cipher_context
= sipe_cipher_context_init(ft
->encryption_key
);
261 ft
->hmac_context
= sipe_hmac_context_init(ft
->hash_key
);
265 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
267 static const gchar BYE
[] = "BYE 16777989\r\n";
268 gsize BUFFER_SIZE
= 50;
269 char buffer
[BUFFER_SIZE
];
270 const gssize MAC_OFFSET
= 4;
271 const gssize CRLF_LEN
= 2;
273 sipe_file_transfer
*ft
;
277 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
278 raise_ft_socket_write_error_and_cancel(xfer
);
282 macLen
= read_line(xfer
, buffer
, BUFFER_SIZE
);
285 raise_ft_socket_read_error_and_cancel(xfer
);
287 } else if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
288 raise_ft_error_and_cancel(xfer
, _("Received MAC is corrupted"));
294 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
295 mac1
= sipe_hmac_finalize(ft
->hmac_context
);
296 if (!sipe_strequal(mac
, mac1
)) {
297 unlink(xfer
->local_filename
);
298 raise_ft_error_and_cancel(xfer
,
299 _("Received file is corrupted"));
304 sipe_ft_free_xfer_struct(xfer
);
308 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
313 sipe_file_transfer
*ft
= xfer
->data
;
315 if (ft
->bytes_remaining_chunk
== 0) {
316 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
318 /* read chunk header */
319 if (!read_fully(xfer
, hdr_buf
, sizeof(hdr_buf
))) {
320 raise_ft_strerror(xfer
, _("Socket read failed"));
324 /* chunk header format:
326 * 0: 00 unknown (always zero?)
327 * 1: LL chunk size in bytes (low byte)
328 * 2: HH chunk size in bytes (high byte)
330 * Convert size from little endian to host order
332 ft
->bytes_remaining_chunk
= hdr_buf
[1] + (hdr_buf
[2] << 8);
335 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
336 xfer
->current_buffer_size
);
337 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
339 *buffer
= g_malloc(bytes_to_read
);
341 raise_ft_error(xfer
, _("Out of memory"));
342 SIPE_DEBUG_ERROR("sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer",
347 bytes_read
= do_read(xfer
, *buffer
, bytes_to_read
);
348 if (bytes_read
< 0) {
349 raise_ft_strerror(xfer
, _("Socket read failed"));
353 if (bytes_read
> 0) {
354 guchar
*decrypted
= g_malloc(bytes_read
);
357 raise_ft_error(xfer
, _("Out of memory"));
358 SIPE_DEBUG_ERROR("sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer",
364 sipe_crypt_ft_stream(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
);
368 sipe_digest_ft_update(ft
->hmac_context
, decrypted
, bytes_read
);
370 ft
->bytes_remaining_chunk
-= bytes_read
;
377 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
379 ssize_t bytes_written
;
380 sipe_file_transfer
*ft
= xfer
->data
;
382 /* When sending data via server with ForeFront installed, block bigger than
383 * this default causes ending of transmission. Hard limit block to this value
384 * when libpurple sends us more data. */
385 const gsize DEFAULT_BLOCK_SIZE
= 2045;
386 if (size
> DEFAULT_BLOCK_SIZE
)
387 size
= DEFAULT_BLOCK_SIZE
;
389 if (ft
->bytes_remaining_chunk
== 0) {
391 guchar local_buf
[16];
392 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
394 memset(local_buf
, 0, sizeof local_buf
);
396 // Check if receiver did not cancel the transfer before it is finished
397 bytes_read
= read(xfer
->fd
,local_buf
,sizeof (local_buf
));
398 if (bytes_read
== -1 && errno
!= EAGAIN
) {
399 raise_ft_strerror(xfer
, _("Socket read failed"));
401 } else if (bytes_read
> 0
402 && (g_str_has_prefix((gchar
*)local_buf
,"CCL\r\n")
403 || g_str_has_prefix((gchar
*)local_buf
,"BYE 2164261682\r\n"))) {
407 if (ft
->outbuf_size
< size
) {
408 g_free(ft
->encrypted_outbuf
);
409 ft
->outbuf_size
= size
;
410 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
411 if (!ft
->encrypted_outbuf
) {
412 raise_ft_error(xfer
, _("Out of memory"));
413 SIPE_DEBUG_ERROR("sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer",
419 ft
->bytes_remaining_chunk
= size
;
420 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
421 sipe_crypt_ft_stream(ft
->cipher_context
, buffer
, size
,
422 ft
->encrypted_outbuf
);
423 sipe_digest_ft_update(ft
->hmac_context
, buffer
, size
);
425 /* chunk header format:
427 * 0: 00 unknown (always zero?)
428 * 1: LL chunk size in bytes (low byte)
429 * 2: HH chunk size in bytes (high byte)
431 * Convert size from host order to little endian
434 hdr_buf
[1] = (ft
->bytes_remaining_chunk
& 0x00FF);
435 hdr_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
437 /* write chunk header */
438 if (write(xfer
->fd
, hdr_buf
, sizeof(hdr_buf
)) == -1) {
439 raise_ft_strerror(xfer
, _("Socket write failed"));
444 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
445 if (bytes_written
== -1) {
449 raise_ft_strerror(xfer
, _("Socket write failed"));
453 if (bytes_written
> 0) {
454 ft
->bytes_remaining_chunk
-= bytes_written
;
455 ft
->outbuf_ptr
+= bytes_written
;
458 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
459 purple_xfer_set_completed(xfer
, TRUE
);
461 return bytes_written
;
465 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
467 struct sip_dialog
*dialog
;
468 sipe_file_transfer
*ft
= xfer
->data
;
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: %lu\r\n"
476 //"Connectivity: N\r\n" TODO
477 "Encryption: R\r\n", // TODO: non encrypted file transfer support
478 ft
->invitation_cookie
,
479 purple_xfer_get_filename(xfer
),
480 (long unsigned) purple_xfer_get_size(xfer
));
482 struct sipe_account_data
*sip
= PURPLE_XFER_TO_SIPE_ACCOUNT_DATA
;
483 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
485 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
488 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
490 dialog
= sipe_dialog_find(session
, xfer
->who
);
491 if (dialog
&& !dialog
->outgoing_invite
) {
493 sipe_im_process_queue(sip
, session
);
494 } else if (!dialog
|| !dialog
->outgoing_invite
) {
495 // Need to send the INVITE to get the outgoing dialog setup
496 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
503 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
505 sipe_file_transfer
*ft
;
506 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
507 const gsize BUFFER_SIZE
= 50;
508 gchar buf
[BUFFER_SIZE
];
510 unsigned auth_cookie_received
;
511 gboolean users_match
;
513 ssize_t bytes_written
;
515 set_socket_nonblock(xfer
->fd
, TRUE
);
519 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
520 raise_ft_socket_read_error_and_cancel(xfer
);
524 if (!sipe_strequal(buf
,VER
)) {
525 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
526 SIPE_DEBUG_INFO("File transfer VER string incorrect, received: %s expected: %s",
531 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
532 raise_ft_socket_write_error_and_cancel(xfer
);
536 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
537 raise_ft_socket_read_error_and_cancel(xfer
);
541 parts
= g_strsplit(buf
, " ", 3);
543 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
545 // xfer->who has 'sip:' prefix, skip these four characters
546 users_match
= sipe_strcase_equal(parts
[1], (xfer
->who
+ 4));
549 SIPE_DEBUG_INFO("File transfer authentication: %s Expected: USR %s %u",
550 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
552 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
553 raise_ft_error_and_cancel(xfer
,
554 _("File transfer authentication failed."));
558 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
559 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
562 if (bytes_written
== -1) {
563 raise_ft_socket_write_error_and_cancel(xfer
);
568 if (read_line(xfer
,buf
,BUFFER_SIZE
) < 0) {
569 raise_ft_socket_read_error_and_cancel(xfer
);
573 ft
->bytes_remaining_chunk
= 0;
575 ft
->cipher_context
= sipe_cipher_context_init(ft
->encryption_key
);
576 ft
->hmac_context
= sipe_hmac_context_init(ft
->hash_key
);
580 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
582 sipe_file_transfer
*ft
= xfer
->data
;
583 gsize BUFFER_SIZE
= 50;
584 char buffer
[BUFFER_SIZE
];
589 if (read_line(xfer
, buffer
, BUFFER_SIZE
) < 0) {
590 raise_ft_socket_read_error_and_cancel(xfer
);
594 mac
= sipe_hmac_finalize(ft
->hmac_context
);
595 g_sprintf(buffer
, "MAC %s \r\n", mac
);
598 mac_strlen
= strlen(buffer
);
599 // There must be this zero byte between mac and \r\n
600 buffer
[mac_strlen
- 3] = 0;
602 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
603 raise_ft_socket_write_error_and_cancel(xfer
);
607 sipe_ft_free_xfer_struct(xfer
);
610 //******************************************************************************
612 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
, const GSList
*body
)
615 struct sipe_account_data
*sip
= PURPLE_ACCOUNT_TO_SIPE_ACCOUNT_DATA
;
616 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
617 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
619 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
620 session
= sipe_session_find_im(sip
, from
);
625 SIPE_DEBUG_ERROR_NOFORMAT("sipe_ft_incoming_transfer: can't find session for remote party");
629 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
633 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
634 ft
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
636 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
638 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
639 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
642 purple_xfer_set_filename(xfer
, sipe_utils_nameval_find(body
, "Application-File"));
644 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
, "Application-FileSize"),NULL
,10);
645 purple_xfer_set_size(xfer
, file_size
);
647 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
648 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
649 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
650 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
651 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
652 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
653 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
655 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
657 purple_xfer_request(xfer
);
661 void sipe_ft_incoming_accept(PurpleAccount
*account
, const GSList
*body
)
663 struct sipe_account_data
*sip
= PURPLE_ACCOUNT_TO_SIPE_ACCOUNT_DATA
;
664 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
665 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
668 const gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
669 const gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
670 const gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
671 const gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
672 const gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
674 sipe_file_transfer
*ft
= xfer
->data
;
677 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
680 guchar
*enc_key
= g_base64_decode(enc_key_b64
, &ret_len
);
681 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
682 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
684 raise_ft_error_and_cancel(xfer
,
685 _("Received encryption key has wrong size."));
693 guchar
*hash_key
= g_base64_decode(hash_key_b64
, &ret_len
);
694 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
695 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
697 raise_ft_error_and_cancel(xfer
,
698 _("Received hash key has wrong size."));
705 if (ip
&& port_str
) {
706 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
708 ft
->listener
= purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
709 SIPE_FT_TCP_PORT_MAX
,
711 sipe_ft_listen_socket_created
,
714 raise_ft_error_and_cancel(xfer
,
715 _("Could not create listen socket"));
722 void sipe_ft_incoming_cancel(PurpleAccount
*account
, GSList
*body
)
724 gchar
*inv_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
726 struct sipe_account_data
*sip
= PURPLE_ACCOUNT_TO_SIPE_ACCOUNT_DATA
;
727 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
729 purple_xfer_cancel_remote(xfer
);
732 static void send_filetransfer_accept(PurpleXfer
* xfer
)
734 sipe_file_transfer
* ft
= xfer
->data
;
735 struct sip_dialog
*dialog
= ft
->dialog
;
737 gchar
*b64_encryption_key
= g_base64_encode(ft
->encryption_key
, 24);
738 gchar
*b64_hash_key
= g_base64_encode(ft
->hash_key
, 24);
740 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
741 "Request-Data: IP-Address:\r\n"
742 "Invitation-Cookie: %s\r\n"
743 "Encryption-Key: %s\r\n"
745 /*"IP-Address: %s\r\n"
748 "Auth-Cookie: 11111111\r\n"
749 "Sender-Connect: TRUE\r\n"*/,
750 ft
->invitation_cookie
,
753 /*,sipe_backend_network_ip_address()*/
756 send_sip_request(ft
->sip
->private, "MESSAGE", dialog
->with
, dialog
->with
,
757 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
761 g_free(b64_encryption_key
);
762 g_free(b64_hash_key
);
765 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
766 sipe_file_transfer
* ft
= xfer
->data
;
767 struct sip_dialog
* dialog
= ft
->dialog
;
769 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
770 "Invitation-Cookie: %s\r\n"
771 "Cancel-Code: REJECT\r\n",
772 ft
->invitation_cookie
);
774 send_sip_request(ft
->sip
->private, "MESSAGE", dialog
->with
, dialog
->with
,
775 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
782 do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
784 ssize_t bytes_read
= read(xfer
->fd
, buf
, len
);
785 if (bytes_read
== 0) {
786 // Sender canceled transfer before it was finished
788 } else if (bytes_read
== -1) {
798 read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
800 const gulong READ_TIMEOUT
= 10000000;
801 gulong time_spent
= 0;
804 ssize_t bytes_read
= do_read(xfer
, buf
, len
);
805 if (bytes_read
== 0) {
807 time_spent
+= 100000;
808 } else if (bytes_read
< 0 || time_spent
> READ_TIMEOUT
) {
819 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
)
823 memset(buffer
,0,size
);
825 if (!read_fully(xfer
, (guchar
*) buffer
+ pos
, 1))
827 } while (buffer
[pos
] != '\n' && ++pos
!= (size
- 1));
829 if (pos
== (size
- 1) && buffer
[pos
- 1] != '\n') {
837 static gpointer
sipe_cipher_context_init(const guchar
*enc_key
)
840 * Decryption of file from SIPE file transfer
843 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
844 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
847 guchar k2
[SIPE_DIGEST_SHA1_LENGTH
];
850 sipe_digest_sha1(enc_key
, SIPE_FT_KEY_LENGTH
, k2
);
852 /* 2.) RC4 decryption */
853 return sipe_crypt_ft_start(k2
);
856 static gpointer
sipe_hmac_context_init(const guchar
*hash_key
)
862 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
863 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
866 guchar k2
[SIPE_DIGEST_SHA1_LENGTH
];
869 sipe_digest_sha1(hash_key
, SIPE_FT_KEY_LENGTH
, k2
);
871 /* 2.) HMAC (initialization only) */
872 return sipe_digest_ft_start(k2
);
875 static gchar
* sipe_hmac_finalize(gpointer hmac_context
)
877 guchar hmac_digest
[SIPE_DIGEST_FILETRANSFER_LENGTH
];
879 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
880 sipe_digest_ft_end(hmac_context
, hmac_digest
);
882 return g_base64_encode(hmac_digest
, sizeof (hmac_digest
));
885 static void generate_key(guchar
*buffer
, gsize size
)
888 for (i
= 0; i
!= size
; ++i
)
892 static void set_socket_nonblock(int fd
, gboolean state
)
894 int flags
= fcntl(fd
, F_GETFL
, 0);
899 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
901 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
904 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
906 PurpleXfer
*xfer
= sipe_ft_new_xfer(gc
, who
);
910 purple_xfer_request_accepted(xfer
, file
);
912 purple_xfer_request(xfer
);
916 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
918 PurpleXfer
*xfer
= NULL
;
920 if (PURPLE_CONNECTION_IS_VALID(gc
)) {
921 xfer
= purple_xfer_new(purple_connection_get_account(gc
),
922 PURPLE_XFER_SEND
, who
);
925 struct sipe_account_data
*sip
= PURPLE_GC_TO_SIPE_ACCOUNT_DATA
;
927 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
928 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
933 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
934 purple_xfer_set_start_fnc(xfer
, sipe_ft_outgoing_start
);
935 purple_xfer_set_end_fnc(xfer
, sipe_ft_outgoing_stop
);
936 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
937 purple_xfer_set_write_fnc(xfer
, sipe_ft_write
);
938 purple_xfer_set_cancel_send_fnc(xfer
, sipe_ft_free_xfer_struct
);
939 purple_xfer_set_cancel_recv_fnc(xfer
, sipe_ft_free_xfer_struct
);
947 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
948 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
950 struct sockaddr_in saddr
;
951 socklen_t slen
= sizeof (saddr
);
953 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
955 PurpleXfer
*xfer
= p_xfer
;
956 sipe_file_transfer
*ft
= xfer
->data
;
958 purple_input_remove(xfer
->watcher
);
964 raise_ft_socket_read_error_and_cancel(xfer
);
966 purple_xfer_start(xfer
, fd
, NULL
, 0);
971 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
974 PurpleXfer
*xfer
= data
;
975 sipe_file_transfer
*ft
= xfer
->data
;
977 struct sockaddr_in addr
;
979 socklen_t socklen
= sizeof (addr
);
982 ft
->listenfd
= listenfd
;
984 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
986 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
987 sipe_ft_client_connected
, xfer
);
989 ft
->auth_cookie
= rand() % 1000000000;
991 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
992 "Invitation-Cookie: %s\r\n"
997 "Request-Data: IP-Address:\r\n",
998 ft
->invitation_cookie
,
999 sipe_utils_get_suitable_local_ip(listenfd
),
1000 ntohs(addr
.sin_port
),
1004 struct sipe_account_data
*sip
= PURPLE_XFER_TO_SIPE_ACCOUNT_DATA
;
1005 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
1006 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
1010 send_sip_request(ft
->sip
->private, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
1011 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
1012 body
, ft
->dialog
, NULL
);
1017 GSList
* sipe_ft_parse_msg_body(const gchar
*body
)
1019 GSList
*list
= NULL
;
1020 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
1021 if (sipe_utils_parse_lines(&list
, lines
, ":") == FALSE
) {
1022 sipe_utils_nameval_free(list
);