purple: fix build when appshare server is disabled
[siplcs.git] / src / core / sipe-appshare.c
blobf2a2266e70a69cc8870bc634e1d195ff099bfa7b
1 /**
2 * @file sipe-appshare.c
4 * pidgin-sipe
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
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;
603 from = parse_from(sipmsg_find_header(msg, "From"));
604 appshare->ask_ctx = ask_accept_applicationsharing(sipe_private, from,
605 accept_cb,
606 decline_cb,
607 appshare);
608 g_free(from);
612 static void
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;
618 gchar * uri;
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,
625 SIPE_ICE_RFC_5245,
626 SIPE_MEDIA_CALL_NO_UI);
628 g_free(uri);
630 stream = sipe_media_stream_add(call, "applicationsharing",
631 SIPE_MEDIA_APPLICATION,
632 SIPE_ICE_RFC_5245, TRUE, 0);
633 if (!stream) {
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);
646 void
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) {
652 const gchar *from;
654 if (chat_session->appshare_ask_ctx) {
655 // Accept dialog already opened.
656 return;
659 if (chat_session->title) {
660 from = chat_session->title;
661 } else if (chat_session->organizer) {
662 from = chat_session->organizer;
663 } else {
664 from = chat_session->id;
667 chat_session->appshare_ask_ctx =
668 ask_accept_applicationsharing(SIPE_CORE_PRIVATE,
669 from,
670 (SipeUserAskCb)connect_conference,
671 NULL,
672 chat_session);
673 } else {
674 connect_conference(SIPE_CORE_PRIVATE, chat_session);
678 sipe_appshare_role
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");
687 if (stream) {
688 struct sipe_appshare *appshare;
690 appshare = sipe_media_stream_get_data(stream);
691 if (appshare) {
692 #ifdef HAVE_APPSHARE_SERVER
693 return appshare->server ? SIPE_APPSHARE_ROLE_PRESENTER :
694 SIPE_APPSHARE_ROLE_VIEWER;
695 #else
696 return SIPE_APPSHARE_ROLE_VIEWER;
697 #endif // HAVE_APPSHARE_SERVER
701 return SIPE_APPSHARE_ROLE_NONE;
704 #ifdef HAVE_APPSHARE_SERVER
705 static void
706 set_shared_display_area(rdpShadowServer *server, guint monitor_id)
708 if (monitor_id == 0) {
709 MONITOR_DEF monitors[16];
710 int monitor_count;
711 int i;
712 UINT16 maxWidth = 0;
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) {
719 maxWidth = m->right;
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;
731 } else {
732 // Index 0 is reserved for "whole desktop" choice.
733 server->selectedMonitor = monitor_id - 1;
737 static void
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();
752 if(!server) {
753 server_error = _("Could not create RDP server.");
754 } else {
755 server->ipcSocket = g_strdup_printf("%s/sipe-appshare-%u-%p",
756 g_get_user_runtime_dir(),
757 getpid(), stream);
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.");
775 if (server_error) {
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"),
781 server_error);
782 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
783 if (server) {
784 shadow_server_uninit(server);
785 shadow_server_free(server);
787 return;
790 appshare->server = server;
791 appshare->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
792 G_SOCKET_TYPE_STREAM,
793 G_SOCKET_PROTOCOL_DEFAULT,
794 &error);
795 if (error) {
796 SIPE_DEBUG_ERROR("Can't create RDP server socket: %s",
797 error->message);
798 g_error_free(error);
799 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
800 return;
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);
811 if (error) {
812 SIPE_DEBUG_ERROR("Can't connect to RDP server: %s", error->message);
813 g_error_free(error);
814 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
815 return;
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);
822 if (error) {
823 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
824 error->message);
825 g_error_free(error);
826 sipe_backend_media_hangup(stream->call->backend_private, TRUE);
827 return;
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;
838 static void
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");
845 if (stream) {
846 struct sipe_appshare *appshare;
848 appshare = sipe_media_stream_get_data(stream);
849 if (appshare) {
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,
858 const gchar *with,
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,
869 NULL,
870 NULL,
871 call);
873 g_free(ask_msg);
874 g_free(alias);
876 return ctx;
879 static void
880 monitor_selected_cb(struct sipe_core_private *sipe_private, gchar *with,
881 guint monitor_id)
883 struct sipe_media_call *call;
884 struct sipe_media_stream *stream;
885 struct sipe_appshare *appshare;
887 if (monitor_id == SIPE_CHOICE_CANCELLED) {
888 g_free(with);
889 return;
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);
899 if (!stream) {
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);
904 g_free(with);
905 return;
908 stream->candidate_pairs_established_cb = candidate_pairs_established_cb;
909 stream->read_cb = read_cb;
911 sipe_media_stream_add_extra_attribute(stream,
912 "mid",
913 "1");
914 sipe_media_stream_add_extra_attribute(stream,
915 "x-applicationsharing-session-id",
916 "1");
917 sipe_media_stream_add_extra_attribute(stream,
918 "x-applicationsharing-role",
919 "sharer");
920 sipe_media_stream_add_extra_attribute(stream,
921 "x-applicationsharing-media-type",
922 "rdp");
924 // These attributes are mandatory when sharing with a conference.
925 sipe_media_stream_add_extra_attribute(stream,
926 "setup",
927 "active");
928 sipe_media_stream_add_extra_attribute(stream,
929 "connection",
930 "new");
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);
941 g_free(with);
944 static void
945 present_monitor_choice(struct sipe_core_public *sipe_public, const gchar *who)
947 MONITOR_DEF monitors[16];
948 int monitor_count;
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);
956 } else {
957 GSList *choices = NULL;
958 int i;
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,
967 mon->left,
968 mon->top);
970 choices = g_slist_append(choices, str);
973 sipe_user_ask_choice(SIPE_CORE_PRIVATE, _("Monitor to share"),
974 choices,
975 (SipeUserAskChoiceCb)monitor_selected_cb,
976 g_strdup(who));
978 g_slist_free_full(choices, g_free);
982 void
983 sipe_core_appshare_share_desktop(struct sipe_core_public *sipe_public,
984 const gchar *with)
986 present_monitor_choice(sipe_public, with);
989 void
990 sipe_core_conf_share_desktop(struct sipe_core_public *sipe_public,
991 struct sipe_chat_session *chat_session)
993 gchar * uri;
995 switch (sipe_core_conf_get_appshare_role(sipe_public, chat_session)) {
996 case SIPE_APPSHARE_ROLE_PRESENTER:
997 // We are already the presenting.
998 return;
999 case SIPE_APPSHARE_ROLE_VIEWER: {
1000 // Close RDP viewer before we start our own presentation.
1001 gchar *mcu_uri;
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);
1007 g_free(mcu_uri);
1009 sipe_backend_media_hangup(call->backend_private, TRUE);
1010 break;
1012 default:
1013 break;
1016 uri = sipe_conf_build_uri(chat_session->id, "applicationsharing");
1017 sipe_core_appshare_share_desktop(sipe_public, uri);
1019 g_free(uri);
1022 void
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");
1028 if (stream) {
1029 struct sipe_appshare *appshare;
1031 appshare = sipe_media_stream_get_data(stream);
1033 if(appshare && appshare->server) {
1034 rdpShadowServer *server = appshare->server;
1035 int i;
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);
1051 gboolean
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");
1057 if (stream) {
1058 struct sipe_appshare *appshare;
1060 appshare = sipe_media_stream_get_data(stream);
1062 if(appshare && appshare->server) {
1063 return appshare->server->mayInteract;
1067 return FALSE;
1069 #endif // HAVE_APPSHARE_SERVER
1072 Local Variables:
1073 mode: c
1074 c-file-style: "bsd"
1075 indent-tabs-mode: t
1076 tab-width: 8
1077 End: