1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2013 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "config.h"
19
20 #include "spice-client.h"
21 #include "spice-common.h"
22 #include "spice-channel-priv.h"
23 #include "spice-session-priv.h"
24 #include "spice-marshal.h"
25 #include "vmcstream.h"
26 #include "giopipe.h"
27
28 /**
29 * SECTION:channel-webdav
30 * @short_description: exports a directory
31 * @title: WebDAV Channel
32 * @section_id:
33 * @see_also: #SpiceChannel
34 * @stability: Stable
35 * @include: spice-client.h
36 *
37 * The "webdav" channel exports a directory to the guest for file
38 * manipulation (read/write/copy etc). The underlying protocol is
39 * implemented using WebDAV (RFC 4918).
40 *
41 * By default, the shared directory is the one associated with GLib
42 * %G_USER_DIRECTORY_PUBLIC_SHARE. You can specify a different
43 * directory with #SpiceSession #SpiceSession:shared-dir property.
44 *
45 * Since: 0.24
46 */
47
48 typedef struct _OutputQueue OutputQueue;
49
50 struct _SpiceWebdavChannelPrivate {
51 SpiceVmcStream *stream;
52 GCancellable *cancellable;
53 GHashTable *clients;
54 OutputQueue *queue;
55
56 gboolean demuxing;
57 struct _demux {
58 gint64 client;
59 guint16 size;
60 guint8 *buf;
61 } demux;
62 };
63
64 G_DEFINE_TYPE_WITH_PRIVATE(SpiceWebdavChannel, spice_webdav_channel, SPICE_TYPE_PORT_CHANNEL)
65
66 static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
67
68 struct _OutputQueue {
69 GOutputStream *output;
70 gboolean flushing;
71 guint idle_id;
72 GQueue *queue;
73 };
74
75 typedef struct _OutputQueueElem {
76 OutputQueue *queue;
77 const guint8 *buf;
78 gsize size;
79 GFunc pushed_cb;
80 gpointer user_data;
81 } OutputQueueElem;
82
output_queue_new(GOutputStream * output)83 static OutputQueue* output_queue_new(GOutputStream *output)
84 {
85 OutputQueue *queue = g_new0(OutputQueue, 1);
86
87 queue->output = g_object_ref(output);
88 queue->queue = g_queue_new();
89
90 return queue;
91 }
92
output_queue_free(OutputQueue * queue)93 static void output_queue_free(OutputQueue *queue)
94 {
95 g_warn_if_fail(g_queue_get_length(queue->queue) == 0);
96 g_warn_if_fail(!queue->flushing);
97
98 g_queue_free_full(queue->queue, g_free);
99 g_clear_object(&queue->output);
100 if (queue->idle_id)
101 g_source_remove(queue->idle_id);
102 g_free(queue);
103 }
104
105 static gboolean output_queue_idle(gpointer user_data);
106
output_queue_flush_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)107 static void output_queue_flush_cb(GObject *source_object,
108 GAsyncResult *res,
109 gpointer user_data)
110 {
111 GError *error = NULL;
112 OutputQueueElem *e = user_data;
113 OutputQueue *q = e->queue;
114
115 q->flushing = FALSE;
116 g_output_stream_flush_finish(G_OUTPUT_STREAM(source_object),
117 res, &error);
118 if (error)
119 g_warning("error: %s", error->message);
120
121 g_clear_error(&error);
122
123 if (!q->idle_id)
124 q->idle_id = g_idle_add(output_queue_idle, q);
125
126 g_free(e);
127 }
128
output_queue_idle(gpointer user_data)129 static gboolean output_queue_idle(gpointer user_data)
130 {
131 OutputQueue *q = user_data;
132 OutputQueueElem *e;
133 GError *error = NULL;
134
135 if (q->flushing) {
136 q->idle_id = 0;
137 return FALSE;
138 }
139
140 e = g_queue_pop_head(q->queue);
141 if (!e) {
142 q->idle_id = 0;
143 return FALSE;
144 }
145
146 if (!g_output_stream_write_all(q->output, e->buf, e->size, NULL, NULL, &error))
147 goto err;
148 else if (e->pushed_cb)
149 e->pushed_cb(q, e->user_data);
150
151 q->flushing = TRUE;
152 g_output_stream_flush_async(q->output, G_PRIORITY_DEFAULT, NULL, output_queue_flush_cb, e);
153
154 return TRUE;
155
156 err:
157 g_warning("failed to write to output stream");
158 if (error)
159 g_warning("error: %s", error->message);
160 g_clear_error(&error);
161
162 q->idle_id = 0;
163 return FALSE;
164 }
165
output_queue_push(OutputQueue * q,const guint8 * buf,gsize size,GFunc pushed_cb,gpointer user_data)166 static void output_queue_push(OutputQueue *q, const guint8 *buf, gsize size,
167 GFunc pushed_cb, gpointer user_data)
168 {
169 OutputQueueElem *e = g_new(OutputQueueElem, 1);
170
171 e->buf = buf;
172 e->size = size;
173 e->pushed_cb = pushed_cb;
174 e->user_data = user_data;
175 e->queue = q;
176 g_queue_push_tail(q->queue, e);
177
178 if (!q->idle_id && !q->flushing)
179 q->idle_id = g_idle_add(output_queue_idle, q);
180 }
181
182 typedef struct Client
183 {
184 guint refs;
185 SpiceWebdavChannel *self;
186 GIOStream *pipe;
187 gint64 id;
188 GCancellable *cancellable;
189
190 struct _mux {
191 gint64 id;
192 guint16 size;
193 guint8 *buf;
194 } mux;
195 } Client;
196
197 static void
client_unref(Client * client)198 client_unref(Client *client)
199 {
200 if (--client->refs > 0)
201 return;
202
203 g_free(client->mux.buf);
204
205 g_object_unref(client->pipe);
206 g_object_unref(client->cancellable);
207
208 g_free(client);
209 }
210
211 static Client *
client_ref(Client * client)212 client_ref(Client *client)
213 {
214 client->refs++;
215 return client;
216 }
217
218 static bool client_start_read(Client *client);
219
remove_client(Client * client)220 static void remove_client(Client *client)
221 {
222 if (g_cancellable_is_cancelled(client->cancellable))
223 return;
224
225 g_cancellable_cancel(client->cancellable);
226
227 g_hash_table_remove(client->self->priv->clients, &client->id);
228 }
229
mux_pushed_cb(OutputQueue * q,gpointer user_data)230 static void mux_pushed_cb(OutputQueue *q, gpointer user_data)
231 {
232 Client *client = user_data;
233
234 if (client->mux.size == 0 ||
235 !client_start_read(client)) {
236 remove_client(client);
237 }
238
239 client_unref(client);
240 }
241
242 #define MAX_MUX_SIZE G_MAXUINT16
243
server_reply_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)244 static void server_reply_cb(GObject *source_object,
245 GAsyncResult *res,
246 gpointer user_data)
247 {
248 Client *client = user_data;
249 SpiceWebdavChannelPrivate *c = client->self->priv;
250 GError *err = NULL;
251 gssize size;
252
253 size = g_input_stream_read_finish(G_INPUT_STREAM(source_object), res, &err);
254 if (err || g_cancellable_is_cancelled(client->cancellable))
255 goto end;
256
257 g_return_if_fail(size <= MAX_MUX_SIZE);
258 g_return_if_fail(size >= 0);
259 client->mux.size = size;
260
261 output_queue_push(c->queue, (guint8 *)&client->mux.id, sizeof(gint64), NULL, NULL);
262 client->mux.size = GUINT16_TO_LE(client->mux.size);
263 output_queue_push(c->queue, (guint8 *)&client->mux.size, sizeof(guint16), NULL, NULL);
264 output_queue_push(c->queue, (guint8 *)client->mux.buf, size, (GFunc)mux_pushed_cb, client);
265
266 return;
267
268 end:
269 if (err) {
270 if (!g_cancellable_is_cancelled(client->cancellable))
271 g_warning("read error: %s", err->message);
272 remove_client(client);
273 g_clear_error(&err);
274 }
275
276 client_unref(client);
277 }
278
client_start_read(Client * client)279 static bool client_start_read(Client *client)
280 {
281 GInputStream *input;
282
283 input = g_io_stream_get_input_stream(G_IO_STREAM(client->pipe));
284 if (g_input_stream_is_closed(input)) {
285 return false;
286 }
287 g_input_stream_read_async(input, client->mux.buf, MAX_MUX_SIZE,
288 G_PRIORITY_DEFAULT, client->cancellable, server_reply_cb,
289 client_ref(client));
290 return true;
291 }
292
293 static void start_demux(SpiceWebdavChannel *self);
294
295 #ifdef USE_PHODAV
demux_to_client_finish(Client * client,gboolean fail)296 static void demux_to_client_finish(Client *client, gboolean fail)
297 {
298 SpiceWebdavChannel *self = client->self;
299 SpiceWebdavChannelPrivate *c = self->priv;
300
301 if (fail) {
302 remove_client(client);
303 }
304
305 c->demuxing = FALSE;
306 start_demux(self);
307 }
308
demux_to_client_cb(GObject * source,GAsyncResult * result,gpointer user_data)309 static void demux_to_client_cb(GObject *source, GAsyncResult *result, gpointer user_data)
310 {
311 Client *client = user_data;
312 SpiceWebdavChannelPrivate *c = client->self->priv;
313 GError *error = NULL;
314 gboolean fail;
315 gsize size;
316
317 g_output_stream_write_all_finish(G_OUTPUT_STREAM(source), result, &size, &error);
318
319 if (error) {
320 CHANNEL_DEBUG(client->self, "write failed: %s", error->message);
321 g_clear_error(&error);
322 }
323
324 fail = (size != c->demux.size);
325 g_warn_if_fail(size == c->demux.size);
326 demux_to_client_finish(client, fail);
327 }
328 #endif
329
demux_to_client(Client * client)330 static void demux_to_client(Client *client)
331 {
332 #ifdef USE_PHODAV
333 SpiceWebdavChannelPrivate *c = client->self->priv;
334 gsize size = c->demux.size;
335
336 CHANNEL_DEBUG(client->self, "pushing %"G_GSIZE_FORMAT" to client %p", size, client);
337
338 if (size > 0) {
339 g_output_stream_write_all_async(g_io_stream_get_output_stream(client->pipe),
340 c->demux.buf, size, G_PRIORITY_DEFAULT,
341 c->cancellable, demux_to_client_cb, client);
342 return;
343 } else {
344 /* Nothing to write */
345 demux_to_client_finish(client, FALSE);
346 }
347 #endif
348 }
349
start_client(SpiceWebdavChannel * self)350 static void start_client(SpiceWebdavChannel *self)
351 {
352 #ifdef USE_PHODAV
353 SpiceWebdavChannelPrivate *c = self->priv;
354 Client *client;
355 GIOStream *peer = NULL;
356 SpiceSession *session;
357 SoupServer *server;
358 GSocketAddress *addr;
359 GError *error = NULL;
360 bool started;
361
362 session = spice_channel_get_session(SPICE_CHANNEL(self));
363 server = phodav_server_get_soup_server(spice_session_get_webdav_server(session));
364
365 CHANNEL_DEBUG(self, "starting client %" G_GINT64_FORMAT, c->demux.client);
366
367 client = g_new0(Client, 1);
368 client->refs = 1;
369 client->id = c->demux.client;
370 client->self = self;
371 client->mux.id = GINT64_TO_LE(client->id);
372 client->mux.buf = g_malloc0(MAX_MUX_SIZE);
373 client->cancellable = g_cancellable_new();
374 spice_make_pipe(&client->pipe, &peer);
375
376 addr = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
377 if (!soup_server_accept_iostream(server, peer, addr, addr, &error))
378 goto fail;
379
380 g_hash_table_insert(c->clients, &client->id, client);
381
382 started = client_start_read(client);
383 g_assert(started);
384 demux_to_client(client);
385
386 g_clear_object(&addr);
387 return;
388
389 fail:
390 if (error)
391 CHANNEL_DEBUG(self, "failed to start client: %s", error->message);
392
393 g_clear_object(&addr);
394 g_clear_object(&peer);
395 g_clear_error(&error);
396 client_unref(client);
397 #endif
398 }
399
data_read_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)400 static void data_read_cb(GObject *source_object,
401 GAsyncResult *res,
402 gpointer user_data)
403 {
404 SpiceWebdavChannel *self = user_data;
405 SpiceWebdavChannelPrivate *c;
406 Client *client;
407 GError *error = NULL;
408 gssize size;
409
410 size = spice_vmc_input_stream_read_all_finish(G_INPUT_STREAM(source_object), res, &error);
411 if (error) {
412 g_warning("error: %s", error->message);
413 g_clear_error(&error);
414 return;
415 }
416
417 c = self->priv;
418 g_return_if_fail(size == c->demux.size);
419
420 client = g_hash_table_lookup(c->clients, &c->demux.client);
421
422 if (client)
423 demux_to_client(client);
424 else
425 start_client(self);
426 }
427
428
size_read_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)429 static void size_read_cb(GObject *source_object,
430 GAsyncResult *res,
431 gpointer user_data)
432 {
433 SpiceWebdavChannel *self = user_data;
434 SpiceWebdavChannelPrivate *c;
435 GInputStream *istream = G_INPUT_STREAM(source_object);
436 GError *error = NULL;
437 gssize size;
438
439 size = spice_vmc_input_stream_read_all_finish(G_INPUT_STREAM(source_object), res, &error);
440 if (error || size != sizeof(guint16))
441 goto end;
442
443 c = self->priv;
444 c->demux.size = GUINT16_FROM_LE(c->demux.size);
445 spice_vmc_input_stream_read_all_async(istream,
446 c->demux.buf, c->demux.size,
447 G_PRIORITY_DEFAULT, c->cancellable, data_read_cb, self);
448 return;
449
450 end:
451 if (error) {
452 g_warning("error: %s", error->message);
453 g_clear_error(&error);
454 }
455 }
456
client_read_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)457 static void client_read_cb(GObject *source_object,
458 GAsyncResult *res,
459 gpointer user_data)
460 {
461 SpiceWebdavChannel *self = user_data;
462 SpiceWebdavChannelPrivate *c = self->priv;
463 GInputStream *istream = G_INPUT_STREAM(source_object);
464 GError *error = NULL;
465 gssize size;
466
467 size = spice_vmc_input_stream_read_all_finish(G_INPUT_STREAM(source_object), res, &error);
468 if (error || size != sizeof(gint64))
469 goto end;
470
471 c->demux.client = GINT64_FROM_LE(c->demux.client);
472 spice_vmc_input_stream_read_all_async(istream,
473 &c->demux.size, sizeof(guint16),
474 G_PRIORITY_DEFAULT, c->cancellable, size_read_cb, self);
475 return;
476
477 end:
478 if (error) {
479 g_warning("error: %s", error->message);
480 g_clear_error(&error);
481 }
482 }
483
start_demux(SpiceWebdavChannel * self)484 static void start_demux(SpiceWebdavChannel *self)
485 {
486 SpiceWebdavChannelPrivate *c = self->priv;
487 GInputStream *istream = g_io_stream_get_input_stream(G_IO_STREAM(c->stream));
488
489 if (c->demuxing)
490 return;
491
492 c->demuxing = TRUE;
493
494 CHANNEL_DEBUG(self, "start demux");
495 spice_vmc_input_stream_read_all_async(istream, &c->demux.client, sizeof(gint64),
496 G_PRIORITY_DEFAULT, c->cancellable, client_read_cb, self);
497
498 }
499
port_event(SpiceWebdavChannel * self,gint event)500 static void port_event(SpiceWebdavChannel *self, gint event)
501 {
502 SpiceWebdavChannelPrivate *c = self->priv;
503
504 CHANNEL_DEBUG(self, "port event:%d", event);
505 if (event == SPICE_PORT_EVENT_OPENED) {
506 g_clear_object(&c->cancellable);
507 c->cancellable = g_cancellable_new();
508 start_demux(self);
509 } else {
510 g_cancellable_cancel(c->cancellable);
511 c->demuxing = FALSE;
512 g_hash_table_remove_all(c->clients);
513 }
514 }
515
client_remove_unref(gpointer data)516 static void client_remove_unref(gpointer data)
517 {
518 Client *client = data;
519
520 g_cancellable_cancel(client->cancellable);
521 client_unref(client);
522 }
523
spice_webdav_channel_init(SpiceWebdavChannel * channel)524 static void spice_webdav_channel_init(SpiceWebdavChannel *channel)
525 {
526 SpiceWebdavChannelPrivate *c = spice_webdav_channel_get_instance_private(channel);
527
528 channel->priv = c;
529 c->stream = spice_vmc_stream_new(SPICE_CHANNEL(channel));
530 c->clients = g_hash_table_new_full(g_int64_hash, g_int64_equal,
531 NULL, client_remove_unref);
532 c->demux.buf = g_malloc0(MAX_MUX_SIZE);
533
534 GOutputStream *ostream = g_io_stream_get_output_stream(G_IO_STREAM(c->stream));
535 c->queue = output_queue_new(ostream);
536 }
537
spice_webdav_channel_finalize(GObject * object)538 static void spice_webdav_channel_finalize(GObject *object)
539 {
540 SpiceWebdavChannelPrivate *c = SPICE_WEBDAV_CHANNEL(object)->priv;
541
542 g_free(c->demux.buf);
543
544 G_OBJECT_CLASS(spice_webdav_channel_parent_class)->finalize(object);
545 }
546
spice_webdav_channel_dispose(GObject * object)547 static void spice_webdav_channel_dispose(GObject *object)
548 {
549 SpiceWebdavChannelPrivate *c = SPICE_WEBDAV_CHANNEL(object)->priv;
550
551 g_cancellable_cancel(c->cancellable);
552 g_clear_object(&c->cancellable);
553 g_clear_pointer(&c->queue, output_queue_free);
554 g_clear_object(&c->stream);
555 g_hash_table_unref(c->clients);
556
557 G_OBJECT_CLASS(spice_webdav_channel_parent_class)->dispose(object);
558 }
559
spice_webdav_channel_up(SpiceChannel * channel)560 static void spice_webdav_channel_up(SpiceChannel *channel)
561 {
562 CHANNEL_DEBUG(channel, "up");
563 }
564
spice_webdav_channel_class_init(SpiceWebdavChannelClass * klass)565 static void spice_webdav_channel_class_init(SpiceWebdavChannelClass *klass)
566 {
567 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
568 SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
569
570 gobject_class->dispose = spice_webdav_channel_dispose;
571 gobject_class->finalize = spice_webdav_channel_finalize;
572 channel_class->handle_msg = spice_webdav_handle_msg;
573 channel_class->channel_up = spice_webdav_channel_up;
574
575 g_signal_override_class_handler("port-event",
576 SPICE_TYPE_WEBDAV_CHANNEL,
577 G_CALLBACK(port_event));
578 }
579
580 /* coroutine context */
webdav_handle_data_msg(SpiceChannel * channel,SpiceMsgIn * in)581 static void webdav_handle_data_msg(SpiceChannel *channel, SpiceMsgIn *in)
582 {
583 SpiceWebdavChannel *self = SPICE_WEBDAV_CHANNEL(channel);
584 SpiceWebdavChannelPrivate *c = self->priv;
585 int size;
586 uint8_t *buf;
587
588 buf = spice_msg_in_raw(in, &size);
589 CHANNEL_DEBUG(channel, "len:%d buf:%p", size, buf);
590
591 spice_vmc_input_stream_co_data(
592 SPICE_VMC_INPUT_STREAM(g_io_stream_get_input_stream(G_IO_STREAM(c->stream))),
593 buf, size);
594 }
595
596
597 /* coroutine context */
spice_webdav_handle_msg(SpiceChannel * channel,SpiceMsgIn * msg)598 static void spice_webdav_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg)
599 {
600 int type = spice_msg_in_type(msg);
601 SpiceChannelClass *parent_class;
602
603 parent_class = SPICE_CHANNEL_CLASS(spice_webdav_channel_parent_class);
604
605 if (type == SPICE_MSG_SPICEVMC_DATA) {
606 webdav_handle_data_msg(channel, msg);
607 return;
608 }
609
610 /* The only message that we need to handle ourselves is SPICE_MSG_SPICEVMC_DATA
611 * as we want to read it with spice_vmc_input/output_stream to handle
612 * channel-webdav inner protocol easily ($client, $data_size, $data).
613 * Everything else is handled by port-event signal from channel-port.c so we
614 * let it read the message for us. */
615 g_return_if_fail(parent_class->handle_msg != NULL);
616 parent_class->handle_msg(channel, msg);
617 }
618