media: split out SDP parsing
[siplcs/bazlsi01.git] / src / core / sipe-ft-lync.c
blobda3b975c76adb9788cd672c11055c150174f4a6a
1 /**
2 * @file sipe-ft-lync.c
4 * pidgin-sipe
6 * Copyright (C) 2014-2017 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"
49 #include "sdpmsg.h"
51 struct sipe_file_transfer_lync {
52 struct sipe_file_transfer public;
54 gchar *sdp;
55 gchar *file_name;
56 gchar *id;
57 gsize file_size;
58 guint request_id;
60 guint bytes_left_in_chunk;
62 guint8 buffer[2048];
63 guint buffer_len;
64 guint buffer_read_pos;
66 int backend_pipe[2];
67 int backend_pipe_write_source_id;
69 struct sipe_core_private *sipe_private;
70 struct sipe_media_call *call;
72 void (*call_reject_parent_cb)(struct sipe_media_call *call,
73 gboolean local);
75 #define SIPE_FILE_TRANSFER ((struct sipe_file_transfer *) ft_private)
76 #define SIPE_FILE_TRANSFER_PRIVATE ((struct sipe_file_transfer_lync *) ft)
78 typedef enum {
79 SIPE_XDATA_DATA_CHUNK = 0x00,
80 SIPE_XDATA_START_OF_STREAM = 0x01,
81 SIPE_XDATA_END_OF_STREAM = 0x02
82 } SipeXDataMessages;
84 #define XDATA_HEADER_SIZE sizeof (guint8) + sizeof (guint16)
86 static void
87 sipe_file_transfer_lync_free(struct sipe_file_transfer_lync *ft_private)
89 int our_pipe_end;
91 our_pipe_end = sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER) ? 1 : 0;
93 if (ft_private->backend_pipe[our_pipe_end] != 0) {
94 // Backend is responsible for closing the pipe's other end.
95 close(ft_private->backend_pipe[our_pipe_end]);
98 g_free(ft_private->file_name);
99 g_free(ft_private->sdp);
100 g_free(ft_private->id);
102 if (ft_private->backend_pipe_write_source_id) {
103 g_source_remove(ft_private->backend_pipe_write_source_id);
106 g_free(ft_private);
109 static void
110 send_ms_filetransfer_msg(char *body, struct sipe_file_transfer_lync *ft_private,
111 TransCallback callback)
113 sip_transport_info(sipe_media_get_sipe_core_private(ft_private->call),
114 "Content-Type: application/ms-filetransfer+xml\r\n",
115 body,
116 sipe_media_get_sip_dialog(ft_private->call),
117 callback);
119 g_free(body);
122 static void
123 send_ms_filetransfer_response(struct sipe_file_transfer_lync *ft_private,
124 const gchar *code, const gchar *reason,
125 TransCallback callback)
127 static const gchar *RESPONSE_STR =
128 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\" code=\"%s\" %s%s%s/>";
130 send_ms_filetransfer_msg(g_strdup_printf(RESPONSE_STR,
131 ft_private->request_id, code,
132 reason ? "reason=\"" : "",
133 reason ? reason : "",
134 reason ? "\"" : ""),
135 ft_private, callback);
138 static void
139 mime_mixed_cb(gpointer user_data, const GSList *fields, const gchar *body,
140 gsize length)
142 struct sipe_file_transfer_lync *ft_private = user_data;
143 const gchar *ctype = sipe_utils_nameval_find(fields, "Content-Type");
145 /* Lync 2010 file transfer */
146 if (g_str_has_prefix(ctype, "application/ms-filetransfer+xml")) {
147 sipe_xml *xml = sipe_xml_parse(body, length);
149 if (xml) {
150 const sipe_xml *node;
152 ft_private->request_id = sipe_xml_int_attribute(xml,
153 "requestId",
154 ft_private->request_id);
156 node = sipe_xml_child(xml, "publishFile/fileInfo/name");
157 if (node) {
158 g_free(ft_private->file_name);
159 ft_private->file_name = sipe_xml_data(node);
162 node = sipe_xml_child(xml, "publishFile/fileInfo/id");
163 if (node) {
164 g_free(ft_private->id);
165 ft_private->id = sipe_xml_data(node);
168 node = sipe_xml_child(xml, "publishFile/fileInfo/size");
169 if (node) {
170 gchar *size_str = sipe_xml_data(node);
171 if (size_str) {
172 ft_private->file_size = atoi(size_str);
173 g_free(size_str);
177 sipe_xml_free(xml);
179 } else if (g_str_has_prefix(ctype, "application/sdp")) {
180 g_free(ft_private->sdp);
181 ft_private->sdp = g_strndup(body, length);
185 static void
186 candidate_pairs_established_cb(struct sipe_media_stream *stream)
188 struct sipe_file_transfer_lync *ft_private;
189 static const gchar *DOWNLOAD_FILE_REQUEST =
190 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\">"
191 "<downloadFile>"
192 "<fileInfo>"
193 "<id>%s</id>"
194 "<name>%s</name>"
195 "</fileInfo>"
196 "</downloadFile>"
197 "</request>";
199 g_return_if_fail(sipe_strequal(stream->id, "data"));
201 ft_private = sipe_media_stream_get_data(stream);
203 send_ms_filetransfer_response(ft_private, "success", NULL, NULL);
205 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_FILE_REQUEST,
206 ++ft_private->request_id,
207 ft_private->id,
208 ft_private->file_name),
209 ft_private, NULL);
212 static gboolean
213 create_pipe(int pipefd[2])
215 #ifdef _WIN32
216 #error "Pipes not implemented for Windows"
217 /* Those interested in porting the code may use Pidgin's wpurple_input_pipe() in
218 * win32dep.c as an inspiration. */
219 #else
220 if (pipe(pipefd) != 0) {
221 return FALSE;
224 /* @TODO: ignoring potential error return - how to handle? */
225 (void) fcntl(pipefd[0], F_SETFL, fcntl(pipefd[0], F_GETFL) | O_NONBLOCK);
226 (void) fcntl(pipefd[1], F_SETFL, fcntl(pipefd[1], F_GETFL) | O_NONBLOCK);
228 return TRUE;
229 #endif
232 static void
233 xdata_start_of_stream_cb(struct sipe_media_stream *stream,
234 guint8 *buffer, gsize len)
236 struct sipe_file_transfer_lync *ft_private =
237 sipe_media_stream_get_data(stream);
238 struct sipe_backend_fd *fd;
240 buffer[len] = 0;
241 SIPE_DEBUG_INFO("Received new stream for requestId : %s", buffer);
243 if (!create_pipe(ft_private->backend_pipe)) {
244 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe");
245 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
246 return;
249 fd = sipe_backend_fd_from_int(ft_private->backend_pipe[0]);
250 sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0);
251 sipe_backend_fd_free(fd);
254 static void
255 xdata_end_of_stream_cb(SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream,
256 guint8 *buffer, gsize len)
258 buffer[len] = 0;
259 SIPE_DEBUG_INFO("Received end of stream for requestId : %s", buffer);
262 static void
263 xdata_got_header_cb(struct sipe_media_stream *stream,
264 guint8 *buffer,
265 SIPE_UNUSED_PARAMETER gsize len)
267 struct sipe_file_transfer_lync *ft_private =
268 sipe_media_stream_get_data(stream);
270 guint8 type = buffer[0];
271 guint16 size = (buffer[1] << 8) + buffer[2]; /* stored as big-endian */
273 switch (type) {
274 case SIPE_XDATA_START_OF_STREAM:
275 sipe_media_stream_read_async(stream,
276 ft_private->buffer, size,
277 xdata_start_of_stream_cb);
278 break;
279 case SIPE_XDATA_DATA_CHUNK:
280 SIPE_DEBUG_INFO("Received new data chunk of size %d",
281 size);
282 ft_private->bytes_left_in_chunk = size;
283 break;
284 /* We'll read the data when read_cb is called again. */
285 case SIPE_XDATA_END_OF_STREAM:
286 sipe_media_stream_read_async(stream,
287 ft_private->buffer, size,
288 xdata_end_of_stream_cb);
289 break;
293 static void
294 read_cb(struct sipe_media_stream *stream)
296 struct sipe_file_transfer_lync *ft_private =
297 sipe_media_stream_get_data(stream);
299 if (ft_private->buffer_read_pos < ft_private->buffer_len) {
300 /* Have data in buffer, write them to the backend. */
302 gpointer buffer;
303 size_t len;
304 ssize_t written;
306 buffer = ft_private->buffer + ft_private->buffer_read_pos;
307 len = ft_private->buffer_len - ft_private->buffer_read_pos;
308 written = write(ft_private->backend_pipe[1], buffer, len);
310 if (written > 0) {
311 ft_private->buffer_read_pos += written;
312 } else if (written < 0 && errno != EAGAIN) {
313 SIPE_DEBUG_ERROR_NOFORMAT("Error while writing into "
314 "backend pipe");
315 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
316 return;
318 } else if (ft_private->bytes_left_in_chunk != 0) {
319 /* Have data from the sender, replenish our buffer with it. */
321 ft_private->buffer_len = MIN(ft_private->bytes_left_in_chunk,
322 sizeof (ft_private->buffer));
324 ft_private->buffer_len =
325 sipe_backend_media_stream_read(stream,
326 ft_private->buffer,
327 ft_private->buffer_len);
329 ft_private->bytes_left_in_chunk -= ft_private->buffer_len;
330 ft_private->buffer_read_pos = 0;
332 SIPE_DEBUG_INFO("Read %d bytes. %d left in this chunk.",
333 ft_private->buffer_len, ft_private->bytes_left_in_chunk);
334 } else {
335 /* No data available. This is either stream start, beginning of
336 * chunk, or stream end. */
338 sipe_media_stream_read_async(stream, ft_private->buffer,
339 XDATA_HEADER_SIZE,
340 xdata_got_header_cb);
344 static void
345 ft_lync_incoming_init(struct sipe_file_transfer *ft,
346 SIPE_UNUSED_PARAMETER const gchar *filename,
347 SIPE_UNUSED_PARAMETER gsize size,
348 SIPE_UNUSED_PARAMETER const gchar *who)
350 struct sipe_media_call *call = SIPE_FILE_TRANSFER_PRIVATE->call;
352 if (call) {
353 sipe_backend_media_accept(call->backend_private, TRUE);
357 static void
358 ft_lync_request_denied(struct sipe_file_transfer *ft)
360 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
361 struct sipe_media_call *call;
363 g_return_if_fail(ft_private);
365 call = ft_private->call;
367 if (call && call->backend_private) {
368 sipe_backend_media_reject(call->backend_private, TRUE);
372 static struct sipe_file_transfer_lync *
373 ft_private_from_call(struct sipe_media_call *call)
375 struct sipe_media_stream *stream =
376 sipe_core_media_get_stream_by_id(call, "data");
377 g_return_val_if_fail(stream, NULL);
379 return sipe_media_stream_get_data(stream);
382 static void
383 send_transfer_progress(struct sipe_file_transfer_lync *ft_private)
385 static const gchar *FILETRANSFER_PROGRESS =
386 "<notify xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" notifyId=\"%d\">"
387 "<fileTransferProgress>"
388 "<transferId>%d</transferId>"
389 "<bytesReceived>"
390 "<from>0</from>"
391 "<to>%d</to>"
392 "</bytesReceived>"
393 "</fileTransferProgress>"
394 "</notify>";
396 send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_PROGRESS,
397 rand(),
398 ft_private->request_id,
399 ft_private->file_size - 1),
400 ft_private, NULL);
403 static gboolean
404 ft_lync_end(struct sipe_file_transfer *ft)
406 send_transfer_progress(SIPE_FILE_TRANSFER_PRIVATE);
408 return TRUE;
411 static void
412 call_reject_cb(struct sipe_media_call *call, gboolean local)
414 struct sipe_file_transfer_lync *ft_private = ft_private_from_call(call);
415 g_return_if_fail(ft_private);
417 if (ft_private->call_reject_parent_cb) {
418 ft_private->call_reject_parent_cb(call, local);
421 if (!local) {
422 sipe_backend_ft_cancel_remote(&ft_private->public);
426 static void
427 ft_lync_incoming_cancelled(struct sipe_file_transfer *ft)
429 static const gchar *FILETRANSFER_CANCEL_REQUEST =
430 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" requestId=\"%d\"/>"
431 "<cancelTransfer>"
432 "<transferId>%d</transferId>"
433 "<fileInfo>"
434 "<id>%s</id>"
435 "<name>%s</name>"
436 "</fileInfo>"
437 "</cancelTransfer>"
438 "</request>";
440 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
441 struct sipe_media_stream *stream;
443 send_ms_filetransfer_msg(g_strdup_printf(FILETRANSFER_CANCEL_REQUEST,
444 ft_private->request_id + 1,
445 ft_private->request_id,
446 ft_private->id,
447 ft_private->file_name),
448 ft_private,
449 NULL);
451 stream = sipe_core_media_get_stream_by_id(ft_private->call, "data");
452 if (stream) {
453 stream->read_cb = NULL;
456 sipe_backend_media_hangup(ft_private->call->backend_private, FALSE);
459 void
460 process_incoming_invite_ft_lync(struct sipe_core_private *sipe_private,
461 struct sipmsg *msg)
463 struct sipe_file_transfer_lync *ft_private;
464 struct sipe_media_stream *stream;
466 ft_private = g_new0(struct sipe_file_transfer_lync, 1);
467 sipe_mime_parts_foreach(sipmsg_find_header(msg, "Content-Type"),
468 msg->body, mime_mixed_cb, ft_private);
470 if (!ft_private->file_name || !ft_private->file_size || !ft_private->sdp) {
471 sip_transport_response(sipe_private, msg, 488, "Not Acceptable Here", NULL);
472 sipe_file_transfer_lync_free(ft_private);
473 return;
476 /* Use the selected SDP part of multipart SIP message to initialize
477 * media session. */
478 ft_private->call = process_incoming_invite_call(sipe_private, msg,
479 sdpmsg_parse_msg(ft_private->sdp));
480 g_free(ft_private->sdp);
481 ft_private->sdp = NULL;
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 ft_private->public.ft_init = ft_lync_incoming_init;
490 ft_private->public.ft_request_denied = ft_lync_request_denied;
491 ft_private->public.ft_cancelled = ft_lync_incoming_cancelled;
492 ft_private->public.ft_end = ft_lync_end;
494 ft_private->call_reject_parent_cb = ft_private->call->call_reject_cb;
495 ft_private->call->call_reject_cb = call_reject_cb;
497 stream = sipe_core_media_get_stream_by_id(ft_private->call, "data");
498 if (stream) {
499 stream->candidate_pairs_established_cb = candidate_pairs_established_cb;
500 stream->read_cb = read_cb;
501 sipe_media_stream_add_extra_attribute(stream, "recvonly", NULL);
502 sipe_media_stream_set_data(stream, ft_private,
503 (GDestroyNotify)sipe_file_transfer_lync_free);
505 sipe_backend_ft_incoming(SIPE_CORE_PUBLIC, SIPE_FILE_TRANSFER,
506 ft_private->call->with,
507 ft_private->file_name,
508 ft_private->file_size);
509 } else {
510 sip_transport_response(sipe_private, msg, 500, "Server Internal Error", NULL);
511 sipe_file_transfer_lync_free(ft_private);
512 return;
516 static void
517 process_response_incoming(struct sipe_file_transfer_lync *ft_private,
518 sipe_xml *xml)
520 const gchar *attr;
521 guint request_id = sipe_xml_int_attribute(xml, "requestId", 0);
523 if (request_id != ft_private->request_id) {
524 return;
527 attr = sipe_xml_attribute(xml, "code");
528 if (sipe_strequal(attr, "failure")) {
529 const gchar *reason = sipe_xml_attribute(xml, "reason");
530 if (sipe_strequal(reason, "requestCancelled")) {
531 sipe_backend_ft_cancel_remote(SIPE_FILE_TRANSFER);
536 static void
537 write_chunk(struct sipe_media_stream *stream,
538 guint8 type, guint16 len, const gchar *buffer)
540 guint16 len_be = GUINT16_TO_BE(len);
542 sipe_media_stream_write(stream, &type, sizeof (guint8));
543 sipe_media_stream_write(stream, (guint8 *)&len_be, sizeof (guint16));
544 sipe_media_stream_write(stream, (guint8 *)buffer, len);
547 static gboolean
548 send_file_chunk(SIPE_UNUSED_PARAMETER GIOChannel *source,
549 SIPE_UNUSED_PARAMETER GIOCondition condition,
550 gpointer data)
552 struct sipe_file_transfer_lync *ft_private = data;
553 struct sipe_media_call *call = ft_private->call;
554 struct sipe_media_stream *stream;
555 gssize bytes_read;
557 stream = sipe_core_media_get_stream_by_id(call, "data");
558 if (!stream) {
559 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't find data stream");
560 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
561 ft_private->backend_pipe_write_source_id = 0;
562 return FALSE; /* G_SOURCE_REMOVE */
565 if (!sipe_media_stream_is_writable(stream)) {
566 return TRUE; /* G_SOURCE_CONTINUE */
569 bytes_read = read(ft_private->backend_pipe[0],
570 ft_private->buffer, sizeof (ft_private->buffer));
571 if (bytes_read > 0) {
572 write_chunk(stream, SIPE_XDATA_DATA_CHUNK,
573 bytes_read, (const gchar *)ft_private->buffer);
574 } else if (bytes_read == 0) {
575 /* EOF, write end of stream */
576 gchar *request_id_str;
578 request_id_str = g_strdup_printf("%u", ft_private->request_id);
579 write_chunk(stream, SIPE_XDATA_END_OF_STREAM,
580 strlen(request_id_str), request_id_str);
581 g_free(request_id_str);
583 return FALSE; /* G_SOURCE_REMOVE */
586 return TRUE; /* G_SOURCE_CONTINUE */
589 static void
590 start_writing(struct sipe_file_transfer_lync *ft_private)
592 struct sipe_media_stream *stream;
593 gchar *request_id_str;
594 struct sipe_backend_fd *fd;
595 GIOChannel *channel;
597 stream = sipe_core_media_get_stream_by_id(ft_private->call, "data");
598 if (!stream) {
599 return;
602 if (!create_pipe(ft_private->backend_pipe)) {
603 SIPE_DEBUG_ERROR_NOFORMAT("Couldn't create backend pipe");
604 sipe_backend_ft_cancel_local(SIPE_FILE_TRANSFER);
605 return;
608 request_id_str = g_strdup_printf("%u", ft_private->request_id);
609 write_chunk(stream, SIPE_XDATA_START_OF_STREAM,
610 strlen(request_id_str), request_id_str);
611 g_free(request_id_str);
613 channel = g_io_channel_unix_new(ft_private->backend_pipe[0]);
614 ft_private->backend_pipe_write_source_id = g_io_add_watch(channel,
615 G_IO_IN | G_IO_HUP,
616 send_file_chunk,
617 ft_private);
618 g_io_channel_unref(channel);
620 fd = sipe_backend_fd_from_int(ft_private->backend_pipe[1]);
621 sipe_backend_ft_start(SIPE_FILE_TRANSFER, fd, NULL, 0);
622 sipe_backend_fd_free(fd);
625 static void
626 process_request(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml)
628 static const gchar *DOWNLOAD_PENDING_RESPONSE =
629 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
630 "requestId=\"%u\" code=\"pending\"/>";
632 if (sipe_xml_child(xml, "downloadFile")) {
633 ft_private->request_id =
634 atoi(sipe_xml_attribute(xml, "requestId"));
636 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_PENDING_RESPONSE,
637 ft_private->request_id),
638 ft_private, NULL);
640 start_writing(ft_private);
644 static void
645 process_notify(struct sipe_file_transfer_lync *ft_private, sipe_xml *xml)
647 static const gchar *DOWNLOAD_SUCCESS_RESPONSE =
648 "<response xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
649 "requestId=\"%u\" code=\"success\"/>";
651 const sipe_xml *progress_node = sipe_xml_child(xml, "fileTransferProgress");
653 if (progress_node) {
654 gchar *to_str = sipe_xml_data(sipe_xml_child(progress_node, "bytesReceived/to"));
656 if (atoi(to_str) == (int)(ft_private->file_size - 1)) {
657 send_ms_filetransfer_msg(g_strdup_printf(DOWNLOAD_SUCCESS_RESPONSE,
658 ft_private->request_id),
659 ft_private, NULL);
660 sipe_backend_media_hangup(ft_private->call->backend_private, TRUE);
662 g_free(to_str);
666 void
667 process_incoming_info_ft_lync(struct sipe_core_private *sipe_private,
668 struct sipmsg *msg)
670 struct sipe_media_call *call;
671 struct sipe_file_transfer_lync *ft_private;
672 sipe_xml *xml;
674 call = g_hash_table_lookup(sipe_private->media_calls,
675 sipmsg_find_header(msg, "Call-ID"));
676 if (!call) {
677 return;
680 ft_private = ft_private_from_call(call);
681 if (!ft_private) {
682 return;
685 xml = sipe_xml_parse(msg->body, msg->bodylen);
686 if (!xml) {
687 return;
690 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
692 if (sipe_backend_ft_is_incoming(SIPE_FILE_TRANSFER)) {
693 if (sipe_strequal(sipe_xml_name(xml), "response")) {
694 process_response_incoming(ft_private, xml);
696 } else {
697 if (sipe_strequal(sipe_xml_name(xml), "request")) {
698 process_request(ft_private, xml);
699 } else if (sipe_strequal(sipe_xml_name(xml), "notify")) {
700 process_notify(ft_private, xml);
704 sipe_xml_free(xml);
707 static void
708 append_publish_file_invite(struct sipe_media_call *call,
709 struct sipe_file_transfer_lync *ft_private)
711 static const gchar *PUBLISH_FILE_REQUEST =
712 "Content-Type: application/ms-filetransfer+xml\r\n"
713 "Content-Transfer-Encoding: 7bit\r\n"
714 "Content-Disposition: render; handling=optional\r\n"
715 "\r\n"
716 "<request xmlns=\"http://schemas.microsoft.com/rtc/2009/05/filetransfer\" "
717 "requestId=\"%u\">"
718 "<publishFile>"
719 "<fileInfo>"
720 "<id>{6244F934-2EB1-443F-8E2C-48BA64AF463D}</id>"
721 "<name>%s</name>"
722 "<size>%u</size>"
723 "</fileInfo>"
724 "</publishFile>"
725 "</request>\r\n";
726 gchar *body;
728 ft_private->request_id =
729 ++ft_private->sipe_private->ms_filetransfer_request_id;
731 body = g_strdup_printf(PUBLISH_FILE_REQUEST, ft_private->request_id,
732 ft_private->file_name, ft_private->file_size);
734 sipe_media_add_extra_invite_section(call, "multipart/mixed", body);
737 static void
738 ft_lync_outgoing_init(struct sipe_file_transfer *ft, const gchar *filename,
739 gsize size, SIPE_UNUSED_PARAMETER const gchar *who)
741 struct sipe_core_private *sipe_private =
742 SIPE_FILE_TRANSFER_PRIVATE->sipe_private;
743 struct sipe_file_transfer_lync *ft_private = SIPE_FILE_TRANSFER_PRIVATE;
744 struct sipe_media_call *call;
745 struct sipe_media_stream *stream;
747 ft_private->file_name = g_strdup(filename);
748 ft_private->file_size = size;
750 call = sipe_media_call_new(sipe_private, who, NULL, SIPE_ICE_RFC_5245,
751 SIPE_MEDIA_CALL_NO_UI);
753 ft_private->call = call;
755 ft_private->call_reject_parent_cb = call->call_reject_cb;
756 call->call_reject_cb = call_reject_cb;
758 stream = sipe_media_stream_add(call, "data", SIPE_MEDIA_APPLICATION,
759 SIPE_ICE_RFC_5245, TRUE, 0);
760 if (!stream) {
761 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
762 _("Error occurred"),
763 _("Error creating data stream"));
765 sipe_backend_media_hangup(call->backend_private, FALSE);
766 sipe_backend_ft_cancel_local(ft);
767 return;
770 sipe_media_stream_add_extra_attribute(stream, "sendonly", NULL);
771 sipe_media_stream_add_extra_attribute(stream, "mid", "1");
772 sipe_media_stream_set_data(stream, ft,
773 (GDestroyNotify)sipe_file_transfer_lync_free);
774 append_publish_file_invite(call, ft_private);
777 struct sipe_file_transfer *
778 sipe_file_transfer_lync_new_outgoing(struct sipe_core_private *sipe_private)
780 struct sipe_file_transfer_lync *ft_private;
782 ft_private = g_new0(struct sipe_file_transfer_lync, 1);
784 ft_private->sipe_private = sipe_private;
785 ft_private->public.ft_init = ft_lync_outgoing_init;
787 return SIPE_FILE_TRANSFER;
791 Local Variables:
792 mode: c
793 c-file-style: "bsd"
794 indent-tabs-mode: t
795 tab-width: 8
796 End: