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>
28 #include <libpurple/debug.h>
32 #include "sipe-dialog.h"
34 #include "sipe-session.h"
35 #include "sipe-utils.h"
38 #include <sys/socket.h>
39 #include <netinet/in.h>
42 #define SIPE_FT_KEY_LENGTH 24
44 #define SIPE_FT_TCP_PORT_MIN 6891
45 #define SIPE_FT_TCP_PORT_MAX 6901
47 struct _sipe_file_transfer
{
48 guchar encryption_key
[SIPE_FT_KEY_LENGTH
];
49 guchar hash_key
[SIPE_FT_KEY_LENGTH
];
50 gchar
*invitation_cookie
;
52 struct sipe_account_data
*sip
;
53 struct sip_dialog
*dialog
;
54 PurpleCipherContext
*cipher_context
;
56 gsize bytes_remaining_chunk
;
57 guchar
* encrypted_outbuf
;
61 typedef struct _sipe_file_transfer sipe_file_transfer
;
63 static void send_filetransfer_accept(PurpleXfer
* xfer
);
64 static void send_filetransfer_cancel(PurpleXfer
* xfer
);
65 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
);
66 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
);
67 static gchar
* sipe_get_mac(const guchar
*data
, size_t data_len
, const guchar
*hash_key
);
68 static void generate_key(guchar
*buffer
, gsize size
);
69 static void set_socket_nonblock(int fd
, gboolean state
);
70 static void sipe_ft_listen_socket_created(int listenfd
, gpointer data
);
71 static const char * sipe_ft_get_suitable_local_ip(int fd
);
73 //******************************************************************************
74 // I/O operations for PurpleXfer structure
75 //******************************************************************************
78 sipe_ft_incoming_init(PurpleXfer
*xfer
)
80 send_filetransfer_accept(xfer
);
84 sipe_ft_free_xfer_struct(PurpleXfer
*xfer
)
86 sipe_file_transfer
*ft
= xfer
->data
;
88 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
90 g_hash_table_remove(sip
->filetransfers
,ft
->invitation_cookie
);
92 if (ft
->cipher_context
)
93 purple_cipher_context_destroy(ft
->cipher_context
);
95 g_free(ft
->encrypted_outbuf
);
96 g_free(ft
->invitation_cookie
);
103 sipe_ft_request_denied(PurpleXfer
*xfer
)
105 if (xfer
->type
== PURPLE_XFER_RECEIVE
)
106 send_filetransfer_cancel(xfer
);
107 sipe_ft_free_xfer_struct(xfer
);
111 void raise_ft_error(PurpleXfer
*xfer
, const char *errmsg
)
113 purple_xfer_error(purple_xfer_get_type(xfer
),
114 xfer
->account
, xfer
->who
,
119 void raise_ft_strerror(PurpleXfer
*xfer
, const char *errmsg
)
121 gchar
*tmp
= g_strdup_printf("%s: %s", errmsg
, strerror(errno
));
122 raise_ft_error(xfer
, tmp
);
127 void raise_ft_error_and_cancel(PurpleXfer
*xfer
, const char *errmsg
)
129 raise_ft_error(xfer
, errmsg
);
130 purple_xfer_cancel_local(xfer
);
134 void raise_ft_socket_read_error_and_cancel(PurpleXfer
*xfer
)
136 raise_ft_error_and_cancel(xfer
, _("Socket read failed"));
140 void raise_ft_socket_write_error_and_cancel(PurpleXfer
*xfer
)
142 raise_ft_error_and_cancel(xfer
, _("Socket write failed"));
146 sipe_ft_incoming_start(PurpleXfer
*xfer
)
148 sipe_file_transfer
*ft
;
149 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
150 static const gchar TFR
[] = "TFR\r\n";
151 const gsize BUFFER_SIZE
= 50;
152 gchar buf
[BUFFER_SIZE
];
153 struct sipe_account_data
*sip
;
155 const gsize FILE_SIZE_OFFSET
= 4;
158 set_socket_nonblock(xfer
->fd
,FALSE
);
162 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
163 raise_ft_socket_write_error_and_cancel(xfer
);
166 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
167 raise_ft_socket_read_error_and_cancel(xfer
);
171 sip
= xfer
->account
->gc
->proto_data
;
173 request
= g_strdup_printf("USR %s %u\r\n", sip
->username
, ft
->auth_cookie
);
174 if (write(xfer
->fd
,request
,strlen(request
)) == -1) {
175 raise_ft_socket_write_error_and_cancel(xfer
);
181 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
183 file_size
= g_ascii_strtoull(buf
+ FILE_SIZE_OFFSET
,NULL
,10);
184 if (file_size
!= xfer
->size
) {
185 raise_ft_error_and_cancel(xfer
,
186 _("File size is different from the advertised value."));
190 if (write(xfer
->fd
,TFR
,strlen(TFR
)) == -1) {
191 raise_ft_socket_write_error_and_cancel(xfer
);
195 ft
->bytes_remaining_chunk
= 0;
197 set_socket_nonblock(xfer
->fd
,TRUE
);
199 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
203 sipe_ft_incoming_stop(PurpleXfer
*xfer
)
205 static const gchar BYE
[] = "BYE 16777989\r\n";
206 gsize BUFFER_SIZE
= 50;
207 char buffer
[BUFFER_SIZE
];
208 const gssize MAC_OFFSET
= 4;
209 const gssize CRLF_LEN
= 2;
213 sipe_file_transfer
*ft
;
217 set_socket_nonblock(xfer
->fd
,FALSE
);
219 if (write(xfer
->fd
,BYE
,strlen(BYE
)) == -1) {
220 raise_ft_socket_write_error_and_cancel(xfer
);
224 macLen
= read_line(xfer
->fd
,buffer
,BUFFER_SIZE
);
226 if (macLen
< (MAC_OFFSET
+ CRLF_LEN
)) {
227 raise_ft_error_and_cancel(xfer
,
228 _("Received MAC is corrupted"));
232 fflush(xfer
->dest_fp
);
236 fdread
= fopen(xfer
->local_filename
,"rb");
238 raise_ft_error_and_cancel(xfer
,
239 _("Unable to open received file."));
243 filebuf
= g_malloc(xfer
->size
);
246 raise_ft_error_and_cancel(xfer
,
248 purple_debug_error("sipe", "sipe_ft_incoming_stop: can't allocate %" G_GSIZE_FORMAT
" bytes for file buffer\n",
253 if (fread(filebuf
, 1, xfer
->size
, fdread
) < 1) {
254 purple_debug_error("sipe", "sipe_ft_incoming_stop: can't read received file: %s\n",
258 raise_ft_error_and_cancel(xfer
,
259 _("Unable to read received file."));
266 mac
= g_strndup(buffer
+ MAC_OFFSET
, macLen
- MAC_OFFSET
- CRLF_LEN
);
267 mac1
= sipe_get_mac(filebuf
, xfer
->size
, ft
->hash_key
);
268 if (!sipe_strequal(mac
, mac1
)) {
269 unlink(xfer
->local_filename
);
270 raise_ft_error_and_cancel(xfer
,
271 _("Received file is corrupted"));
276 sipe_ft_free_xfer_struct(xfer
);
280 sipe_ft_read(guchar
**buffer
, PurpleXfer
*xfer
)
285 sipe_file_transfer
*ft
= xfer
->data
;
287 if (ft
->bytes_remaining_chunk
== 0) {
290 set_socket_nonblock(xfer
->fd
, FALSE
);
292 if (read(xfer
->fd
,chunk_buf
,3) == -1) {
293 raise_ft_strerror(xfer
, _("Socket read failed"));
297 ft
->bytes_remaining_chunk
= chunk_buf
[1] + (chunk_buf
[2] << 8);
298 set_socket_nonblock(xfer
->fd
, TRUE
);
301 bytes_to_read
= MIN(purple_xfer_get_bytes_remaining(xfer
),
302 xfer
->current_buffer_size
);
303 bytes_to_read
= MIN(bytes_to_read
, ft
->bytes_remaining_chunk
);
305 *buffer
= g_malloc(bytes_to_read
);
307 raise_ft_error(xfer
, _("Out of memory"));
308 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for receive buffer\n",
313 bytes_read
= read(xfer
->fd
, *buffer
, bytes_to_read
);
314 if (bytes_read
== -1) {
318 raise_ft_strerror(xfer
, _("Socket read failed"));
322 if (bytes_read
> 0) {
323 guchar
*decrypted
= g_malloc(bytes_read
);
326 raise_ft_error(xfer
, _("Out of memory"));
327 purple_debug_error("sipe", "sipe_ft_read: can't allocate %" G_GSIZE_FORMAT
" bytes for decryption buffer\n",
332 purple_cipher_context_encrypt(ft
->cipher_context
, *buffer
, bytes_read
, decrypted
, NULL
);
336 ft
->bytes_remaining_chunk
-= bytes_read
;
343 sipe_ft_write(const guchar
*buffer
, size_t size
, PurpleXfer
*xfer
)
345 ssize_t bytes_written
;
346 sipe_file_transfer
*ft
= xfer
->data
;
348 /* When sending data via server with ForeFront installed, block bigger than
349 * this default causes ending of transmission. Hard limit block to this value
350 * when libpurple sends us more data. */
351 const gsize DEFAULT_BLOCK_SIZE
= 2045;
352 if (size
> DEFAULT_BLOCK_SIZE
)
353 size
= DEFAULT_BLOCK_SIZE
;
355 if (ft
->bytes_remaining_chunk
== 0) {
358 if (ft
->outbuf_size
< size
) {
359 g_free(ft
->encrypted_outbuf
);
360 ft
->outbuf_size
= size
;
361 ft
->encrypted_outbuf
= g_malloc(ft
->outbuf_size
);
362 if (!ft
->encrypted_outbuf
) {
363 raise_ft_error(xfer
, _("Out of memory"));
364 purple_debug_error("sipe", "sipe_ft_write: can't allocate %" G_GSIZE_FORMAT
" bytes for send buffer\n",
370 ft
->bytes_remaining_chunk
= size
;
371 ft
->outbuf_ptr
= ft
->encrypted_outbuf
;
372 purple_cipher_context_encrypt(ft
->cipher_context
, buffer
, size
,
373 ft
->encrypted_outbuf
, NULL
);
376 chunk_buf
[1] = ft
->bytes_remaining_chunk
& 0x00FF;
377 chunk_buf
[2] = (ft
->bytes_remaining_chunk
& 0xFF00) >> 8;
379 set_socket_nonblock(xfer
->fd
, FALSE
);
380 if (write(xfer
->fd
,chunk_buf
,3) == -1) {
381 raise_ft_strerror(xfer
, _("Socket write failed"));
384 set_socket_nonblock(xfer
->fd
, TRUE
);
387 bytes_written
= write(xfer
->fd
, ft
->outbuf_ptr
, ft
->bytes_remaining_chunk
);
388 if (bytes_written
== -1) {
392 raise_ft_strerror(xfer
, _("Socket write failed"));
396 if (bytes_written
> 0) {
397 ft
->bytes_remaining_chunk
-= bytes_written
;
398 ft
->outbuf_ptr
+= bytes_written
;
401 if ((xfer
->bytes_remaining
- bytes_written
) == 0)
402 purple_xfer_set_completed(xfer
, TRUE
);
404 return bytes_written
;
408 sipe_ft_outgoing_init(PurpleXfer
*xfer
)
410 struct sip_dialog
*dialog
;
411 sipe_file_transfer
*ft
= xfer
->data
;
413 gchar
*body
= g_strdup_printf("Application-Name: File Transfer\r\n"
414 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
415 "Invitation-Command: INVITE\r\n"
416 "Invitation-Cookie: %s\r\n"
417 "Application-File: %s\r\n"
418 "Application-FileSize: %lu\r\n"
419 //"Connectivity: N\r\n" TODO
420 "Encryption: R\r\n", // TODO: non encrypted file transfer support
421 ft
->invitation_cookie
,
422 purple_xfer_get_filename(xfer
),
423 (long unsigned) purple_xfer_get_size(xfer
));
425 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
426 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
428 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
431 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
433 dialog
= sipe_dialog_find(session
, xfer
->who
);
434 if (dialog
&& !dialog
->outgoing_invite
) {
436 sipe_im_process_queue(sip
, session
);
437 } else if (!dialog
|| !dialog
->outgoing_invite
) {
438 // Need to send the INVITE to get the outgoing dialog setup
439 sipe_invite(sip
, session
, xfer
->who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
446 sipe_ft_outgoing_start(PurpleXfer
*xfer
)
448 sipe_file_transfer
*ft
;
449 static const gchar VER
[] = "VER MSN_SECURE_FTP\r\n";
450 const gsize BUFFER_SIZE
= 50;
451 gchar buf
[BUFFER_SIZE
];
453 unsigned auth_cookie_received
;
454 gboolean users_match
;
456 ssize_t bytes_written
;
458 set_socket_nonblock(xfer
->fd
,FALSE
);
462 memset(buf
,0,BUFFER_SIZE
);
463 if (read(xfer
->fd
,buf
,strlen(VER
)) == -1) {
464 raise_ft_socket_read_error_and_cancel(xfer
);
468 if (!sipe_strequal(buf
,VER
)) {
469 raise_ft_error_and_cancel(xfer
,_("File transfer initialization failed."));
470 purple_debug_info("sipe","File transfer VER string incorrect, received: %s expected: %s",
475 if (write(xfer
->fd
,VER
,strlen(VER
)) == -1) {
476 raise_ft_socket_write_error_and_cancel(xfer
);
480 read_line(xfer
->fd
, buf
, BUFFER_SIZE
);
482 parts
= g_strsplit(buf
, " ", 3);
484 auth_cookie_received
= g_ascii_strtoull(parts
[2],NULL
,10);
486 // xfer->who has 'sip:' prefix, skip these four characters
487 users_match
= sipe_strequal(parts
[1], (xfer
->who
+ 4));
490 purple_debug_info("sipe","File transfer authentication: %s Expected: USR %s %u\n",
491 buf
, xfer
->who
+ 4, ft
->auth_cookie
);
493 if (!users_match
|| (ft
->auth_cookie
!= auth_cookie_received
)) {
494 raise_ft_error_and_cancel(xfer
,
495 _("File transfer authentication failed."));
499 tmp
= g_strdup_printf("FIL %lu\r\n",(long unsigned) xfer
->size
);
500 bytes_written
= write(xfer
->fd
, tmp
, strlen(tmp
));
503 if (bytes_written
== -1) {
504 raise_ft_socket_write_error_and_cancel(xfer
);
509 read_line(xfer
->fd
,buf
,BUFFER_SIZE
);
511 ft
->bytes_remaining_chunk
= 0;
513 set_socket_nonblock(xfer
->fd
,TRUE
);
515 sipe_cipher_context_init(&ft
->cipher_context
, ft
->encryption_key
);
519 sipe_ft_outgoing_stop(PurpleXfer
*xfer
)
521 gsize BUFFER_SIZE
= 50;
522 char buffer
[BUFFER_SIZE
];
524 sipe_file_transfer
*ft
;
528 set_socket_nonblock(xfer
->fd
,FALSE
);
531 read_line(xfer
->fd
, buffer
, BUFFER_SIZE
);
533 filebuf
= g_malloc(xfer
->size
);
535 raise_ft_error_and_cancel(xfer
,
537 purple_debug_error("sipe", "sipe_ft_outgoing_stop: can't allocate %" G_GSIZE_FORMAT
" bytes for file buffer\n",
541 fseek(xfer
->dest_fp
,0,SEEK_SET
);
542 if (fread(filebuf
,xfer
->size
,1,xfer
->dest_fp
) < 1) {
543 purple_debug_error("sipe", "sipe_ft_outgoing_stop: can't read sent file: %s\n",
546 raise_ft_socket_read_error_and_cancel(xfer
);
551 mac
= sipe_get_mac(filebuf
,xfer
->size
,ft
->hash_key
);
553 g_sprintf(buffer
, "MAC %s \r\n", mac
);
556 mac_strlen
= strlen(buffer
);
557 // There must be this zero byte between mac and \r\n
558 buffer
[mac_strlen
- 3] = 0;
560 if (write(xfer
->fd
,buffer
,mac_strlen
) == -1) {
561 raise_ft_socket_write_error_and_cancel(xfer
);
565 sipe_ft_free_xfer_struct(xfer
);
568 //******************************************************************************
570 void sipe_ft_incoming_transfer(PurpleAccount
*account
, struct sipmsg
*msg
)
573 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
574 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
575 struct sip_session
*session
= sipe_session_find_chat_by_callid(sip
, callid
);
577 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
578 session
= sipe_session_find_im(sip
, from
);
582 xfer
= purple_xfer_new(account
, PURPLE_XFER_RECEIVE
, session
->with
);
586 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
587 ft
->invitation_cookie
= g_strdup(sipmsg_find_header(msg
, "Invitation-Cookie"));
589 ft
->dialog
= sipe_dialog_find(session
, session
->with
);
590 generate_key(ft
->encryption_key
, SIPE_FT_KEY_LENGTH
);
591 generate_key(ft
->hash_key
, SIPE_FT_KEY_LENGTH
);
594 purple_xfer_set_filename(xfer
, sipmsg_find_header(msg
,"Application-File"));
596 file_size
= g_ascii_strtoull(sipmsg_find_header(msg
,"Application-FileSize"),NULL
,10);
597 purple_xfer_set_size(xfer
, file_size
);
599 purple_xfer_set_init_fnc(xfer
, sipe_ft_incoming_init
);
600 purple_xfer_set_start_fnc(xfer
,sipe_ft_incoming_start
);
601 purple_xfer_set_end_fnc(xfer
,sipe_ft_incoming_stop
);
602 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
603 purple_xfer_set_read_fnc(xfer
,sipe_ft_read
);
604 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
605 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
607 g_hash_table_insert(sip
->filetransfers
,g_strdup(ft
->invitation_cookie
),xfer
);
609 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
611 purple_xfer_request(xfer
);
615 void sipe_ft_incoming_accept(PurpleAccount
*account
, struct sipmsg
*msg
)
617 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
618 gchar
*inv_cookie
= sipmsg_find_header(msg
,"Invitation-Cookie");
619 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
622 /* ip and port_str must be copied, because send_sip_response changes
623 * the headers and we need to use this values afterwards. */
624 gchar
*ip
= g_strdup(sipmsg_find_header(msg
, "IP-Address"));
625 gchar
*port_str
= g_strdup(sipmsg_find_header(msg
, "Port"));
626 gchar
*auth_cookie
= sipmsg_find_header(msg
, "AuthCookie");
627 gchar
*enc_key_b64
= sipmsg_find_header(msg
, "Encryption-Key");
628 gchar
*hash_key_b64
= sipmsg_find_header(msg
, "Hash-Key");
630 sipe_file_transfer
*ft
= xfer
->data
;
633 ft
->auth_cookie
= g_ascii_strtoull(auth_cookie
,NULL
,10);
636 guchar
*enc_key
= purple_base64_decode(enc_key_b64
, &ret_len
);
637 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
638 memcpy(ft
->encryption_key
,enc_key
,SIPE_FT_KEY_LENGTH
);
640 raise_ft_error_and_cancel(xfer
,
641 _("Received encryption key has wrong size."));
651 guchar
*hash_key
= purple_base64_decode(hash_key_b64
, &ret_len
);
652 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
653 memcpy(ft
->hash_key
,hash_key
,SIPE_FT_KEY_LENGTH
);
655 raise_ft_error_and_cancel(xfer
,
656 _("Received hash key has wrong size."));
665 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
667 if (ip
&& port_str
) {
668 purple_xfer_start(xfer
, -1, ip
, g_ascii_strtoull(port_str
,NULL
,10));
670 purple_network_listen_range(SIPE_FT_TCP_PORT_MIN
, SIPE_FT_TCP_PORT_MAX
,
671 SOCK_STREAM
, sipe_ft_listen_socket_created
,xfer
);
679 void sipe_ft_incoming_cancel(PurpleAccount
*account
, struct sipmsg
*msg
)
681 gchar
*inv_cookie
= g_strdup(sipmsg_find_header(msg
, "Invitation-Cookie"));
683 struct sipe_account_data
*sip
= account
->gc
->proto_data
;
684 PurpleXfer
*xfer
= g_hash_table_lookup(sip
->filetransfers
,inv_cookie
);
686 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
688 purple_xfer_cancel_remote(xfer
);
691 static void send_filetransfer_accept(PurpleXfer
* xfer
)
693 sipe_file_transfer
* ft
= xfer
->data
;
694 struct sip_dialog
*dialog
= ft
->dialog
;
696 gchar
*b64_encryption_key
= purple_base64_encode(ft
->encryption_key
,24);
697 gchar
*b64_hash_key
= purple_base64_encode(ft
->hash_key
,24);
699 gchar
*body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
700 "Request-Data: IP-Address:\r\n"
701 "Invitation-Cookie: %s\r\n"
702 "Encryption-Key: %s\r\n"
704 /*"IP-Address: %s\r\n"
707 "Auth-Cookie: 11111111\r\n"
708 "Sender-Connect: TRUE\r\n"*/,
709 ft
->invitation_cookie
,
712 /*,purple_network_get_my_ip(-1)*/
715 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
716 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
720 g_free(b64_encryption_key
);
721 g_free(b64_hash_key
);
724 static void send_filetransfer_cancel(PurpleXfer
* xfer
) {
725 sipe_file_transfer
* ft
= xfer
->data
;
726 struct sip_dialog
* dialog
= ft
->dialog
;
728 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
729 "Invitation-Cookie: %s\r\n"
730 "Cancel-Code: REJECT\r\n",
731 ft
->invitation_cookie
);
733 send_sip_request(ft
->sip
->gc
, "MESSAGE", dialog
->with
, dialog
->with
,
734 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
740 static gssize
read_line(int fd
, gchar
*buffer
, gssize size
)
744 memset(buffer
,0,size
);
746 if (read(fd
,buffer
+ pos
,1) == -1)
748 } while (buffer
[pos
] != '\n' && ++pos
< size
);
753 static void sipe_cipher_context_init(PurpleCipherContext
**rc4_context
, const guchar
*enc_key
)
756 * Decryption of file from SIPE file transfer
759 * 1.) SHA1-Key = SHA1sum (Encryption-Key); Do SHA1 digest from Encryption-Key, return 20 bytes SHA1-Key.
760 * 2.) Decrypt-Data = RC4 (Encrypt-Data, substr(SHA1-Key, 0, 15)); Decryption of encrypted data, used 16 bytes SHA1-Key;
763 PurpleCipherContext
*sha1_context
;
767 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
768 purple_cipher_context_append(sha1_context
, enc_key
, SIPE_FT_KEY_LENGTH
);
769 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
770 purple_cipher_context_destroy(sha1_context
);
772 /* 2.) RC4 decryption */
773 *rc4_context
= purple_cipher_context_new_by_name("rc4", NULL
);
774 purple_cipher_context_set_option(*rc4_context
, "key_len", (gpointer
)0x10); // only 16 chars key used
775 purple_cipher_context_set_key(*rc4_context
, k2
);
779 static gchar
* sipe_get_mac(const guchar
*data
, size_t data_len
, const guchar
*hash_key
)
785 * 1.) SHA1-Key = SHA1sum (Hash-Key); Do SHA1 digest from Hash-Key, return 20 bytes SHA1-Key.
786 * 2.) MAC = HMAC_SHA1 (Decrypt-Data, substr(HMAC-Key,0,15)); Digest of decrypted file and SHA1-Key (used again only 16 bytes)
789 PurpleCipherContext
*sha1_context
;
790 PurpleCipherContext
*hmac_context
;
791 guchar hmac_digest
[20];
795 sha1_context
= purple_cipher_context_new_by_name("sha1", NULL
);
796 purple_cipher_context_append(sha1_context
, hash_key
, SIPE_FT_KEY_LENGTH
);
797 purple_cipher_context_digest(sha1_context
, sizeof(k2
), k2
, NULL
);
798 purple_cipher_context_destroy(sha1_context
);
801 hmac_context
= purple_cipher_context_new_by_name("hmac", NULL
);
802 purple_cipher_context_set_option(hmac_context
, "hash", "sha1");
803 purple_cipher_context_set_key_with_len(hmac_context
, k2
, 16);
804 purple_cipher_context_append(hmac_context
, data
, data_len
);
805 purple_cipher_context_digest(hmac_context
, sizeof(hmac_digest
), hmac_digest
, NULL
);
806 purple_cipher_context_destroy(hmac_context
);
808 return purple_base64_encode(hmac_digest
, sizeof (hmac_digest
));
811 static void generate_key(guchar
*buffer
, gsize size
)
814 for (i
= 0; i
!= size
; ++i
)
818 static void set_socket_nonblock(int fd
, gboolean state
)
820 int flags
= fcntl(fd
, F_GETFL
, 0);
825 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
827 fcntl(fd
, F_SETFL
, flags
& ~O_NONBLOCK
);
830 void sipe_ft_send_file(PurpleConnection
*gc
, const char *who
, const char *file
)
834 xfer
= sipe_ft_new_xfer(gc
, who
);
837 purple_xfer_request_accepted(xfer
, file
);
839 purple_xfer_request(xfer
);
842 PurpleXfer
* sipe_ft_new_xfer(PurpleConnection
*gc
, const char *who
)
844 PurpleAccount
*account
= purple_connection_get_account(gc
);
845 PurpleXfer
*xfer
= purple_xfer_new(account
, PURPLE_XFER_SEND
, who
);
848 struct sipe_account_data
*sip
= purple_connection_get_protocol_data(gc
);
850 sipe_file_transfer
*ft
= g_new0(sipe_file_transfer
, 1);
851 ft
->invitation_cookie
= g_strdup_printf("%u", rand() % 1000000000);
856 purple_xfer_set_init_fnc(xfer
, sipe_ft_outgoing_init
);
857 purple_xfer_set_start_fnc(xfer
,sipe_ft_outgoing_start
);
858 purple_xfer_set_end_fnc(xfer
,sipe_ft_outgoing_stop
);
859 purple_xfer_set_request_denied_fnc(xfer
, sipe_ft_request_denied
);
860 purple_xfer_set_write_fnc(xfer
,sipe_ft_write
);
861 purple_xfer_set_cancel_send_fnc(xfer
,sipe_ft_free_xfer_struct
);
862 purple_xfer_set_cancel_recv_fnc(xfer
,sipe_ft_free_xfer_struct
);
869 void sipe_ft_client_connected(gpointer p_xfer
, gint listenfd
,
870 SIPE_UNUSED_PARAMETER PurpleInputCondition cond
)
872 struct sockaddr_in saddr
;
873 socklen_t slen
= sizeof (saddr
);
875 int fd
= accept(listenfd
, (struct sockaddr
*)&saddr
, &slen
);
877 PurpleXfer
*xfer
= p_xfer
;
879 purple_input_remove(xfer
->watcher
);
883 purple_xfer_start(xfer
,fd
,NULL
,0);
887 void sipe_ft_listen_socket_created(int listenfd
, gpointer data
)
890 PurpleXfer
*xfer
= data
;
891 sipe_file_transfer
*ft
= xfer
->data
;
893 struct sockaddr_in addr
;
895 socklen_t socklen
= sizeof (addr
);
897 getsockname(listenfd
, (struct sockaddr
*)&addr
, &socklen
);
899 xfer
->watcher
= purple_input_add(listenfd
, PURPLE_INPUT_READ
,
900 sipe_ft_client_connected
, xfer
);
902 ft
->auth_cookie
= rand() % 1000000000;
904 body
= g_strdup_printf("Invitation-Command: ACCEPT\r\n"
905 "Invitation-Cookie: %s\r\n"
910 "Request-Data: IP-Address:\r\n",
911 ft
->invitation_cookie
,
912 sipe_ft_get_suitable_local_ip(listenfd
),
913 ntohs(addr
.sin_port
),
917 struct sipe_account_data
*sip
= xfer
->account
->gc
->proto_data
;
918 struct sip_session
*session
= sipe_session_find_or_add_im(sip
, xfer
->who
);
919 ft
->dialog
= sipe_dialog_find(session
, xfer
->who
);
923 send_sip_request(ft
->sip
->gc
, "MESSAGE", ft
->dialog
->with
, ft
->dialog
->with
,
924 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
925 body
, ft
->dialog
, NULL
);
932 #include <sys/ioctl.h>
938 * Calling sizeof(struct ifreq) isn't always correct on
939 * Mac OS X (and maybe others).
941 #ifdef _SIZEOF_ADDR_IFREQ
942 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
944 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
948 * Returns local IP address suitable for connection.
950 * purple_network_get_my_ip() will not do this, because it might return an
951 * address within 169.254.x.x range that was assigned to interface disconnected
952 * from the network (when multiple network adapters are available). This is a
953 * copy-paste from libpurple's network.c, only change is that link local addresses
956 * Maybe this should be fixed in libpurple or some better solution found.
959 const char * sipe_ft_get_suitable_local_ip(int fd
)
961 int source
= (fd
>= 0) ? fd
: socket(PF_INET
,SOCK_STREAM
, 0);
968 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
969 guint32 llocal
= htonl((169 << 24) + (254 << 16));
971 ifc
.ifc_len
= sizeof(buffer
);
972 ifc
.ifc_req
= (struct ifreq
*)buffer
;
973 ioctl(source
, SIOCGIFCONF
, &ifc
);
979 while (tmp
< buffer
+ ifc
.ifc_len
)
981 struct ifreq
*ifr
= (struct ifreq
*)tmp
;
982 tmp
+= HX_SIZE_OF_IFREQ(*ifr
);
984 if (ifr
->ifr_addr
.sa_family
== AF_INET
)
986 struct sockaddr_in
*sinptr
= (struct sockaddr_in
*)&ifr
->ifr_addr
;
987 if (sinptr
->sin_addr
.s_addr
!= lhost
988 && (sinptr
->sin_addr
.s_addr
& htonl(0xFFFF0000)) != llocal
)
990 long unsigned int add
= ntohl(sinptr
->sin_addr
.s_addr
);
991 g_snprintf(ip
, 16, "%lu.%lu.%lu.%lu",