2 * @file sipe-appshare.c
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
29 #include "sipe-appshare.h"
30 #include "sipe-appshare-client.h"
31 #include "sipe-backend.h"
32 #include "sipe-buddy.h"
33 #include "sipe-chat.h"
34 #include "sipe-common.h"
35 #include "sipe-conf.h"
36 #include "sipe-core.h"
37 #include "sipe-core-private.h"
38 #include "sipe-media.h"
40 #include "sipe-schedule.h"
41 #include "sipe-user.h"
42 #include "sipe-utils.h"
45 struct sipe_appshare
{
46 struct sipe_media_stream
*stream
;
49 guint rdp_channel_readable_watch_id
;
50 guint rdp_channel_writable_watch_id
;
51 struct sipe_user_ask_ctx
*ask_ctx
;
53 gchar rdp_channel_buffer
[0x800];
54 gchar
*rdp_channel_buffer_pos
;
55 gsize rdp_channel_buffer_len
;
57 struct sipe_rdp_client client
;
61 sipe_appshare_free(struct sipe_appshare
*appshare
)
63 if (appshare
->rdp_channel_readable_watch_id
!= 0) {
64 g_source_destroy(g_main_context_find_source_by_id(NULL
,
65 appshare
->rdp_channel_readable_watch_id
));
68 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
69 g_source_destroy(g_main_context_find_source_by_id(NULL
,
70 appshare
->rdp_channel_writable_watch_id
));
73 if (appshare
->channel
) {
76 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
78 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
82 g_io_channel_unref(appshare
->channel
);
85 if (appshare
->socket
) {
86 g_object_unref(appshare
->socket
);
89 if (appshare
->ask_ctx
) {
90 sipe_user_close_ask(appshare
->ask_ctx
);
93 g_free(appshare
->client
.cmdline
);
94 if (appshare
->client
.free_cb
) {
95 appshare
->client
.free_cb(&appshare
->client
);
102 rdp_channel_readable_cb(GIOChannel
*channel
,
103 GIOCondition condition
,
106 struct sipe_appshare
*appshare
= data
;
107 GError
*error
= NULL
;
111 if (condition
& G_IO_HUP
) {
112 struct sipe_media_call
*call
= appshare
->stream
->call
;
114 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
118 buffer
= g_malloc(2048);
119 while (sipe_media_stream_is_writable(appshare
->stream
)) {
122 status
= g_io_channel_read_chars(channel
,
124 &bytes_read
, &error
);
126 struct sipe_media_call
*call
= appshare
->stream
->call
;
128 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
131 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
136 if (status
== G_IO_STATUS_EOF
) {
137 struct sipe_media_call
*call
= appshare
->stream
->call
;
139 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
144 if (bytes_read
== 0) {
148 sipe_media_stream_write(appshare
->stream
, (guint8
*)buffer
,
150 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT
"\n", bytes_read
);
158 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
159 SIPE_UNUSED_PARAMETER GIOCondition condition
,
162 struct sipe_appshare
*appshare
= data
;
163 GError
*error
= NULL
;
164 GSocket
*data_socket
;
167 data_socket
= g_socket_accept(appshare
->socket
, NULL
, &error
);
169 struct sipe_media_call
*call
= appshare
->stream
->call
;
171 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
174 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
178 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
180 struct sipe_media_call
*call
= appshare
->stream
->call
;
182 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
185 g_object_unref(data_socket
);
186 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
189 g_io_channel_unref(appshare
->channel
);
191 g_object_unref(appshare
->socket
);
192 appshare
->socket
= data_socket
;
194 fd
= g_socket_get_fd(appshare
->socket
);
196 struct sipe_media_call
*call
= appshare
->stream
->call
;
198 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client connection socket");
199 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
202 appshare
->channel
= g_io_channel_unix_new(fd
);
204 // No encoding for binary data
205 g_io_channel_set_encoding(appshare
->channel
, NULL
, &error
);
207 struct sipe_media_call
*call
= appshare
->stream
->call
;
209 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
212 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
216 appshare
->rdp_channel_readable_watch_id
=
217 g_io_add_watch(appshare
->channel
, G_IO_IN
| G_IO_HUP
,
218 rdp_channel_readable_cb
, appshare
);
224 launch_rdp_client(struct sipe_appshare
*appshare
)
226 struct sipe_rdp_client
*client
= &appshare
->client
;
227 struct sipe_media_call
*call
= appshare
->stream
->call
;
228 GSocketAddress
*address
;
229 GError
*error
= NULL
;
232 address
= client
->get_listen_address_cb(client
);
234 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
238 appshare
->socket
= g_socket_new(g_socket_address_get_family(address
),
239 G_SOCKET_TYPE_STREAM
,
240 G_SOCKET_PROTOCOL_DEFAULT
,
243 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
246 g_object_unref(address
);
247 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
251 g_socket_set_blocking(appshare
->socket
, FALSE
);
253 g_socket_bind(appshare
->socket
, address
, TRUE
, &error
);
254 g_object_unref(address
);
256 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
259 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
263 g_socket_listen(appshare
->socket
, &error
);
265 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
268 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
272 fd
= g_socket_get_fd(appshare
->socket
);
274 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket");
275 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
278 appshare
->channel
= g_io_channel_unix_new(fd
);
280 appshare
->rdp_channel_readable_watch_id
=
281 g_io_add_watch(appshare
->channel
, G_IO_IN
,
282 socket_connect_cb
, appshare
);
284 address
= g_socket_get_local_address(appshare
->socket
, &error
);
286 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
289 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
293 if (!client
->launch_cb(client
, address
, appshare
->stream
)) {
294 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
297 g_object_unref(address
);
301 rdp_client_channel_write(struct sipe_appshare
*appshare
)
304 GError
*error
= NULL
;
306 g_io_channel_write_chars(appshare
->channel
,
307 appshare
->rdp_channel_buffer_pos
,
308 appshare
->rdp_channel_buffer_len
,
309 &bytes_written
, &error
);
311 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
317 g_io_channel_flush(appshare
->channel
, &error
);
319 if (g_error_matches(error
, G_IO_CHANNEL_ERROR
,
320 G_IO_CHANNEL_ERROR_PIPE
)) {
321 /* Ignore broken pipe here and wait for the call to be
322 * hung up upon G_IO_HUP in client_channel_cb(). */
327 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
333 appshare
->rdp_channel_buffer_pos
+= bytes_written
;
334 appshare
->rdp_channel_buffer_len
-= bytes_written
;
336 return bytes_written
;
340 delayed_hangup_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
343 struct sipe_media_call
*call
= data
;
345 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
349 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
350 SIPE_UNUSED_PARAMETER GIOCondition condition
,
353 struct sipe_appshare
*appshare
= data
;
354 struct sipe_media_call
*call
= appshare
->stream
->call
;
356 if (rdp_client_channel_write(appshare
) < 0) {
357 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
361 if (appshare
->rdp_channel_buffer_len
== 0) {
362 // Writing done, disconnect writable watch.
363 appshare
->rdp_channel_writable_watch_id
= 0;
371 read_cb(struct sipe_media_stream
*stream
)
373 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
375 gssize bytes_written
= 0;
377 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
378 // Data still in the buffer. Let the client read it first.
382 while (bytes_read
== (gint
)bytes_written
) {
383 bytes_read
= sipe_backend_media_stream_read(stream
,
384 (guint8
*)appshare
->rdp_channel_buffer
,
385 sizeof (appshare
->rdp_channel_buffer
));
386 if (bytes_read
== 0) {
390 appshare
->rdp_channel_buffer_pos
= appshare
->rdp_channel_buffer
;
391 appshare
->rdp_channel_buffer_len
= bytes_read
;
393 bytes_written
= rdp_client_channel_write(appshare
);
395 if (bytes_written
< 0) {
396 /* Don't deallocate stream while in its read callback.
397 * Schedule call hangup to be executed after we're back
398 * in the message loop. */
399 sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream
->call
),
400 "appshare delayed hangup",
401 stream
->call
->backend_private
,
409 if (bytes_read
!= (gint
)bytes_written
) {
410 /* Schedule writing of the buffer's remainder to when
411 * RDP channel becomes writable again. */
412 appshare
->rdp_channel_writable_watch_id
=
413 g_io_add_watch(appshare
->channel
, G_IO_OUT
,
414 rdp_channel_writable_cb
,
420 writable_cb(struct sipe_media_stream
*stream
)
422 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
424 if (!appshare
->socket
) {
425 launch_rdp_client(appshare
);
430 accept_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
433 struct sipe_appshare
*appshare
= data
;
434 appshare
->ask_ctx
= NULL
;
436 sipe_backend_media_accept(appshare
->stream
->call
->backend_private
, TRUE
);
440 decline_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
443 struct sipe_appshare
*appshare
= data
;
444 appshare
->ask_ctx
= NULL
;
446 sipe_backend_media_hangup(appshare
->stream
->call
->backend_private
, TRUE
);
449 static struct sipe_user_ask_ctx
*
450 ask_accept_applicationsharing(struct sipe_core_private
*sipe_private
,
452 SipeUserAskCb accept_cb
,
453 SipeUserAskCb decline_cb
,
456 struct sipe_user_ask_ctx
*ctx
;
457 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, from
);
458 gchar
*ask_msg
= g_strdup_printf(_("%s wants to start presenting"),
459 alias
? alias
: from
);
461 ctx
= sipe_user_ask(sipe_private
, ask_msg
,
462 _("Accept"), accept_cb
,
463 _("Decline"), decline_cb
,
472 static struct sipe_appshare
*
473 initialize_appshare(struct sipe_media_stream
*stream
)
475 struct sipe_appshare
*appshare
;
476 struct sipe_media_call
*call
;
477 struct sipe_core_private
*sipe_private
;
478 const gchar
*cmdline
;
481 sipe_private
= sipe_media_get_sipe_core_private(call
);
483 appshare
= g_new0(struct sipe_appshare
, 1);
484 appshare
->stream
= stream
;
486 sipe_media_stream_set_data(stream
, appshare
,
487 (GDestroyNotify
)sipe_appshare_free
);
489 cmdline
= sipe_backend_setting(SIPE_CORE_PUBLIC
,
490 SIPE_SETTING_RDP_CLIENT
);
491 if (is_empty(cmdline
))
493 appshare
->client
.cmdline
= g_strdup(cmdline
);
495 if (strstr(cmdline
, "xfreerdp")) {
496 sipe_appshare_xfreerdp_init(&appshare
->client
);
497 } else if (strstr(cmdline
, "remmina")) {
498 sipe_appshare_remmina_init(&appshare
->client
);
500 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
501 _("Application sharing error"),
502 _("Unknown remote desktop client configured."));
503 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
507 sipe_media_stream_add_extra_attribute(stream
,
508 "x-applicationsharing-session-id", "1");
509 sipe_media_stream_add_extra_attribute(stream
,
510 "x-applicationsharing-role", "viewer");
511 sipe_media_stream_add_extra_attribute(stream
,
512 "x-applicationsharing-media-type", "rdp");
514 stream
->read_cb
= read_cb
;
515 stream
->writable_cb
= writable_cb
;
521 process_incoming_invite_appshare(struct sipe_core_private
*sipe_private
,
524 struct sipe_media_call
*call
;
525 struct sipe_media_stream
*stream
;
526 struct sipe_appshare
*appshare
;
527 struct sdpmsg
*sdpmsg
;
530 sdpmsg
= sdpmsg_parse_msg(msg
->body
);
532 /* Skype for Business compatibility - ignore desktop video. */
535 struct sdpmedia
*media
= i
->data
;
540 label
= sipe_utils_nameval_find(media
->attributes
, "label");
542 if (sipe_strequal(media
->name
, "video") &&
543 sipe_strequal(label
, "applicationsharing-video")) {
544 sdpmsg
->media
= g_slist_remove(sdpmsg
->media
, media
);
545 sdpmedia_free(media
);
549 call
= process_incoming_invite_call_parsed_sdp(sipe_private
,
556 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
558 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
562 appshare
= initialize_appshare(stream
);
567 from
= parse_from(sipmsg_find_header(msg
, "From"));
568 appshare
->ask_ctx
= ask_accept_applicationsharing(sipe_private
, from
,
577 connect_conference(struct sipe_core_private
*sipe_private
,
578 struct sipe_chat_session
*chat_session
)
580 struct sipe_media_call
*call
;
581 struct sipe_media_stream
*stream
;
584 chat_session
->appshare_ask_ctx
= NULL
;
586 uri
= sipe_conf_build_uri(chat_session
->id
, "applicationsharing");
588 call
= sipe_media_call_new(sipe_private
, uri
, NULL
,
590 SIPE_MEDIA_CALL_NO_UI
);
594 stream
= sipe_media_stream_add(call
, "applicationsharing",
595 SIPE_MEDIA_APPLICATION
,
596 SIPE_ICE_RFC_5245
, TRUE
, 0);
598 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
599 _("Application sharing error"),
600 _("Couldn't connect application sharing"));
601 sipe_backend_media_hangup(call
->backend_private
, FALSE
);
604 sipe_media_stream_add_extra_attribute(stream
, "connection", "new");
605 sipe_media_stream_add_extra_attribute(stream
, "setup", "active");
607 initialize_appshare(stream
);
611 sipe_core_appshare_connect_conference(struct sipe_core_public
*sipe_public
,
612 struct sipe_chat_session
*chat_session
,
613 gboolean user_must_accept
)
615 if (user_must_accept
) {
618 if (chat_session
->appshare_ask_ctx
) {
619 // Accept dialog already opened.
623 if (chat_session
->title
) {
624 from
= chat_session
->title
;
625 } else if (chat_session
->organizer
) {
626 from
= chat_session
->organizer
;
628 from
= chat_session
->id
;
631 chat_session
->appshare_ask_ctx
=
632 ask_accept_applicationsharing(SIPE_CORE_PRIVATE
,
634 (SipeUserAskCb
)connect_conference
,
638 connect_conference(SIPE_CORE_PRIVATE
, chat_session
);