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