6 * Copyright (C) 2010-2016 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 Jakub Adam <jakub.adam@ktknet.cz>
8 * Copyright (C) 2010 Tomáš Hrabčík <tomas.hrabcik@tieto.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #include "sip-transport.h"
36 #include "sipe-backend.h"
37 #include "sipe-common.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-crypt.h"
41 #include "sipe-dialog.h"
42 #include "sipe-digest.h"
44 #include "sipe-ft-lync.h"
45 #include "sipe-ft-tftp.h"
48 #include "sipe-session.h"
49 #include "sipe-utils.h"
52 * DO NOT CHANGE THE FOLLOWING CONSTANTS!!!
54 * It seems that Microsoft Office Communicator client will accept
55 * file transfer invitations *only* within this port range!
57 * If a firewall is active on your system you need to open these ports if
58 * you want to *send* files to other users. Receiving files uses an outgoing
59 * connection and should therefore automatically penetrate your firewall.
61 #define SIPE_FT_TCP_PORT_MIN 6891
62 #define SIPE_FT_TCP_PORT_MAX 6901
65 ft_outgoing_init(struct sipe_file_transfer
*ft
, const gchar
*filename
,
66 gsize size
, const gchar
*who
);
68 void sipe_ft_raise_error_and_cancel(struct sipe_file_transfer_private
*ft_private
,
71 sipe_backend_ft_error(SIPE_FILE_TRANSFER_PUBLIC
, errmsg
);
72 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER_PUBLIC
);
75 static void generate_key(guchar
*buffer
, gsize size
)
78 while (i
< size
) buffer
[i
++] = rand();
81 static struct sipe_file_transfer
*
82 sipe_file_transfer_new_outgoing(struct sipe_core_private
*sipe_private
)
84 struct sipe_file_transfer_private
*ft_private
;
86 ft_private
= g_new0(struct sipe_file_transfer_private
, 1);
88 ft_private
->sipe_private
= sipe_private
;
90 ft_private
->public.ft_init
= ft_outgoing_init
;
91 ft_private
->public.ft_start
= sipe_ft_tftp_start_sending
;
92 ft_private
->public.ft_write
= sipe_ft_tftp_write
;
93 ft_private
->public.ft_cancelled
= sipe_ft_free
;
94 ft_private
->public.ft_end
= sipe_ft_tftp_stop_sending
;
96 ft_private
->invitation_cookie
= g_strdup_printf("%u",
99 return SIPE_FILE_TRANSFER_PUBLIC
;
102 struct sipe_file_transfer
*
103 sipe_core_ft_create_outgoing(struct sipe_core_public
*sipe_public
,
107 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
108 struct sipe_file_transfer
*ft
;
111 if (SIPE_CORE_PRIVATE_FLAG_IS(LYNC2013
)) {
112 ft
= sipe_file_transfer_lync_new_outgoing(sipe_private
);
116 ft
= sipe_file_transfer_new_outgoing(sipe_private
);
120 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't initialize core file "
121 "transfer structure");
125 sipe_backend_ft_outgoing(sipe_public
, ft
, who
, file
);
131 sipe_ft_free(struct sipe_file_transfer
*ft
)
133 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
134 struct sip_dialog
*dialog
= ft_private
->dialog
;
137 dialog
->filetransfers
=
138 g_slist_remove(dialog
->filetransfers
, ft_private
);
140 if (ft
->backend_private
)
141 sipe_backend_ft_deallocate(ft
);
143 if (ft_private
->listendata
)
144 sipe_backend_network_listen_cancel(ft_private
->listendata
);
146 if (ft_private
->cipher_context
)
147 sipe_crypt_ft_destroy(ft_private
->cipher_context
);
149 if (ft_private
->hmac_context
)
150 sipe_digest_ft_destroy(ft_private
->hmac_context
);
152 g_free(ft_private
->invitation_cookie
);
153 g_free(ft_private
->encrypted_outbuf
);
157 static void sipe_ft_request(struct sipe_file_transfer_private
*ft_private
,
160 struct sip_dialog
*dialog
= ft_private
->dialog
;
161 sip_transport_request(ft_private
->sipe_private
,
165 "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n",
172 ft_request_denied(struct sipe_file_transfer
*ft
)
174 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
176 gchar
*body
= g_strdup_printf("Invitation-Command: CANCEL\r\n"
177 "Invitation-Cookie: %s\r\n"
178 "Cancel-Code: REJECT\r\n",
179 ft_private
->invitation_cookie
);
180 sipe_ft_request(ft_private
, body
);
187 send_ft_accept(struct sipe_file_transfer_private
*ft_private
,
188 gboolean send_enc_key
,
189 gboolean send_connect_data
,
190 gboolean sender_connect
)
192 GString
*body
= g_string_new("");
194 g_string_append_printf(body
,
195 "Invitation-Command: ACCEPT\r\n"
196 "Request-Data: IP-Address:\r\n"
197 "Invitation-Cookie: %s\r\n",
198 ft_private
->invitation_cookie
);
201 gchar
*b64_encryption_key
;
204 b64_encryption_key
= g_base64_encode(ft_private
->encryption_key
,
206 b64_hash_key
= g_base64_encode(ft_private
->hash_key
,
209 g_string_append_printf(body
,
210 "Encryption-Key: %s\r\n"
215 g_free(b64_hash_key
);
216 g_free(b64_encryption_key
);
219 if (send_connect_data
) {
220 struct sipe_core_private
*sipe_private
= ft_private
->sipe_private
;
222 g_string_append_printf(body
,
226 "AuthCookie: %u\r\n",
227 sip_transport_ip_address(sipe_private
),
229 ft_private
->auth_cookie
);
232 if (sender_connect
) {
233 g_string_append(body
,
234 "Sender-Connect: TRUE\r\n");
237 sipe_ft_request(ft_private
, body
->str
);
239 g_string_free(body
, TRUE
);
243 listen_socket_created_cb(unsigned short port
, gpointer data
)
245 struct sipe_file_transfer
*ft
= data
;
247 SIPE_FILE_TRANSFER_PRIVATE
->port
= port
;
248 SIPE_FILE_TRANSFER_PRIVATE
->auth_cookie
= rand() % 1000000000;
250 if (sipe_backend_ft_is_incoming(ft
))
251 send_ft_accept(SIPE_FILE_TRANSFER_PRIVATE
, TRUE
, TRUE
, TRUE
);
253 send_ft_accept(SIPE_FILE_TRANSFER_PRIVATE
, FALSE
, TRUE
, FALSE
);
257 client_connected_cb(struct sipe_backend_fd
*fd
, gpointer data
)
259 struct sipe_file_transfer
*ft
= data
;
261 SIPE_FILE_TRANSFER_PRIVATE
->listendata
= NULL
;
263 if (!sipe_backend_fd_is_valid(fd
)) {
264 sipe_backend_ft_error(ft
, _("Socket read failed"));
265 sipe_backend_ft_cancel_local(ft
);
267 sipe_backend_ft_start(ft
, fd
, NULL
, 0);
270 sipe_backend_fd_free(fd
);
274 ft_incoming_init(struct sipe_file_transfer
*ft
,
275 SIPE_UNUSED_PARAMETER
const gchar
*filename
,
276 SIPE_UNUSED_PARAMETER gsize size
,
277 SIPE_UNUSED_PARAMETER
const gchar
*who
)
279 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
281 if (ft_private
->peer_using_nat
) {
282 ft_private
->listendata
=
283 sipe_backend_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
284 SIPE_FT_TCP_PORT_MAX
,
285 listen_socket_created_cb
,
289 send_ft_accept(ft_private
, TRUE
, FALSE
, FALSE
);
294 ft_outgoing_init(struct sipe_file_transfer
*ft
, const gchar
*filename
,
295 gsize size
, const gchar
*who
)
297 struct sipe_file_transfer_private
*ft_private
= SIPE_FILE_TRANSFER_PRIVATE
;
298 struct sipe_core_private
*sipe_private
= ft_private
->sipe_private
;
299 struct sip_dialog
*dialog
;
301 const gchar
*ip
= sip_transport_ip_address(sipe_private
);
302 gchar
*body
= g_strdup_printf("Application-Name: File Transfer\r\n"
303 "Application-GUID: {5D3E02AB-6190-11d3-BBBB-00C04F795683}\r\n"
304 "Invitation-Command: INVITE\r\n"
305 "Invitation-Cookie: %s\r\n"
306 "Application-File: %s\r\n"
307 "Application-FileSize: %" G_GSIZE_FORMAT
"\r\n"
309 "Encryption: R\r\n", // TODO: non encrypted file transfer support
310 ft_private
->invitation_cookie
,
313 sipe_utils_ip_is_private(ip
) ? "Connectivity: N\r\n" : "");
315 struct sip_session
*session
= sipe_session_find_or_add_im(sipe_private
, who
);
318 sipe_session_enqueue_message(session
, body
, "text/x-msmsgsinvite");
320 dialog
= sipe_dialog_find(session
, who
);
321 if (dialog
&& !dialog
->outgoing_invite
) {
322 sipe_im_process_queue(sipe_private
, session
);
323 } else if (!dialog
|| !dialog
->outgoing_invite
) {
324 // Need to send the INVITE to get the outgoing dialog setup
325 sipe_im_invite(sipe_private
, session
, who
, body
, "text/x-msmsgsinvite", NULL
, FALSE
);
326 dialog
= sipe_dialog_find(session
, who
);
329 dialog
->filetransfers
= g_slist_append(dialog
->filetransfers
, ft_private
);
330 ft_private
->dialog
= dialog
;
335 void sipe_ft_incoming_transfer(struct sipe_core_private
*sipe_private
,
336 struct sip_dialog
*dialog
,
339 struct sipe_file_transfer_private
*ft_private
;
342 ft_private
= g_new0(struct sipe_file_transfer_private
, 1);
343 ft_private
->sipe_private
= sipe_private
;
345 ft_private
->public.ft_init
= ft_incoming_init
;
346 ft_private
->public.ft_start
= sipe_ft_tftp_start_receiving
;
347 ft_private
->public.ft_read
= sipe_ft_tftp_read
;
348 ft_private
->public.ft_cancelled
= sipe_ft_free
;
349 ft_private
->public.ft_end
= sipe_ft_tftp_stop_receiving
;
350 ft_private
->public.ft_request_denied
= ft_request_denied
;
352 generate_key(ft_private
->encryption_key
, SIPE_FT_KEY_LENGTH
);
353 generate_key(ft_private
->hash_key
, SIPE_FT_KEY_LENGTH
);
355 ft_private
->invitation_cookie
= g_strdup(sipe_utils_nameval_find(body
, "Invitation-Cookie"));
356 ft_private
->peer_using_nat
= sipe_strequal(sipe_utils_nameval_find(body
, "Connectivity"), "N");
358 ft_private
->dialog
= dialog
;
360 file_size
= g_ascii_strtoull(sipe_utils_nameval_find(body
,
361 "Application-FileSize"),
363 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC
,
364 SIPE_FILE_TRANSFER_PUBLIC
,
366 sipe_utils_nameval_find(body
, "Application-File"),
369 if (ft_private
->public.backend_private
!= NULL
) {
370 ft_private
->dialog
->filetransfers
= g_slist_append(ft_private
->dialog
->filetransfers
, ft_private
);
372 sipe_ft_free(SIPE_FILE_TRANSFER_PUBLIC
);
376 static struct sipe_file_transfer_private
*
377 sipe_find_ft(const struct sip_dialog
*dialog
, const gchar
*inv_cookie
)
379 GSList
*ftlist
= dialog
->filetransfers
;
380 for (; ftlist
!= NULL
; ftlist
= ftlist
->next
) {
381 struct sipe_file_transfer_private
*ft_private
= ftlist
->data
;
382 if (sipe_strequal(ft_private
->invitation_cookie
, inv_cookie
))
388 void sipe_ft_incoming_accept(struct sip_dialog
*dialog
, const GSList
*body
)
390 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
391 struct sipe_file_transfer_private
*ft_private
= sipe_find_ft(dialog
, inv_cookie
);
394 const gchar
*ip
= sipe_utils_nameval_find(body
, "IP-Address");
395 const gchar
*port_str
= sipe_utils_nameval_find(body
, "Port");
396 const gchar
*auth_cookie
= sipe_utils_nameval_find(body
, "AuthCookie");
397 const gchar
*enc_key_b64
= sipe_utils_nameval_find(body
, "Encryption-Key");
398 const gchar
*hash_key_b64
= sipe_utils_nameval_find(body
, "Hash-Key");
401 ft_private
->auth_cookie
= g_ascii_strtoull(auth_cookie
,
405 guchar
*enc_key
= g_base64_decode(enc_key_b64
,
407 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
408 memcpy(ft_private
->encryption_key
,
409 enc_key
, SIPE_FT_KEY_LENGTH
);
411 sipe_ft_raise_error_and_cancel(ft_private
,
412 _("Received encryption key has wrong size."));
420 guchar
*hash_key
= g_base64_decode(hash_key_b64
,
422 if (ret_len
== SIPE_FT_KEY_LENGTH
) {
423 memcpy(ft_private
->hash_key
,
424 hash_key
, SIPE_FT_KEY_LENGTH
);
426 sipe_ft_raise_error_and_cancel(ft_private
,
427 _("Received hash key has wrong size."));
435 if (ip
&& port_str
) {
436 sipe_backend_ft_start(SIPE_FILE_TRANSFER_PUBLIC
, NULL
, ip
,
437 g_ascii_strtoull(port_str
, NULL
, 10));
439 ft_private
->listendata
=
440 sipe_backend_network_listen_range(SIPE_FT_TCP_PORT_MIN
,
441 SIPE_FT_TCP_PORT_MAX
,
442 listen_socket_created_cb
,
445 if (!ft_private
->listendata
)
446 sipe_ft_raise_error_and_cancel(ft_private
,
447 _("Could not create listen socket"));
452 void sipe_ft_incoming_cancel(struct sip_dialog
*dialog
, const GSList
*body
)
454 const gchar
*inv_cookie
= sipe_utils_nameval_find(body
, "Invitation-Cookie");
455 struct sipe_file_transfer_private
*ft_private
= sipe_find_ft(dialog
, inv_cookie
);
458 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER_PUBLIC
);
461 GSList
*sipe_ft_parse_msg_body(const gchar
*body
)
464 gchar
**lines
= g_strsplit(body
, "\r\n", 0);
465 if (sipe_utils_parse_lines(&list
, lines
, ":") == FALSE
) {
466 sipe_utils_nameval_free(list
);