2 * @file sipe-appshare.c
6 * Copyright (C) 2014-2018 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
32 #ifdef HAVE_APPSHARE_SERVER
33 #include <glib/gstdio.h>
35 #include <sys/socket.h>
38 #include <freerdp/server/shadow.h>
39 #endif // HAVE_APPSHARE_SERVER
42 #include "sipe-appshare-client.h"
43 #include "sipe-backend.h"
44 #include "sipe-buddy.h"
45 #include "sipe-chat.h"
46 #include "sipe-common.h"
47 #include "sipe-conf.h"
48 #include "sipe-core.h"
49 #include "sipe-core-private.h"
50 #include "sipe-appshare.h"
51 #include "sipe-media.h"
53 #include "sipe-schedule.h"
54 #include "sipe-user.h"
55 #include "sipe-utils.h"
58 struct sipe_appshare
{
59 struct sipe_media_stream
*stream
;
62 guint rdp_channel_readable_watch_id
;
63 guint rdp_channel_writable_watch_id
;
65 struct sipe_user_ask_ctx
*ask_ctx
;
67 gchar rdp_channel_buffer
[0x800];
68 gchar
*rdp_channel_buffer_pos
;
69 gsize rdp_channel_buffer_len
;
71 struct sipe_rdp_client client
;
73 #ifdef HAVE_APPSHARE_SERVER
74 rdpShadowServer
*server
;
75 #endif // HAVE_APPSHARE_SERVER
79 sipe_appshare_free(struct sipe_appshare
*appshare
)
81 if (appshare
->rdp_channel_readable_watch_id
!= 0) {
82 g_source_destroy(g_main_context_find_source_by_id(NULL
,
83 appshare
->rdp_channel_readable_watch_id
));
86 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
87 g_source_destroy(g_main_context_find_source_by_id(NULL
,
88 appshare
->rdp_channel_writable_watch_id
));
91 if (appshare
->channel
) {
94 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
96 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
100 g_io_channel_unref(appshare
->channel
);
103 if (appshare
->socket
) {
104 g_object_unref(appshare
->socket
);
107 #ifdef HAVE_APPSHARE_SERVER
108 if (appshare
->server
) {
109 if (appshare
->server
->ipcSocket
) {
110 g_unlink(appshare
->server
->ipcSocket
);
113 shadow_server_stop(appshare
->server
);
114 shadow_server_uninit(appshare
->server
);
115 shadow_server_free(appshare
->server
);
117 #endif // HAVE_APPSHARE_SERVER
119 if (appshare
->ask_ctx
) {
120 sipe_user_close_ask(appshare
->ask_ctx
);
123 g_free(appshare
->client
.cmdline
);
124 if (appshare
->client
.free_cb
) {
125 appshare
->client
.free_cb(&appshare
->client
);
132 rdp_channel_readable_cb(GIOChannel
*channel
,
133 GIOCondition condition
,
136 struct sipe_appshare
*appshare
= data
;
137 GError
*error
= NULL
;
141 if (condition
& G_IO_HUP
) {
142 struct sipe_media_call
*call
= appshare
->stream
->call
;
144 SIPE_DEBUG_INFO_NOFORMAT("Received HUP from RDP client.");
145 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
149 buffer
= g_malloc(2048);
150 while (sipe_media_stream_is_writable(appshare
->stream
)) {
153 status
= g_io_channel_read_chars(channel
,
155 &bytes_read
, &error
);
157 struct sipe_media_call
*call
= appshare
->stream
->call
;
159 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
162 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
167 if (status
== G_IO_STATUS_EOF
) {
168 struct sipe_media_call
*call
= appshare
->stream
->call
;
170 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
175 if (bytes_read
== 0) {
179 sipe_media_stream_write(appshare
->stream
, (guint8
*)buffer
,
181 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT
"\n", bytes_read
);
189 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
190 SIPE_UNUSED_PARAMETER GIOCondition condition
,
193 struct sipe_appshare
*appshare
= data
;
194 GError
*error
= NULL
;
195 GSocket
*data_socket
;
198 SIPE_DEBUG_INFO_NOFORMAT("RDP client has connected.");
200 data_socket
= g_socket_accept(appshare
->socket
, NULL
, &error
);
202 struct sipe_media_call
*call
= appshare
->stream
->call
;
204 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
207 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
211 g_io_channel_shutdown(appshare
->channel
, TRUE
, &error
);
213 struct sipe_media_call
*call
= appshare
->stream
->call
;
215 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
218 g_object_unref(data_socket
);
219 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
222 g_io_channel_unref(appshare
->channel
);
224 g_object_unref(appshare
->socket
);
225 appshare
->socket
= data_socket
;
227 fd
= g_socket_get_fd(appshare
->socket
);
229 struct sipe_media_call
*call
= appshare
->stream
->call
;
231 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client connection socket");
232 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
235 appshare
->channel
= g_io_channel_unix_new(fd
);
237 // No encoding for binary data
238 g_io_channel_set_encoding(appshare
->channel
, NULL
, &error
);
240 struct sipe_media_call
*call
= appshare
->stream
->call
;
242 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
245 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
249 appshare
->rdp_channel_readable_watch_id
=
250 g_io_add_watch(appshare
->channel
, G_IO_IN
| G_IO_HUP
,
251 rdp_channel_readable_cb
, appshare
);
257 launch_rdp_client(struct sipe_appshare
*appshare
)
259 struct sipe_rdp_client
*client
= &appshare
->client
;
260 struct sipe_media_call
*call
= appshare
->stream
->call
;
261 GSocketAddress
*address
;
262 GError
*error
= NULL
;
265 address
= client
->get_listen_address_cb(client
);
267 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
271 appshare
->socket
= g_socket_new(g_socket_address_get_family(address
),
272 G_SOCKET_TYPE_STREAM
,
273 G_SOCKET_PROTOCOL_DEFAULT
,
276 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
279 g_object_unref(address
);
280 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
284 g_socket_set_blocking(appshare
->socket
, FALSE
);
286 g_socket_bind(appshare
->socket
, address
, TRUE
, &error
);
287 g_object_unref(address
);
289 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
292 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
296 g_socket_listen(appshare
->socket
, &error
);
298 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
301 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
305 fd
= g_socket_get_fd(appshare
->socket
);
307 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket");
308 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
311 appshare
->channel
= g_io_channel_unix_new(fd
);
313 appshare
->rdp_channel_readable_watch_id
=
314 g_io_add_watch(appshare
->channel
, G_IO_IN
,
315 socket_connect_cb
, appshare
);
317 address
= g_socket_get_local_address(appshare
->socket
, &error
);
319 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
322 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
326 if (!client
->launch_cb(client
, address
, appshare
->stream
)) {
327 SIPE_DEBUG_ERROR_NOFORMAT("Failed to launch RDP client.");
328 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
331 SIPE_DEBUG_INFO_NOFORMAT("RDP client launched.");
333 g_object_unref(address
);
337 rdp_client_channel_write(struct sipe_appshare
*appshare
)
340 GError
*error
= NULL
;
342 g_io_channel_write_chars(appshare
->channel
,
343 appshare
->rdp_channel_buffer_pos
,
344 appshare
->rdp_channel_buffer_len
,
345 &bytes_written
, &error
);
347 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
353 g_io_channel_flush(appshare
->channel
, &error
);
355 if (g_error_matches(error
, G_IO_CHANNEL_ERROR
,
356 G_IO_CHANNEL_ERROR_PIPE
)) {
357 /* Ignore broken pipe here and wait for the call to be
358 * hung up upon G_IO_HUP in client_channel_cb(). */
363 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
369 appshare
->rdp_channel_buffer_pos
+= bytes_written
;
370 appshare
->rdp_channel_buffer_len
-= bytes_written
;
372 return bytes_written
;
376 delayed_hangup_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
379 struct sipe_media_call
*call
= data
;
381 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
385 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel
*channel
,
386 SIPE_UNUSED_PARAMETER GIOCondition condition
,
389 struct sipe_appshare
*appshare
= data
;
390 struct sipe_media_call
*call
= appshare
->stream
->call
;
392 if (rdp_client_channel_write(appshare
) < 0) {
393 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
397 if (appshare
->rdp_channel_buffer_len
== 0) {
398 // Writing done, disconnect writable watch.
399 appshare
->rdp_channel_writable_watch_id
= 0;
407 read_cb(struct sipe_media_stream
*stream
)
409 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
411 gssize bytes_written
= 0;
413 if (appshare
->rdp_channel_writable_watch_id
!= 0) {
414 // Data still in the buffer. Let the client read it first.
418 while (bytes_read
== (gint
)bytes_written
) {
419 bytes_read
= sipe_backend_media_stream_read(stream
,
420 (guint8
*)appshare
->rdp_channel_buffer
,
421 sizeof (appshare
->rdp_channel_buffer
));
422 if (bytes_read
== 0) {
426 appshare
->rdp_channel_buffer_pos
= appshare
->rdp_channel_buffer
;
427 appshare
->rdp_channel_buffer_len
= bytes_read
;
429 bytes_written
= rdp_client_channel_write(appshare
);
431 if (bytes_written
< 0) {
432 /* Don't deallocate stream while in its read callback.
433 * Schedule call hangup to be executed after we're back
434 * in the message loop. */
435 sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream
->call
),
436 "appshare delayed hangup",
437 stream
->call
->backend_private
,
445 if (bytes_read
!= (gint
)bytes_written
) {
446 /* Schedule writing of the buffer's remainder to when
447 * RDP channel becomes writable again. */
448 appshare
->rdp_channel_writable_watch_id
=
449 g_io_add_watch(appshare
->channel
, G_IO_OUT
,
450 rdp_channel_writable_cb
,
456 writable_cb(struct sipe_media_stream
*stream
)
458 struct sipe_appshare
*appshare
= sipe_media_stream_get_data(stream
);
460 if (!appshare
->socket
) {
461 launch_rdp_client(appshare
);
466 accept_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
469 struct sipe_appshare
*appshare
= data
;
470 appshare
->ask_ctx
= NULL
;
472 sipe_backend_media_accept(appshare
->stream
->call
->backend_private
, TRUE
);
476 decline_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
479 struct sipe_appshare
*appshare
= data
;
480 appshare
->ask_ctx
= NULL
;
482 sipe_backend_media_hangup(appshare
->stream
->call
->backend_private
, TRUE
);
485 static struct sipe_user_ask_ctx
*
486 ask_accept_applicationsharing(struct sipe_core_private
*sipe_private
,
488 SipeUserAskCb accept_cb
,
489 SipeUserAskCb decline_cb
,
492 struct sipe_user_ask_ctx
*ctx
;
493 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, from
);
494 gchar
*ask_msg
= g_strdup_printf(_("%s wants to start presenting"),
495 alias
? alias
: from
);
497 ctx
= sipe_user_ask(sipe_private
, ask_msg
,
498 _("Accept"), accept_cb
,
499 _("Decline"), decline_cb
,
508 static struct sipe_appshare
*
509 initialize_appshare(struct sipe_media_stream
*stream
)
511 struct sipe_appshare
*appshare
;
512 struct sipe_media_call
*call
;
513 struct sipe_core_private
*sipe_private
;
514 const gchar
*cmdline
;
517 sipe_private
= sipe_media_get_sipe_core_private(call
);
519 appshare
= g_new0(struct sipe_appshare
, 1);
520 appshare
->stream
= stream
;
522 sipe_media_stream_set_data(stream
, appshare
,
523 (GDestroyNotify
)sipe_appshare_free
);
525 cmdline
= sipe_backend_setting(SIPE_CORE_PUBLIC
,
526 SIPE_SETTING_RDP_CLIENT
);
527 if (is_empty(cmdline
))
529 appshare
->client
.cmdline
= g_strdup(cmdline
);
531 if (strstr(cmdline
, "xfreerdp")) {
532 sipe_appshare_xfreerdp_init(&appshare
->client
);
533 } else if (strstr(cmdline
, "remmina")) {
534 sipe_appshare_remmina_init(&appshare
->client
);
536 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
537 _("Application sharing error"),
538 _("Unknown remote desktop client configured."));
539 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
543 sipe_media_stream_add_extra_attribute(stream
,
544 "x-applicationsharing-session-id", "1");
545 sipe_media_stream_add_extra_attribute(stream
,
546 "x-applicationsharing-role", "viewer");
547 sipe_media_stream_add_extra_attribute(stream
,
548 "x-applicationsharing-media-type", "rdp");
550 stream
->read_cb
= read_cb
;
551 stream
->writable_cb
= writable_cb
;
557 process_incoming_invite_appshare(struct sipe_core_private
*sipe_private
,
560 struct sipe_media_call
*call
;
561 struct sipe_media_stream
*stream
;
562 struct sipe_appshare
*appshare
;
563 struct sdpmsg
*sdpmsg
;
566 sdpmsg
= sdpmsg_parse_msg(msg
->body
);
568 /* Skype for Business compatibility - ignore desktop video. */
571 struct sdpmedia
*media
= i
->data
;
576 label
= sipe_utils_nameval_find(media
->attributes
, "label");
578 if (sipe_strequal(media
->name
, "video") &&
579 sipe_strequal(label
, "applicationsharing-video")) {
580 sdpmsg
->media
= g_slist_remove(sdpmsg
->media
, media
);
581 sdpmedia_free(media
);
585 call
= process_incoming_invite_call_parsed_sdp(sipe_private
,
592 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
594 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
598 appshare
= initialize_appshare(stream
);
603 from
= parse_from(sipmsg_find_header(msg
, "From"));
604 appshare
->ask_ctx
= ask_accept_applicationsharing(sipe_private
, from
,
613 connect_conference(struct sipe_core_private
*sipe_private
,
614 struct sipe_chat_session
*chat_session
)
616 struct sipe_media_call
*call
;
617 struct sipe_media_stream
*stream
;
620 chat_session
->appshare_ask_ctx
= NULL
;
622 uri
= sipe_conf_build_uri(chat_session
->id
, "applicationsharing");
624 call
= sipe_media_call_new(sipe_private
, uri
, NULL
,
626 SIPE_MEDIA_CALL_NO_UI
);
630 stream
= sipe_media_stream_add(call
, "applicationsharing",
631 SIPE_MEDIA_APPLICATION
,
632 SIPE_ICE_RFC_5245
, TRUE
, 0);
634 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
635 _("Application sharing error"),
636 _("Couldn't connect application sharing"));
637 sipe_backend_media_hangup(call
->backend_private
, FALSE
);
640 sipe_media_stream_add_extra_attribute(stream
, "connection", "new");
641 sipe_media_stream_add_extra_attribute(stream
, "setup", "active");
643 initialize_appshare(stream
);
647 sipe_core_appshare_connect_conference(struct sipe_core_public
*sipe_public
,
648 struct sipe_chat_session
*chat_session
,
649 gboolean user_must_accept
)
651 if (user_must_accept
) {
654 if (chat_session
->appshare_ask_ctx
) {
655 // Accept dialog already opened.
659 if (chat_session
->title
) {
660 from
= chat_session
->title
;
661 } else if (chat_session
->organizer
) {
662 from
= chat_session
->organizer
;
664 from
= chat_session
->id
;
667 chat_session
->appshare_ask_ctx
=
668 ask_accept_applicationsharing(SIPE_CORE_PRIVATE
,
670 (SipeUserAskCb
)connect_conference
,
674 connect_conference(SIPE_CORE_PRIVATE
, chat_session
);
679 sipe_appshare_get_role(struct sipe_media_call
*call
)
681 struct sipe_media_stream
*stream
;
683 g_return_val_if_fail(call
, SIPE_APPSHARE_ROLE_NONE
);
685 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
688 struct sipe_appshare
*appshare
;
690 appshare
= sipe_media_stream_get_data(stream
);
692 #ifdef HAVE_APPSHARE_SERVER
693 return appshare
->server
? SIPE_APPSHARE_ROLE_PRESENTER
:
694 SIPE_APPSHARE_ROLE_VIEWER
;
696 return SIPE_APPSHARE_ROLE_VIEWER
;
697 #endif // HAVE_APPSHARE_SERVER
701 return SIPE_APPSHARE_ROLE_NONE
;
704 #ifdef HAVE_APPSHARE_SERVER
706 set_shared_display_area(rdpShadowServer
*server
, guint monitor_id
)
708 if (monitor_id
== 0) {
709 MONITOR_DEF monitors
[16];
713 UINT16 maxHeight
= 0;
715 monitor_count
= shadow_enum_monitors(monitors
, 16);
716 for (i
= 0; i
!= monitor_count
; ++i
) {
717 MONITOR_DEF
*m
= &monitors
[i
];
718 if (m
->right
> maxWidth
) {
721 if (m
->bottom
> maxHeight
) {
722 maxHeight
= m
->bottom
;
726 server
->subRect
.top
= 0;
727 server
->subRect
.left
= 0;
728 server
->subRect
.right
= maxWidth
;
729 server
->subRect
.bottom
= maxHeight
;
730 server
->shareSubRect
= TRUE
;
732 // Index 0 is reserved for "whole desktop" choice.
733 server
->selectedMonitor
= monitor_id
- 1;
738 candidate_pairs_established_cb(struct sipe_media_stream
*stream
)
740 struct sipe_appshare
*appshare
;
741 GSocketAddress
*address
;
742 GError
*error
= NULL
;
743 struct sockaddr_un native
;
744 rdpShadowServer
* server
;
745 const gchar
*server_error
= NULL
;
747 g_return_if_fail(sipe_strequal(stream
->id
, "applicationsharing"));
749 appshare
= sipe_media_stream_get_data(stream
);
751 server
= shadow_server_new();
753 server_error
= _("Could not create RDP server.");
755 server
->ipcSocket
= g_strdup_printf("%s/sipe-appshare-%u-%p",
756 g_get_user_runtime_dir(),
758 server
->authentication
= FALSE
;
759 server
->mayInteract
= FALSE
;
760 set_shared_display_area(server
, appshare
->monitor_id
);
762 /* Experimentally determined cap on multifrag max request size
763 * Lync client would accept. Higher values result in a black
764 * screen being displayed on the remote end.
766 * See related https://github.com/FreeRDP/FreeRDP/pull/3669. */
767 server
->settings
->MultifragMaxRequestSize
= 0x3EFFFF;
769 if(shadow_server_init(server
) < 0) {
770 server_error
= _("Could not initialize RDP server.");
771 } else if(shadow_server_start(server
) < 0) {
772 server_error
= _("Could not start RDP server.");
776 struct sipe_core_private
*sipe_private
;
778 sipe_private
= sipe_media_get_sipe_core_private(stream
->call
);
779 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
780 _("Application sharing error"),
782 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
784 shadow_server_uninit(server
);
785 shadow_server_free(server
);
790 appshare
->server
= server
;
791 appshare
->socket
= g_socket_new(G_SOCKET_FAMILY_UNIX
,
792 G_SOCKET_TYPE_STREAM
,
793 G_SOCKET_PROTOCOL_DEFAULT
,
796 SIPE_DEBUG_ERROR("Can't create RDP server socket: %s",
799 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
803 g_socket_set_blocking(appshare
->socket
, FALSE
);
805 native
.sun_family
= AF_LOCAL
;
806 strncpy(native
.sun_path
, server
->ipcSocket
, sizeof (native
.sun_path
) - 1);
807 native
.sun_path
[sizeof (native
.sun_path
) - 1] = '\0';
808 address
= g_socket_address_new_from_native(&native
, sizeof native
);
810 g_socket_connect(appshare
->socket
, address
, NULL
, &error
);
812 SIPE_DEBUG_ERROR("Can't connect to RDP server: %s", error
->message
);
814 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
818 appshare
->channel
= g_io_channel_unix_new(g_socket_get_fd(appshare
->socket
));
820 // No encoding for binary data
821 g_io_channel_set_encoding(appshare
->channel
, NULL
, &error
);
823 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
826 sipe_backend_media_hangup(stream
->call
->backend_private
, TRUE
);
830 appshare
->rdp_channel_readable_watch_id
=
831 g_io_add_watch(appshare
->channel
, G_IO_IN
| G_IO_HUP
,
832 rdp_channel_readable_cb
, appshare
);
834 // Appshare structure initialized; don't call this again.
835 stream
->candidate_pairs_established_cb
= NULL
;
839 stop_presenting_cb(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
840 struct sipe_media_call
*call
)
842 struct sipe_media_stream
*stream
;
844 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
846 struct sipe_appshare
*appshare
;
848 appshare
= sipe_media_stream_get_data(stream
);
850 appshare
->ask_ctx
= NULL
;
851 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
856 static struct sipe_user_ask_ctx
*
857 ask_end_presentation(struct sipe_core_private
*sipe_private
,
859 struct sipe_media_call
*call
)
861 struct sipe_user_ask_ctx
*ctx
;
862 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, with
);
863 gchar
*ask_msg
= g_strdup_printf(_("Sharing desktop with %s"),
864 alias
? alias
: with
);
866 ctx
= sipe_user_ask(sipe_private
, ask_msg
,
867 _("Stop presenting"),
868 (SipeUserAskCb
)stop_presenting_cb
,
880 monitor_selected_cb(struct sipe_core_private
*sipe_private
, gchar
*with
,
883 struct sipe_media_call
*call
;
884 struct sipe_media_stream
*stream
;
885 struct sipe_appshare
*appshare
;
887 if (monitor_id
== SIPE_CHOICE_CANCELLED
) {
892 call
= sipe_media_call_new(sipe_private
, with
, NULL
, SIPE_ICE_RFC_5245
,
893 SIPE_MEDIA_CALL_INITIATOR
|
894 SIPE_MEDIA_CALL_NO_UI
);
896 stream
= sipe_media_stream_add(call
, "applicationsharing",
897 SIPE_MEDIA_APPLICATION
,
898 SIPE_ICE_RFC_5245
, TRUE
, 0);
900 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
901 _("Application sharing error"),
902 _("Couldn't initialize application sharing"));
903 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
908 stream
->candidate_pairs_established_cb
= candidate_pairs_established_cb
;
909 stream
->read_cb
= read_cb
;
911 sipe_media_stream_add_extra_attribute(stream
,
914 sipe_media_stream_add_extra_attribute(stream
,
915 "x-applicationsharing-session-id",
917 sipe_media_stream_add_extra_attribute(stream
,
918 "x-applicationsharing-role",
920 sipe_media_stream_add_extra_attribute(stream
,
921 "x-applicationsharing-media-type",
924 // These attributes are mandatory when sharing with a conference.
925 sipe_media_stream_add_extra_attribute(stream
,
928 sipe_media_stream_add_extra_attribute(stream
,
932 appshare
= g_new0(struct sipe_appshare
, 1);
933 appshare
->stream
= stream
;
934 appshare
->monitor_id
= monitor_id
;
936 appshare
->ask_ctx
= ask_end_presentation(sipe_private
, with
, call
);
938 sipe_media_stream_set_data(stream
, appshare
,
939 (GDestroyNotify
)sipe_appshare_free
);
945 present_monitor_choice(struct sipe_core_public
*sipe_public
, const gchar
*who
)
947 MONITOR_DEF monitors
[16];
950 shadow_subsystem_set_entry_builtin("X11");
951 monitor_count
= shadow_enum_monitors(monitors
, 16);
953 if (monitor_count
== 1) {
954 // Don't show choice dialog, share whole desktop right away.
955 monitor_selected_cb(SIPE_CORE_PRIVATE
, g_strdup(who
), 0);
957 GSList
*choices
= NULL
;
960 choices
= g_slist_append(choices
, g_strdup(_("Whole desktop")));
962 for (i
= 0; i
!= monitor_count
; ++i
) {
963 MONITOR_DEF
*mon
= &monitors
[i
];
964 gchar
*str
= g_strdup_printf("%dx%d @ [%d, %d]",
965 mon
->right
- mon
->left
,
966 mon
->bottom
- mon
->top
,
970 choices
= g_slist_append(choices
, str
);
973 sipe_user_ask_choice(SIPE_CORE_PRIVATE
, _("Monitor to share"),
975 (SipeUserAskChoiceCb
)monitor_selected_cb
,
978 g_slist_free_full(choices
, g_free
);
983 sipe_core_appshare_share_desktop(struct sipe_core_public
*sipe_public
,
986 present_monitor_choice(sipe_public
, with
);
990 sipe_core_conf_share_desktop(struct sipe_core_public
*sipe_public
,
991 struct sipe_chat_session
*chat_session
)
995 switch (sipe_core_conf_get_appshare_role(sipe_public
, chat_session
)) {
996 case SIPE_APPSHARE_ROLE_PRESENTER
:
997 // We are already the presenting.
999 case SIPE_APPSHARE_ROLE_VIEWER
: {
1000 // Close RDP viewer before we start our own presentation.
1002 struct sipe_media_call
*call
;
1004 mcu_uri
= sipe_conf_build_uri(chat_session
->id
,
1005 "applicationsharing");
1006 call
= sipe_media_call_find(SIPE_CORE_PRIVATE
, mcu_uri
);
1009 sipe_backend_media_hangup(call
->backend_private
, TRUE
);
1016 uri
= sipe_conf_build_uri(chat_session
->id
, "applicationsharing");
1017 sipe_core_appshare_share_desktop(sipe_public
, uri
);
1023 sipe_core_appshare_set_remote_control(struct sipe_media_call
* call
, gboolean enabled
)
1025 struct sipe_media_stream
*stream
;
1027 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
1029 struct sipe_appshare
*appshare
;
1031 appshare
= sipe_media_stream_get_data(stream
);
1033 if(appshare
&& appshare
->server
) {
1034 rdpShadowServer
*server
= appshare
->server
;
1037 server
->mayInteract
= enabled
;
1039 ArrayList_Lock(server
->clients
);
1040 for (i
= 0; i
< ArrayList_Count(server
->clients
); i
++) {
1041 rdpShadowClient
*client
;
1043 client
= ArrayList_GetItem(server
->clients
, i
);
1044 client
->mayInteract
= enabled
;
1046 ArrayList_Unlock(server
->clients
);
1052 sipe_core_appshare_get_remote_control(struct sipe_media_call
* call
)
1054 struct sipe_media_stream
*stream
;
1056 stream
= sipe_core_media_get_stream_by_id(call
, "applicationsharing");
1058 struct sipe_appshare
*appshare
;
1060 appshare
= sipe_media_stream_get_data(stream
);
1062 if(appshare
&& appshare
->server
) {
1063 return appshare
->server
->mayInteract
;
1069 #endif // HAVE_APPSHARE_SERVER