appshare: move default to core
[siplcs/bazlsi01.git] / src / core / sipe-appshare.c
blob31952194d713db57f359ff649f12134e23bcac42
1 /**
2 * @file sipe-appshare.c
4 * pidgin-sipe
6 * Copyright (C) 2014-2016 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 #include <glib.h>
24 #include <string.h>
26 #include <gio/gio.h>
28 #include "sipmsg.h"
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"
39 #include "sipe-nls.h"
40 #include "sipe-schedule.h"
41 #include "sipe-user.h"
42 #include "sipe-utils.h"
44 struct sipe_appshare {
45 struct sipe_media_stream *stream;
46 GSocket *socket;
47 GIOChannel *channel;
48 guint rdp_channel_readable_watch_id;
49 guint rdp_channel_writable_watch_id;
50 struct sipe_user_ask_ctx *ask_ctx;
52 gchar rdp_channel_buffer[0x800];
53 gchar *rdp_channel_buffer_pos;
54 gsize rdp_channel_buffer_len;
56 struct sipe_rdp_client client;
59 static void
60 sipe_appshare_free(struct sipe_appshare *appshare)
62 if (appshare->rdp_channel_readable_watch_id != 0) {
63 g_source_destroy(g_main_context_find_source_by_id(NULL,
64 appshare->rdp_channel_readable_watch_id));
67 if (appshare->rdp_channel_writable_watch_id != 0) {
68 g_source_destroy(g_main_context_find_source_by_id(NULL,
69 appshare->rdp_channel_writable_watch_id));
72 if (appshare->channel) {
73 GError *error = NULL;
75 g_io_channel_shutdown(appshare->channel, TRUE, &error);
76 if (error) {
77 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
78 error->message);
79 g_error_free(error);
81 g_io_channel_unref(appshare->channel);
84 if (appshare->socket) {
85 g_object_unref(appshare->socket);
88 if (appshare->ask_ctx) {
89 sipe_user_close_ask(appshare->ask_ctx);
92 g_free(appshare->client.cmdline);
93 if (appshare->client.free_cb) {
94 appshare->client.free_cb(&appshare->client);
97 g_free(appshare);
100 static gboolean
101 rdp_channel_readable_cb(GIOChannel *channel,
102 GIOCondition condition,
103 gpointer data)
105 struct sipe_appshare *appshare = data;
106 GError *error = NULL;
107 gchar *buffer;
108 gsize bytes_read;
110 if (condition & G_IO_HUP) {
111 struct sipe_media_call *call = appshare->stream->call;
113 sipe_backend_media_hangup(call->backend_private, TRUE);
114 return FALSE;
117 buffer = g_malloc(2048);
118 while (sipe_media_stream_is_writable(appshare->stream)) {
119 GIOStatus status;
121 status = g_io_channel_read_chars(channel,
122 buffer, 2048,
123 &bytes_read, &error);
124 if (error) {
125 struct sipe_media_call *call = appshare->stream->call;
127 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
128 error->message);
129 g_error_free(error);
130 sipe_backend_media_hangup(call->backend_private, TRUE);
131 g_free(buffer);
132 return FALSE;
135 if (status == G_IO_STATUS_EOF) {
136 struct sipe_media_call *call = appshare->stream->call;
138 sipe_backend_media_hangup(call->backend_private, TRUE);
139 g_free(buffer);
140 return FALSE;
143 if (bytes_read == 0) {
144 break;
147 sipe_media_stream_write(appshare->stream, (guint8 *)buffer,
148 bytes_read);
149 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT "\n", bytes_read);
151 g_free(buffer);
153 return TRUE;
156 static gboolean
157 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
158 SIPE_UNUSED_PARAMETER GIOCondition condition,
159 gpointer data)
161 struct sipe_appshare *appshare = data;
162 GError *error = NULL;
163 GSocket *data_socket;
164 int fd;
166 data_socket = g_socket_accept(appshare->socket, NULL, &error);
167 if (error) {
168 struct sipe_media_call *call = appshare->stream->call;
170 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
171 error->message);
172 g_error_free(error);
173 sipe_backend_media_hangup(call->backend_private, TRUE);
174 return FALSE;
177 g_io_channel_shutdown(appshare->channel, TRUE, &error);
178 if (error) {
179 struct sipe_media_call *call = appshare->stream->call;
181 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
182 error->message);
183 g_error_free(error);
184 g_object_unref(data_socket);
185 sipe_backend_media_hangup(call->backend_private, TRUE);
186 return FALSE;
188 g_io_channel_unref(appshare->channel);
190 g_object_unref(appshare->socket);
191 appshare->socket = data_socket;
193 fd = g_socket_get_fd(appshare->socket);
194 if (fd < 0) {
195 struct sipe_media_call *call = appshare->stream->call;
197 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client connection socket");
198 sipe_backend_media_hangup(call->backend_private, TRUE);
199 return FALSE;
201 appshare->channel = g_io_channel_unix_new(fd);
203 // No encoding for binary data
204 g_io_channel_set_encoding(appshare->channel, NULL, &error);
205 if (error) {
206 struct sipe_media_call *call = appshare->stream->call;
208 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
209 error->message);
210 g_error_free(error);
211 sipe_backend_media_hangup(call->backend_private, TRUE);
212 return FALSE;
215 appshare->rdp_channel_readable_watch_id =
216 g_io_add_watch(appshare->channel, G_IO_IN | G_IO_HUP,
217 rdp_channel_readable_cb, appshare);
219 return FALSE;
222 static void
223 launch_rdp_client(struct sipe_appshare *appshare)
225 struct sipe_rdp_client *client = &appshare->client;
226 struct sipe_media_call *call = appshare->stream->call;
227 GSocketAddress *address;
228 GError *error = NULL;
229 int fd;
231 address = client->get_listen_address_cb(client);
232 if (!address) {
233 sipe_backend_media_hangup(call->backend_private, TRUE);
234 return;
237 appshare->socket = g_socket_new(g_socket_address_get_family(address),
238 G_SOCKET_TYPE_STREAM,
239 G_SOCKET_PROTOCOL_DEFAULT,
240 &error);
241 if (error) {
242 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
243 error->message);
244 g_error_free(error);
245 g_object_unref(address);
246 sipe_backend_media_hangup(call->backend_private, TRUE);
247 return;
250 g_socket_set_blocking(appshare->socket, FALSE);
252 g_socket_bind(appshare->socket, address, TRUE, &error);
253 g_object_unref(address);
254 if (error) {
255 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
256 error->message);
257 g_error_free(error);
258 sipe_backend_media_hangup(call->backend_private, TRUE);
259 return;
262 g_socket_listen(appshare->socket, &error);
263 if (error) {
264 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
265 error->message);
266 g_error_free(error);
267 sipe_backend_media_hangup(call->backend_private, TRUE);
268 return;
271 fd = g_socket_get_fd(appshare->socket);
272 if (fd < 0) {
273 SIPE_DEBUG_ERROR_NOFORMAT("Invalid file descriptor for RDP client listen socket");
274 sipe_backend_media_hangup(call->backend_private, TRUE);
275 return;
277 appshare->channel = g_io_channel_unix_new(fd);
279 appshare->rdp_channel_readable_watch_id =
280 g_io_add_watch(appshare->channel, G_IO_IN,
281 socket_connect_cb, appshare);
283 address = g_socket_get_local_address(appshare->socket, &error);
284 if (error) {
285 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
286 error->message);
287 g_error_free(error);
288 sipe_backend_media_hangup(call->backend_private, TRUE);
289 return;
292 if (!client->launch_cb(client, address, appshare->stream)) {
293 sipe_backend_media_hangup(call->backend_private, TRUE);
296 g_object_unref(address);
299 static gssize
300 rdp_client_channel_write(struct sipe_appshare *appshare)
302 gsize bytes_written;
303 GError *error = NULL;
305 g_io_channel_write_chars(appshare->channel,
306 appshare->rdp_channel_buffer_pos,
307 appshare->rdp_channel_buffer_len,
308 &bytes_written, &error);
309 if (error) {
310 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
311 error->message);
312 g_error_free(error);
313 return -1;
316 g_io_channel_flush(appshare->channel, &error);
317 if (error) {
318 if (g_error_matches(error, G_IO_CHANNEL_ERROR,
319 G_IO_CHANNEL_ERROR_PIPE)) {
320 /* Ignore broken pipe here and wait for the call to be
321 * hung up upon G_IO_HUP in client_channel_cb(). */
322 g_error_free(error);
323 return 0;
326 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
327 error->message);
328 g_error_free(error);
329 return -1;
332 appshare->rdp_channel_buffer_pos += bytes_written;
333 appshare->rdp_channel_buffer_len -= bytes_written;
335 return bytes_written;
338 static void
339 delayed_hangup_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
340 gpointer data)
342 struct sipe_media_call *call = data;
344 sipe_backend_media_hangup(call->backend_private, TRUE);
347 static gboolean
348 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
349 SIPE_UNUSED_PARAMETER GIOCondition condition,
350 gpointer data)
352 struct sipe_appshare *appshare = data;
353 struct sipe_media_call *call = appshare->stream->call;
355 if (rdp_client_channel_write(appshare) < 0) {
356 sipe_backend_media_hangup(call->backend_private, TRUE);
357 return FALSE;
360 if (appshare->rdp_channel_buffer_len == 0) {
361 // Writing done, disconnect writable watch.
362 appshare->rdp_channel_writable_watch_id = 0;
363 return FALSE;
366 return TRUE;
369 static void
370 read_cb(struct sipe_media_stream *stream)
372 struct sipe_appshare *appshare = sipe_media_stream_get_data(stream);
373 gint bytes_read = 0;
374 gssize bytes_written = 0;
376 if (appshare->rdp_channel_writable_watch_id != 0) {
377 // Data still in the buffer. Let the client read it first.
378 return;
381 while (bytes_read == (gint)bytes_written) {
382 bytes_read = sipe_backend_media_stream_read(stream,
383 (guint8 *)appshare->rdp_channel_buffer,
384 sizeof (appshare->rdp_channel_buffer));
385 if (bytes_read == 0) {
386 return;
389 appshare->rdp_channel_buffer_pos = appshare->rdp_channel_buffer;
390 appshare->rdp_channel_buffer_len = bytes_read;
392 bytes_written = rdp_client_channel_write(appshare);
394 if (bytes_written < 0) {
395 /* Don't deallocate stream while in its read callback.
396 * Schedule call hangup to be executed after we're back
397 * in the message loop. */
398 sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream->call),
399 "appshare delayed hangup",
400 stream->call->backend_private,
402 delayed_hangup_cb,
403 NULL);
404 return;
408 if (bytes_read != (gint)bytes_written) {
409 /* Schedule writing of the buffer's remainder to when
410 * RDP channel becomes writable again. */
411 appshare->rdp_channel_writable_watch_id =
412 g_io_add_watch(appshare->channel, G_IO_OUT,
413 rdp_channel_writable_cb,
414 appshare);
418 static void
419 writable_cb(struct sipe_media_stream *stream)
421 struct sipe_appshare *appshare = sipe_media_stream_get_data(stream);
423 if (!appshare->socket) {
424 launch_rdp_client(appshare);
428 static void
429 accept_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
430 gpointer data)
432 struct sipe_appshare *appshare = data;
433 appshare->ask_ctx = NULL;
435 sipe_backend_media_accept(appshare->stream->call->backend_private, TRUE);
438 static void
439 decline_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
440 gpointer data)
442 struct sipe_appshare *appshare = data;
443 appshare->ask_ctx = NULL;
445 sipe_backend_media_hangup(appshare->stream->call->backend_private, TRUE);
448 static struct sipe_user_ask_ctx *
449 ask_accept_applicationsharing(struct sipe_core_private *sipe_private,
450 const gchar *from,
451 SipeUserAskCb accept_cb,
452 SipeUserAskCb decline_cb,
453 gpointer user_data)
455 struct sipe_user_ask_ctx *ctx;
456 gchar *alias = sipe_buddy_get_alias(sipe_private, from);
457 gchar *ask_msg = g_strdup_printf(_("%s wants to start presenting"),
458 alias ? alias : from);
460 ctx = sipe_user_ask(sipe_private, ask_msg,
461 _("Accept"), accept_cb,
462 _("Decline"), decline_cb,
463 user_data);
465 g_free(ask_msg);
466 g_free(alias);
468 return ctx;
471 static struct sipe_appshare *
472 initialize_appshare(struct sipe_media_stream *stream)
474 struct sipe_appshare *appshare;
475 struct sipe_media_call *call;
476 struct sipe_core_private *sipe_private;
477 const gchar *cmdline;
479 call = stream->call;
480 sipe_private = sipe_media_get_sipe_core_private(call);
482 appshare = g_new0(struct sipe_appshare, 1);
483 appshare->stream = stream;
485 sipe_media_stream_set_data(stream, appshare,
486 (GDestroyNotify)sipe_appshare_free);
488 cmdline = sipe_backend_setting(SIPE_CORE_PUBLIC,
489 SIPE_SETTING_RDP_CLIENT);
490 if (is_empty(cmdline))
491 cmdline = "remmina";
492 appshare->client.cmdline = g_strdup(cmdline);
494 if (strstr(cmdline, "xfreerdp")) {
495 sipe_appshare_xfreerdp_init(&appshare->client);
496 } else if (strstr(cmdline, "remmina")) {
497 sipe_appshare_remmina_init(&appshare->client);
498 } else {
499 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
500 _("Application sharing error"),
501 _("Unknown remote desktop client configured."));
502 sipe_backend_media_hangup(call->backend_private, TRUE);
503 return NULL;
506 sipe_media_stream_add_extra_attribute(stream,
507 "x-applicationsharing-session-id", "1");
508 sipe_media_stream_add_extra_attribute(stream,
509 "x-applicationsharing-role", "viewer");
510 sipe_media_stream_add_extra_attribute(stream,
511 "x-applicationsharing-media-type", "rdp");
513 stream->read_cb = read_cb;
514 stream->writable_cb = writable_cb;
516 return appshare;
519 void
520 process_incoming_invite_appshare(struct sipe_core_private *sipe_private,
521 struct sipmsg *msg)
523 struct sipe_media_call *call;
524 struct sipe_media_stream *stream;
525 struct sipe_appshare *appshare;
527 call = process_incoming_invite_call(sipe_private, msg);
528 if (!call) {
529 return;
532 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
533 if (!stream) {
534 sipe_backend_media_hangup(call->backend_private, TRUE);
535 return;
538 appshare = initialize_appshare(stream);
540 if (appshare) {
541 gchar *from;
543 from = parse_from(sipmsg_find_header(msg, "From"));
544 appshare->ask_ctx = ask_accept_applicationsharing(sipe_private, from,
545 accept_cb,
546 decline_cb,
547 appshare);
548 g_free(from);
552 static void
553 connect_conference(struct sipe_core_private *sipe_private,
554 struct sipe_chat_session *chat_session)
556 struct sipe_media_call *call;
557 struct sipe_media_stream *stream;
558 gchar * uri;
560 chat_session->appshare_ask_ctx = NULL;
562 uri = sipe_conf_build_uri(chat_session->id, "applicationsharing");
564 call = sipe_media_call_new(sipe_private, uri, NULL,
565 SIPE_ICE_RFC_5245,
566 SIPE_MEDIA_CALL_NO_UI);
568 g_free(uri);
570 stream = sipe_media_stream_add(call, "applicationsharing",
571 SIPE_MEDIA_APPLICATION,
572 SIPE_ICE_RFC_5245, TRUE, 0);
573 if (!stream) {
574 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
575 _("Application sharing error"),
576 _("Couldn't connect application sharing"));
577 sipe_backend_media_hangup(call->backend_private, FALSE);
580 sipe_media_stream_add_extra_attribute(stream, "connection", "new");
581 sipe_media_stream_add_extra_attribute(stream, "setup", "active");
583 initialize_appshare(stream);
586 void
587 sipe_core_appshare_connect_conference(struct sipe_core_public *sipe_public,
588 struct sipe_chat_session *chat_session,
589 gboolean user_must_accept)
591 if (user_must_accept) {
592 const gchar *from;
594 if (chat_session->appshare_ask_ctx) {
595 // Accept dialog already opened.
596 return;
599 if (chat_session->title) {
600 from = chat_session->title;
601 } else if (chat_session->organizer) {
602 from = chat_session->organizer;
603 } else {
604 from = chat_session->id;
607 chat_session->appshare_ask_ctx =
608 ask_accept_applicationsharing(SIPE_CORE_PRIVATE,
609 from,
610 (SipeUserAskCb)connect_conference,
611 NULL,
612 chat_session);
613 } else {
614 connect_conference(SIPE_CORE_PRIVATE, chat_session);
619 Local Variables:
620 mode: c
621 c-file-style: "bsd"
622 indent-tabs-mode: t
623 tab-width: 8
624 End: