1/*
2 * Copyright 2011 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/** \file spiceqxl_spice_server.c
24 * \author Alon Levy <alevy@redhat.com>
25 *
26 * spice server helpers for spiceqxl.
27 */
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32#include "qxl.h"
33#include "qxl_option_helpers.h"
34#include "spiceqxl_spice_server.h"
35
36/* Single instance of spice server per Xorg executable.
37 */
38SpiceServer *xspice_get_spice_server(void)
39{
40    static SpiceServer *spice_server;
41    if (!spice_server) {
42        spice_server = spice_server_new();
43    }
44    return spice_server;
45}
46
47#define X509_CA_CERT_FILE "ca-cert.pem"
48#define X509_SERVER_KEY_FILE "server-key.pem"
49#define X509_SERVER_CERT_FILE "server-cert.pem"
50
51/* config string parsing */
52
53#define SPICE_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
54
55static int name2enum(const char *string, const char *table[], int entries)
56{
57    int i;
58
59    if (string) {
60        for (i = 0; i < entries; i++) {
61            if (!table[i]) {
62                continue;
63            }
64            if (strcmp(string, table[i]) != 0) {
65                continue;
66            }
67            return i;
68        }
69    }
70    return -1;
71}
72
73static int parse_name(const char *string, const char *optname,
74                      const char *table[], int entries)
75{
76    int value = name2enum(string, table, entries);
77
78    if (value != -1) {
79        return value;
80    }
81    fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
82    exit(1);
83}
84
85static const char *stream_video_names[] = {
86    [ SPICE_STREAM_VIDEO_OFF ]    = "off",
87    [ SPICE_STREAM_VIDEO_ALL ]    = "all",
88    [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
89};
90#define parse_stream_video(_name) \
91    name2enum(_name, stream_video_names, SPICE_ARRAY_SIZE(stream_video_names))
92
93static const char *compression_names[] = {
94    [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
95    [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
96    [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
97    [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
98    [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
99    [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
100};
101#define parse_compression(_name)                                        \
102    parse_name(_name, "image compression",                              \
103               compression_names, SPICE_ARRAY_SIZE(compression_names))
104
105static const char *wan_compression_names[] = {
106    [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
107    [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
108    [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
109};
110#define parse_wan_compression(_name)                                    \
111    parse_name(_name, "wan compression",                                \
112               wan_compression_names, SPICE_ARRAY_SIZE(wan_compression_names))
113
114void xspice_set_spice_server_options(OptionInfoPtr options)
115{
116    /* environment variables take precedence. If not then take
117     * parameters from the config file. */
118    int addr_flags;
119    int len;
120    spice_image_compression_t compression;
121    spice_wan_compression_t wan_compr;
122    int port = get_int_option(options, OPTION_SPICE_PORT, "XSPICE_PORT");
123    int tls_port =
124        get_int_option(options, OPTION_SPICE_TLS_PORT, "XSPICE_TLS_PORT");
125    const char *password =
126        get_str_option(options, OPTION_SPICE_PASSWORD, "XSPICE_PASSWORD");
127    int disable_ticketing =
128        get_bool_option(options, OPTION_SPICE_DISABLE_TICKETING, "XSPICE_DISABLE_TICKETING");
129    const char *x509_dir =
130        get_str_option(options, OPTION_SPICE_X509_DIR, "XSPICE_X509_DIR");
131    int sasl = get_bool_option(options, OPTION_SPICE_SASL, "XSPICE_SASL");
132    const char *x509_key_file_base =
133        get_str_option(options, OPTION_SPICE_X509_KEY_FILE,
134                       "XSPICE_X509_KEY_FILE");
135    char *x509_key_file = NULL;
136    const char *x509_cert_file_base =
137        get_str_option(options, OPTION_SPICE_X509_CERT_FILE,
138                       "XSPICE_X509_CERT_FILE");
139    char *x509_cert_file = NULL;
140    const char *x509_key_password =
141        get_str_option(options, OPTION_SPICE_X509_KEY_PASSWORD,
142                    "XSPICE_X509_KEY_PASSWORD");
143    const char *tls_ciphers =
144        get_str_option(options, OPTION_SPICE_TLS_CIPHERS,
145                    "XSPICE_TLS_CIPHERS");
146    const char *x509_cacert_file_base =
147        get_str_option(options, OPTION_SPICE_CACERT_FILE,
148                    "XSPICE_CACERT_FILE");
149    char *x509_cacert_file = NULL;
150    const char * addr =
151        get_str_option(options, OPTION_SPICE_ADDR, "XSPICE_ADDR");
152    int ipv4 =
153        get_bool_option(options, OPTION_SPICE_IPV4_ONLY, "XSPICE_IPV4_ONLY");
154    int ipv6 =
155        get_bool_option(options, OPTION_SPICE_IPV6_ONLY, "XSPICE_IPV6_ONLY");
156    const char *x509_dh_file =
157        get_str_option(options, OPTION_SPICE_DH_FILE, "XSPICE_DH_FILE");
158    int disable_copy_paste =
159        get_bool_option(options, OPTION_SPICE_DISABLE_COPY_PASTE,
160                        "XSPICE_DISABLE_COPY_PASTE");
161    int exit_on_disconnect =
162        get_bool_option(options, OPTION_SPICE_EXIT_ON_DISCONNECT,
163                        "XSPICE_EXIT_ON_DISCONNECT");
164    const char *image_compression =
165        get_str_option(options, OPTION_SPICE_IMAGE_COMPRESSION,
166                       "XSPICE_IMAGE_COMPRESSION");
167    const char *jpeg_wan_compression =
168        get_str_option(options, OPTION_SPICE_JPEG_WAN_COMPRESSION,
169                       "XSPICE_JPEG_WAN_COMPRESSION");
170    const char *zlib_glz_wan_compression =
171        get_str_option(options, OPTION_SPICE_ZLIB_GLZ_WAN_COMPRESSION,
172                       "XSPICE_ZLIB_GLZ_WAN_COMPRESSION");
173    const char *streaming_video =
174        get_str_option(options, OPTION_SPICE_STREAMING_VIDEO,
175                       "XSPICE_STREAMING_VIDEO");
176    const char *video_codecs =
177        get_str_option(options, OPTION_SPICE_VIDEO_CODECS,
178                       "XSPICE_VIDEO_CODECS");
179    int agent_mouse =
180        get_bool_option(options, OPTION_SPICE_AGENT_MOUSE,
181                        "XSPICE_AGENT_MOUSE");
182    int playback_compression =
183        get_bool_option(options, OPTION_SPICE_PLAYBACK_COMPRESSION,
184                        "XSPICE_PLAYBACK_COMPRESSION");
185
186    SpiceServer *spice_server = xspice_get_spice_server();
187
188    if (!port && !tls_port) {
189        printf("one of tls-port or port must be set\n");
190        exit(1);
191    }
192    printf("xspice: port = %d, tls_port = %d\n", port, tls_port);
193    if (disable_ticketing) {
194        spice_server_set_noauth(spice_server);
195    }
196    if (tls_port) {
197        if (NULL == x509_dir) {
198            x509_dir = ".";
199        }
200        len = strlen(x509_dir) + 32;
201
202        if (x509_key_file_base) {
203            x509_key_file = xnfstrdup(x509_key_file_base);
204        } else {
205            x509_key_file = xnfalloc(len);
206            snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
207        }
208
209        if (x509_cert_file_base) {
210            x509_cert_file = xnfstrdup(x509_cert_file_base);
211        } else {
212            x509_cert_file = xnfalloc(len);
213            snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
214        }
215
216        if (x509_cacert_file_base) {
217            x509_cacert_file = xnfstrdup(x509_cert_file_base);
218        } else {
219            x509_cacert_file = xnfalloc(len);
220            snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
221        }
222    }
223
224    addr_flags = 0;
225    if (ipv4) {
226        addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
227    } else if (ipv6) {
228        addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
229    }
230
231    spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
232    if (port) {
233        spice_server_set_port(spice_server, port);
234    }
235    if (tls_port) {
236        spice_server_set_tls(spice_server, tls_port,
237                             x509_cacert_file,
238                             x509_cert_file,
239                             x509_key_file,
240                             x509_key_password,
241                             x509_dh_file,
242                             tls_ciphers);
243    }
244    if (password) {
245        spice_server_set_ticket(spice_server, password, 0, 0, 0);
246    }
247    if (sasl) {
248#if SPICE_SERVER_VERSION >= 0x000802 /* 0.8.2 */
249        if (spice_server_set_sasl_appname(spice_server, "xspice") == -1 ||
250            spice_server_set_sasl(spice_server, 1) == -1) {
251            fprintf(stderr, "spice: failed to enable sasl\n");
252            exit(1);
253        }
254#else
255        fprintf(stderr, "spice: sasl is not available (spice >= 0.8.2 required)\n");
256        exit(1);
257#endif
258    }
259
260#if SPICE_SERVER_VERSION >= 0x000801
261    /* we still don't actually support agent in xspice, but this
262     * can't hurt for later, just copied from qemn/ui/spice-core.c */
263    if (disable_copy_paste) {
264        spice_server_set_agent_copypaste(spice_server, 0);
265    }
266#endif
267
268    if (exit_on_disconnect) {
269#if SPICE_SERVER_VERSION >= 0x000b04 /* 0.11.4 */
270        spice_server_set_exit_on_disconnect(spice_server, exit_on_disconnect);
271#else
272        fprintf(stderr, "spice: cannot set exit_on_disconnect (spice >= 0.11.4 required)\n");
273        exit(1);
274#endif
275    }
276
277    compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
278    if (image_compression) {
279        compression = parse_compression(image_compression);
280    }
281    spice_server_set_image_compression(spice_server, compression);
282
283    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
284    if (jpeg_wan_compression) {
285        wan_compr = parse_wan_compression(jpeg_wan_compression);
286    }
287    spice_server_set_jpeg_compression(spice_server, wan_compr);
288
289    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
290    if (zlib_glz_wan_compression) {
291        wan_compr = parse_wan_compression(zlib_glz_wan_compression);
292    }
293    spice_server_set_zlib_glz_compression(spice_server, wan_compr);
294
295    if (streaming_video) {
296        int streaming_video_opt = parse_stream_video(streaming_video);
297        spice_server_set_streaming_video(spice_server, streaming_video_opt);
298    }
299
300    if (video_codecs) {
301#if SPICE_SERVER_VERSION >= 0x000d02 /* 0.13.2 */
302        if (spice_server_set_video_codecs(spice_server, video_codecs)) {
303            fprintf(stderr, "spice: invalid video encoder %s\n", video_codecs);
304            exit(1);
305        }
306#else
307        fprintf(stderr, "spice: video_codecs are not available (spice >= 0.13.2 required)\n");
308        exit(1);
309#endif
310    }
311
312    spice_server_set_agent_mouse(spice_server, agent_mouse);
313    spice_server_set_playback_compression(spice_server, playback_compression);
314
315    free(x509_key_file);
316    free(x509_cert_file);
317    free(x509_cacert_file);
318}
319