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
27 #include <glib/gprintf.h>
33 #include "sipe-dialog.h"
35 #include "sipe-session.h"
36 #include "sipe-utils.h"
39 #include <sys/socket.h>
40 #include <netinet/in.h>
43 #define SIPE_FT_KEY_LENGTH 24
45 #define SIPE_FT_TCP_PORT_MIN 6891
46 #define SIPE_FT_TCP_PORT_MAX 6901
48 struct _sipe_file_transfer
{
49 guchar encryption_key
[SIPE_FT_KEY_LENGTH
];
50 guchar hash_key
[SIPE_FT_KEY_LENGTH
];
51 gchar
*invitation_cookie
;
53 struct sipe_account_data
*sip
;
54 struct sip_dialog
*dialog
;
55 PurpleCipherContext
*cipher_context
;
56 PurpleCipherContext
*hmac_context
;
58 PurpleNetworkListenData
*listener
;
61 gsize bytes_remaining_chunk
;
62 guchar
* encrypted_outbuf
;
66 typedef struct _sipe_file_transfer sipe_file_transfer
;
68 static void send_filetransfer_accept(PurpleXfer
* xfer
);
69 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
70 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
);
71 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
);
72 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
);
73 static gchar
*sipe_hmac_finalize(PurpleCipherContext
*hmac_context
);
74 static void generate_key(guchar
*buffer
, gsize size
);
75 static void set_socket_nonblock(int fd
, gboolean state
);
76 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
77 static const char * sipe_ft_get_suitable_local_ip(int fd
);
79 //******************************************************************************
80 // I/O operations for PurpleXfer structure
81 //******************************************************************************
84 sipe_ft_incoming_init(PurpleXfer
*xfer
)
86 send_filetransfer_accept(xfer
);
90 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
92 sipe_file_transfer
*ft
= xfer
->data
;
94 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
96 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
99 purple_input_remove(xfer
->watcher
);
102 if (ft
->listenfd
>= 0) {
103 purple_debug_info("sipe", "sipe_ft_free_xfer_struct: closing listening socket %d\n", ft
->listenfd
);
107 purple_network_listen_cancel(ft
->listener
);
108 if (ft
->cipher_context
)
109 purple_cipher_context_destroy(ft
->cipher_context
);
111 if (ft
->hmac_context
)
112 purple_cipher_context_destroy(ft
->hmac_context
);
114 g_free(ft
->encrypted_outbuf
);
115 g_free(ft
->invitation_cookie
);
122 sipe_ft_request_denied(PurpleXfer
*xfer
)
124 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
125 send_filetransfer_cancel(xfer
);
126 sipe_ft_free_xfer_struct(xfer
);
130 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
132 purple_xfer_error(purple_xfer_get_type(xfer
),
133 xfer
->account
, xfer
->who
,
138 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
140 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
141 raise_ft_error(xfer
, tmp
);
146 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
148 raise_ft_error(xfer
, errmsg
);
149 purple_xfer_cancel_local(xfer
);
153 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
155 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
159 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
161 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
165 sipe_ft_incoming_start(PurpleXfer
*xfer
)
167 sipe_file_transfer
*ft
;
168 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
169 static const gchar TFR
[] = "TFR\r\n";
170 const gsize BUFFER_SIZE
= 50;
171 gchar buf
[BUFFER_SIZE
];
172 struct sipe_account_data
*sip
;
174 const gsize FILE_SIZE_OFFSET
= 4;
177 set_socket_nonblock(xfer
->fd
,FALSE
);
181 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
182 raise_ft_socket_write_error_and_cancel(xfer
);
185 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
186 raise_ft_socket_read_error_and_cancel(xfer
);
190 sip
= xfer
->account
->gc
->proto_data
;
192 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
193 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
194 raise_ft_socket_write_error_and_cancel(xfer
);
200 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
202 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
203 if (file_size
!= xfer
->size
) {
204 raise_ft_error_and_cancel(xfer
,
205 _("File size is different from the advertised value."));
209 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
210 raise_ft_socket_write_error_and_cancel(xfer
);
214 ft
->bytes_remaining_chunk
= 0;
216 set_socket_nonblock(xfer
->fd
,TRUE
);
218 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
219 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
223 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
225 static const gchar BYE
[] = "BYE 16777989\r\n";
226 gsize BUFFER_SIZE
= 50;
227 char buffer
[BUFFER_SIZE
];
228 const gssize MAC_OFFSET
= 4;
229 const gssize CRLF_LEN
= 2;
231 sipe_file_transfer
*ft
;
235 set_socket_nonblock(xfer
->fd
,FALSE
);
237 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
238 raise_ft_socket_write_error_and_cancel(xfer
);
242 macLen
= read_line(xfer
->fd
,buffer
,BUFFER_SIZE
);
244 if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
245 raise_ft_error_and_cancel(xfer
,
246 _("Received MAC is corrupted"));
252 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
253 mac1
= sipe_hmac_finalize(ft
->hmac_context
);
254 if (!sipe_strequal(mac
, mac1
)) {
255 unlink(xfer
->local_filename
);
256 raise_ft_error_and_cancel(xfer
,
257 _("Received file is corrupted"));
262 sipe_ft_free_xfer_struct(xfer
);
266 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
271 sipe_file_transfer
*ft
= xfer
->data
;
273 if (ft
->bytes_remaining_chunk
== 0) {
276 set_socket_nonblock(xfer
->fd
, FALSE
);
278 if (read(xfer
->fd
,chunk_buf
,3) == -1) {
279 raise_ft_strerror(xfer
, _("Socket read failed"));
283 ft
->bytes_remaining_chunk
= chunk_buf
[1] + (chunk_buf
[2] << 8);
284 set_socket_nonblock(xfer
->fd
, TRUE
);
287 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
288 xfer
->current_buffer_size
);
289 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
291 *buffer
= g_malloc(bytes_to_read
);
293 raise_ft_error(xfer
, _("Out of memory"));
294 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer\n",
299 bytes_read
= read(xfer
->fd
, *buffer
, bytes_to_read
);
300 if (bytes_read
== -1) {
304 raise_ft_strerror(xfer
, _("Socket read failed"));
308 if (bytes_read
> 0) {
309 guchar
*decrypted
= g_malloc(bytes_read
);
312 raise_ft_error(xfer
, _("Out of memory"));
313 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer\n",
319 purple_cipher_context_encrypt(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
, NULL
);
323 purple_cipher_context_append(ft
->hmac_context
, decrypted
, bytes_read
);
325 ft
->bytes_remaining_chunk
-= bytes_read
;
332 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
334 ssize_t bytes_written
;
335 sipe_file_transfer
*ft
= xfer
->data
;
337 /* When sending data via server with ForeFront installed, block bigger than
338 * this default causes ending of transmission. Hard limit block to this value
339 * when libpurple sends us more data. */
340 const gsize DEFAULT_BLOCK_SIZE
= 2045;
341 if (size
> DEFAULT_BLOCK_SIZE
)
342 size
= DEFAULT_BLOCK_SIZE
;
344 if (ft
->bytes_remaining_chunk
== 0) {
346 guchar local_buf
[16];
347 memset(local_buf
, 0, sizeof local_buf
);
349 // Check if receiver did not cancel the transfer before it is finished
350 bytes_read
= read(xfer
->fd
,local_buf
,sizeof (local_buf
));
351 if (bytes_read
== -1 && errno
!= EAGAIN
) {
352 raise_ft_strerror(xfer
, _("Socket read failed"));
354 } else if (bytes_read
!= 0) {
355 if ( g_str_has_prefix((gchar
*)local_buf
,"CCL\r\n")
356 || g_str_has_prefix((gchar
*)local_buf
,"BYE 2164261682\r\n")) {
361 if (ft
->outbuf_size
< size
) {
362 g_free(ft
->encrypted_outbuf
);
363 ft
->outbuf_size
= size
;
364 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
365 if (!ft
->encrypted_outbuf
) {
366 raise_ft_error(xfer
, _("Out of memory"));
367 purple_debug_error("sipe", "sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer\n",
373 ft
->bytes_remaining_chunk
= size
;
374 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
375 purple_cipher_context_encrypt(ft
->cipher_context
, buffer
, size
,
376 ft
->encrypted_outbuf
, NULL
);
377 purple_cipher_context_append(ft
->hmac_context
, buffer
, size
);
380 local_buf
[1] = ft
->bytes_remaining_chunk
& 0x00FF;
381 local_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
383 set_socket_nonblock(xfer
->fd
, FALSE
);
384 if (write(xfer
->fd
,local_buf
,3) == -1) {
385 raise_ft_strerror(xfer
, _("Socket write failed"));
388 set_socket_nonblock(xfer
->fd
, TRUE
);
391 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
392 if (bytes_written
== -1) {
396 raise_ft_strerror(xfer
, _("Socket write failed"));
400 if (bytes_written
> 0) {
401 ft
->bytes_remaining_chunk
-= bytes_written
;
402 ft
->outbuf_ptr
+= bytes_written
;
405 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
406 purple_xfer_set_completed(xfer
, TRUE
);
408 return bytes_written
;
412 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
414 struct sip_dialog
*dialog
;
415 sipe_file_transfer
*ft
= xfer
->data
;
417 gchar
*body
= g_strdup_printf("Application-Name: File Transfer\r\n"
418 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
419 "Invitation-Command: INVITE\r\n"
420 "Invitation-Cookie: %s\r\n"
421 "Application-File: %s\r\n"
422 "Application-FileSize: %lu\r\n"
423 //"Connectivity: N\r\n" TODO
424 "Encryption: R\r\n", // TODO: non encrypted file transfer support
425 ft
->invitation_cookie
,
426 purple_xfer_get_filename(xfer
),
427 (long unsigned) purple_xfer_get_size(xfer
));
429 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
430 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
432 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
435 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
437 dialog
= sipe_dialog_find(session
, xfer
->who
);
438 if (dialog
&& !dialog
->outgoing_invite
) {
440 sipe_im_process_queue(sip
, session
);
441 } else if (!dialog
|| !dialog
->outgoing_invite
) {
442 // Need to send the INVITE to get the outgoing dialog setup
443 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
450 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
452 sipe_file_transfer
*ft
;
453 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
454 const gsize BUFFER_SIZE
= 50;
455 gchar buf
[BUFFER_SIZE
];
457 unsigned auth_cookie_received
;
458 gboolean users_match
;
460 ssize_t bytes_written
;
462 set_socket_nonblock(xfer
->fd
,FALSE
);
466 memset(buf
,0,BUFFER_SIZE
);
467 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
468 raise_ft_socket_read_error_and_cancel(xfer
);
472 if (!sipe_strequal(buf
,VER
)) {
473 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
474 purple_debug_info("sipe","File transfer VER string incorrect, received: %s expected: %s",
479 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
480 raise_ft_socket_write_error_and_cancel(xfer
);
484 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
486 parts
= g_strsplit(buf
, " ", 3);
488 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
490 // xfer->who has 'sip:' prefix, skip these four characters
491 users_match
= !g_ascii_strcasecmp(parts
[1], (xfer
->who
+ 4));
494 purple_debug_info("sipe","File transfer authentication: %s Expected: USR %s %u\n",
495 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
497 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
498 raise_ft_error_and_cancel(xfer
,
499 _("File transfer authentication failed."));
503 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
504 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
507 if (bytes_written
== -1) {
508 raise_ft_socket_write_error_and_cancel(xfer
);
513 read_line(xfer
->fd
,buf
,BUFFER_SIZE
);
515 ft
->bytes_remaining_chunk
= 0;
517 set_socket_nonblock(xfer
->fd
,TRUE
);
519 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
520 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
524 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
526 sipe_file_transfer
*ft
= xfer
->data
;
527 gsize BUFFER_SIZE
= 50;
528 char buffer
[BUFFER_SIZE
];
532 set_socket_nonblock(xfer
->fd
,FALSE
);
535 read_line(xfer
->fd
, buffer
, BUFFER_SIZE
);
537 mac
= sipe_hmac_finalize(ft
->hmac_context
);
538 g_sprintf(buffer
, "MAC %s \r\n", mac
);
541 mac_strlen
= strlen(buffer
);
542 // There must be this zero byte between mac and \r\n
543 buffer
[mac_strlen
- 3] = 0;
545 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
546 raise_ft_socket_write_error_and_cancel(xfer
);
550 sipe_ft_free_xfer_struct(xfer
);
553 //******************************************************************************
555 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
, const GSList
*body
)
558 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
559 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
560 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
562 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
563 session
= sipe_session_find_im(sip
, from
);
567 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
571 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
572 ft
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
574 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
576 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
577 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
580 purple_xfer_set_filename(xfer
, sipe_utils_nameval_find(body
, "Application-File"));
582 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
, "Application-FileSize"),NULL
,10);
583 purple_xfer_set_size(xfer
, file_size
);
585 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
586 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
587 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
588 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
589 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
590 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
591 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
593 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
595 purple_xfer_request(xfer
);
599 void sipe_ft_incoming_accept(PurpleAccount
*account
, const GSList
*body
)
601 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
602 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
603 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
606 const gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
607 const gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
608 const gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
609 const gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
610 const gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
612 sipe_file_transfer
*ft
= xfer
->data
;
615 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
618 guchar
*enc_key
= purple_base64_decode(enc_key_b64
, &ret_len
);
619 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
620 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
622 raise_ft_error_and_cancel(xfer
,
623 _("Received encryption key has wrong size."));
631 guchar
*hash_key
= purple_base64_decode(hash_key_b64
, &ret_len
);
632 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
633 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
635 raise_ft_error_and_cancel(xfer
,
636 _("Received hash key has wrong size."));
643 if (ip
&& port_str
) {
644 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
646 ft
->listener
= purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
647 SIPE_FT_TCP_PORT_MAX
,
649 sipe_ft_listen_socket_created
,
652 raise_ft_error_and_cancel(xfer
,
653 _("Could not create listen socket"));
660 void sipe_ft_incoming_cancel(PurpleAccount
*account
, GSList
*body
)
662 gchar
*inv_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
664 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
665 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
667 purple_xfer_cancel_remote(xfer
);
670 static void send_filetransfer_accept(PurpleXfer
* xfer
)
672 sipe_file_transfer
* ft
= xfer
->data
;
673 struct sip_dialog
*dialog
= ft
->dialog
;
675 gchar
*b64_encryption_key
= purple_base64_encode(ft
->encryption_key
,24);
676 gchar
*b64_hash_key
= purple_base64_encode(ft
->hash_key
,24);
678 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
679 "Request-Data: IP-Address:\r\n"
680 "Invitation-Cookie: %s\r\n"
681 "Encryption-Key: %s\r\n"
683 /*"IP-Address: %s\r\n"
686 "Auth-Cookie: 11111111\r\n"
687 "Sender-Connect: TRUE\r\n"*/,
688 ft
->invitation_cookie
,
691 /*,purple_network_get_my_ip(-1)*/
694 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
695 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
699 g_free(b64_encryption_key
);
700 g_free(b64_hash_key
);
703 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
704 sipe_file_transfer
* ft
= xfer
->data
;
705 struct sip_dialog
* dialog
= ft
->dialog
;
707 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
708 "Invitation-Cookie: %s\r\n"
709 "Cancel-Code: REJECT\r\n",
710 ft
->invitation_cookie
);
712 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
713 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
719 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
)
723 memset(buffer
,0,size
);
725 if (read(fd
,buffer
+ pos
,1) == -1)
727 } while (buffer
[pos
] != '\n' && ++pos
< size
);
732 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
)
735 * Decryption of file from SIPE file transfer
738 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
739 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
742 PurpleCipherContext
*sha1_context
;
746 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
747 purple_cipher_context_append(sha1_context
, enc_key
, SIPE_FT_KEY_LENGTH
);
748 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
749 purple_cipher_context_destroy(sha1_context
);
751 /* 2.) RC4 decryption */
752 *rc4_context
= purple_cipher_context_new_by_name("rc4", NULL
);
753 purple_cipher_context_set_option(*rc4_context
, "key_len", (gpointer
)0x10); // only 16 chars key used
754 purple_cipher_context_set_key(*rc4_context
, k2
);
758 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
)
764 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
765 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
768 PurpleCipherContext
*sha1_context
;
772 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
773 purple_cipher_context_append(sha1_context
, hash_key
, SIPE_FT_KEY_LENGTH
);
774 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
775 purple_cipher_context_destroy(sha1_context
);
777 /* 2.) HMAC (initialization only) */
778 *hmac_context
= purple_cipher_context_new_by_name("hmac", NULL
);
779 purple_cipher_context_set_option(*hmac_context
, "hash", "sha1");
780 purple_cipher_context_set_key_with_len(*hmac_context
, k2
, 16);
783 static gchar
* sipe_hmac_finalize(PurpleCipherContext
*hmac_context
)
785 guchar hmac_digest
[20];
787 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
788 purple_cipher_context_digest(hmac_context
, sizeof(hmac_digest
), hmac_digest
, NULL
);
790 return purple_base64_encode(hmac_digest
, sizeof (hmac_digest
));
793 static void generate_key(guchar
*buffer
, gsize size
)
796 for (i
= 0; i
!= size
; ++i
)
800 static void set_socket_nonblock(int fd
, gboolean state
)
802 int flags
= fcntl(fd
, F_GETFL
, 0);
807 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
809 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
812 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
816 xfer
= sipe_ft_new_xfer(gc
, who
);
819 purple_xfer_request_accepted(xfer
, file
);
821 purple_xfer_request(xfer
);
824 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
826 PurpleAccount
*account
= purple_connection_get_account(gc
);
827 PurpleXfer
*xfer
= purple_xfer_new(account
, PURPLE_XFER_SEND
, who
);
830 struct sipe_account_data
*sip
= purple_connection_get_protocol_data(gc
);
832 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
833 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
838 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
839 purple_xfer_set_start_fnc(xfer
,sipe_ft_outgoing_start
);
840 purple_xfer_set_end_fnc(xfer
,sipe_ft_outgoing_stop
);
841 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
842 purple_xfer_set_write_fnc(xfer
,sipe_ft_write
);
843 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
844 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
851 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
852 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
854 struct sockaddr_in saddr
;
855 socklen_t slen
= sizeof (saddr
);
857 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
859 PurpleXfer
*xfer
= p_xfer
;
860 sipe_file_transfer
*ft
= xfer
->data
;
862 purple_input_remove(xfer
->watcher
);
867 purple_xfer_start(xfer
,fd
,NULL
,0);
871 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
874 PurpleXfer
*xfer
= data
;
875 sipe_file_transfer
*ft
= xfer
->data
;
877 struct sockaddr_in addr
;
879 socklen_t socklen
= sizeof (addr
);
882 ft
->listenfd
= listenfd
;
884 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
886 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
887 sipe_ft_client_connected
, xfer
);
889 ft
->auth_cookie
= rand() % 1000000000;
891 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
892 "Invitation-Cookie: %s\r\n"
897 "Request-Data: IP-Address:\r\n",
898 ft
->invitation_cookie
,
899 sipe_ft_get_suitable_local_ip(listenfd
),
900 ntohs(addr
.sin_port
),
904 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
905 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
906 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
910 send_sip_request(ft
->sip
->gc
, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
911 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
912 body
, ft
->dialog
, NULL
);
919 #include <sys/ioctl.h>
925 * Calling sizeof(struct ifreq) isn't always correct on
926 * Mac OS X (and maybe others).
928 #ifdef _SIZEOF_ADDR_IFREQ
929 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
931 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
935 * Returns local IP address suitable for connection.
937 * purple_network_get_my_ip() will not do this, because it might return an
938 * address within 169.254.x.x range that was assigned to interface disconnected
939 * from the network (when multiple network adapters are available). This is a
940 * copy-paste from libpurple's network.c, only change is that link local addresses
943 * Maybe this should be fixed in libpurple or some better solution found.
946 const char * sipe_ft_get_suitable_local_ip(int fd
)
948 int source
= (fd
>= 0) ? fd
: socket(PF_INET
,SOCK_STREAM
, 0);
955 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
956 guint32 llocal
= htonl((169 << 24) + (254 << 16));
958 ifc
.ifc_len
= sizeof(buffer
);
959 ifc
.ifc_req
= (struct ifreq
*)buffer
;
960 ioctl(source
, SIOCGIFCONF
, &ifc
);
966 while (tmp
< buffer
+ ifc
.ifc_len
)
968 struct ifreq
*ifr
= (struct ifreq
*)tmp
;
969 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
971 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
973 struct sockaddr_in
*sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
974 if (sinptr
->sin_addr
.s_addr
!= lhost
975 && (sinptr
->sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
977 long unsigned int add
= ntohl(sinptr
->sin_addr
.s_addr
);
978 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",
993 GSList
* sipe_ft_parse_msg_body(const gchar
*body
)
996 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
997 if (sipe_utils_parse_lines(&list
, lines
) == FALSE
) {
998 sipe_utils_nameval_free(list
);