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 gsize bytes_remaining_chunk
;
59 guchar
* encrypted_outbuf
;
63 typedef struct _sipe_file_transfer sipe_file_transfer
;
65 static void send_filetransfer_accept(PurpleXfer
* xfer
);
66 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
67 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
);
68 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
);
69 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
);
70 static gchar
*sipe_hmac_finalize(PurpleCipherContext
*hmac_context
);
71 static void generate_key(guchar
*buffer
, gsize size
);
72 static void set_socket_nonblock(int fd
, gboolean state
);
73 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
74 static const char * sipe_ft_get_suitable_local_ip(int fd
);
76 //******************************************************************************
77 // I/O operations for PurpleXfer structure
78 //******************************************************************************
81 sipe_ft_incoming_init(PurpleXfer
*xfer
)
83 send_filetransfer_accept(xfer
);
87 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
89 sipe_file_transfer
*ft
= xfer
->data
;
91 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
93 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
95 if (ft
->cipher_context
)
96 purple_cipher_context_destroy(ft
->cipher_context
);
99 purple_cipher_context_destroy(ft
->hmac_context
);
101 g_free(ft
->encrypted_outbuf
);
102 g_free(ft
->invitation_cookie
);
109 sipe_ft_request_denied(PurpleXfer
*xfer
)
111 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
112 send_filetransfer_cancel(xfer
);
113 sipe_ft_free_xfer_struct(xfer
);
117 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
119 purple_xfer_error(purple_xfer_get_type(xfer
),
120 xfer
->account
, xfer
->who
,
125 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
127 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
128 raise_ft_error(xfer
, tmp
);
133 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
135 raise_ft_error(xfer
, errmsg
);
136 purple_xfer_cancel_local(xfer
);
140 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
142 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
146 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
148 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
152 sipe_ft_incoming_start(PurpleXfer
*xfer
)
154 sipe_file_transfer
*ft
;
155 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
156 static const gchar TFR
[] = "TFR\r\n";
157 const gsize BUFFER_SIZE
= 50;
158 gchar buf
[BUFFER_SIZE
];
159 struct sipe_account_data
*sip
;
161 const gsize FILE_SIZE_OFFSET
= 4;
164 set_socket_nonblock(xfer
->fd
,FALSE
);
168 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
169 raise_ft_socket_write_error_and_cancel(xfer
);
172 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
173 raise_ft_socket_read_error_and_cancel(xfer
);
177 sip
= xfer
->account
->gc
->proto_data
;
179 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
180 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
181 raise_ft_socket_write_error_and_cancel(xfer
);
187 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
189 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
190 if (file_size
!= xfer
->size
) {
191 raise_ft_error_and_cancel(xfer
,
192 _("File size is different from the advertised value."));
196 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
197 raise_ft_socket_write_error_and_cancel(xfer
);
201 ft
->bytes_remaining_chunk
= 0;
203 set_socket_nonblock(xfer
->fd
,TRUE
);
205 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
206 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
210 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
212 static const gchar BYE
[] = "BYE 16777989\r\n";
213 gsize BUFFER_SIZE
= 50;
214 char buffer
[BUFFER_SIZE
];
215 const gssize MAC_OFFSET
= 4;
216 const gssize CRLF_LEN
= 2;
218 sipe_file_transfer
*ft
;
222 set_socket_nonblock(xfer
->fd
,FALSE
);
224 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
225 raise_ft_socket_write_error_and_cancel(xfer
);
229 macLen
= read_line(xfer
->fd
,buffer
,BUFFER_SIZE
);
231 if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
232 raise_ft_error_and_cancel(xfer
,
233 _("Received MAC is corrupted"));
239 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
240 mac1
= sipe_hmac_finalize(ft
->hmac_context
);
241 if (!sipe_strequal(mac
, mac1
)) {
242 unlink(xfer
->local_filename
);
243 raise_ft_error_and_cancel(xfer
,
244 _("Received file is corrupted"));
249 sipe_ft_free_xfer_struct(xfer
);
253 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
258 sipe_file_transfer
*ft
= xfer
->data
;
260 if (ft
->bytes_remaining_chunk
== 0) {
263 set_socket_nonblock(xfer
->fd
, FALSE
);
265 if (read(xfer
->fd
,chunk_buf
,3) == -1) {
266 raise_ft_strerror(xfer
, _("Socket read failed"));
270 ft
->bytes_remaining_chunk
= chunk_buf
[1] + (chunk_buf
[2] << 8);
271 set_socket_nonblock(xfer
->fd
, TRUE
);
274 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
275 xfer
->current_buffer_size
);
276 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
278 *buffer
= g_malloc(bytes_to_read
);
280 raise_ft_error(xfer
, _("Out of memory"));
281 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer\n",
286 bytes_read
= read(xfer
->fd
, *buffer
, bytes_to_read
);
287 if (bytes_read
== -1) {
291 raise_ft_strerror(xfer
, _("Socket read failed"));
295 if (bytes_read
> 0) {
296 guchar
*decrypted
= g_malloc(bytes_read
);
299 raise_ft_error(xfer
, _("Out of memory"));
300 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer\n",
306 purple_cipher_context_encrypt(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
, NULL
);
310 purple_cipher_context_append(ft
->hmac_context
, decrypted
, bytes_read
);
312 ft
->bytes_remaining_chunk
-= bytes_read
;
319 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
321 ssize_t bytes_written
;
322 sipe_file_transfer
*ft
= xfer
->data
;
324 /* When sending data via server with ForeFront installed, block bigger than
325 * this default causes ending of transmission. Hard limit block to this value
326 * when libpurple sends us more data. */
327 const gsize DEFAULT_BLOCK_SIZE
= 2045;
328 if (size
> DEFAULT_BLOCK_SIZE
)
329 size
= DEFAULT_BLOCK_SIZE
;
331 if (ft
->bytes_remaining_chunk
== 0) {
332 guchar local_buf
[16];
333 memset(local_buf
, 0, sizeof local_buf
);
335 // Check if receiver did not cancel the transfer before it is finished
336 ssize_t bytes_read
= read(xfer
->fd
,local_buf
,sizeof (local_buf
));
337 if (bytes_read
== -1 && errno
!= EAGAIN
) {
338 raise_ft_strerror(xfer
, _("Socket read failed"));
340 } else if (bytes_read
!= 0) {
341 if ( g_str_has_prefix((gchar
*)local_buf
,"CCL\r\n")
342 || g_str_has_prefix((gchar
*)local_buf
,"BYE 2164261682\r\n")) {
347 if (ft
->outbuf_size
< size
) {
348 g_free(ft
->encrypted_outbuf
);
349 ft
->outbuf_size
= size
;
350 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
351 if (!ft
->encrypted_outbuf
) {
352 raise_ft_error(xfer
, _("Out of memory"));
353 purple_debug_error("sipe", "sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer\n",
359 ft
->bytes_remaining_chunk
= size
;
360 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
361 purple_cipher_context_encrypt(ft
->cipher_context
, buffer
, size
,
362 ft
->encrypted_outbuf
, NULL
);
363 purple_cipher_context_append(ft
->hmac_context
, buffer
, size
);
366 local_buf
[1] = ft
->bytes_remaining_chunk
& 0x00FF;
367 local_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
369 set_socket_nonblock(xfer
->fd
, FALSE
);
370 if (write(xfer
->fd
,local_buf
,3) == -1) {
371 raise_ft_strerror(xfer
, _("Socket write failed"));
374 set_socket_nonblock(xfer
->fd
, TRUE
);
377 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
378 if (bytes_written
== -1) {
382 raise_ft_strerror(xfer
, _("Socket write failed"));
386 if (bytes_written
> 0) {
387 ft
->bytes_remaining_chunk
-= bytes_written
;
388 ft
->outbuf_ptr
+= bytes_written
;
391 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
392 purple_xfer_set_completed(xfer
, TRUE
);
394 return bytes_written
;
398 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
400 struct sip_dialog
*dialog
;
401 sipe_file_transfer
*ft
= xfer
->data
;
403 gchar
*body
= g_strdup_printf("Application-Name: File Transfer\r\n"
404 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
405 "Invitation-Command: INVITE\r\n"
406 "Invitation-Cookie: %s\r\n"
407 "Application-File: %s\r\n"
408 "Application-FileSize: %lu\r\n"
409 //"Connectivity: N\r\n" TODO
410 "Encryption: R\r\n", // TODO: non encrypted file transfer support
411 ft
->invitation_cookie
,
412 purple_xfer_get_filename(xfer
),
413 (long unsigned) purple_xfer_get_size(xfer
));
415 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
416 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
418 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
421 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
423 dialog
= sipe_dialog_find(session
, xfer
->who
);
424 if (dialog
&& !dialog
->outgoing_invite
) {
426 sipe_im_process_queue(sip
, session
);
427 } else if (!dialog
|| !dialog
->outgoing_invite
) {
428 // Need to send the INVITE to get the outgoing dialog setup
429 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
436 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
438 sipe_file_transfer
*ft
;
439 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
440 const gsize BUFFER_SIZE
= 50;
441 gchar buf
[BUFFER_SIZE
];
443 unsigned auth_cookie_received
;
444 gboolean users_match
;
446 ssize_t bytes_written
;
448 set_socket_nonblock(xfer
->fd
,FALSE
);
452 memset(buf
,0,BUFFER_SIZE
);
453 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
454 raise_ft_socket_read_error_and_cancel(xfer
);
458 if (!sipe_strequal(buf
,VER
)) {
459 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
460 purple_debug_info("sipe","File transfer VER string incorrect, received: %s expected: %s",
465 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
466 raise_ft_socket_write_error_and_cancel(xfer
);
470 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
472 parts
= g_strsplit(buf
, " ", 3);
474 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
476 // xfer->who has 'sip:' prefix, skip these four characters
477 users_match
= !g_ascii_strcasecmp(parts
[1], (xfer
->who
+ 4));
480 purple_debug_info("sipe","File transfer authentication: %s Expected: USR %s %u\n",
481 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
483 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
484 raise_ft_error_and_cancel(xfer
,
485 _("File transfer authentication failed."));
489 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
490 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
493 if (bytes_written
== -1) {
494 raise_ft_socket_write_error_and_cancel(xfer
);
499 read_line(xfer
->fd
,buf
,BUFFER_SIZE
);
501 ft
->bytes_remaining_chunk
= 0;
503 set_socket_nonblock(xfer
->fd
,TRUE
);
505 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
506 sipe_hmac_context_init(&ft
->hmac_context
, ft
->hash_key
);
510 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
512 sipe_file_transfer
*ft
= xfer
->data
;
513 gsize BUFFER_SIZE
= 50;
514 char buffer
[BUFFER_SIZE
];
518 set_socket_nonblock(xfer
->fd
,FALSE
);
521 read_line(xfer
->fd
, buffer
, BUFFER_SIZE
);
523 mac
= sipe_hmac_finalize(ft
->hmac_context
);
524 g_sprintf(buffer
, "MAC %s \r\n", mac
);
527 mac_strlen
= strlen(buffer
);
528 // There must be this zero byte between mac and \r\n
529 buffer
[mac_strlen
- 3] = 0;
531 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
532 raise_ft_socket_write_error_and_cancel(xfer
);
536 sipe_ft_free_xfer_struct(xfer
);
539 //******************************************************************************
541 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
, const GSList
*body
)
544 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
545 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
546 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
548 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
549 session
= sipe_session_find_im(sip
, from
);
553 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
557 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
558 ft
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
560 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
561 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
562 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
565 purple_xfer_set_filename(xfer
, sipe_utils_nameval_find(body
, "Application-File"));
567 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
, "Application-FileSize"),NULL
,10);
568 purple_xfer_set_size(xfer
, file_size
);
570 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
571 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
572 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
573 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
574 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
575 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
576 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
578 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
580 purple_xfer_request(xfer
);
584 void sipe_ft_incoming_accept(PurpleAccount
*account
, const GSList
*body
)
586 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
587 gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
588 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
591 gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
592 gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
593 gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
594 gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
595 gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
597 sipe_file_transfer
*ft
= xfer
->data
;
600 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
603 guchar
*enc_key
= purple_base64_decode(enc_key_b64
, &ret_len
);
604 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
605 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
607 raise_ft_error_and_cancel(xfer
,
608 _("Received encryption key has wrong size."));
618 guchar
*hash_key
= purple_base64_decode(hash_key_b64
, &ret_len
);
619 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
620 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
622 raise_ft_error_and_cancel(xfer
,
623 _("Received hash key has wrong size."));
632 if (ip
&& port_str
) {
633 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
635 purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
, SIPE_FT_TCP_PORT_MAX
,
636 SOCK_STREAM
, sipe_ft_listen_socket_created
,xfer
);
641 void sipe_ft_incoming_cancel(PurpleAccount
*account
, GSList
*body
)
643 gchar
*inv_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
645 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
646 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
648 purple_xfer_cancel_remote(xfer
);
651 static void send_filetransfer_accept(PurpleXfer
* xfer
)
653 sipe_file_transfer
* ft
= xfer
->data
;
654 struct sip_dialog
*dialog
= ft
->dialog
;
656 gchar
*b64_encryption_key
= purple_base64_encode(ft
->encryption_key
,24);
657 gchar
*b64_hash_key
= purple_base64_encode(ft
->hash_key
,24);
659 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
660 "Request-Data: IP-Address:\r\n"
661 "Invitation-Cookie: %s\r\n"
662 "Encryption-Key: %s\r\n"
664 /*"IP-Address: %s\r\n"
667 "Auth-Cookie: 11111111\r\n"
668 "Sender-Connect: TRUE\r\n"*/,
669 ft
->invitation_cookie
,
672 /*,purple_network_get_my_ip(-1)*/
675 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
676 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
680 g_free(b64_encryption_key
);
681 g_free(b64_hash_key
);
684 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
685 sipe_file_transfer
* ft
= xfer
->data
;
686 struct sip_dialog
* dialog
= ft
->dialog
;
688 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
689 "Invitation-Cookie: %s\r\n"
690 "Cancel-Code: REJECT\r\n",
691 ft
->invitation_cookie
);
693 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
694 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
700 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
)
704 memset(buffer
,0,size
);
706 if (read(fd
,buffer
+ pos
,1) == -1)
708 } while (buffer
[pos
] != '\n' && ++pos
< size
);
713 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
)
716 * Decryption of file from SIPE file transfer
719 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
720 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
723 PurpleCipherContext
*sha1_context
;
727 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
728 purple_cipher_context_append(sha1_context
, enc_key
, SIPE_FT_KEY_LENGTH
);
729 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
730 purple_cipher_context_destroy(sha1_context
);
732 /* 2.) RC4 decryption */
733 *rc4_context
= purple_cipher_context_new_by_name("rc4", NULL
);
734 purple_cipher_context_set_option(*rc4_context
, "key_len", (gpointer
)0x10); // only 16 chars key used
735 purple_cipher_context_set_key(*rc4_context
, k2
);
739 static void sipe_hmac_context_init(PurpleCipherContext
**hmac_context
, const guchar
*hash_key
)
745 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
746 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
749 PurpleCipherContext
*sha1_context
;
753 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
754 purple_cipher_context_append(sha1_context
, hash_key
, SIPE_FT_KEY_LENGTH
);
755 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
756 purple_cipher_context_destroy(sha1_context
);
758 /* 2.) HMAC (initialization only) */
759 *hmac_context
= purple_cipher_context_new_by_name("hmac", NULL
);
760 purple_cipher_context_set_option(*hmac_context
, "hash", "sha1");
761 purple_cipher_context_set_key_with_len(*hmac_context
, k2
, 16);
764 static gchar
* sipe_hmac_finalize(PurpleCipherContext
*hmac_context
)
766 guchar hmac_digest
[20];
768 /* MAC = Digest of decrypted file and SHA1-Key (used again only 16 bytes) */
769 purple_cipher_context_digest(hmac_context
, sizeof(hmac_digest
), hmac_digest
, NULL
);
771 return purple_base64_encode(hmac_digest
, sizeof (hmac_digest
));
774 static void generate_key(guchar
*buffer
, gsize size
)
777 for (i
= 0; i
!= size
; ++i
)
781 static void set_socket_nonblock(int fd
, gboolean state
)
783 int flags
= fcntl(fd
, F_GETFL
, 0);
788 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
790 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
793 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
797 xfer
= sipe_ft_new_xfer(gc
, who
);
800 purple_xfer_request_accepted(xfer
, file
);
802 purple_xfer_request(xfer
);
805 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
807 PurpleAccount
*account
= purple_connection_get_account(gc
);
808 PurpleXfer
*xfer
= purple_xfer_new(account
, PURPLE_XFER_SEND
, who
);
811 struct sipe_account_data
*sip
= purple_connection_get_protocol_data(gc
);
813 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
814 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
819 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
820 purple_xfer_set_start_fnc(xfer
,sipe_ft_outgoing_start
);
821 purple_xfer_set_end_fnc(xfer
,sipe_ft_outgoing_stop
);
822 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
823 purple_xfer_set_write_fnc(xfer
,sipe_ft_write
);
824 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
825 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
832 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
833 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
835 struct sockaddr_in saddr
;
836 socklen_t slen
= sizeof (saddr
);
838 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
840 PurpleXfer
*xfer
= p_xfer
;
842 purple_input_remove(xfer
->watcher
);
846 purple_xfer_start(xfer
,fd
,NULL
,0);
850 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
853 PurpleXfer
*xfer
= data
;
854 sipe_file_transfer
*ft
= xfer
->data
;
856 struct sockaddr_in addr
;
858 socklen_t socklen
= sizeof (addr
);
860 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
862 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
863 sipe_ft_client_connected
, xfer
);
865 ft
->auth_cookie
= rand() % 1000000000;
867 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
868 "Invitation-Cookie: %s\r\n"
873 "Request-Data: IP-Address:\r\n",
874 ft
->invitation_cookie
,
875 sipe_ft_get_suitable_local_ip(listenfd
),
876 ntohs(addr
.sin_port
),
880 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
881 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
882 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
886 send_sip_request(ft
->sip
->gc
, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
887 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
888 body
, ft
->dialog
, NULL
);
895 #include <sys/ioctl.h>
901 * Calling sizeof(struct ifreq) isn't always correct on
902 * Mac OS X (and maybe others).
904 #ifdef _SIZEOF_ADDR_IFREQ
905 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
907 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
911 * Returns local IP address suitable for connection.
913 * purple_network_get_my_ip() will not do this, because it might return an
914 * address within 169.254.x.x range that was assigned to interface disconnected
915 * from the network (when multiple network adapters are available). This is a
916 * copy-paste from libpurple's network.c, only change is that link local addresses
919 * Maybe this should be fixed in libpurple or some better solution found.
922 const char * sipe_ft_get_suitable_local_ip(int fd
)
924 int source
= (fd
>= 0) ? fd
: socket(PF_INET
,SOCK_STREAM
, 0);
931 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
932 guint32 llocal
= htonl((169 << 24) + (254 << 16));
934 ifc
.ifc_len
= sizeof(buffer
);
935 ifc
.ifc_req
= (struct ifreq
*)buffer
;
936 ioctl(source
, SIOCGIFCONF
, &ifc
);
942 while (tmp
< buffer
+ ifc
.ifc_len
)
944 struct ifreq
*ifr
= (struct ifreq
*)tmp
;
945 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
947 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
949 struct sockaddr_in
*sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
950 if (sinptr
->sin_addr
.s_addr
!= lhost
951 && (sinptr
->sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
953 long unsigned int add
= ntohl(sinptr
->sin_addr
.s_addr
);
954 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",
969 GSList
* sipe_ft_parse_msg_body(const gchar
*body
)
972 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
973 if (sipe_utils_parse_lines(&list
, lines
) == FALSE
) {
974 sipe_utils_nameval_free(list
);