appshare: move connect_conference to core
[siplcs.git] / src / core / sipe-appshare.c
blob182bd62ff9e869daf019acfdb6061c442d1df9fb
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>
25 #include <gio/gio.h>
27 #include "sipmsg.h"
28 #include "sipe-appshare.h"
29 #include "sipe-appshare-xfreerdp.h"
30 #include "sipe-appshare-remmina.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 typedef gboolean (*rdp_init_func)(struct sipe_rdp_client *);
61 rdp_init_func rdp_init_functions[] = {
62 sipe_appshare_remmina_init,
63 sipe_appshare_xfreerdp_init,
64 NULL
67 static void
68 sipe_appshare_free(struct sipe_appshare *appshare)
70 if (appshare->rdp_channel_readable_watch_id != 0) {
71 g_source_destroy(g_main_context_find_source_by_id(NULL,
72 appshare->rdp_channel_readable_watch_id));
75 if (appshare->rdp_channel_writable_watch_id != 0) {
76 g_source_destroy(g_main_context_find_source_by_id(NULL,
77 appshare->rdp_channel_writable_watch_id));
80 if (appshare->channel) {
81 GError *error = NULL;
83 g_io_channel_shutdown(appshare->channel, TRUE, &error);
84 if (error) {
85 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
86 error->message);
87 g_error_free(error);
89 g_io_channel_unref(appshare->channel);
92 if (appshare->socket) {
93 g_object_unref(appshare->socket);
96 if (appshare->ask_ctx) {
97 sipe_user_close_ask(appshare->ask_ctx);
100 if (appshare->client.free_cb) {
101 appshare->client.free_cb(&appshare->client);
104 g_free(appshare);
107 static gboolean
108 rdp_channel_readable_cb(GIOChannel *channel,
109 GIOCondition condition,
110 gpointer data)
112 struct sipe_appshare *appshare = data;
113 GError *error = NULL;
114 gchar *buffer;
115 gsize bytes_read;
117 if (condition & G_IO_HUP) {
118 struct sipe_media_call *call = appshare->stream->call;
120 sipe_backend_media_hangup(call->backend_private, TRUE);
121 return FALSE;
124 buffer = g_malloc(2048);
125 while (sipe_media_stream_is_writable(appshare->stream)) {
126 GIOStatus status;
128 status = g_io_channel_read_chars(channel,
129 buffer, 2048,
130 &bytes_read, &error);
131 if (error) {
132 struct sipe_media_call *call = appshare->stream->call;
134 SIPE_DEBUG_ERROR("Error reading from RDP channel: %s",
135 error->message);
136 g_error_free(error);
137 sipe_backend_media_hangup(call->backend_private, TRUE);
138 g_free(buffer);
139 return FALSE;
142 if (status == G_IO_STATUS_EOF) {
143 struct sipe_media_call *call = appshare->stream->call;
145 sipe_backend_media_hangup(call->backend_private, TRUE);
146 g_free(buffer);
147 return FALSE;
150 if (bytes_read == 0) {
151 break;
154 sipe_media_stream_write(appshare->stream, (guint8 *)buffer,
155 bytes_read);
156 SIPE_DEBUG_INFO("Written: %" G_GSIZE_FORMAT "\n", bytes_read);
158 g_free(buffer);
160 return TRUE;
163 static gboolean
164 socket_connect_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
165 SIPE_UNUSED_PARAMETER GIOCondition condition,
166 gpointer data)
168 struct sipe_appshare *appshare = data;
169 GError *error = NULL;
170 GSocket *data_socket;
172 data_socket = g_socket_accept(appshare->socket, NULL, &error);
173 if (error) {
174 struct sipe_media_call *call = appshare->stream->call;
176 SIPE_DEBUG_ERROR("Error accepting RDP client connection: %s",
177 error->message);
178 g_error_free(error);
179 sipe_backend_media_hangup(call->backend_private, TRUE);
180 return FALSE;
183 g_io_channel_shutdown(appshare->channel, TRUE, &error);
184 if (error) {
185 struct sipe_media_call *call = appshare->stream->call;
187 SIPE_DEBUG_ERROR("Error shutting down RDP channel: %s",
188 error->message);
189 g_error_free(error);
190 g_object_unref(data_socket);
191 sipe_backend_media_hangup(call->backend_private, TRUE);
192 return FALSE;
194 g_io_channel_unref(appshare->channel);
196 g_object_unref(appshare->socket);
197 appshare->socket = data_socket;
199 appshare->channel = g_io_channel_unix_new(
200 g_socket_get_fd(appshare->socket));
202 // No encoding for binary data
203 g_io_channel_set_encoding(appshare->channel, NULL, &error);
204 if (error) {
205 struct sipe_media_call *call = appshare->stream->call;
207 SIPE_DEBUG_ERROR("Error setting RDP channel encoding: %s",
208 error->message);
209 g_error_free(error);
210 sipe_backend_media_hangup(call->backend_private, TRUE);
211 return FALSE;
214 appshare->rdp_channel_readable_watch_id =
215 g_io_add_watch(appshare->channel, G_IO_IN | G_IO_HUP,
216 rdp_channel_readable_cb, appshare);
218 return FALSE;
221 static void
222 launch_rdp_client(struct sipe_appshare *appshare)
224 struct sipe_rdp_client *client = &appshare->client;
225 struct sipe_media_call *call = appshare->stream->call;
226 GSocketAddress *address;
227 GError *error = NULL;
229 address = client->get_listen_address_cb(client);
230 if (!address) {
231 sipe_backend_media_hangup(call->backend_private, TRUE);
232 return;
235 appshare->socket = g_socket_new(g_socket_address_get_family(address),
236 G_SOCKET_TYPE_STREAM,
237 G_SOCKET_PROTOCOL_DEFAULT,
238 &error);
239 if (error) {
240 SIPE_DEBUG_ERROR("Can't create RDP client listen socket: %s",
241 error->message);
242 g_error_free(error);
243 sipe_backend_media_hangup(call->backend_private, TRUE);
244 return;
247 g_socket_set_blocking(appshare->socket, FALSE);
249 g_socket_bind(appshare->socket, address, TRUE, &error);
250 g_object_unref(address);
251 if (error) {
252 SIPE_DEBUG_ERROR("Can't bind to RDP client socket: %s",
253 error->message);
254 g_error_free(error);
255 sipe_backend_media_hangup(call->backend_private, TRUE);
256 return;
259 g_socket_listen(appshare->socket, &error);
260 if (error) {
261 SIPE_DEBUG_ERROR("Can't listen on RDP client socket: %s",
262 error->message);
263 g_error_free(error);
264 sipe_backend_media_hangup(call->backend_private, TRUE);
265 return;
268 appshare->channel = g_io_channel_unix_new(
269 g_socket_get_fd(appshare->socket));
270 appshare->rdp_channel_readable_watch_id =
271 g_io_add_watch(appshare->channel, G_IO_IN,
272 socket_connect_cb, appshare);
274 address = g_socket_get_local_address(appshare->socket, &error);
275 if (error) {
276 SIPE_DEBUG_ERROR("Couldn't get appshare socket address: %s",
277 error->message);
278 g_error_free(error);
279 sipe_backend_media_hangup(call->backend_private, TRUE);
280 return;
283 if (!client->launch_cb(client, address, appshare->stream)) {
284 sipe_backend_media_hangup(call->backend_private, TRUE);
287 g_object_unref(address);
290 static gssize
291 rdp_client_channel_write(struct sipe_appshare *appshare)
293 gsize bytes_written;
294 GError *error = NULL;
296 g_io_channel_write_chars(appshare->channel,
297 appshare->rdp_channel_buffer_pos,
298 appshare->rdp_channel_buffer_len,
299 &bytes_written, &error);
300 if (error) {
301 SIPE_DEBUG_ERROR("Couldn't write data to RDP client: %s",
302 error->message);
303 g_error_free(error);
304 return -1;
307 g_io_channel_flush(appshare->channel, &error);
308 if (error) {
309 if (g_error_matches(error, G_IO_CHANNEL_ERROR,
310 G_IO_CHANNEL_ERROR_PIPE)) {
311 /* Ignore broken pipe here and wait for the call to be
312 * hung up upon G_IO_HUP in client_channel_cb(). */
313 g_error_free(error);
314 return 0;
317 SIPE_DEBUG_ERROR("Couldn't flush RDP channel: %s",
318 error->message);
319 g_error_free(error);
320 return -1;
323 appshare->rdp_channel_buffer_pos += bytes_written;
324 appshare->rdp_channel_buffer_len -= bytes_written;
326 return bytes_written;
329 static void
330 delayed_hangup_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
331 gpointer data)
333 struct sipe_media_call *call = data;
335 sipe_backend_media_hangup(call->backend_private, TRUE);
338 static gboolean
339 rdp_channel_writable_cb(SIPE_UNUSED_PARAMETER GIOChannel *channel,
340 SIPE_UNUSED_PARAMETER GIOCondition condition,
341 gpointer data)
343 struct sipe_appshare *appshare = data;
344 struct sipe_media_call *call = appshare->stream->call;
346 if (rdp_client_channel_write(appshare) < 0) {
347 sipe_backend_media_hangup(call->backend_private, TRUE);
348 return FALSE;
351 if (appshare->rdp_channel_buffer_len == 0) {
352 // Writing done, disconnect writable watch.
353 appshare->rdp_channel_writable_watch_id = 0;
354 return FALSE;
357 return TRUE;
360 static void
361 read_cb(struct sipe_media_stream *stream)
363 struct sipe_appshare *appshare = sipe_media_stream_get_data(stream);
364 gint bytes_read = 0;
365 gssize bytes_written = 0;
367 if (appshare->rdp_channel_writable_watch_id != 0) {
368 // Data still in the buffer. Let the client read it first.
369 return;
372 while (bytes_read == (gint)bytes_written) {
373 bytes_read = sipe_backend_media_stream_read(stream,
374 (guint8 *)appshare->rdp_channel_buffer,
375 sizeof (appshare->rdp_channel_buffer));
376 if (bytes_read == 0) {
377 return;
380 appshare->rdp_channel_buffer_pos = appshare->rdp_channel_buffer;
381 appshare->rdp_channel_buffer_len = bytes_read;
383 bytes_written = rdp_client_channel_write(appshare);
385 if (bytes_written < 0) {
386 /* Don't deallocate stream while in its read callback.
387 * Schedule call hangup to be executed after we're back
388 * in the message loop. */
389 sipe_schedule_seconds(sipe_media_get_sipe_core_private(stream->call),
390 "appshare delayed hangup",
391 stream->call->backend_private,
393 delayed_hangup_cb,
394 NULL);
395 return;
399 if (bytes_read != (gint)bytes_written) {
400 /* Schedule writing of the buffer's remainder to when
401 * RDP channel becomes writable again. */
402 appshare->rdp_channel_writable_watch_id =
403 g_io_add_watch(appshare->channel, G_IO_OUT,
404 rdp_channel_writable_cb,
405 appshare);
409 static void
410 writable_cb(struct sipe_media_stream *stream)
412 struct sipe_appshare *appshare = sipe_media_stream_get_data(stream);
414 if (!appshare->socket) {
415 launch_rdp_client(appshare);
419 static void
420 accept_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
421 gpointer data)
423 struct sipe_appshare *appshare = data;
424 appshare->ask_ctx = NULL;
426 sipe_backend_media_accept(appshare->stream->call->backend_private, TRUE);
429 static void
430 decline_cb(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
431 gpointer data)
433 struct sipe_appshare *appshare = data;
434 appshare->ask_ctx = NULL;
436 sipe_backend_media_hangup(appshare->stream->call->backend_private, TRUE);
439 static struct sipe_user_ask_ctx *
440 ask_accept_applicationsharing(struct sipe_core_private *sipe_private,
441 const gchar *from,
442 SipeUserAskCb accept_cb,
443 SipeUserAskCb decline_cb,
444 gpointer user_data)
446 struct sipe_user_ask_ctx *ctx;
447 gchar *alias = sipe_buddy_get_alias(sipe_private, from);
448 gchar *ask_msg = g_strdup_printf(_("%s wants to start presenting"),
449 alias ? alias : from);
451 ctx = sipe_user_ask(sipe_private, ask_msg,
452 _("Accept"), accept_cb,
453 _("Decline"), decline_cb,
454 user_data);
456 g_free(ask_msg);
457 g_free(alias);
459 return ctx;
462 static struct sipe_appshare *
463 initialize_appshare(struct sipe_media_stream *stream)
465 struct sipe_appshare *appshare;
466 struct sipe_media_call *call;
467 struct sipe_core_private *sipe_private;
468 SipeRDPClient client;
470 call = stream->call;
471 sipe_private = sipe_media_get_sipe_core_private(call);
473 appshare = g_new0(struct sipe_appshare, 1);
474 appshare->stream = stream;
476 sipe_media_stream_set_data(stream, appshare,
477 (GDestroyNotify)sipe_appshare_free);
479 client = sipe_backend_appshare_get_rdp_client(SIPE_CORE_PUBLIC);
480 if (!rdp_init_functions[client](&appshare->client)) {
481 /* Preferred client isn't available. Fall back to whatever
482 * application we can find. */
483 rdp_init_func *init;
485 for (init = rdp_init_functions; *init; ++init) {
486 if ((*init)(&appshare->client)) {
487 break;
491 if (*init == NULL) {
492 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
493 _("Application sharing error"),
494 _("Remote desktop client isn't installed."));
495 sipe_backend_media_hangup(call->backend_private, TRUE);
496 return NULL;
500 sipe_media_stream_add_extra_attribute(stream,
501 "x-applicationsharing-session-id", "1");
502 sipe_media_stream_add_extra_attribute(stream,
503 "x-applicationsharing-role", "viewer");
504 sipe_media_stream_add_extra_attribute(stream,
505 "x-applicationsharing-media-type", "rdp");
507 stream->read_cb = read_cb;
508 stream->writable_cb = writable_cb;
510 return appshare;
513 void
514 process_incoming_invite_appshare(struct sipe_core_private *sipe_private,
515 struct sipmsg *msg)
517 struct sipe_media_call *call;
518 struct sipe_media_stream *stream;
519 struct sipe_appshare *appshare;
521 call = process_incoming_invite_call(sipe_private, msg);
522 if (!call) {
523 return;
526 stream = sipe_core_media_get_stream_by_id(call, "applicationsharing");
527 if (!stream) {
528 sipe_backend_media_hangup(call->backend_private, TRUE);
529 return;
532 appshare = initialize_appshare(stream);
534 if (appshare) {
535 gchar *from;
537 from = parse_from(sipmsg_find_header(msg, "From"));
538 appshare->ask_ctx = ask_accept_applicationsharing(sipe_private, from,
539 accept_cb,
540 decline_cb,
541 appshare);
542 g_free(from);
546 static void
547 connect_conference(struct sipe_core_private *sipe_private,
548 struct sipe_chat_session *chat_session)
550 struct sipe_media_call *call;
551 struct sipe_media_stream *stream;
552 gchar * uri;
554 uri = sipe_conf_build_uri(chat_session->id, "applicationsharing");
556 call = sipe_media_call_new(sipe_private, uri, NULL,
557 SIPE_ICE_RFC_5245,
558 SIPE_MEDIA_CALL_NO_UI);
560 g_free(uri);
562 stream = sipe_media_stream_add(call, "applicationsharing",
563 SIPE_MEDIA_APPLICATION,
564 SIPE_ICE_RFC_5245, TRUE, 0);
565 if (!stream) {
566 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
567 _("Application sharing error"),
568 _("Couldn't connect application sharing"));
569 sipe_backend_media_hangup(call->backend_private, FALSE);
572 sipe_media_stream_add_extra_attribute(stream, "connection", "new");
573 sipe_media_stream_add_extra_attribute(stream, "setup", "active");
575 initialize_appshare(stream);
578 void
579 sipe_core_appshare_connect_conference(struct sipe_core_public *sipe_public,
580 struct sipe_chat_session *chat_session,
581 gboolean user_must_accept)
583 if (user_must_accept) {
584 const gchar *from;
586 if (chat_session->title) {
587 from = chat_session->title;
588 } else if (chat_session->organizer) {
589 from = chat_session->organizer;
590 } else {
591 from = chat_session->id;
594 ask_accept_applicationsharing(SIPE_CORE_PRIVATE, from,
595 (SipeUserAskCb)connect_conference,
596 NULL,
597 chat_session);
598 } else {
599 connect_conference(SIPE_CORE_PRIVATE, chat_session);
604 Local Variables:
605 mode: c
606 c-file-style: "bsd"
607 indent-tabs-mode: t
608 tab-width: 8
609 End: