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"
46 #include "sipe-dialog.h"
49 #include "sipe-session.h"
50 #include "sipe-utils.h"
55 #include "libc_interface.h"
58 #include <sys/types.h>
59 #include <sys/socket.h>
60 #include <sys/ioctl.h>
61 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #ifdef HAVE_SYS_SOCKIO_H
65 #include <sys/sockio.h> /* SIOCGIFCONF for Solaris */
69 #define SIPE_FT_KEY_LENGTH 24
70 #define SIPE_FT_CHUNK_HEADER_LENGTH 3
73 * DO NOT CHANGE THE FOLLOWING CONSTANTS!!!
75 * It seems that Microsoft Office Communicator client will accept
76 * file transfer invitations *only* within this port range!
78 * If a firewall is active on your system you need to open these ports if
79 * you want to *send* files to other users. Receiving files uses an ougoing
80 * connection and should therefore automatically penetrate your firewall.
82 #define SIPE_FT_TCP_PORT_MIN 6891
83 #define SIPE_FT_TCP_PORT_MAX 6901
85 struct _sipe_file_transfer
{
86 guchar encryption_key
[SIPE_FT_KEY_LENGTH
];
87 guchar hash_key
[SIPE_FT_KEY_LENGTH
];
88 gchar
*invitation_cookie
;
90 struct sipe_account_data
*sip
;
91 struct sip_dialog
*dialog
;
92 PurpleCipherContext
*cipher_context
;
93 PurpleCipherContext
*hmac_context
;
95 PurpleNetworkListenData
*listener
;
98 gsize bytes_remaining_chunk
;
99 guchar
* encrypted_outbuf
;
103 typedef struct _sipe_file_transfer sipe_file_transfer
;
105 static void send_filetransfer_accept(PurpleXfer
* xfer
);
106 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
107 static ssize_t
do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
108 static gboolean
read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
);
109 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
);
110 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
);
111 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
);
112 static gchar
*sipe_hmac_finalize(PurpleCipherContext
*hmac_context
);
113 static void generate_key(guchar
*buffer
, gsize size
);
114 static void set_socket_nonblock(int fd
, gboolean state
);
115 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
116 static const char * sipe_ft_get_suitable_local_ip(int fd
);
118 //******************************************************************************
119 // I/O operations for PurpleXfer structure
120 //******************************************************************************
123 sipe_ft_incoming_init(PurpleXfer
*xfer
)
125 send_filetransfer_accept(xfer
);
129 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
131 sipe_file_transfer
*ft
= xfer
->data
;
133 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
135 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
138 purple_input_remove(xfer
->watcher
);
141 if (ft
->listenfd
>= 0) {
142 purple_debug_info("sipe", "sipe_ft_free_xfer_struct: closing listening socket %d\n", ft
->listenfd
);
146 purple_network_listen_cancel(ft
->listener
);
147 if (ft
->cipher_context
)
148 purple_cipher_context_destroy(ft
->cipher_context
);
150 if (ft
->hmac_context
)
151 purple_cipher_context_destroy(ft
->hmac_context
);
153 g_free(ft
->encrypted_outbuf
);
154 g_free(ft
->invitation_cookie
);
161 sipe_ft_request_denied(PurpleXfer
*xfer
)
163 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
164 send_filetransfer_cancel(xfer
);
165 sipe_ft_free_xfer_struct(xfer
);
169 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
171 purple_xfer_error(purple_xfer_get_type(xfer
),
172 xfer
->account
, xfer
->who
,
177 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
179 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
180 raise_ft_error(xfer
, tmp
);
185 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
187 raise_ft_error(xfer
, errmsg
);
188 purple_xfer_cancel_local(xfer
);
192 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
194 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
198 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
200 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
204 sipe_ft_incoming_start(PurpleXfer
*xfer
)
206 sipe_file_transfer
*ft
;
207 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
208 static const gchar TFR
[] = "TFR\r\n";
209 const gsize BUFFER_SIZE
= 50;
210 gchar buf
[BUFFER_SIZE
];
211 struct sipe_account_data
*sip
;
213 const gsize FILE_SIZE_OFFSET
= 4;
218 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
219 raise_ft_socket_write_error_and_cancel(xfer
);
222 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
223 raise_ft_socket_read_error_and_cancel(xfer
);
227 sip
= xfer
->account
->gc
->proto_data
;
229 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
230 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
231 raise_ft_socket_write_error_and_cancel(xfer
);
237 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
238 raise_ft_socket_read_error_and_cancel(xfer
);
242 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
243 if (file_size
!= xfer
->size
) {
244 raise_ft_error_and_cancel(xfer
,
245 _("File size is different from the advertised value."));
249 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
250 raise_ft_socket_write_error_and_cancel(xfer
);
254 ft
->bytes_remaining_chunk
= 0;
256 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
257 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
261 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
263 static const gchar BYE
[] = "BYE 16777989\r\n";
264 gsize BUFFER_SIZE
= 50;
265 char buffer
[BUFFER_SIZE
];
266 const gssize MAC_OFFSET
= 4;
267 const gssize CRLF_LEN
= 2;
269 sipe_file_transfer
*ft
;
273 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
274 raise_ft_socket_write_error_and_cancel(xfer
);
278 macLen
= read_line(xfer
, buffer
, BUFFER_SIZE
);
281 raise_ft_socket_read_error_and_cancel(xfer
);
283 } else if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
284 raise_ft_error_and_cancel(xfer
, _("Received MAC is corrupted"));
290 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
291 mac1
= sipe_hmac_finalize(ft
->hmac_context
);
292 if (!sipe_strequal(mac
, mac1
)) {
293 unlink(xfer
->local_filename
);
294 raise_ft_error_and_cancel(xfer
,
295 _("Received file is corrupted"));
300 sipe_ft_free_xfer_struct(xfer
);
304 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
309 sipe_file_transfer
*ft
= xfer
->data
;
311 if (ft
->bytes_remaining_chunk
== 0) {
312 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
314 /* read chunk header */
315 if (!read_fully(xfer
, hdr_buf
, sizeof(hdr_buf
))) {
316 raise_ft_strerror(xfer
, _("Socket read failed"));
320 /* chunk header format:
322 * 0: 00 unknown (always zero?)
323 * 1: LL chunk size in bytes (low byte)
324 * 2: HH chunk size in bytes (high byte)
326 * Convert size from little endian to host order
328 ft
->bytes_remaining_chunk
= hdr_buf
[1] + (hdr_buf
[2] << 8);
331 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
332 xfer
->current_buffer_size
);
333 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
335 *buffer
= g_malloc(bytes_to_read
);
337 raise_ft_error(xfer
, _("Out of memory"));
338 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer\n",
343 bytes_read
= do_read(xfer
, *buffer
, bytes_to_read
);
344 if (bytes_read
< 0) {
345 raise_ft_strerror(xfer
, _("Socket read failed"));
349 if (bytes_read
> 0) {
350 guchar
*decrypted
= g_malloc(bytes_read
);
353 raise_ft_error(xfer
, _("Out of memory"));
354 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer\n",
360 purple_cipher_context_encrypt(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
, NULL
);
364 purple_cipher_context_append(ft
->hmac_context
, decrypted
, bytes_read
);
366 ft
->bytes_remaining_chunk
-= bytes_read
;
373 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
375 ssize_t bytes_written
;
376 sipe_file_transfer
*ft
= xfer
->data
;
378 /* When sending data via server with ForeFront installed, block bigger than
379 * this default causes ending of transmission. Hard limit block to this value
380 * when libpurple sends us more data. */
381 const gsize DEFAULT_BLOCK_SIZE
= 2045;
382 if (size
> DEFAULT_BLOCK_SIZE
)
383 size
= DEFAULT_BLOCK_SIZE
;
385 if (ft
->bytes_remaining_chunk
== 0) {
387 guchar local_buf
[16];
388 guchar hdr_buf
[SIPE_FT_CHUNK_HEADER_LENGTH
];
390 memset(local_buf
, 0, sizeof local_buf
);
392 // Check if receiver did not cancel the transfer before it is finished
393 bytes_read
= read(xfer
->fd
,local_buf
,sizeof (local_buf
));
394 if (bytes_read
== -1 && errno
!= EAGAIN
) {
395 raise_ft_strerror(xfer
, _("Socket read failed"));
397 } else if (bytes_read
> 0
398 && (g_str_has_prefix((gchar
*)local_buf
,"CCL\r\n")
399 || g_str_has_prefix((gchar
*)local_buf
,"BYE 2164261682\r\n"))) {
403 if (ft
->outbuf_size
< size
) {
404 g_free(ft
->encrypted_outbuf
);
405 ft
->outbuf_size
= size
;
406 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
407 if (!ft
->encrypted_outbuf
) {
408 raise_ft_error(xfer
, _("Out of memory"));
409 purple_debug_error("sipe", "sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer\n",
415 ft
->bytes_remaining_chunk
= size
;
416 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
417 purple_cipher_context_encrypt(ft
->cipher_context
, buffer
, size
,
418 ft
->encrypted_outbuf
, NULL
);
419 purple_cipher_context_append(ft
->hmac_context
, buffer
, size
);
421 /* chunk header format:
423 * 0: 00 unknown (always zero?)
424 * 1: LL chunk size in bytes (low byte)
425 * 2: HH chunk size in bytes (high byte)
427 * Convert size from host order to little endian
430 hdr_buf
[1] = (ft
->bytes_remaining_chunk
& 0x00FF);
431 hdr_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
433 /* write chunk header */
434 if (write(xfer
->fd
, hdr_buf
, sizeof(hdr_buf
)) == -1) {
435 raise_ft_strerror(xfer
, _("Socket write failed"));
440 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
441 if (bytes_written
== -1) {
445 raise_ft_strerror(xfer
, _("Socket write failed"));
449 if (bytes_written
> 0) {
450 ft
->bytes_remaining_chunk
-= bytes_written
;
451 ft
->outbuf_ptr
+= bytes_written
;
454 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
455 purple_xfer_set_completed(xfer
, TRUE
);
457 return bytes_written
;
461 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
463 struct sip_dialog
*dialog
;
464 sipe_file_transfer
*ft
= xfer
->data
;
466 gchar
*body
= g_strdup_printf("Application-Name: File Transfer\r\n"
467 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
468 "Invitation-Command: INVITE\r\n"
469 "Invitation-Cookie: %s\r\n"
470 "Application-File: %s\r\n"
471 "Application-FileSize: %lu\r\n"
472 //"Connectivity: N\r\n" TODO
473 "Encryption: R\r\n", // TODO: non encrypted file transfer support
474 ft
->invitation_cookie
,
475 purple_xfer_get_filename(xfer
),
476 (long unsigned) purple_xfer_get_size(xfer
));
478 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
479 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
481 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
484 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
486 dialog
= sipe_dialog_find(session
, xfer
->who
);
487 if (dialog
&& !dialog
->outgoing_invite
) {
489 sipe_im_process_queue(sip
, session
);
490 } else if (!dialog
|| !dialog
->outgoing_invite
) {
491 // Need to send the INVITE to get the outgoing dialog setup
492 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
499 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
501 sipe_file_transfer
*ft
;
502 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
503 const gsize BUFFER_SIZE
= 50;
504 gchar buf
[BUFFER_SIZE
];
506 unsigned auth_cookie_received
;
507 gboolean users_match
;
509 ssize_t bytes_written
;
511 set_socket_nonblock(xfer
->fd
, TRUE
);
515 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
516 raise_ft_socket_read_error_and_cancel(xfer
);
520 if (!sipe_strequal(buf
,VER
)) {
521 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
522 purple_debug_info("sipe","File transfer VER string incorrect, received: %s expected: %s",
527 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
528 raise_ft_socket_write_error_and_cancel(xfer
);
532 if (read_line(xfer
, buf
, BUFFER_SIZE
) < 0) {
533 raise_ft_socket_read_error_and_cancel(xfer
);
537 parts
= g_strsplit(buf
, " ", 3);
539 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
541 // xfer->who has 'sip:' prefix, skip these four characters
542 users_match
= sipe_strcase_equal(parts
[1], (xfer
->who
+ 4));
545 purple_debug_info("sipe","File transfer authentication: %s Expected: USR %s %u\n",
546 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
548 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
549 raise_ft_error_and_cancel(xfer
,
550 _("File transfer authentication failed."));
554 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
555 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
558 if (bytes_written
== -1) {
559 raise_ft_socket_write_error_and_cancel(xfer
);
564 if (read_line(xfer
,buf
,BUFFER_SIZE
) < 0) {
565 raise_ft_socket_read_error_and_cancel(xfer
);
569 ft
->bytes_remaining_chunk
= 0;
571 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
572 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
576 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
578 sipe_file_transfer
*ft
= xfer
->data
;
579 gsize BUFFER_SIZE
= 50;
580 char buffer
[BUFFER_SIZE
];
585 if (read_line(xfer
, buffer
, BUFFER_SIZE
) < 0) {
586 raise_ft_socket_read_error_and_cancel(xfer
);
590 mac
= sipe_hmac_finalize(ft
->hmac_context
);
591 g_sprintf(buffer
, "MAC %s \r\n", mac
);
594 mac_strlen
= strlen(buffer
);
595 // There must be this zero byte between mac and \r\n
596 buffer
[mac_strlen
- 3] = 0;
598 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
599 raise_ft_socket_write_error_and_cancel(xfer
);
603 sipe_ft_free_xfer_struct(xfer
);
606 //******************************************************************************
608 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
, const GSList
*body
)
611 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
612 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
613 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
615 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
616 session
= sipe_session_find_im(sip
, from
);
621 purple_debug_error("sipe", "sipe_ft_incoming_transfer: can't find session for remote party\n");
625 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
629 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
630 ft
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
632 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
634 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
635 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
638 purple_xfer_set_filename(xfer
, sipe_utils_nameval_find(body
, "Application-File"));
640 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
, "Application-FileSize"),NULL
,10);
641 purple_xfer_set_size(xfer
, file_size
);
643 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
644 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
645 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
646 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
647 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
648 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
649 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
651 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
653 purple_xfer_request(xfer
);
657 void sipe_ft_incoming_accept(PurpleAccount
*account
, const GSList
*body
)
659 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
660 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
661 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
664 const gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
665 const gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
666 const gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
667 const gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
668 const gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
670 sipe_file_transfer
*ft
= xfer
->data
;
673 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
676 guchar
*enc_key
= purple_base64_decode(enc_key_b64
, &ret_len
);
677 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
678 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
680 raise_ft_error_and_cancel(xfer
,
681 _("Received encryption key has wrong size."));
689 guchar
*hash_key
= purple_base64_decode(hash_key_b64
, &ret_len
);
690 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
691 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
693 raise_ft_error_and_cancel(xfer
,
694 _("Received hash key has wrong size."));
701 if (ip
&& port_str
) {
702 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
704 ft
->listener
= purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
705 SIPE_FT_TCP_PORT_MAX
,
707 sipe_ft_listen_socket_created
,
710 raise_ft_error_and_cancel(xfer
,
711 _("Could not create listen socket"));
718 void sipe_ft_incoming_cancel(PurpleAccount
*account
, GSList
*body
)
720 gchar
*inv_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
722 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
723 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
725 purple_xfer_cancel_remote(xfer
);
728 static void send_filetransfer_accept(PurpleXfer
* xfer
)
730 sipe_file_transfer
* ft
= xfer
->data
;
731 struct sip_dialog
*dialog
= ft
->dialog
;
733 gchar
*b64_encryption_key
= purple_base64_encode(ft
->encryption_key
,24);
734 gchar
*b64_hash_key
= purple_base64_encode(ft
->hash_key
,24);
736 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
737 "Request-Data: IP-Address:\r\n"
738 "Invitation-Cookie: %s\r\n"
739 "Encryption-Key: %s\r\n"
741 /*"IP-Address: %s\r\n"
744 "Auth-Cookie: 11111111\r\n"
745 "Sender-Connect: TRUE\r\n"*/,
746 ft
->invitation_cookie
,
749 /*,purple_network_get_my_ip(-1)*/
752 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
753 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
757 g_free(b64_encryption_key
);
758 g_free(b64_hash_key
);
761 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
762 sipe_file_transfer
* ft
= xfer
->data
;
763 struct sip_dialog
* dialog
= ft
->dialog
;
765 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
766 "Invitation-Cookie: %s\r\n"
767 "Cancel-Code: REJECT\r\n",
768 ft
->invitation_cookie
);
770 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
771 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
778 do_read(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
780 ssize_t bytes_read
= read(xfer
->fd
, buf
, len
);
781 if (bytes_read
== 0) {
782 // Sender canceled transfer before it was finished
784 } else if (bytes_read
== -1) {
794 read_fully(PurpleXfer
*xfer
, guchar
*buf
, size_t len
)
796 const gulong READ_TIMEOUT
= 10000000;
797 gulong time_spent
= 0;
800 ssize_t bytes_read
= do_read(xfer
, buf
, len
);
801 if (bytes_read
== 0) {
803 time_spent
+= 100000;
804 } else if (bytes_read
< 0 || time_spent
> READ_TIMEOUT
) {
815 static gssize
read_line(PurpleXfer
*xfer
, gchar
*buffer
, gssize size
)
819 memset(buffer
,0,size
);
821 if (!read_fully(xfer
, (guchar
*) buffer
+ pos
, 1))
823 } while (buffer
[pos
] != '\n' && ++pos
!= (size
- 1));
825 if (pos
== (size
- 1) && buffer
[pos
- 1] != '\n') {
833 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
)
836 * Decryption of file from SIPE file transfer
839 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
840 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
843 PurpleCipherContext
*sha1_context
;
847 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
848 purple_cipher_context_append(sha1_context
, enc_key
, SIPE_FT_KEY_LENGTH
);
849 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
850 purple_cipher_context_destroy(sha1_context
);
852 /* 2.) RC4 decryption */
853 *rc4_context
= purple_cipher_context_new_by_name("rc4", NULL
);
854 purple_cipher_context_set_option(*rc4_context
, "key_len", (gpointer
)0x10); // only 16 chars key used
855 purple_cipher_context_set_key(*rc4_context
, k2
);
859 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
)
865 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
866 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
869 PurpleCipherContext
*sha1_context
;
873 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
874 purple_cipher_context_append(sha1_context
, hash_key
, SIPE_FT_KEY_LENGTH
);
875 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
876 purple_cipher_context_destroy(sha1_context
);
878 /* 2.) HMAC (initialization only) */
879 *hmac_context
= purple_cipher_context_new_by_name("hmac", NULL
);
880 purple_cipher_context_set_option(*hmac_context
, "hash", "sha1");
881 purple_cipher_context_set_key_with_len(*hmac_context
, k2
, 16);
884 static gchar
* sipe_hmac_finalize(PurpleCipherContext
*hmac_context
)
886 guchar hmac_digest
[20];
888 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
889 purple_cipher_context_digest(hmac_context
, sizeof(hmac_digest
), hmac_digest
, NULL
);
891 return purple_base64_encode(hmac_digest
, sizeof (hmac_digest
));
894 static void generate_key(guchar
*buffer
, gsize size
)
897 for (i
= 0; i
!= size
; ++i
)
901 static void set_socket_nonblock(int fd
, gboolean state
)
903 int flags
= fcntl(fd
, F_GETFL
, 0);
908 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
910 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
913 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
915 PurpleXfer
*xfer
= sipe_ft_new_xfer(gc
, who
);
919 purple_xfer_request_accepted(xfer
, file
);
921 purple_xfer_request(xfer
);
925 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
927 PurpleXfer
*xfer
= NULL
;
929 if (PURPLE_CONNECTION_IS_VALID(gc
)) {
930 xfer
= purple_xfer_new(purple_connection_get_account(gc
),
931 PURPLE_XFER_SEND
, who
);
934 struct sipe_account_data
*sip
= gc
->proto_data
;
936 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
937 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
942 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
943 purple_xfer_set_start_fnc(xfer
, sipe_ft_outgoing_start
);
944 purple_xfer_set_end_fnc(xfer
, sipe_ft_outgoing_stop
);
945 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
946 purple_xfer_set_write_fnc(xfer
, sipe_ft_write
);
947 purple_xfer_set_cancel_send_fnc(xfer
, sipe_ft_free_xfer_struct
);
948 purple_xfer_set_cancel_recv_fnc(xfer
, sipe_ft_free_xfer_struct
);
956 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
957 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
959 struct sockaddr_in saddr
;
960 socklen_t slen
= sizeof (saddr
);
962 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
964 PurpleXfer
*xfer
= p_xfer
;
965 sipe_file_transfer
*ft
= xfer
->data
;
967 purple_input_remove(xfer
->watcher
);
973 raise_ft_socket_read_error_and_cancel(xfer
);
975 purple_xfer_start(xfer
, fd
, NULL
, 0);
980 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
983 PurpleXfer
*xfer
= data
;
984 sipe_file_transfer
*ft
= xfer
->data
;
986 struct sockaddr_in addr
;
988 socklen_t socklen
= sizeof (addr
);
991 ft
->listenfd
= listenfd
;
993 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
995 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
996 sipe_ft_client_connected
, xfer
);
998 ft
->auth_cookie
= rand() % 1000000000;
1000 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
1001 "Invitation-Cookie: %s\r\n"
1002 "IP-Address: %s\r\n"
1005 "AuthCookie: %u\r\n"
1006 "Request-Data: IP-Address:\r\n",
1007 ft
->invitation_cookie
,
1008 sipe_ft_get_suitable_local_ip(listenfd
),
1009 ntohs(addr
.sin_port
),
1013 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
1014 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
1015 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
1019 send_sip_request(ft
->sip
->gc
, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
1020 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
1021 body
, ft
->dialog
, NULL
);
1027 * Calling sizeof(struct ifreq) isn't always correct on
1028 * Mac OS X (and maybe others).
1030 #ifdef _SIZEOF_ADDR_IFREQ
1031 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
1033 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
1037 * Returns local IP address suitable for connection.
1039 * purple_network_get_my_ip() will not do this, because it might return an
1040 * address within 169.254.x.x range that was assigned to interface disconnected
1041 * from the network (when multiple network adapters are available). This is a
1042 * copy-paste from libpurple's network.c, only change is that link local addresses
1045 * Maybe this should be fixed in libpurple or some better solution found.
1048 const char * sipe_ft_get_suitable_local_ip(int fd
)
1050 int source
= (fd
>= 0) ? fd
: socket(PF_INET
,SOCK_STREAM
, 0);
1057 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
1058 guint32 llocal
= htonl((169 << 24) + (254 << 16));
1060 ifc
.ifc_len
= sizeof(buffer
);
1061 ifc
.ifc_req
= (struct ifreq
*)buffer
;
1062 ioctl(source
, SIOCGIFCONF
, &ifc
);
1068 while (tmp
< buffer
+ ifc
.ifc_len
)
1070 struct ifreq
*ifr
= (struct ifreq
*)tmp
;
1071 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
1073 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
1075 struct sockaddr_in
*sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
1076 if (sinptr
->sin_addr
.s_addr
!= lhost
1077 && (sinptr
->sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
1079 long unsigned int add
= ntohl(sinptr
->sin_addr
.s_addr
);
1080 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",
1081 ((add
>> 24) & 255),
1082 ((add
>> 16) & 255),
1095 GSList
* sipe_ft_parse_msg_body(const gchar
*body
)
1097 GSList
*list
= NULL
;
1098 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
1099 if (sipe_utils_parse_lines(&list
, lines
) == FALSE
) {
1100 sipe_utils_nameval_free(list
);