Release 1.25.0 -- Buddy Idle Time, RTF
[siplcs.git] / src / core / sipe-appshare.c
blobdec3d6dc25c0fc10e5b8c4787eca4acd0f899ea1
1 /**
2 * @file sipe-appshare.c
4 * pidgin-sipe
6 * Copyright (C) 2014-2019 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>
28 #include <string.h>
30 #include <gio/gio.h>
32 #ifdef HAVE_APPSHARE_SERVER
33 #include <glib/gstdio.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
38 #include <freerdp/server/shadow.h>
39 #endif // HAVE_APPSHARE_SERVER
41 #include "sipmsg.h"
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"
52 #include "sipe-nls.h"
53 #include "sipe-schedule.h"
54 #include "sipe-user.h"
55 #include "sipe-utils.h"
56 #include "sdpmsg.h"
58 struct sipe_appshare {
59 struct sipe_media_stream *stream;
60 GSocket *socket;
61 GIOChannel *channel;
62 guint rdp_channel_readable_watch_id;
63 guint rdp_channel_writable_watch_id;
64 guint monitor_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
78 static void
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) {
92 GError *error = NULL;
94 g_io_channel_shutdown(appshare->channel, TRUE, &error);
95 if (error) {
96 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
97 error->message);
98 g_error_free(error);
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);
128 g_free(appshare);
131 static gboolean
132 rdp_channel_readable_cb(GIOChannel *channel,
133 GIOCondition condition,
134 gpointer data)
136 struct sipe_appshare *appshare = data;
137 GError *error = NULL;
138 gchar *buffer;
139 gsize bytes_read;
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);
146 return FALSE;
149 buffer = g_malloc(2048);
150 while (sipe_media_stream_is_writable(appshare->stream)) {
151 GIOStatus status;
153 status = g_io_channel_read_chars(channel,
154 buffer, 2048,
155 &bytes_read, &error);
156 if (error) {
157 struct sipe_media_call *call = appshare->stream->call;
159 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
160 error->message);
161 g_error_free(error);
162 sipe_backend_media_hangup(call->backend_private, TRUE);
163 g_free(buffer);
164 return FALSE;
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);
171 g_free(buffer);
172 return FALSE;
175 if (bytes_read == 0) {
176 break;
179 sipe_media_stream_write(appshare->stream, (guint8 *)buffer,
180 bytes_read);
181 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT "\n", bytes_read);
183 g_free(buffer);
185 return TRUE;
188 static gboolean
189 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
190 SIPE_UNUSED_PARAMETER GIOCondition condition,
191 gpointer data)
193 struct sipe_appshare *appshare = data;
194 GError *error = NULL;
195 GSocket *data_socket;
196 int fd;
198 SIPE_DEBUG_INFO_NOFORMAT("RDP client has connected.");
200 data_socket = g_socket_accept(appshare->socket, NULL, &error);
201 if (error) {
202 struct sipe_media_call *call = appshare->stream->call;
204 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
205 error->message);
206 g_error_free(error);
207 sipe_backend_media_hangup(call->backend_private, TRUE);
208 return FALSE;
211 g_io_channel_shutdown(appshare->channel, TRUE, &error);
212 if (error) {
213 struct sipe_media_call *call = appshare->stream->call;
215 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
216 error->message);
217 g_error_free(error);
218 g_object_unref(data_socket);
219 sipe_backend_media_hangup(call->backend_private, TRUE);
220 return FALSE;
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);
228 if (fd < 0) {
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);
233 return FALSE;
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);
239 if (error) {
240 struct sipe_media_call *call = appshare->stream->call;
242 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
243 error->message);
244 g_error_free(error);
245 sipe_backend_media_hangup(call->backend_private, TRUE);
246 return FALSE;
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);
253 return FALSE;
256 static void
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;
263 int fd;
265 address = client->get_listen_address_cb(client);
266 if (!address) {
267 sipe_backend_media_hangup(call->backend_private, TRUE);
268 return;
271 appshare->socket = g_socket_new(g_socket_address_get_family(address),
272 G_SOCKET_TYPE_STREAM,
273 G_SOCKET_PROTOCOL_DEFAULT,
274 &error);
275 if (error) {
276 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
277 error->message);
278 g_error_free(error);
279 g_object_unref(address);
280 sipe_backend_media_hangup(call->backend_private, TRUE);
281 return;
284 g_socket_set_blocking(appshare->socket, FALSE);
286 g_socket_bind(appshare->socket, address, TRUE, &error);
287 g_object_unref(address);
288 if (error) {
289 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
290 error->message);
291 g_error_free(error);
292 sipe_backend_media_hangup(call->backend_private, TRUE);
293 return;
296 g_socket_listen(appshare->socket, &error);
297 if (error) {
298 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
299 error->message);
300 g_error_free(error);
301 sipe_backend_media_hangup(call->backend_private, TRUE);
302 return;
305 fd = g_socket_get_fd(appshare->socket);
306 if (fd < 0) {
307 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket");
308 sipe_backend_media_hangup(call->backend_private, TRUE);
309 return;
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);
318 if (error) {
319 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
320 error->message);
321 g_error_free(error);
322 sipe_backend_media_hangup(call->backend_private, TRUE);
323 return;
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);
336 static gssize
337 rdp_client_channel_write(struct sipe_appshare *appshare)
339 gsize bytes_written;
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);
346 if (error) {
347 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
348 error->message);
349 g_error_free(error);
350 return -1;
353 g_io_channel_flush(appshare->channel, &error);
354 if (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(). */
359 g_error_free(error);
360 return 0;
363 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
364 error->message);
365 g_error_free(error);
366 return -1;
369 appshare->rdp_channel_buffer_pos += bytes_written;
370 appshare->rdp_channel_buffer_len -= bytes_written;
372 return bytes_written;
375 static void
376 delayed_hangup_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
377 gpointer data)
379 struct sipe_media_call *call = data;
381 sipe_backend_media_hangup(call->backend_private, TRUE);
384 static gboolean
385 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
386 SIPE_UNUSED_PARAMETER GIOCondition condition,
387 gpointer data)
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);
394 return FALSE;
397 if (appshare->rdp_channel_buffer_len == 0) {
398 // Writing done, disconnect writable watch.
399 appshare->rdp_channel_writable_watch_id = 0;
400 return FALSE;
403 return TRUE;
406 static void
407 read_cb(struct sipe_media_stream *stream)
409 struct sipe_appshare *appshare = sipe_media_stream_get_data(stream);
410 gint bytes_read = 0;
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.
415 return;
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) {
423 return;
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,
439 delayed_hangup_cb,
440 NULL);
441 return;
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,
451 appshare);
455 static void
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);
465 static void
466 accept_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
467 gpointer data)
469 struct sipe_appshare *appshare = data;
470 appshare->ask_ctx = NULL;
472 sipe_backend_media_accept(appshare->stream->call->backend_private, TRUE);
475 static void
476 decline_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
477 gpointer data)
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,
487 const gchar *from,
488 SipeUserAskCb accept_cb,
489 SipeUserAskCb decline_cb,
490 gpointer user_data)
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,
500 user_data);
502 g_free(ask_msg);
503 g_free(alias);
505 return ctx;
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;
516 call = stream->call;
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))
528 cmdline = "remmina";
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);
535 } else {
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);
540 return NULL;
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;
553 return appshare;
556 void
557 process_incoming_invite_appshare(struct sipe_core_private *sipe_private,
558 struct sipmsg *msg)
560 struct sipe_media_call *call;
561 struct sipe_media_stream *stream;
562 struct sipe_appshare *appshare;
563 struct sdpmsg *sdpmsg;
564 GSList *i;
566 sdpmsg = sdpmsg_parse_msg(msg->body);
568 /* Skype for Business compatibility - ignore desktop video. */
569 i = sdpmsg->media;
570 while (i) {
571 struct sdpmedia *media = i->data;
572 const gchar *label;
574 i = i->next;
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,
586 msg,
587 sdpmsg);
588 if (!call) {
589 return;
592 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
593 if (!stream) {
594 sipe_backend_media_hangup(call->backend_private, TRUE);
595 return;
598 appshare = initialize_appshare(stream);
600 if (appshare) {
601 gchar *from = sipmsg_parse_from_address(msg);
602 appshare->ask_ctx = ask_accept_applicationsharing(sipe_private, from,
603 accept_cb,
604 decline_cb,
605 appshare);
606 g_free(from);
610 static void
611 connect_conference(struct sipe_core_private *sipe_private,
612 struct sipe_chat_session *chat_session)
614 struct sipe_media_call *call;
615 struct sipe_media_stream *stream;
616 gchar * uri;
618 chat_session->appshare_ask_ctx = NULL;
620 uri = sipe_conf_build_uri(chat_session->id, "applicationsharing");
622 call = sipe_media_call_new(sipe_private, uri, NULL,
623 SIPE_ICE_RFC_5245,
624 SIPE_MEDIA_CALL_NO_UI);
626 g_free(uri);
628 stream = sipe_media_stream_add(call, "applicationsharing",
629 SIPE_MEDIA_APPLICATION,
630 SIPE_ICE_RFC_5245, TRUE, 0);
631 if (!stream) {
632 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
633 _("Application sharing error"),
634 _("Couldn't connect application sharing"));
635 sipe_backend_media_hangup(call->backend_private, FALSE);
638 sipe_media_stream_add_extra_attribute(stream, "connection", "new");
639 sipe_media_stream_add_extra_attribute(stream, "setup", "active");
641 initialize_appshare(stream);
644 void
645 sipe_core_appshare_connect_conference(struct sipe_core_public *sipe_public,
646 struct sipe_chat_session *chat_session,
647 gboolean user_must_accept)
649 if (user_must_accept) {
650 const gchar *from;
652 if (chat_session->appshare_ask_ctx) {
653 // Accept dialog already opened.
654 return;
657 if (chat_session->title) {
658 from = chat_session->title;
659 } else if (chat_session->organizer) {
660 from = chat_session->organizer;
661 } else {
662 from = chat_session->id;
665 chat_session->appshare_ask_ctx =
666 ask_accept_applicationsharing(SIPE_CORE_PRIVATE,
667 from,
668 (SipeUserAskCb)connect_conference,
669 NULL,
670 chat_session);
671 } else {
672 connect_conference(SIPE_CORE_PRIVATE, chat_session);
676 sipe_appshare_role
677 sipe_appshare_get_role(struct sipe_media_call *call)
679 struct sipe_media_stream *stream;
681 g_return_val_if_fail(call, SIPE_APPSHARE_ROLE_NONE);
683 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
685 if (stream) {
686 struct sipe_appshare *appshare;
688 appshare = sipe_media_stream_get_data(stream);
689 if (appshare) {
690 #ifdef HAVE_APPSHARE_SERVER
691 return appshare->server ? SIPE_APPSHARE_ROLE_PRESENTER :
692 SIPE_APPSHARE_ROLE_VIEWER;
693 #else
694 return SIPE_APPSHARE_ROLE_VIEWER;
695 #endif // HAVE_APPSHARE_SERVER
699 return SIPE_APPSHARE_ROLE_NONE;
702 #ifdef HAVE_APPSHARE_SERVER
704 /* Limit shared screen size to 2160p. Screen updates of larger monitors might
705 * not fit into MultifragMaxRequestSize. */
706 #define APPSHARE_MAX_SCREEN_WIDTH 3840
707 #define APPSHARE_MAX_SCREEN_HEIGHT 2160
709 static void
710 set_shared_display_area(rdpShadowServer *server, guint monitor_id)
712 MONITOR_DEF monitors[16];
713 MONITOR_DEF *monitor;
714 UINT32 monitor_count;
716 monitor_count = shadow_enum_monitors(monitors, 16);
718 if (monitor_id >= monitor_count) {
719 server->selectedMonitor = 0;
720 return;
723 server->selectedMonitor = monitor_id;
725 monitor = &monitors[monitor_id];
726 if ((monitor->right - monitor->left) > APPSHARE_MAX_SCREEN_WIDTH ||
727 (monitor->bottom - monitor->top) > APPSHARE_MAX_SCREEN_HEIGHT) {
728 server->subRect.top = 0;
729 server->subRect.left = 0;
730 server->subRect.right = MIN(monitor->right - monitor->left,
731 APPSHARE_MAX_SCREEN_WIDTH);
732 server->subRect.bottom = MIN(monitor->bottom - monitor->top,
733 APPSHARE_MAX_SCREEN_HEIGHT);
734 server->shareSubRect = TRUE;
736 SIPE_DEBUG_INFO("Cropping the shared screen to %dx%d",
737 server->subRect.right, server->subRect.bottom);
741 static void
742 candidate_pairs_established_cb(struct sipe_media_stream *stream)
744 struct sipe_appshare *appshare;
745 GSocketAddress *address;
746 GError *error = NULL;
747 struct sockaddr_un native;
748 rdpShadowServer* server;
749 const gchar *server_error = NULL;
751 g_return_if_fail(sipe_strequal(stream->id, "applicationsharing"));
753 appshare = sipe_media_stream_get_data(stream);
755 server = shadow_server_new();
756 if(!server) {
757 server_error = _("Could not create RDP server.");
758 } else {
759 server->ipcSocket = g_strdup_printf("%s/sipe-appshare-%u-%p",
760 g_get_user_runtime_dir(),
761 getpid(), stream);
762 server->authentication = FALSE;
763 server->mayInteract = FALSE;
764 set_shared_display_area(server, appshare->monitor_id);
766 /* Experimentally determined cap on multifrag max request size
767 * Lync client would accept. Higher values result in a black
768 * screen being displayed on the remote end.
770 * See related https://github.com/FreeRDP/FreeRDP/pull/3669. */
771 server->settings->MultifragMaxRequestSize = 0x3EFFFF;
773 if(shadow_server_init(server) < 0) {
774 server_error = _("Could not initialize RDP server.");
775 } else if(shadow_server_start(server) < 0) {
776 server_error = _("Could not start RDP server.");
779 if (server_error) {
780 struct sipe_core_private *sipe_private;
782 sipe_private = sipe_media_get_sipe_core_private(stream->call);
783 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
784 _("Application sharing error"),
785 server_error);
786 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
787 if (server) {
788 shadow_server_uninit(server);
789 shadow_server_free(server);
791 return;
794 appshare->server = server;
795 appshare->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
796 G_SOCKET_TYPE_STREAM,
797 G_SOCKET_PROTOCOL_DEFAULT,
798 &error);
799 if (error) {
800 SIPE_DEBUG_ERROR("Can't create RDP server socket: %s",
801 error->message);
802 g_error_free(error);
803 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
804 return;
807 g_socket_set_blocking(appshare->socket, FALSE);
809 native.sun_family = AF_LOCAL;
810 strncpy(native.sun_path, server->ipcSocket, sizeof (native.sun_path) - 1);
811 native.sun_path[sizeof (native.sun_path) - 1] = '\0';
812 address = g_socket_address_new_from_native(&native, sizeof native);
814 g_socket_connect(appshare->socket, address, NULL, &error);
815 if (error) {
816 SIPE_DEBUG_ERROR("Can't connect to RDP server: %s", error->message);
817 g_error_free(error);
818 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
819 return;
822 appshare->channel = g_io_channel_unix_new(g_socket_get_fd(appshare->socket));
824 // No encoding for binary data
825 g_io_channel_set_encoding(appshare->channel, NULL, &error);
826 if (error) {
827 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
828 error->message);
829 g_error_free(error);
830 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
831 return;
834 appshare->rdp_channel_readable_watch_id =
835 g_io_add_watch(appshare->channel, G_IO_IN | G_IO_HUP,
836 rdp_channel_readable_cb, appshare);
838 // Appshare structure initialized; don't call this again.
839 stream->candidate_pairs_established_cb = NULL;
842 static void
843 stop_presenting_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
844 struct sipe_media_call *call)
846 struct sipe_media_stream *stream;
848 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
849 if (stream) {
850 struct sipe_appshare *appshare;
852 appshare = sipe_media_stream_get_data(stream);
853 if (appshare) {
854 appshare->ask_ctx = NULL;
855 sipe_backend_media_hangup(call->backend_private, TRUE);
860 static struct sipe_user_ask_ctx *
861 ask_end_presentation(struct sipe_core_private *sipe_private,
862 const gchar *with,
863 struct sipe_media_call *call)
865 struct sipe_user_ask_ctx *ctx;
866 gchar *alias = sipe_buddy_get_alias(sipe_private, with);
867 gchar *ask_msg = g_strdup_printf(_("Sharing desktop with %s"),
868 alias ? alias : with);
870 ctx = sipe_user_ask(sipe_private, ask_msg,
871 _("Stop presenting"),
872 (SipeUserAskCb)stop_presenting_cb,
873 NULL,
874 NULL,
875 call);
877 g_free(ask_msg);
878 g_free(alias);
880 return ctx;
883 static void
884 monitor_selected_cb(struct sipe_core_private *sipe_private, gchar *with,
885 guint monitor_id)
887 struct sipe_media_call *call;
888 struct sipe_media_stream *stream;
889 struct sipe_appshare *appshare;
891 if (monitor_id == SIPE_CHOICE_CANCELLED) {
892 g_free(with);
893 return;
896 call = sipe_media_call_new(sipe_private, with, NULL, SIPE_ICE_RFC_5245,
897 SIPE_MEDIA_CALL_INITIATOR |
898 SIPE_MEDIA_CALL_NO_UI);
900 stream = sipe_media_stream_add(call, "applicationsharing",
901 SIPE_MEDIA_APPLICATION,
902 SIPE_ICE_RFC_5245, TRUE, 0);
903 if (!stream) {
904 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
905 _("Application sharing error"),
906 _("Couldn't initialize application sharing"));
907 sipe_backend_media_hangup(call->backend_private, TRUE);
908 g_free(with);
909 return;
912 stream->candidate_pairs_established_cb = candidate_pairs_established_cb;
913 stream->read_cb = read_cb;
915 sipe_media_stream_add_extra_attribute(stream,
916 "mid",
917 "1");
918 sipe_media_stream_add_extra_attribute(stream,
919 "x-applicationsharing-session-id",
920 "1");
921 sipe_media_stream_add_extra_attribute(stream,
922 "x-applicationsharing-role",
923 "sharer");
924 sipe_media_stream_add_extra_attribute(stream,
925 "x-applicationsharing-media-type",
926 "rdp");
928 // These attributes are mandatory when sharing with a conference.
929 sipe_media_stream_add_extra_attribute(stream,
930 "setup",
931 "active");
932 sipe_media_stream_add_extra_attribute(stream,
933 "connection",
934 "new");
936 appshare = g_new0(struct sipe_appshare, 1);
937 appshare->stream = stream;
938 appshare->monitor_id = monitor_id;
940 appshare->ask_ctx = ask_end_presentation(sipe_private, with, call);
942 sipe_media_stream_set_data(stream, appshare,
943 (GDestroyNotify)sipe_appshare_free);
945 g_free(with);
948 static void
949 present_monitor_choice(struct sipe_core_public *sipe_public, const gchar *who)
951 MONITOR_DEF monitors[16];
952 int monitor_count;
954 shadow_subsystem_set_entry_builtin("X11");
955 monitor_count = shadow_enum_monitors(monitors, 16);
957 if (monitor_count == 1) {
958 // Skip the choice, use the first (only) display right away.
959 monitor_selected_cb(SIPE_CORE_PRIVATE, g_strdup(who), 0);
960 } else {
961 GSList *choices = NULL;
962 int i;
964 for (i = 0; i != monitor_count; ++i) {
965 MONITOR_DEF *mon = &monitors[i];
966 gchar *str = g_strdup_printf("%dx%d @ [%d, %d]",
967 mon->right - mon->left,
968 mon->bottom - mon->top,
969 mon->left,
970 mon->top);
972 choices = g_slist_append(choices, str);
975 sipe_user_ask_choice(SIPE_CORE_PRIVATE, _("Monitor to share"),
976 choices,
977 (SipeUserAskChoiceCb)monitor_selected_cb,
978 g_strdup(who));
980 g_slist_free_full(choices, g_free);
984 void
985 sipe_core_appshare_share_desktop(struct sipe_core_public *sipe_public,
986 const gchar *with)
988 present_monitor_choice(sipe_public, with);
991 void
992 sipe_core_conf_share_desktop(struct sipe_core_public *sipe_public,
993 struct sipe_chat_session *chat_session)
995 gchar * uri;
997 switch (sipe_core_conf_get_appshare_role(sipe_public, chat_session)) {
998 case SIPE_APPSHARE_ROLE_PRESENTER:
999 // We are already the presenting.
1000 return;
1001 case SIPE_APPSHARE_ROLE_VIEWER: {
1002 // Close RDP viewer before we start our own presentation.
1003 gchar *mcu_uri;
1004 struct sipe_media_call *call;
1006 mcu_uri = sipe_conf_build_uri(chat_session->id,
1007 "applicationsharing");
1008 call = sipe_media_call_find(SIPE_CORE_PRIVATE, mcu_uri);
1009 g_free(mcu_uri);
1011 sipe_backend_media_hangup(call->backend_private, TRUE);
1012 break;
1014 default:
1015 break;
1018 uri = sipe_conf_build_uri(chat_session->id, "applicationsharing");
1019 sipe_core_appshare_share_desktop(sipe_public, uri);
1021 g_free(uri);
1024 void
1025 sipe_core_appshare_set_remote_control(struct sipe_media_call * call, gboolean enabled)
1027 struct sipe_media_stream *stream;
1029 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
1030 if (stream) {
1031 struct sipe_appshare *appshare;
1033 appshare = sipe_media_stream_get_data(stream);
1035 if(appshare && appshare->server) {
1036 rdpShadowServer *server = appshare->server;
1037 int i;
1039 server->mayInteract = enabled;
1041 ArrayList_Lock(server->clients);
1042 for (i = 0; i < ArrayList_Count(server->clients); i++) {
1043 rdpShadowClient *client;
1045 client = ArrayList_GetItem(server->clients, i);
1046 client->mayInteract = enabled;
1048 ArrayList_Unlock(server->clients);
1053 gboolean
1054 sipe_core_appshare_get_remote_control(struct sipe_media_call * call)
1056 struct sipe_media_stream *stream;
1058 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
1059 if (stream) {
1060 struct sipe_appshare *appshare;
1062 appshare = sipe_media_stream_get_data(stream);
1064 if(appshare && appshare->server) {
1065 return appshare->server->mayInteract;
1069 return FALSE;
1071 #endif // HAVE_APPSHARE_SERVER
1074 Local Variables:
1075 mode: c
1076 c-file-style: "bsd"
1077 indent-tabs-mode: t
1078 tab-width: 8
1079 End: