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>
37 #include "connection.h"
39 #include "eventloop.h"
45 #include "sipe-dialog.h"
48 #include "sipe-session.h"
49 #include "sipe-utils.h"
54 #include "libc_interface.h"
57 #include <sys/types.h>
58 #include <sys/socket.h>
59 #include <sys/ioctl.h>
60 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #ifdef HAVE_SYS_SOCKIO_H
64 #include <sys/sockio.h> /* SIOCGIFCONF for Solaris */
68 #define SIPE_FT_KEY_LENGTH 24
69 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
72 * DO NOT CHANGE THE FOLLOWING CONSTANTS!!!
74 * It seems that Microsoft Office Communicator client will accept
75 * file transfer invitations *only* within this port range!
77 * If a firewall is active on your system you need to open these ports if
78 * you want to *send* files to other users. Receiving files uses an ougoing
79 * connection and should therefore automatically penetrate your firewall.
81 #define SIPE_FT_TCP_PORT_MIN 6891
82 #define SIPE_FT_TCP_PORT_MAX 6901
84 struct _sipe_file_transfer
{
85 guchar encryption_key
[SIPE_FT_KEY_LENGTH
];
86 guchar hash_key
[SIPE_FT_KEY_LENGTH
];
87 gchar
*invitation_cookie
;
89 struct sipe_account_data
*sip
;
90 struct sip_dialog
*dialog
;
91 PurpleCipherContext
*cipher_context
;
92 PurpleCipherContext
*hmac_context
;
94 PurpleNetworkListenData
*listener
;
97 gsize bytes_remaining_chunk
;
98 guchar
* encrypted_outbuf
;
102 typedef struct _sipe_file_transfer sipe_file_transfer
;
104 static void send_filetransfer_accept(PurpleXfer
* xfer
);
105 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
106 static ssize_t
do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
107 static gboolean
read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
108 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
);
109 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
);
110 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
);
111 static gchar
*sipe_hmac_finalize(PurpleCipherContext
*hmac_context
);
112 static void generate_key(guchar
*buffer
, gsize size
);
113 static void set_socket_nonblock(int fd
, gboolean state
);
114 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
115 static const char * sipe_ft_get_suitable_local_ip(int fd
);
117 //******************************************************************************
118 // I/O operations for PurpleXfer structure
119 //******************************************************************************
122 sipe_ft_incoming_init(PurpleXfer
*xfer
)
124 send_filetransfer_accept(xfer
);
128 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
130 sipe_file_transfer
*ft
= xfer
->data
;
132 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
134 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
137 purple_input_remove(xfer
->watcher
);
140 if (ft
->listenfd
>= 0) {
141 purple_debug_info("sipe", "sipe_ft_free_xfer_struct: closing listening socket %d\n", ft
->listenfd
);
145 purple_network_listen_cancel(ft
->listener
);
146 if (ft
->cipher_context
)
147 purple_cipher_context_destroy(ft
->cipher_context
);
149 if (ft
->hmac_context
)
150 purple_cipher_context_destroy(ft
->hmac_context
);
152 g_free(ft
->encrypted_outbuf
);
153 g_free(ft
->invitation_cookie
);
160 sipe_ft_request_denied(PurpleXfer
*xfer
)
162 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
163 send_filetransfer_cancel(xfer
);
164 sipe_ft_free_xfer_struct(xfer
);
168 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
170 purple_xfer_error(purple_xfer_get_type(xfer
),
171 xfer
->account
, xfer
->who
,
176 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
178 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
179 raise_ft_error(xfer
, tmp
);
184 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
186 raise_ft_error(xfer
, errmsg
);
187 purple_xfer_cancel_local(xfer
);
191 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
193 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
197 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
199 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
203 sipe_ft_incoming_start(PurpleXfer
*xfer
)
205 sipe_file_transfer
*ft
;
206 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
207 static const gchar TFR
[] = "TFR\r\n";
208 const gsize BUFFER_SIZE
= 50;
209 gchar buf
[BUFFER_SIZE
];
210 struct sipe_account_data
*sip
;
212 const gsize FILE_SIZE_OFFSET
= 4;
217 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
218 raise_ft_socket_write_error_and_cancel(xfer
);
221 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
222 raise_ft_socket_read_error_and_cancel(xfer
);
226 sip
= xfer
->account
->gc
->proto_data
;
228 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
229 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
230 raise_ft_socket_write_error_and_cancel(xfer
);
236 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
237 raise_ft_socket_read_error_and_cancel(xfer
);
241 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
242 if (file_size
!= xfer
->size
) {
243 raise_ft_error_and_cancel(xfer
,
244 _("File size is different from the advertised value."));
248 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
249 raise_ft_socket_write_error_and_cancel(xfer
);
253 ft
->bytes_remaining_chunk
= 0;
255 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
256 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
260 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
262 static const gchar BYE
[] = "BYE 16777989\r\n";
263 gsize BUFFER_SIZE
= 50;
264 char buffer
[BUFFER_SIZE
];
265 const gssize MAC_OFFSET
= 4;
266 const gssize CRLF_LEN
= 2;
268 sipe_file_transfer
*ft
;
272 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
273 raise_ft_socket_write_error_and_cancel(xfer
);
277 macLen
= read_line(xfer
, buffer
, BUFFER_SIZE
);
280 raise_ft_socket_read_error_and_cancel(xfer
);
282 } else if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
283 raise_ft_error_and_cancel(xfer
, _("Received MAC is corrupted"));
289 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
290 mac1
= sipe_hmac_finalize(ft
->hmac_context
);
291 if (!sipe_strequal(mac
, mac1
)) {
292 unlink(xfer
->local_filename
);
293 raise_ft_error_and_cancel(xfer
,
294 _("Received file is corrupted"));
299 sipe_ft_free_xfer_struct(xfer
);
303 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
308 sipe_file_transfer
*ft
= xfer
->data
;
310 if (ft
->bytes_remaining_chunk
== 0) {
311 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
313 /* read chunk header */
314 if (!read_fully(xfer
, hdr_buf
, sizeof(hdr_buf
))) {
315 raise_ft_strerror(xfer
, _("Socket read failed"));
319 /* chunk header format:
321 * 0: 00 unknown (always zero?)
322 * 1: LL chunk size in bytes (low byte)
323 * 2: HH chunk size in bytes (high byte)
325 * Convert size from little endian to host order
327 ft
->bytes_remaining_chunk
= hdr_buf
[1] + (hdr_buf
[2] << 8);
330 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
331 xfer
->current_buffer_size
);
332 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
334 *buffer
= g_malloc(bytes_to_read
);
336 raise_ft_error(xfer
, _("Out of memory"));
337 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer\n",
342 bytes_read
= do_read(xfer
, *buffer
, bytes_to_read
);
343 if (bytes_read
< 0) {
344 raise_ft_strerror(xfer
, _("Socket read failed"));
348 if (bytes_read
> 0) {
349 guchar
*decrypted
= g_malloc(bytes_read
);
352 raise_ft_error(xfer
, _("Out of memory"));
353 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer\n",
359 purple_cipher_context_encrypt(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
, NULL
);
363 purple_cipher_context_append(ft
->hmac_context
, decrypted
, bytes_read
);
365 ft
->bytes_remaining_chunk
-= bytes_read
;
372 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
374 ssize_t bytes_written
;
375 sipe_file_transfer
*ft
= xfer
->data
;
377 /* When sending data via server with ForeFront installed, block bigger than
378 * this default causes ending of transmission. Hard limit block to this value
379 * when libpurple sends us more data. */
380 const gsize DEFAULT_BLOCK_SIZE
= 2045;
381 if (size
> DEFAULT_BLOCK_SIZE
)
382 size
= DEFAULT_BLOCK_SIZE
;
384 if (ft
->bytes_remaining_chunk
== 0) {
386 guchar local_buf
[16];
387 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
389 memset(local_buf
, 0, sizeof local_buf
);
391 // Check if receiver did not cancel the transfer before it is finished
392 bytes_read
= read(xfer
->fd
,local_buf
,sizeof (local_buf
));
393 if (bytes_read
== -1 && errno
!= EAGAIN
) {
394 raise_ft_strerror(xfer
, _("Socket read failed"));
396 } else if (bytes_read
> 0
397 && (g_str_has_prefix((gchar
*)local_buf
,"CCL\r\n")
398 || g_str_has_prefix((gchar
*)local_buf
,"BYE 2164261682\r\n"))) {
402 if (ft
->outbuf_size
< size
) {
403 g_free(ft
->encrypted_outbuf
);
404 ft
->outbuf_size
= size
;
405 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
406 if (!ft
->encrypted_outbuf
) {
407 raise_ft_error(xfer
, _("Out of memory"));
408 purple_debug_error("sipe", "sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer\n",
414 ft
->bytes_remaining_chunk
= size
;
415 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
416 purple_cipher_context_encrypt(ft
->cipher_context
, buffer
, size
,
417 ft
->encrypted_outbuf
, NULL
);
418 purple_cipher_context_append(ft
->hmac_context
, buffer
, size
);
420 /* chunk header format:
422 * 0: 00 unknown (always zero?)
423 * 1: LL chunk size in bytes (low byte)
424 * 2: HH chunk size in bytes (high byte)
426 * Convert size from host order to little endian
429 hdr_buf
[1] = (ft
->bytes_remaining_chunk
& 0x00FF);
430 hdr_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
432 /* write chunk header */
433 if (write(xfer
->fd
, hdr_buf
, sizeof(hdr_buf
)) == -1) {
434 raise_ft_strerror(xfer
, _("Socket write failed"));
439 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
440 if (bytes_written
== -1) {
444 raise_ft_strerror(xfer
, _("Socket write failed"));
448 if (bytes_written
> 0) {
449 ft
->bytes_remaining_chunk
-= bytes_written
;
450 ft
->outbuf_ptr
+= bytes_written
;
453 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
454 purple_xfer_set_completed(xfer
, TRUE
);
456 return bytes_written
;
460 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
462 struct sip_dialog
*dialog
;
463 sipe_file_transfer
*ft
= xfer
->data
;
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: %lu\r\n"
471 //"Connectivity: N\r\n" TODO
472 "Encryption: R\r\n", // TODO: non encrypted file transfer support
473 ft
->invitation_cookie
,
474 purple_xfer_get_filename(xfer
),
475 (long unsigned) purple_xfer_get_size(xfer
));
477 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
478 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
480 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
483 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
485 dialog
= sipe_dialog_find(session
, xfer
->who
);
486 if (dialog
&& !dialog
->outgoing_invite
) {
488 sipe_im_process_queue(sip
, session
);
489 } else if (!dialog
|| !dialog
->outgoing_invite
) {
490 // Need to send the INVITE to get the outgoing dialog setup
491 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
498 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
500 sipe_file_transfer
*ft
;
501 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
502 const gsize BUFFER_SIZE
= 50;
503 gchar buf
[BUFFER_SIZE
];
505 unsigned auth_cookie_received
;
506 gboolean users_match
;
508 ssize_t bytes_written
;
510 set_socket_nonblock(xfer
->fd
, TRUE
);
514 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
515 raise_ft_socket_read_error_and_cancel(xfer
);
519 if (!sipe_strequal(buf
,VER
)) {
520 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
521 purple_debug_info("sipe","File transfer VER string incorrect, received: %s expected: %s",
526 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
527 raise_ft_socket_write_error_and_cancel(xfer
);
531 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
532 raise_ft_socket_read_error_and_cancel(xfer
);
536 parts
= g_strsplit(buf
, " ", 3);
538 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
540 // xfer->who has 'sip:' prefix, skip these four characters
541 users_match
= sipe_strcase_equal(parts
[1], (xfer
->who
+ 4));
544 purple_debug_info("sipe","File transfer authentication: %s Expected: USR %s %u\n",
545 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
547 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
548 raise_ft_error_and_cancel(xfer
,
549 _("File transfer authentication failed."));
553 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
554 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
557 if (bytes_written
== -1) {
558 raise_ft_socket_write_error_and_cancel(xfer
);
563 if (read_line(xfer
,buf
,BUFFER_SIZE
) < 0) {
564 raise_ft_socket_read_error_and_cancel(xfer
);
568 ft
->bytes_remaining_chunk
= 0;
570 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
571 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
575 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
577 sipe_file_transfer
*ft
= xfer
->data
;
578 gsize BUFFER_SIZE
= 50;
579 char buffer
[BUFFER_SIZE
];
584 if (read_line(xfer
, buffer
, BUFFER_SIZE
) < 0) {
585 raise_ft_socket_read_error_and_cancel(xfer
);
589 mac
= sipe_hmac_finalize(ft
->hmac_context
);
590 g_sprintf(buffer
, "MAC %s \r\n", mac
);
593 mac_strlen
= strlen(buffer
);
594 // There must be this zero byte between mac and \r\n
595 buffer
[mac_strlen
- 3] = 0;
597 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
598 raise_ft_socket_write_error_and_cancel(xfer
);
602 sipe_ft_free_xfer_struct(xfer
);
605 //******************************************************************************
607 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
, const GSList
*body
)
610 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
611 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
612 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
614 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
615 session
= sipe_session_find_im(sip
, from
);
620 purple_debug_error("sipe", "sipe_ft_incoming_transfer: can't find session for remote party\n");
624 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
628 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
629 ft
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
631 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
633 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
634 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
637 purple_xfer_set_filename(xfer
, sipe_utils_nameval_find(body
, "Application-File"));
639 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
, "Application-FileSize"),NULL
,10);
640 purple_xfer_set_size(xfer
, file_size
);
642 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
643 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
644 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
645 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
646 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
647 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
648 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
650 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
652 purple_xfer_request(xfer
);
656 void sipe_ft_incoming_accept(PurpleAccount
*account
, const GSList
*body
)
658 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
659 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
660 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
663 const gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
664 const gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
665 const gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
666 const gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
667 const gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
669 sipe_file_transfer
*ft
= xfer
->data
;
672 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
675 guchar
*enc_key
= g_base64_decode(enc_key_b64
, &ret_len
);
676 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
677 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
679 raise_ft_error_and_cancel(xfer
,
680 _("Received encryption key has wrong size."));
688 guchar
*hash_key
= g_base64_decode(hash_key_b64
, &ret_len
);
689 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
690 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
692 raise_ft_error_and_cancel(xfer
,
693 _("Received hash key has wrong size."));
700 if (ip
&& port_str
) {
701 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
703 ft
->listener
= purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
704 SIPE_FT_TCP_PORT_MAX
,
706 sipe_ft_listen_socket_created
,
709 raise_ft_error_and_cancel(xfer
,
710 _("Could not create listen socket"));
717 void sipe_ft_incoming_cancel(PurpleAccount
*account
, GSList
*body
)
719 gchar
*inv_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
721 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
722 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
724 purple_xfer_cancel_remote(xfer
);
727 static void send_filetransfer_accept(PurpleXfer
* xfer
)
729 sipe_file_transfer
* ft
= xfer
->data
;
730 struct sip_dialog
*dialog
= ft
->dialog
;
732 gchar
*b64_encryption_key
= g_base64_encode(ft
->encryption_key
, 24);
733 gchar
*b64_hash_key
= g_base64_encode(ft
->hash_key
, 24);
735 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
736 "Request-Data: IP-Address:\r\n"
737 "Invitation-Cookie: %s\r\n"
738 "Encryption-Key: %s\r\n"
740 /*"IP-Address: %s\r\n"
743 "Auth-Cookie: 11111111\r\n"
744 "Sender-Connect: TRUE\r\n"*/,
745 ft
->invitation_cookie
,
748 /*,purple_network_get_my_ip(-1)*/
751 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
752 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
756 g_free(b64_encryption_key
);
757 g_free(b64_hash_key
);
760 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
761 sipe_file_transfer
* ft
= xfer
->data
;
762 struct sip_dialog
* dialog
= ft
->dialog
;
764 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
765 "Invitation-Cookie: %s\r\n"
766 "Cancel-Code: REJECT\r\n",
767 ft
->invitation_cookie
);
769 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
770 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
777 do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
779 ssize_t bytes_read
= read(xfer
->fd
, buf
, len
);
780 if (bytes_read
== 0) {
781 // Sender canceled transfer before it was finished
783 } else if (bytes_read
== -1) {
793 read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
795 const gulong READ_TIMEOUT
= 10000000;
796 gulong time_spent
= 0;
799 ssize_t bytes_read
= do_read(xfer
, buf
, len
);
800 if (bytes_read
== 0) {
802 time_spent
+= 100000;
803 } else if (bytes_read
< 0 || time_spent
> READ_TIMEOUT
) {
814 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
)
818 memset(buffer
,0,size
);
820 if (!read_fully(xfer
, (guchar
*) buffer
+ pos
, 1))
822 } while (buffer
[pos
] != '\n' && ++pos
!= (size
- 1));
824 if (pos
== (size
- 1) && buffer
[pos
- 1] != '\n') {
832 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
)
835 * Decryption of file from SIPE file transfer
838 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
839 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
842 PurpleCipherContext
*sha1_context
;
846 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
847 purple_cipher_context_append(sha1_context
, enc_key
, SIPE_FT_KEY_LENGTH
);
848 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
849 purple_cipher_context_destroy(sha1_context
);
851 /* 2.) RC4 decryption */
852 *rc4_context
= purple_cipher_context_new_by_name("rc4", NULL
);
853 purple_cipher_context_set_option(*rc4_context
, "key_len", (gpointer
)0x10); // only 16 chars key used
854 purple_cipher_context_set_key(*rc4_context
, k2
);
858 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
)
864 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
865 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
868 PurpleCipherContext
*sha1_context
;
872 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
873 purple_cipher_context_append(sha1_context
, hash_key
, SIPE_FT_KEY_LENGTH
);
874 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
875 purple_cipher_context_destroy(sha1_context
);
877 /* 2.) HMAC (initialization only) */
878 *hmac_context
= purple_cipher_context_new_by_name("hmac", NULL
);
879 purple_cipher_context_set_option(*hmac_context
, "hash", "sha1");
880 purple_cipher_context_set_key_with_len(*hmac_context
, k2
, 16);
883 static gchar
* sipe_hmac_finalize(PurpleCipherContext
*hmac_context
)
885 guchar hmac_digest
[20];
887 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
888 purple_cipher_context_digest(hmac_context
, sizeof(hmac_digest
), hmac_digest
, NULL
);
890 return g_base64_encode(hmac_digest
, sizeof (hmac_digest
));
893 static void generate_key(guchar
*buffer
, gsize size
)
896 for (i
= 0; i
!= size
; ++i
)
900 static void set_socket_nonblock(int fd
, gboolean state
)
902 int flags
= fcntl(fd
, F_GETFL
, 0);
907 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
909 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
912 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
914 PurpleXfer
*xfer
= sipe_ft_new_xfer(gc
, who
);
918 purple_xfer_request_accepted(xfer
, file
);
920 purple_xfer_request(xfer
);
924 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
926 PurpleXfer
*xfer
= NULL
;
928 if (PURPLE_CONNECTION_IS_VALID(gc
)) {
929 xfer
= purple_xfer_new(purple_connection_get_account(gc
),
930 PURPLE_XFER_SEND
, who
);
933 struct sipe_account_data
*sip
= gc
->proto_data
;
935 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
936 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
941 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
942 purple_xfer_set_start_fnc(xfer
, sipe_ft_outgoing_start
);
943 purple_xfer_set_end_fnc(xfer
, sipe_ft_outgoing_stop
);
944 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
945 purple_xfer_set_write_fnc(xfer
, sipe_ft_write
);
946 purple_xfer_set_cancel_send_fnc(xfer
, sipe_ft_free_xfer_struct
);
947 purple_xfer_set_cancel_recv_fnc(xfer
, sipe_ft_free_xfer_struct
);
955 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
956 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
958 struct sockaddr_in saddr
;
959 socklen_t slen
= sizeof (saddr
);
961 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
963 PurpleXfer
*xfer
= p_xfer
;
964 sipe_file_transfer
*ft
= xfer
->data
;
966 purple_input_remove(xfer
->watcher
);
972 raise_ft_socket_read_error_and_cancel(xfer
);
974 purple_xfer_start(xfer
, fd
, NULL
, 0);
979 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
982 PurpleXfer
*xfer
= data
;
983 sipe_file_transfer
*ft
= xfer
->data
;
985 struct sockaddr_in addr
;
987 socklen_t socklen
= sizeof (addr
);
990 ft
->listenfd
= listenfd
;
992 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
994 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
995 sipe_ft_client_connected
, xfer
);
997 ft
->auth_cookie
= rand() % 1000000000;
999 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
1000 "Invitation-Cookie: %s\r\n"
1001 "IP-Address: %s\r\n"
1004 "AuthCookie: %u\r\n"
1005 "Request-Data: IP-Address:\r\n",
1006 ft
->invitation_cookie
,
1007 sipe_ft_get_suitable_local_ip(listenfd
),
1008 ntohs(addr
.sin_port
),
1012 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
1013 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
1014 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
1018 send_sip_request(ft
->sip
->gc
, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
1019 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
1020 body
, ft
->dialog
, NULL
);
1026 * Calling sizeof(struct ifreq) isn't always correct on
1027 * Mac OS X (and maybe others).
1029 #ifdef _SIZEOF_ADDR_IFREQ
1030 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
1032 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
1036 * Returns local IP address suitable for connection.
1038 * purple_network_get_my_ip() will not do this, because it might return an
1039 * address within 169.254.x.x range that was assigned to interface disconnected
1040 * from the network (when multiple network adapters are available). This is a
1041 * copy-paste from libpurple's network.c, only change is that link local addresses
1044 * Maybe this should be fixed in libpurple or some better solution found.
1047 const char * sipe_ft_get_suitable_local_ip(int fd
)
1049 int source
= (fd
>= 0) ? fd
: socket(PF_INET
,SOCK_STREAM
, 0);
1056 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
1057 guint32 llocal
= htonl((169 << 24) + (254 << 16));
1059 ifc
.ifc_len
= sizeof(buffer
);
1060 ifc
.ifc_req
= (struct ifreq
*)buffer
;
1061 ioctl(source
, SIOCGIFCONF
, &ifc
);
1067 while (tmp
< buffer
+ ifc
.ifc_len
)
1069 struct ifreq
*ifr
= (struct ifreq
*)tmp
;
1070 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
1072 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
1074 struct sockaddr_in
*sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1075 if (sinptr
->sin_addr
.s_addr
!= lhost
1076 && (sinptr
->sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
1078 long unsigned int add
= ntohl(sinptr
->sin_addr
.s_addr
);
1079 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",
1080 ((add
>> 24) & 255),
1081 ((add
>> 16) & 255),
1094 GSList
* sipe_ft_parse_msg_body(const gchar
*body
)
1096 GSList
*list
= NULL
;
1097 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
1098 if (sipe_utils_parse_lines(&list
, lines
) == FALSE
) {
1099 sipe_utils_nameval_free(list
);