filetransfer: fix rejection of outgoing file
[siplcs.git] / src / core / sipe-ft-lync.c
blob0ff11e5968fc3927990622fa78b20ca970972785
1 /**
2 * @file sipe-ft-lync.c
4 * pidgin-sipe
6 * Copyright (C) 2014-2015 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <glib.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #include "sip-transport.h"
38 #include "sipe-backend.h"
39 #include "sipe-common.h"
40 #include "sipe-core.h"
41 #include "sipe-core-private.h"
42 #include "sipe-ft-lync.h"
43 #include "sipe-media.h"
44 #include "sipe-mime.h"
45 #include "sipe-nls.h"
46 #include "sipe-utils.h"
47 #include "sipe-xml.h"
48 #include "sipmsg.h"
50 struct sipe_file_transfer_lync {
51 struct sipe_file_transfer public;
53 gchar *sdp;
54 gchar *file_name;
55 gchar *id;
56 gsize file_size;
57 guint request_id;
59 guint bytes_left_in_chunk;
61 guint8 buffer[2048];
62 guint buffer_len;
63 guint buffer_read_pos;
65 int backend_pipe[2];
66 int backend_pipe_write_source_id;
68 struct sipe_core_private *sipe_private;
69 struct sipe_media_call *call;
71 void (*call_reject_parent_cb)(struct sipe_media_call *call,
72 gboolean local);
74 #define SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) ft_private)
75 #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_lync *) ft)
77 typedef enum {
78 SIPE_XDATA_DATA_CHUNK = 0x00,
79 SIPE_XDATA_START_OF_STREAM = 0x01,
80 SIPE_XDATA_END_OF_STREAM = 0x02
81 } SipeXDataMessages;
83 #define XDATA_HEADER_SIZE sizeof (guint8) + sizeof (guint16)
85 static void
86 sipe_file_transfer_lync_free(struct sipe_file_transfer_lync *ft_private)
88 int our_pipe_end;
90 our_pipe_end = sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER) ? 1 : 0;
92 if (ft_private->backend_pipe[our_pipe_end] != 0) {
93 // Backend is responsible for closing the pipe's other end.
94 close(ft_private->backend_pipe[our_pipe_end]);
97 g_free(ft_private->file_name);
98 g_free(ft_private->sdp);
99 g_free(ft_private->id);
101 if (ft_private->backend_pipe_write_source_id) {
102 g_source_remove(ft_private->backend_pipe_write_source_id);
105 g_free(ft_private);
108 static void
109 send_ms_filetransfer_msg(char *body, struct sipe_file_transfer_lync *ft_private,
110 TransCallback callback)
112 sip_transport_info(sipe_media_get_sipe_core_private(ft_private->call),
113 "Content-Type: application/ms-filetransfer+xml\r\n",
114 body,
115 sipe_media_get_sip_dialog(ft_private->call),
116 callback);
118 g_free(body);
121 static void
122 send_ms_filetransfer_response(struct sipe_file_transfer_lync *ft_private,
123 const gchar *code, const gchar *reason,
124 TransCallback callback)
126 static const gchar *RESPONSE_STR =
127 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\" code=\"%s\" %s%s%s/>";
129 send_ms_filetransfer_msg(g_strdup_printf(RESPONSE_STR,
130 ft_private->request_id, code,
131 reason ? "reason=\"" : "",
132 reason ? reason : "",
133 reason ? "\"" : ""),
134 ft_private, callback);
137 static void
138 mime_mixed_cb(gpointer user_data, const GSList *fields, const gchar *body,
139 gsize length)
141 struct sipe_file_transfer_lync *ft_private = user_data;
142 const gchar *ctype = sipe_utils_nameval_find(fields, "Content-Type");
144 /* Lync 2010 file transfer */
145 if (g_str_has_prefix(ctype, "application/ms-filetransfer+xml")) {
146 sipe_xml *xml = sipe_xml_parse(body, length);
148 if (xml) {
149 const sipe_xml *node;
151 ft_private->request_id = sipe_xml_int_attribute(xml,
152 "requestId",
153 ft_private->request_id);
155 node = sipe_xml_child(xml, "publishFile/fileInfo/name");
156 if (node) {
157 g_free(ft_private->file_name);
158 ft_private->file_name = sipe_xml_data(node);
161 node = sipe_xml_child(xml, "publishFile/fileInfo/id");
162 if (node) {
163 g_free(ft_private->id);
164 ft_private->id = sipe_xml_data(node);
167 node = sipe_xml_child(xml, "publishFile/fileInfo/size");
168 if (node) {
169 gchar *size_str = sipe_xml_data(node);
170 if (size_str) {
171 ft_private->file_size = atoi(size_str);
172 g_free(size_str);
176 sipe_xml_free(xml);
178 } else if (g_str_has_prefix(ctype, "application/sdp")) {
179 g_free(ft_private->sdp);
180 ft_private->sdp = g_strndup(body, length);
184 static void
185 candidate_pairs_established_cb(struct sipe_media_stream *stream)
187 struct sipe_file_transfer_lync *ft_private;
188 static const gchar *DOWNLOAD_FILE_REQUEST =
189 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\">"
190 "<downloadFile>"
191 "<fileInfo>"
192 "<id>%s</id>"
193 "<name>%s</name>"
194 "</fileInfo>"
195 "</downloadFile>"
196 "</request>";
198 g_return_if_fail(sipe_strequal(stream->id, "data"));
200 ft_private = sipe_media_stream_get_data(stream);
202 send_ms_filetransfer_response(ft_private, "success", NULL, NULL);
204 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_FILE_REQUEST,
205 ++ft_private->request_id,
206 ft_private->id,
207 ft_private->file_name),
208 ft_private, NULL);
211 static gboolean
212 create_pipe(int pipefd[2])
214 #ifdef _WIN32
215 #error "Pipes not implemented for Windows"
216 /* Those interested in porting the code may use Pidgin's wpurple_input_pipe() in
217 * win32dep.c as an inspiration. */
218 #else
219 if (pipe(pipefd) != 0) {
220 return FALSE;
223 fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK);
224 fcntl(pipefd[1], F_SETFL, fcntl(pipefd[1], F_GETFL) | O_NONBLOCK);
226 return TRUE;
227 #endif
230 static void
231 xdata_start_of_stream_cb(struct sipe_media_stream *stream,
232 guint8 *buffer, gsize len)
234 struct sipe_file_transfer_lync *ft_private =
235 sipe_media_stream_get_data(stream);
236 struct sipe_backend_fd *fd;
238 buffer[len] = 0;
239 SIPE_DEBUG_INFO("Received new stream for requestId : %s", buffer);
241 if (!create_pipe(ft_private->backend_pipe)) {
242 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe");
243 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
244 return;
247 fd = sipe_backend_fd_from_int(ft_private->backend_pipe[0]);
248 sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0);
249 sipe_backend_fd_free(fd);
252 static void
253 xdata_end_of_stream_cb(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream,
254 guint8 *buffer, gsize len)
256 buffer[len] = 0;
257 SIPE_DEBUG_INFO("Received end of stream for requestId : %s", buffer);
260 static void
261 xdata_got_header_cb(struct sipe_media_stream *stream,
262 guint8 *buffer,
263 SIPE_UNUSED_PARAMETER gsize len)
265 struct sipe_file_transfer_lync *ft_private =
266 sipe_media_stream_get_data(stream);
268 guint8 type = buffer[0];
269 guint16 size = (buffer[1] << 8) + buffer[2]; /* stored as big-endian */
271 switch (type) {
272 case SIPE_XDATA_START_OF_STREAM:
273 sipe_media_stream_read_async(stream,
274 ft_private->buffer, size,
275 xdata_start_of_stream_cb);
276 break;
277 case SIPE_XDATA_DATA_CHUNK:
278 SIPE_DEBUG_INFO("Received new data chunk of size %d",
279 size);
280 ft_private->bytes_left_in_chunk = size;
281 break;
282 /* We'll read the data when read_cb is called again. */
283 case SIPE_XDATA_END_OF_STREAM:
284 sipe_media_stream_read_async(stream,
285 ft_private->buffer, size,
286 xdata_end_of_stream_cb);
287 break;
291 static void
292 read_cb(struct sipe_media_stream *stream)
294 struct sipe_file_transfer_lync *ft_private =
295 sipe_media_stream_get_data(stream);
297 if (ft_private->buffer_read_pos < ft_private->buffer_len) {
298 /* Have data in buffer, write them to the backend. */
300 gpointer buffer;
301 size_t len;
302 ssize_t written;
304 buffer = ft_private->buffer + ft_private->buffer_read_pos;
305 len = ft_private->buffer_len - ft_private->buffer_read_pos;
306 written = write(ft_private->backend_pipe[1], buffer, len);
308 if (written > 0) {
309 ft_private->buffer_read_pos += written;
310 } else if (written < 0 && errno != EAGAIN) {
311 SIPE_DEBUG_ERROR_NOFORMAT("Error while writing into "
312 "backend pipe");
313 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
314 return;
316 } else if (ft_private->bytes_left_in_chunk != 0) {
317 /* Have data from the sender, replenish our buffer with it. */
319 ft_private->buffer_len = MIN(ft_private->bytes_left_in_chunk,
320 sizeof (ft_private->buffer));
322 ft_private->buffer_len =
323 sipe_backend_media_stream_read(stream,
324 ft_private->buffer,
325 ft_private->buffer_len);
327 ft_private->bytes_left_in_chunk -= ft_private->buffer_len;
328 ft_private->buffer_read_pos = 0;
330 SIPE_DEBUG_INFO("Read %d bytes. %d left in this chunk.",
331 ft_private->buffer_len, ft_private->bytes_left_in_chunk);
332 } else {
333 /* No data available. This is either stream start, beginning of
334 * chunk, or stream end. */
336 sipe_media_stream_read_async(stream, ft_private->buffer,
337 XDATA_HEADER_SIZE,
338 xdata_got_header_cb);
342 static void
343 ft_lync_incoming_init(struct sipe_file_transfer *ft,
344 SIPE_UNUSED_PARAMETER const gchar *filename,
345 SIPE_UNUSED_PARAMETER gsize size,
346 SIPE_UNUSED_PARAMETER const gchar *who)
348 struct sipe_media_call *call = SIPE_FILE_TRANSFER_PRIVATE->call;
350 if (call) {
351 sipe_backend_media_accept(call->backend_private, TRUE);
355 static void
356 ft_lync_request_denied(struct sipe_file_transfer *ft)
358 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
359 struct sipe_media_call *call;
361 g_return_if_fail(ft_private);
363 call = ft_private->call;
365 if (call && call->backend_private) {
366 sipe_backend_media_reject(call->backend_private, TRUE);
370 static struct sipe_file_transfer_lync *
371 ft_private_from_call(struct sipe_media_call *call)
373 struct sipe_media_stream *stream =
374 sipe_core_media_get_stream_by_id(call, "data");
375 g_return_val_if_fail(stream, NULL);
377 return sipe_media_stream_get_data(stream);
380 static void
381 send_transfer_progress(struct sipe_file_transfer_lync *ft_private)
383 static const gchar *FILETRANSFER_PROGRESS =
384 "<notify xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" notifyId=\"%d\">"
385 "<fileTransferProgress>"
386 "<transferId>%d</transferId>"
387 "<bytesReceived>"
388 "<from>0</from>"
389 "<to>%d</to>"
390 "</bytesReceived>"
391 "</fileTransferProgress>"
392 "</notify>";
394 send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_PROGRESS,
395 rand(),
396 ft_private->request_id,
397 ft_private->file_size - 1),
398 ft_private, NULL);
401 static gboolean
402 ft_lync_end(struct sipe_file_transfer *ft)
404 send_transfer_progress(SIPE_FILE_TRANSFER_PRIVATE);
406 return TRUE;
409 static void
410 call_reject_cb(struct sipe_media_call *call, gboolean local)
412 struct sipe_file_transfer_lync *ft_private = ft_private_from_call(call);
413 g_return_if_fail(ft_private);
415 if (ft_private->call_reject_parent_cb) {
416 ft_private->call_reject_parent_cb(call, local);
419 if (!local) {
420 sipe_backend_ft_cancel_remote(&ft_private->public);
424 static void
425 ft_lync_incoming_cancelled(struct sipe_file_transfer *ft)
427 static const gchar *FILETRANSFER_CANCEL_REQUEST =
428 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\"/>"
429 "<cancelTransfer>"
430 "<transferId>%d</transferId>"
431 "<fileInfo>"
432 "<id>%s</id>"
433 "<name>%s</name>"
434 "</fileInfo>"
435 "</cancelTransfer>"
436 "</request>";
438 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
439 struct sipe_media_stream *stream;
441 send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_CANCEL_REQUEST,
442 ft_private->request_id + 1,
443 ft_private->request_id,
444 ft_private->id,
445 ft_private->file_name),
446 ft_private,
447 NULL);
449 stream = sipe_core_media_get_stream_by_id(ft_private->call, "data");
450 if (stream) {
451 stream->read_cb = NULL;
454 sipe_backend_media_hangup(ft_private->call->backend_private, FALSE);
457 void
458 process_incoming_invite_ft_lync(struct sipe_core_private *sipe_private,
459 struct sipmsg *msg)
461 struct sipe_file_transfer_lync *ft_private;
462 struct sipe_media_call *call;
463 struct sipe_media_stream *stream;
465 ft_private = g_new0(struct sipe_file_transfer_lync, 1);
466 sipe_mime_parts_foreach(sipmsg_find_header(msg, "Content-Type"),
467 msg->body, mime_mixed_cb, ft_private);
469 if (!ft_private->file_name || !ft_private->file_size || !ft_private->sdp) {
470 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
471 sipe_file_transfer_lync_free(ft_private);
472 return;
475 /* Replace multipart message body with the selected SDP part and
476 * initialize media session as if invited to a media call. */
477 g_free(msg->body);
478 msg->body = ft_private->sdp;
479 msg->bodylen = strlen(msg->body);
480 ft_private->sdp = NULL;
482 ft_private->call = process_incoming_invite_call(sipe_private, msg);
483 if (!ft_private->call) {
484 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
485 sipe_file_transfer_lync_free(ft_private);
486 return;
489 call = ft_private->call;
491 ft_private->public.ft_init = ft_lync_incoming_init;
492 ft_private->public.ft_request_denied = ft_lync_request_denied;
493 ft_private->public.ft_cancelled = ft_lync_incoming_cancelled;
494 ft_private->public.ft_end = ft_lync_end;
496 ft_private->call_reject_parent_cb = call->call_reject_cb;
497 call->call_reject_cb = call_reject_cb;
499 stream = sipe_core_media_get_stream_by_id(call, "data");
500 stream->candidate_pairs_established_cb = candidate_pairs_established_cb;
501 stream->read_cb = read_cb;
502 sipe_media_stream_add_extra_attribute(stream, "recvonly", NULL);
503 sipe_media_stream_set_data(stream, ft_private,
504 (GDestroyNotify)sipe_file_transfer_lync_free);
506 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC, SIPE_FILE_TRANSFER,
507 call->with, ft_private->file_name,
508 ft_private->file_size);
511 static void
512 process_response_incoming(struct sipe_file_transfer_lync *ft_private,
513 sipe_xml *xml)
515 const gchar *attr;
516 guint request_id = sipe_xml_int_attribute(xml, "requestId", 0);
518 if (request_id != ft_private->request_id) {
519 return;
522 attr = sipe_xml_attribute(xml, "code");
523 if (sipe_strequal(attr, "failure")) {
524 const gchar *reason = sipe_xml_attribute(xml, "reason");
525 if (sipe_strequal(reason, "requestCancelled")) {
526 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER);
531 static void
532 write_chunk(struct sipe_media_stream *stream,
533 guint8 type, guint16 len, const gchar *buffer)
535 guint16 len_be = GUINT16_TO_BE(len);
537 sipe_media_stream_write(stream, &type, sizeof (guint8));
538 sipe_media_stream_write(stream, (guint8 *)&len_be, sizeof (guint16));
539 sipe_media_stream_write(stream, (guint8 *)buffer, len);
542 static gboolean
543 send_file_chunk(SIPE_UNUSED_PARAMETER GIOChannel *source,
544 SIPE_UNUSED_PARAMETER GIOCondition condition,
545 gpointer data)
547 struct sipe_file_transfer_lync *ft_private = data;
548 struct sipe_media_call *call = ft_private->call;
549 struct sipe_media_stream *stream;
550 gssize bytes_read;
552 stream = sipe_core_media_get_stream_by_id(call, "data");
553 if (!stream) {
554 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't find data stream");
555 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
556 ft_private->backend_pipe_write_source_id = 0;
557 return FALSE; /* G_SOURCE_REMOVE */
560 if (!sipe_media_stream_is_writable(stream)) {
561 return TRUE; /* G_SOURCE_CONTINUE */
564 bytes_read = read(ft_private->backend_pipe[0],
565 ft_private->buffer, sizeof (ft_private->buffer));
566 if (bytes_read > 0) {
567 write_chunk(stream, SIPE_XDATA_DATA_CHUNK,
568 bytes_read, (const gchar *)ft_private->buffer);
569 } else if (bytes_read == 0) {
570 /* EOF, write end of stream */
571 gchar *request_id_str;
573 request_id_str = g_strdup_printf("%u", ft_private->request_id);
574 write_chunk(stream, SIPE_XDATA_END_OF_STREAM,
575 strlen(request_id_str), request_id_str);
576 g_free(request_id_str);
578 return FALSE; /* G_SOURCE_REMOVE */
581 return TRUE; /* G_SOURCE_CONTINUE */
584 static void
585 start_writing(struct sipe_file_transfer_lync *ft_private)
587 struct sipe_media_stream *stream;
588 gchar *request_id_str;
589 struct sipe_backend_fd *fd;
590 GIOChannel *channel;
592 stream = sipe_core_media_get_stream_by_id(ft_private->call, "data");
593 if (!stream) {
594 return;
597 if (!create_pipe(ft_private->backend_pipe)) {
598 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe");
599 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
600 return;
603 request_id_str = g_strdup_printf("%u", ft_private->request_id);
604 write_chunk(stream, SIPE_XDATA_START_OF_STREAM,
605 strlen(request_id_str), request_id_str);
606 g_free(request_id_str);
608 channel = g_io_channel_unix_new(ft_private->backend_pipe[0]);
609 ft_private->backend_pipe_write_source_id = g_io_add_watch(channel,
610 G_IO_IN | G_IO_HUP,
611 send_file_chunk,
612 ft_private);
613 g_io_channel_unref(channel);
615 fd = sipe_backend_fd_from_int(ft_private->backend_pipe[1]);
616 sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0);
617 sipe_backend_fd_free(fd);
620 static void
621 process_request(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml)
623 static const gchar *DOWNLOAD_PENDING_RESPONSE =
624 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
625 "requestId=\"%u\" code=\"pending\"/>";
627 if (sipe_xml_child(xml, "downloadFile")) {
628 ft_private->request_id =
629 atoi(sipe_xml_attribute(xml, "requestId"));
631 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_PENDING_RESPONSE,
632 ft_private->request_id),
633 ft_private, NULL);
635 start_writing(ft_private);
639 static void
640 process_notify(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml)
642 static const gchar *DOWNLOAD_SUCCESS_RESPONSE =
643 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
644 "requestId=\"%u\" code=\"success\"/>";
646 const sipe_xml *progress_node = sipe_xml_child(xml, "fileTransferProgress");
648 if (progress_node) {
649 gchar *to_str = sipe_xml_data(sipe_xml_child(progress_node, "bytesReceived/to"));
651 if (atoi(to_str) == (int)(ft_private->file_size - 1)) {
652 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_SUCCESS_RESPONSE,
653 ft_private->request_id),
654 ft_private, NULL);
655 sipe_backend_media_hangup(ft_private->call->backend_private, TRUE);
657 g_free(to_str);
661 void
662 process_incoming_info_ft_lync(struct sipe_core_private *sipe_private,
663 struct sipmsg *msg)
665 struct sipe_media_call *call;
666 struct sipe_file_transfer_lync *ft_private;
667 sipe_xml *xml;
669 call = g_hash_table_lookup(sipe_private->media_calls,
670 sipmsg_find_header(msg, "Call-ID"));
671 if (!call) {
672 return;
675 ft_private = ft_private_from_call(call);
676 if (!ft_private) {
677 return;
680 xml = sipe_xml_parse(msg->body, msg->bodylen);
681 if (!xml) {
682 return;
685 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
687 if (sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER)) {
688 if (sipe_strequal(sipe_xml_name(xml), "response")) {
689 process_response_incoming(ft_private, xml);
691 } else {
692 if (sipe_strequal(sipe_xml_name(xml), "request")) {
693 process_request(ft_private, xml);
694 } else if (sipe_strequal(sipe_xml_name(xml), "notify")) {
695 process_notify(ft_private, xml);
699 sipe_xml_free(xml);
702 static void
703 append_publish_file_invite(struct sipe_media_call *call,
704 struct sipe_file_transfer_lync *ft_private)
706 static const gchar *PUBLISH_FILE_REQUEST =
707 "Content-Type: application/ms-filetransfer+xml\r\n"
708 "Content-Transfer-Encoding: 7bit\r\n"
709 "Content-Disposition: render; handling=optional\r\n"
710 "\r\n"
711 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
712 "requestId=\"%u\">"
713 "<publishFile>"
714 "<fileInfo>"
715 "<id>{6244F934-2EB1-443F-8E2C-48BA64AF463D}</id>"
716 "<name>%s</name>"
717 "<size>%u</size>"
718 "</fileInfo>"
719 "</publishFile>"
720 "</request>\r\n";
721 gchar *body;
723 ft_private->request_id =
724 ++ft_private->sipe_private->ms_filetransfer_request_id;
726 body = g_strdup_printf(PUBLISH_FILE_REQUEST, ft_private->request_id,
727 ft_private->file_name, ft_private->file_size);
729 sipe_media_add_extra_invite_section(call, "multipart/mixed", body);
732 static void
733 ft_lync_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename,
734 gsize size, SIPE_UNUSED_PARAMETER const gchar *who)
736 struct sipe_core_private *sipe_private =
737 SIPE_FILE_TRANSFER_PRIVATE->sipe_private;
738 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
739 struct sipe_media_call *call;
740 struct sipe_media_stream *stream;
742 ft_private->file_name = g_strdup(filename);
743 ft_private->file_size = size;
745 call = sipe_media_call_new(sipe_private, who, NULL, SIPE_ICE_RFC_5245,
746 SIPE_MEDIA_CALL_NO_UI);
748 ft_private->call = call;
750 ft_private->call_reject_parent_cb = call->call_reject_cb;
751 call->call_reject_cb = call_reject_cb;
753 stream = sipe_media_stream_add(call, "data", SIPE_MEDIA_APPLICATION,
754 SIPE_ICE_RFC_5245, TRUE);
755 if (!stream) {
756 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
757 _("Error occurred"),
758 _("Error creating data stream"));
760 sipe_backend_media_hangup(call->backend_private, FALSE);
761 sipe_backend_ft_cancel_local(ft);
762 return;
765 sipe_media_stream_add_extra_attribute(stream, "sendonly", NULL);
766 sipe_media_stream_add_extra_attribute(stream, "mid", "1");
767 sipe_media_stream_set_data(stream, ft,
768 (GDestroyNotify)sipe_file_transfer_lync_free);
769 append_publish_file_invite(call, ft_private);
772 struct sipe_file_transfer *
773 sipe_file_transfer_lync_new_outgoing(struct sipe_core_private *sipe_private)
775 struct sipe_file_transfer_lync *ft_private;
777 ft_private = g_new0(struct sipe_file_transfer_lync, 1);
779 ft_private->sipe_private = sipe_private;
780 ft_private->public.ft_init = ft_lync_outgoing_init;
782 return SIPE_FILE_TRANSFER;
786 Local Variables:
787 mode: c
788 c-file-style: "bsd"
789 indent-tabs-mode: t
790 tab-width: 8
791 End: