cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

protocol.c (43579B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21
     22#include "guacamole/error.h"
     23#include "guacamole/layer.h"
     24#include "guacamole/object.h"
     25#include "guacamole/protocol.h"
     26#include "guacamole/protocol-types.h"
     27#include "guacamole/socket.h"
     28#include "guacamole/stream.h"
     29#include "guacamole/unicode.h"
     30#include "palette.h"
     31
     32#include <cairo/cairo.h>
     33
     34#include <inttypes.h>
     35#include <setjmp.h>
     36#include <stdarg.h>
     37#include <stdint.h>
     38#include <stdio.h>
     39#include <stdlib.h>
     40#include <string.h>
     41#include <sys/types.h>
     42
     43/**
     44 * A structure mapping the enum value of a Guacamole protocol version to the
     45 * string representation of the version.
     46 */
     47typedef struct guac_protocol_version_mapping {
     48    
     49    /**
     50     * The enum value of the protocol version.
     51     */
     52    guac_protocol_version version;
     53    
     54    /**
     55     * The string value representing the protocol version.
     56     */
     57    char* version_string;
     58    
     59} guac_protocol_version_mapping;
     60
     61/**
     62 * The map of known protocol versions to the corresponding string value.
     63 */
     64guac_protocol_version_mapping guac_protocol_version_table[] = {
     65    { GUAC_PROTOCOL_VERSION_1_0_0,   "VERSION_1_0_0" },
     66    { GUAC_PROTOCOL_VERSION_1_1_0,   "VERSION_1_1_0" },
     67    { GUAC_PROTOCOL_VERSION_1_3_0,   "VERSION_1_3_0" },
     68    { GUAC_PROTOCOL_VERSION_1_5_0,   "VERSION_1_5_0" },
     69    { GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
     70};
     71
     72/* Output formatting functions */
     73
     74ssize_t __guac_socket_write_length_string(guac_socket* socket, const char* str) {
     75
     76    return
     77           guac_socket_write_int(socket, guac_utf8_strlen(str))
     78        || guac_socket_write_string(socket, ".")
     79        || guac_socket_write_string(socket, str);
     80
     81}
     82
     83ssize_t __guac_socket_write_length_int(guac_socket* socket, int64_t i) {
     84
     85    char buffer[128];
     86    snprintf(buffer, sizeof(buffer), "%"PRIi64, i);
     87    return __guac_socket_write_length_string(socket, buffer);
     88
     89}
     90
     91ssize_t __guac_socket_write_length_double(guac_socket* socket, double d) {
     92
     93    char buffer[128];
     94    snprintf(buffer, sizeof(buffer), "%.16g", d);
     95    return __guac_socket_write_length_string(socket, buffer);
     96
     97}
     98
     99/**
    100 * Loop through the provided NULL-terminated array, writing the values in the
    101 * array to the given socket. Values are written as a series of Guacamole
    102 * protocol elements, including the leading comma and the value length in
    103 * addition to the value itself. Returns zero on success, non-zero on error.
    104 *
    105 * @param socket
    106 *     The socket to which the data should be written.
    107 *
    108 * @param array
    109 *     The NULL-terminated array of values to write.
    110 *
    111 * @return
    112 *     Zero on success, non-zero on error.
    113 */
    114static int guac_socket_write_array(guac_socket* socket, const char** array) {
    115
    116    /* Loop through array, writing provided values to the socket. */
    117    for (int i=0; array[i] != NULL; i++) {
    118
    119        if (guac_socket_write_string(socket, ","))
    120            return -1;
    121
    122        if (__guac_socket_write_length_string(socket, array[i]))
    123            return -1;
    124
    125    }
    126
    127    return 0;
    128
    129}
    130
    131/* Protocol functions */
    132
    133int guac_protocol_send_ack(guac_socket* socket, guac_stream* stream,
    134        const char* error, guac_protocol_status status) {
    135
    136    int ret_val;
    137
    138    guac_socket_instruction_begin(socket);
    139    ret_val =
    140           guac_socket_write_string(socket, "3.ack,")
    141        || __guac_socket_write_length_int(socket, stream->index)
    142        || guac_socket_write_string(socket, ",")
    143        || __guac_socket_write_length_string(socket, error)
    144        || guac_socket_write_string(socket, ",")
    145        || __guac_socket_write_length_int(socket, status)
    146        || guac_socket_write_string(socket, ";");
    147
    148    guac_socket_instruction_end(socket);
    149    return ret_val;
    150
    151}
    152
    153static int __guac_protocol_send_args(guac_socket* socket, const char** args) {
    154
    155    if (guac_socket_write_string(socket, "4.args")) return -1;
    156    
    157    /* Send protocol version ahead of other args. */
    158    if (guac_socket_write_string(socket, ",")
    159            || __guac_socket_write_length_string(socket, GUACAMOLE_PROTOCOL_VERSION))
    160        return -1;
    161
    162    if (guac_socket_write_array(socket, args))
    163        return -1;
    164
    165    return guac_socket_write_string(socket, ";");
    166
    167}
    168
    169int guac_protocol_send_args(guac_socket* socket, const char** args) {
    170
    171    int ret_val;
    172
    173    guac_socket_instruction_begin(socket);
    174    ret_val = __guac_protocol_send_args(socket, args);
    175    guac_socket_instruction_end(socket);
    176
    177    return ret_val;
    178
    179}
    180
    181int guac_protocol_send_argv(guac_socket* socket, guac_stream* stream,
    182        const char* mimetype, const char* name) {
    183
    184    int ret_val;
    185
    186    guac_socket_instruction_begin(socket);
    187    ret_val =
    188           guac_socket_write_string(socket, "4.argv,")
    189        || __guac_socket_write_length_int(socket, stream->index)
    190        || guac_socket_write_string(socket, ",")
    191        || __guac_socket_write_length_string(socket, mimetype)
    192        || guac_socket_write_string(socket, ",")
    193        || __guac_socket_write_length_string(socket, name)
    194        || guac_socket_write_string(socket, ";");
    195
    196    guac_socket_instruction_end(socket);
    197    return ret_val;
    198
    199}
    200
    201int guac_protocol_send_arc(guac_socket* socket, const guac_layer* layer,
    202        int x, int y, int radius, double startAngle, double endAngle,
    203        int negative) {
    204
    205    int ret_val;
    206
    207    guac_socket_instruction_begin(socket);
    208    ret_val =
    209           guac_socket_write_string(socket, "3.arc,")
    210        || __guac_socket_write_length_int(socket, layer->index)
    211        || guac_socket_write_string(socket, ",")
    212        || __guac_socket_write_length_int(socket, x)
    213        || guac_socket_write_string(socket, ",")
    214        || __guac_socket_write_length_int(socket, y)
    215        || guac_socket_write_string(socket, ",")
    216        || __guac_socket_write_length_int(socket, radius)
    217        || guac_socket_write_string(socket, ",")
    218        || __guac_socket_write_length_double(socket, startAngle)
    219        || guac_socket_write_string(socket, ",")
    220        || __guac_socket_write_length_double(socket, endAngle)
    221        || guac_socket_write_string(socket, ",")
    222        || guac_socket_write_string(socket, negative ? "1.1" : "1.0")
    223        || guac_socket_write_string(socket, ";");
    224    guac_socket_instruction_end(socket);
    225
    226    return ret_val;
    227
    228}
    229
    230int guac_protocol_send_audio(guac_socket* socket, const guac_stream* stream,
    231        const char* mimetype) {
    232
    233    int ret_val;
    234
    235    guac_socket_instruction_begin(socket);
    236    ret_val = 
    237           guac_socket_write_string(socket, "5.audio,")
    238        || __guac_socket_write_length_int(socket, stream->index)
    239        || guac_socket_write_string(socket, ",")
    240        || __guac_socket_write_length_string(socket, mimetype)
    241        || guac_socket_write_string(socket, ";");
    242    guac_socket_instruction_end(socket);
    243
    244    return ret_val;
    245
    246}
    247
    248int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
    249        const void* data, int count) {
    250
    251    int base64_length = (count + 2) / 3 * 4;
    252
    253    int ret_val;
    254
    255    guac_socket_instruction_begin(socket);
    256    ret_val =
    257           guac_socket_write_string(socket, "4.blob,")
    258        || __guac_socket_write_length_int(socket, stream->index)
    259        || guac_socket_write_string(socket, ",")
    260        || guac_socket_write_int(socket, base64_length)
    261        || guac_socket_write_string(socket, ".")
    262        || guac_socket_write_base64(socket, data, count)
    263        || guac_socket_flush_base64(socket)
    264        || guac_socket_write_string(socket, ";");
    265
    266    guac_socket_instruction_end(socket);
    267    return ret_val;
    268
    269}
    270
    271int guac_protocol_send_blobs(guac_socket* socket, const guac_stream* stream,
    272        const void* data, int count) {
    273
    274    int ret_val = 0;
    275
    276    /* Send blob instructions while data remains and instructions are being
    277     * sent successfully */
    278    while (count > 0 && ret_val == 0) {
    279
    280        /* Limit blob size to maximum allowed */
    281        int blob_size = count;
    282        if (blob_size > GUAC_PROTOCOL_BLOB_MAX_LENGTH)
    283            blob_size = GUAC_PROTOCOL_BLOB_MAX_LENGTH;
    284
    285        /* Send next blob of data */
    286        ret_val = guac_protocol_send_blob(socket, stream, data, blob_size);
    287
    288        /* Advance to next blob */
    289        data = (const char*) data + blob_size;
    290        count -= blob_size;
    291
    292    }
    293
    294    return ret_val;
    295
    296}
    297
    298int guac_protocol_send_body(guac_socket* socket, const guac_object* object,
    299        const guac_stream* stream, const char* mimetype, const char* name) {
    300
    301    int ret_val;
    302
    303    guac_socket_instruction_begin(socket);
    304    ret_val =
    305           guac_socket_write_string(socket, "4.body,")
    306        || __guac_socket_write_length_int(socket, object->index)
    307        || guac_socket_write_string(socket, ",")
    308        || __guac_socket_write_length_int(socket, stream->index)
    309        || guac_socket_write_string(socket, ",")
    310        || __guac_socket_write_length_string(socket, mimetype)
    311        || guac_socket_write_string(socket, ",")
    312        || __guac_socket_write_length_string(socket, name)
    313        || guac_socket_write_string(socket, ";");
    314
    315    guac_socket_instruction_end(socket);
    316    return ret_val;
    317
    318}
    319
    320int guac_protocol_send_cfill(guac_socket* socket,
    321        guac_composite_mode mode, const guac_layer* layer,
    322        int r, int g, int b, int a) {
    323
    324    int ret_val;
    325
    326    guac_socket_instruction_begin(socket);
    327    ret_val =
    328           guac_socket_write_string(socket, "5.cfill,")
    329        || __guac_socket_write_length_int(socket, mode)
    330        || guac_socket_write_string(socket, ",")
    331        || __guac_socket_write_length_int(socket, layer->index)
    332        || guac_socket_write_string(socket, ",")
    333        || __guac_socket_write_length_int(socket, r)
    334        || guac_socket_write_string(socket, ",")
    335        || __guac_socket_write_length_int(socket, g)
    336        || guac_socket_write_string(socket, ",")
    337        || __guac_socket_write_length_int(socket, b)
    338        || guac_socket_write_string(socket, ",")
    339        || __guac_socket_write_length_int(socket, a)
    340        || guac_socket_write_string(socket, ";");
    341
    342    guac_socket_instruction_end(socket);
    343    return ret_val;
    344
    345}
    346
    347int guac_protocol_send_close(guac_socket* socket, const guac_layer* layer) {
    348
    349    int ret_val;
    350
    351    guac_socket_instruction_begin(socket);
    352    ret_val =
    353           guac_socket_write_string(socket, "5.close,")
    354        || __guac_socket_write_length_int(socket, layer->index)
    355        || guac_socket_write_string(socket, ";");
    356
    357    guac_socket_instruction_end(socket);
    358    return ret_val;
    359
    360}
    361
    362static int __guac_protocol_send_connect(guac_socket* socket, const char** args) {
    363
    364    if (guac_socket_write_string(socket, "7.connect"))
    365        return -1;
    366
    367    if (guac_socket_write_array(socket, args))
    368        return -1;
    369
    370    return guac_socket_write_string(socket, ";");
    371
    372}
    373
    374int guac_protocol_send_connect(guac_socket* socket, const char** args) {
    375
    376    int ret_val;
    377
    378    guac_socket_instruction_begin(socket);
    379    ret_val = __guac_protocol_send_connect(socket, args);
    380    guac_socket_instruction_end(socket);
    381
    382    return ret_val;
    383
    384}
    385
    386int guac_protocol_send_clip(guac_socket* socket, const guac_layer* layer) {
    387
    388    int ret_val;
    389
    390    guac_socket_instruction_begin(socket);
    391    ret_val =
    392           guac_socket_write_string(socket, "4.clip,")
    393        || __guac_socket_write_length_int(socket, layer->index)
    394        || guac_socket_write_string(socket, ";");
    395
    396    guac_socket_instruction_end(socket);
    397    return ret_val;
    398
    399}
    400
    401int guac_protocol_send_clipboard(guac_socket* socket, const guac_stream* stream,
    402        const char* mimetype) {
    403
    404    int ret_val;
    405
    406    guac_socket_instruction_begin(socket);
    407    ret_val =
    408           guac_socket_write_string(socket, "9.clipboard,")
    409        || __guac_socket_write_length_int(socket, stream->index)
    410        || guac_socket_write_string(socket, ",")
    411        || __guac_socket_write_length_string(socket, mimetype)
    412        || guac_socket_write_string(socket, ";");
    413
    414    guac_socket_instruction_end(socket);
    415    return ret_val;
    416
    417}
    418
    419int guac_protocol_send_copy(guac_socket* socket,
    420        const guac_layer* srcl, int srcx, int srcy, int w, int h,
    421        guac_composite_mode mode, const guac_layer* dstl, int dstx, int dsty) {
    422
    423    int ret_val;
    424
    425    guac_socket_instruction_begin(socket);
    426    ret_val =
    427           guac_socket_write_string(socket, "4.copy,")
    428        || __guac_socket_write_length_int(socket, srcl->index)
    429        || guac_socket_write_string(socket, ",")
    430        || __guac_socket_write_length_int(socket, srcx)
    431        || guac_socket_write_string(socket, ",")
    432        || __guac_socket_write_length_int(socket, srcy)
    433        || guac_socket_write_string(socket, ",")
    434        || __guac_socket_write_length_int(socket, w)
    435        || guac_socket_write_string(socket, ",")
    436        || __guac_socket_write_length_int(socket, h)
    437        || guac_socket_write_string(socket, ",")
    438        || __guac_socket_write_length_int(socket, mode)
    439        || guac_socket_write_string(socket, ",")
    440        || __guac_socket_write_length_int(socket, dstl->index)
    441        || guac_socket_write_string(socket, ",")
    442        || __guac_socket_write_length_int(socket, dstx)
    443        || guac_socket_write_string(socket, ",")
    444        || __guac_socket_write_length_int(socket, dsty)
    445        || guac_socket_write_string(socket, ";");
    446
    447    guac_socket_instruction_end(socket);
    448    return ret_val;
    449
    450}
    451
    452int guac_protocol_send_cstroke(guac_socket* socket,
    453        guac_composite_mode mode, const guac_layer* layer,
    454        guac_line_cap_style cap, guac_line_join_style join, int thickness,
    455        int r, int g, int b, int a) {
    456
    457    int ret_val;
    458
    459    guac_socket_instruction_begin(socket);
    460    ret_val =
    461           guac_socket_write_string(socket, "7.cstroke,")
    462        || __guac_socket_write_length_int(socket, mode)
    463        || guac_socket_write_string(socket, ",")
    464        || __guac_socket_write_length_int(socket, layer->index)
    465        || guac_socket_write_string(socket, ",")
    466        || __guac_socket_write_length_int(socket, cap)
    467        || guac_socket_write_string(socket, ",")
    468        || __guac_socket_write_length_int(socket, join)
    469        || guac_socket_write_string(socket, ",")
    470        || __guac_socket_write_length_int(socket, thickness)
    471        || guac_socket_write_string(socket, ",")
    472        || __guac_socket_write_length_int(socket, r)
    473        || guac_socket_write_string(socket, ",")
    474        || __guac_socket_write_length_int(socket, g)
    475        || guac_socket_write_string(socket, ",")
    476        || __guac_socket_write_length_int(socket, b)
    477        || guac_socket_write_string(socket, ",")
    478        || __guac_socket_write_length_int(socket, a)
    479        || guac_socket_write_string(socket, ";");
    480
    481    guac_socket_instruction_end(socket);
    482    return ret_val;
    483
    484}
    485
    486int guac_protocol_send_cursor(guac_socket* socket, int x, int y,
    487        const guac_layer* srcl, int srcx, int srcy, int w, int h) {
    488    int ret_val;
    489
    490    guac_socket_instruction_begin(socket);
    491    ret_val =
    492           guac_socket_write_string(socket, "6.cursor,")
    493        || __guac_socket_write_length_int(socket, x)
    494        || guac_socket_write_string(socket, ",")
    495        || __guac_socket_write_length_int(socket, y)
    496        || guac_socket_write_string(socket, ",")
    497        || __guac_socket_write_length_int(socket, srcl->index)
    498        || guac_socket_write_string(socket, ",")
    499        || __guac_socket_write_length_int(socket, srcx)
    500        || guac_socket_write_string(socket, ",")
    501        || __guac_socket_write_length_int(socket, srcy)
    502        || guac_socket_write_string(socket, ",")
    503        || __guac_socket_write_length_int(socket, w)
    504        || guac_socket_write_string(socket, ",")
    505        || __guac_socket_write_length_int(socket, h)
    506        || guac_socket_write_string(socket, ";");
    507
    508    guac_socket_instruction_end(socket);
    509    return ret_val;
    510
    511}
    512
    513int guac_protocol_send_curve(guac_socket* socket, const guac_layer* layer,
    514        int cp1x, int cp1y, int cp2x, int cp2y, int x, int y) {
    515
    516    int ret_val;
    517
    518    guac_socket_instruction_begin(socket);
    519    ret_val =
    520           guac_socket_write_string(socket, "5.curve,")
    521        || __guac_socket_write_length_int(socket, layer->index)
    522        || guac_socket_write_string(socket, ",")
    523        || __guac_socket_write_length_int(socket, cp1x)
    524        || guac_socket_write_string(socket, ",")
    525        || __guac_socket_write_length_int(socket, cp1y)
    526        || guac_socket_write_string(socket, ",")
    527        || __guac_socket_write_length_int(socket, cp2x)
    528        || guac_socket_write_string(socket, ",")
    529        || __guac_socket_write_length_int(socket, cp2y)
    530        || guac_socket_write_string(socket, ",")
    531        || __guac_socket_write_length_int(socket, x)
    532        || guac_socket_write_string(socket, ",")
    533        || __guac_socket_write_length_int(socket, y)
    534        || guac_socket_write_string(socket, ";");
    535
    536    guac_socket_instruction_end(socket);
    537    return ret_val;
    538
    539}
    540
    541int guac_protocol_send_disconnect(guac_socket* socket) {
    542    int ret_val;
    543
    544    guac_socket_instruction_begin(socket);
    545    ret_val = guac_socket_write_string(socket, "10.disconnect;");
    546    guac_socket_instruction_end(socket);
    547    return ret_val;
    548
    549}
    550
    551int guac_protocol_send_dispose(guac_socket* socket, const guac_layer* layer) {
    552
    553    int ret_val;
    554
    555    guac_socket_instruction_begin(socket);
    556    ret_val =
    557           guac_socket_write_string(socket, "7.dispose,")
    558        || __guac_socket_write_length_int(socket, layer->index)
    559        || guac_socket_write_string(socket, ";");
    560
    561    guac_socket_instruction_end(socket);
    562    return ret_val;
    563
    564}
    565
    566int guac_protocol_send_distort(guac_socket* socket, const guac_layer* layer,
    567        double a, double b, double c,
    568        double d, double e, double f) {
    569
    570    int ret_val;
    571
    572    guac_socket_instruction_begin(socket);
    573    ret_val = 
    574           guac_socket_write_string(socket, "7.distort,")
    575        || __guac_socket_write_length_int(socket, layer->index)
    576        || guac_socket_write_string(socket, ",")
    577        || __guac_socket_write_length_double(socket, a)
    578        || guac_socket_write_string(socket, ",")
    579        || __guac_socket_write_length_double(socket, b)
    580        || guac_socket_write_string(socket, ",")
    581        || __guac_socket_write_length_double(socket, c)
    582        || guac_socket_write_string(socket, ",")
    583        || __guac_socket_write_length_double(socket, d)
    584        || guac_socket_write_string(socket, ",")
    585        || __guac_socket_write_length_double(socket, e)
    586        || guac_socket_write_string(socket, ",")
    587        || __guac_socket_write_length_double(socket, f)
    588        || guac_socket_write_string(socket, ";");
    589
    590    guac_socket_instruction_end(socket);
    591    return ret_val;
    592
    593}
    594
    595int guac_protocol_send_end(guac_socket* socket, const guac_stream* stream) {
    596
    597    int ret_val;
    598
    599    guac_socket_instruction_begin(socket);
    600    ret_val =
    601           guac_socket_write_string(socket, "3.end,")
    602        || __guac_socket_write_length_int(socket, stream->index)
    603        || guac_socket_write_string(socket, ";");
    604
    605    guac_socket_instruction_end(socket);
    606    return ret_val;
    607
    608}
    609
    610int guac_protocol_send_error(guac_socket* socket, const char* error,
    611        guac_protocol_status status) {
    612
    613    int ret_val;
    614
    615    guac_socket_instruction_begin(socket);
    616    ret_val =
    617           guac_socket_write_string(socket, "5.error,")
    618        || __guac_socket_write_length_string(socket, error)
    619        || guac_socket_write_string(socket, ",")
    620        || __guac_socket_write_length_int(socket, status)
    621        || guac_socket_write_string(socket, ";");
    622
    623    guac_socket_instruction_end(socket);
    624    return ret_val;
    625
    626}
    627
    628int vguac_protocol_send_log(guac_socket* socket, const char* format,
    629        va_list args) {
    630
    631    int ret_val;
    632
    633    /* Copy log message into buffer */
    634    char message[4096];
    635    vsnprintf(message, sizeof(message), format, args);
    636
    637    /* Log to instruction */
    638    guac_socket_instruction_begin(socket);
    639    ret_val =
    640           guac_socket_write_string(socket, "3.log,")
    641        || __guac_socket_write_length_string(socket, message)
    642        || guac_socket_write_string(socket, ";");
    643
    644    guac_socket_instruction_end(socket);
    645    return ret_val;
    646
    647}
    648
    649int guac_protocol_send_log(guac_socket* socket, const char* format, ...) {
    650
    651    int ret_val;
    652
    653    va_list args;
    654    va_start(args, format);
    655    ret_val = vguac_protocol_send_log(socket, format, args);
    656    va_end(args);
    657
    658    return ret_val;
    659
    660}
    661
    662int guac_protocol_send_msg(guac_socket* socket, guac_message_type msg,
    663        const char** args) {
    664
    665    int ret_val;
    666
    667    guac_socket_instruction_begin(socket);
    668    ret_val =
    669           guac_socket_write_string(socket, "3.msg,")
    670        || __guac_socket_write_length_int(socket, msg)
    671        || guac_socket_write_array(socket, args)
    672        || guac_socket_write_string(socket, ";");
    673
    674    guac_socket_instruction_end(socket);
    675    return ret_val;
    676
    677}
    678
    679int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
    680        const char* mimetype, const char* name) {
    681
    682    int ret_val;
    683
    684    guac_socket_instruction_begin(socket);
    685    ret_val =
    686           guac_socket_write_string(socket, "4.file,")
    687        || __guac_socket_write_length_int(socket, stream->index)
    688        || guac_socket_write_string(socket, ",")
    689        || __guac_socket_write_length_string(socket, mimetype)
    690        || guac_socket_write_string(socket, ",")
    691        || __guac_socket_write_length_string(socket, name)
    692        || guac_socket_write_string(socket, ";");
    693
    694    guac_socket_instruction_end(socket);
    695    return ret_val;
    696
    697}
    698
    699int guac_protocol_send_filesystem(guac_socket* socket,
    700        const guac_object* object, const char* name) {
    701
    702    int ret_val;
    703
    704    guac_socket_instruction_begin(socket);
    705    ret_val =
    706           guac_socket_write_string(socket, "10.filesystem,")
    707        || __guac_socket_write_length_int(socket, object->index)
    708        || guac_socket_write_string(socket, ",")
    709        || __guac_socket_write_length_string(socket, name)
    710        || guac_socket_write_string(socket, ";");
    711
    712    guac_socket_instruction_end(socket);
    713    return ret_val;
    714
    715}
    716
    717int guac_protocol_send_identity(guac_socket* socket, const guac_layer* layer) {
    718
    719    int ret_val;
    720
    721    guac_socket_instruction_begin(socket);
    722    ret_val =
    723           guac_socket_write_string(socket, "8.identity,")
    724        || __guac_socket_write_length_int(socket, layer->index)
    725        || guac_socket_write_string(socket, ";");
    726
    727    guac_socket_instruction_end(socket);
    728    return ret_val;
    729
    730}
    731
    732int guac_protocol_send_key(guac_socket* socket, int keysym, int pressed,
    733        guac_timestamp timestamp) {
    734
    735    int ret_val;
    736
    737    guac_socket_instruction_begin(socket);
    738    ret_val =
    739           guac_socket_write_string(socket, "3.key,")
    740        || __guac_socket_write_length_int(socket, keysym)
    741        || guac_socket_write_string(socket, pressed ? ",1.1," : ",1.0,")
    742        || __guac_socket_write_length_int(socket, timestamp)
    743        || guac_socket_write_string(socket, ";");
    744
    745    guac_socket_instruction_end(socket);
    746    return ret_val;
    747
    748}
    749
    750int guac_protocol_send_lfill(guac_socket* socket,
    751        guac_composite_mode mode, const guac_layer* layer,
    752        const guac_layer* srcl) {
    753
    754    int ret_val;
    755
    756    guac_socket_instruction_begin(socket);
    757    ret_val =
    758           guac_socket_write_string(socket, "5.lfill,")
    759        || __guac_socket_write_length_int(socket, mode)
    760        || guac_socket_write_string(socket, ",")
    761        || __guac_socket_write_length_int(socket, layer->index)
    762        || guac_socket_write_string(socket, ",")
    763        || __guac_socket_write_length_int(socket, srcl->index)
    764        || guac_socket_write_string(socket, ";");
    765
    766    guac_socket_instruction_end(socket);
    767    return ret_val;
    768
    769}
    770
    771int guac_protocol_send_line(guac_socket* socket, const guac_layer* layer,
    772        int x, int y) {
    773
    774    int ret_val;
    775
    776    guac_socket_instruction_begin(socket);
    777    ret_val =
    778           guac_socket_write_string(socket, "4.line,")
    779        || __guac_socket_write_length_int(socket, layer->index)
    780        || guac_socket_write_string(socket, ",")
    781        || __guac_socket_write_length_int(socket, x)
    782        || guac_socket_write_string(socket, ",")
    783        || __guac_socket_write_length_int(socket, y)
    784        || guac_socket_write_string(socket, ";");
    785
    786    guac_socket_instruction_end(socket);
    787    return ret_val;
    788
    789}
    790
    791int guac_protocol_send_lstroke(guac_socket* socket,
    792        guac_composite_mode mode, const guac_layer* layer,
    793        guac_line_cap_style cap, guac_line_join_style join, int thickness,
    794        const guac_layer* srcl) {
    795
    796    int ret_val;
    797
    798    guac_socket_instruction_begin(socket);
    799    ret_val =
    800           guac_socket_write_string(socket, "7.lstroke,")
    801        || __guac_socket_write_length_int(socket, mode)
    802        || guac_socket_write_string(socket, ",")
    803        || __guac_socket_write_length_int(socket, layer->index)
    804        || guac_socket_write_string(socket, ",")
    805        || __guac_socket_write_length_int(socket, cap)
    806        || guac_socket_write_string(socket, ",")
    807        || __guac_socket_write_length_int(socket, join)
    808        || guac_socket_write_string(socket, ",")
    809        || __guac_socket_write_length_int(socket, thickness)
    810        || guac_socket_write_string(socket, ",")
    811        || __guac_socket_write_length_int(socket, srcl->index)
    812        || guac_socket_write_string(socket, ";");
    813
    814    guac_socket_instruction_end(socket);
    815    return ret_val;
    816
    817}
    818
    819int guac_protocol_send_mouse(guac_socket* socket, int x, int y,
    820        int button_mask, guac_timestamp timestamp) {
    821
    822    int ret_val;
    823
    824    guac_socket_instruction_begin(socket);
    825    ret_val =
    826           guac_socket_write_string(socket, "5.mouse,")
    827        || __guac_socket_write_length_int(socket, x)
    828        || guac_socket_write_string(socket, ",")
    829        || __guac_socket_write_length_int(socket, y)
    830        || guac_socket_write_string(socket, ",")
    831        || __guac_socket_write_length_int(socket, button_mask)
    832        || guac_socket_write_string(socket, ",")
    833        || __guac_socket_write_length_int(socket, timestamp)
    834        || guac_socket_write_string(socket, ";");
    835
    836    guac_socket_instruction_end(socket);
    837    return ret_val;
    838
    839}
    840
    841int guac_protocol_send_touch(guac_socket* socket, int id, int x, int y,
    842        int x_radius, int y_radius, double angle, double force,
    843        guac_timestamp timestamp) {
    844
    845    int ret_val;
    846
    847    guac_socket_instruction_begin(socket);
    848    ret_val =
    849           guac_socket_write_string(socket, "5.touch,")
    850        || __guac_socket_write_length_int(socket, id)
    851        || guac_socket_write_string(socket, ",")
    852        || __guac_socket_write_length_int(socket, x)
    853        || guac_socket_write_string(socket, ",")
    854        || __guac_socket_write_length_int(socket, y)
    855        || guac_socket_write_string(socket, ",")
    856        || __guac_socket_write_length_int(socket, x_radius)
    857        || guac_socket_write_string(socket, ",")
    858        || __guac_socket_write_length_int(socket, y_radius)
    859        || guac_socket_write_string(socket, ",")
    860        || __guac_socket_write_length_double(socket, angle)
    861        || guac_socket_write_string(socket, ",")
    862        || __guac_socket_write_length_double(socket, force)
    863        || guac_socket_write_string(socket, ",")
    864        || __guac_socket_write_length_int(socket, timestamp)
    865        || guac_socket_write_string(socket, ";");
    866
    867    guac_socket_instruction_end(socket);
    868    return ret_val;
    869
    870}
    871
    872int guac_protocol_send_move(guac_socket* socket, const guac_layer* layer,
    873        const guac_layer* parent, int x, int y, int z) {
    874
    875    int ret_val;
    876
    877    guac_socket_instruction_begin(socket);
    878    ret_val =
    879           guac_socket_write_string(socket, "4.move,")
    880        || __guac_socket_write_length_int(socket, layer->index)
    881        || guac_socket_write_string(socket, ",")
    882        || __guac_socket_write_length_int(socket, parent->index)
    883        || guac_socket_write_string(socket, ",")
    884        || __guac_socket_write_length_int(socket, x)
    885        || guac_socket_write_string(socket, ",")
    886        || __guac_socket_write_length_int(socket, y)
    887        || guac_socket_write_string(socket, ",")
    888        || __guac_socket_write_length_int(socket, z)
    889        || guac_socket_write_string(socket, ";");
    890
    891    guac_socket_instruction_end(socket);
    892    return ret_val;
    893
    894}
    895
    896int guac_protocol_send_name(guac_socket* socket, const char* name) {
    897
    898    int ret_val;
    899
    900    guac_socket_instruction_begin(socket);
    901    ret_val =
    902           guac_socket_write_string(socket, "4.name,")
    903        || __guac_socket_write_length_string(socket, name)
    904        || guac_socket_write_string(socket, ";");
    905
    906    guac_socket_instruction_end(socket);
    907    return ret_val;
    908
    909}
    910
    911int guac_protocol_send_nest(guac_socket* socket, int index,
    912        const char* data) {
    913
    914    int ret_val;
    915
    916    guac_socket_instruction_begin(socket);
    917    ret_val =
    918           guac_socket_write_string(socket, "4.nest,")
    919        || __guac_socket_write_length_int(socket, index)
    920        || guac_socket_write_string(socket, ",")
    921        || __guac_socket_write_length_string(socket, data)
    922        || guac_socket_write_string(socket, ";");
    923
    924    guac_socket_instruction_end(socket);
    925    return ret_val;
    926
    927}
    928
    929int guac_protocol_send_nop(guac_socket* socket) {
    930
    931    int ret_val;
    932
    933    guac_socket_instruction_begin(socket);
    934    ret_val = guac_socket_write_string(socket, "3.nop;");
    935    guac_socket_instruction_end(socket);
    936
    937    return ret_val;
    938
    939}
    940
    941int guac_protocol_send_pipe(guac_socket* socket, const guac_stream* stream,
    942        const char* mimetype, const char* name) {
    943
    944    int ret_val;
    945
    946    guac_socket_instruction_begin(socket);
    947    ret_val =
    948           guac_socket_write_string(socket, "4.pipe,")
    949        || __guac_socket_write_length_int(socket, stream->index)
    950        || guac_socket_write_string(socket, ",")
    951        || __guac_socket_write_length_string(socket, mimetype)
    952        || guac_socket_write_string(socket, ",")
    953        || __guac_socket_write_length_string(socket, name)
    954        || guac_socket_write_string(socket, ";");
    955
    956    guac_socket_instruction_end(socket);
    957    return ret_val;
    958
    959}
    960
    961int guac_protocol_send_img(guac_socket* socket, const guac_stream* stream,
    962        guac_composite_mode mode, const guac_layer* layer,
    963        const char* mimetype, int x, int y) {
    964
    965    int ret_val;
    966
    967    guac_socket_instruction_begin(socket);
    968    ret_val =
    969           guac_socket_write_string(socket, "3.img,")
    970        || __guac_socket_write_length_int(socket, stream->index)
    971        || guac_socket_write_string(socket, ",")
    972        || __guac_socket_write_length_int(socket, mode)
    973        || guac_socket_write_string(socket, ",")
    974        || __guac_socket_write_length_int(socket, layer->index)
    975        || guac_socket_write_string(socket, ",")
    976        || __guac_socket_write_length_string(socket, mimetype)
    977        || guac_socket_write_string(socket, ",")
    978        || __guac_socket_write_length_int(socket, x)
    979        || guac_socket_write_string(socket, ",")
    980        || __guac_socket_write_length_int(socket, y)
    981        || guac_socket_write_string(socket, ";");
    982
    983    guac_socket_instruction_end(socket);
    984    return ret_val;
    985
    986}
    987
    988int guac_protocol_send_pop(guac_socket* socket, const guac_layer* layer) {
    989
    990    int ret_val;
    991
    992    guac_socket_instruction_begin(socket);
    993    ret_val =
    994           guac_socket_write_string(socket, "3.pop,")
    995        || __guac_socket_write_length_int(socket, layer->index)
    996        || guac_socket_write_string(socket, ";");
    997
    998    guac_socket_instruction_end(socket);
    999    return ret_val;
   1000
   1001}
   1002
   1003int guac_protocol_send_push(guac_socket* socket, const guac_layer* layer) {
   1004
   1005    int ret_val;
   1006
   1007    guac_socket_instruction_begin(socket);
   1008    ret_val =
   1009           guac_socket_write_string(socket, "4.push,")
   1010        || __guac_socket_write_length_int(socket, layer->index)
   1011        || guac_socket_write_string(socket, ";");
   1012
   1013    guac_socket_instruction_end(socket);
   1014    return ret_val;
   1015
   1016}
   1017
   1018int guac_protocol_send_ready(guac_socket* socket, const char* id) {
   1019
   1020    int ret_val;
   1021
   1022    guac_socket_instruction_begin(socket);
   1023    ret_val =
   1024           guac_socket_write_string(socket, "5.ready,")
   1025        || __guac_socket_write_length_string(socket, id)
   1026        || guac_socket_write_string(socket, ";");
   1027
   1028    guac_socket_instruction_end(socket);
   1029    return ret_val;
   1030
   1031}
   1032
   1033int guac_protocol_send_rect(guac_socket* socket,
   1034        const guac_layer* layer, int x, int y, int width, int height) {
   1035
   1036    int ret_val;
   1037
   1038    guac_socket_instruction_begin(socket);
   1039    ret_val =
   1040           guac_socket_write_string(socket, "4.rect,")
   1041        || __guac_socket_write_length_int(socket, layer->index)
   1042        || guac_socket_write_string(socket, ",")
   1043        || __guac_socket_write_length_int(socket, x)
   1044        || guac_socket_write_string(socket, ",")
   1045        || __guac_socket_write_length_int(socket, y)
   1046        || guac_socket_write_string(socket, ",")
   1047        || __guac_socket_write_length_int(socket, width)
   1048        || guac_socket_write_string(socket, ",")
   1049        || __guac_socket_write_length_int(socket, height)
   1050        || guac_socket_write_string(socket, ";");
   1051
   1052    guac_socket_instruction_end(socket);
   1053    return ret_val;
   1054
   1055}
   1056
   1057int guac_protocol_send_required(guac_socket* socket, const char** required) {
   1058    
   1059    int ret_val;
   1060    
   1061    guac_socket_instruction_begin(socket);
   1062
   1063    ret_val = guac_socket_write_string(socket, "8.required")
   1064        || guac_socket_write_array(socket, required)
   1065        || guac_socket_write_string(socket, ";")
   1066        || guac_socket_flush(socket);
   1067    
   1068    guac_socket_instruction_end(socket);
   1069
   1070    return ret_val;
   1071    
   1072}
   1073
   1074int guac_protocol_send_reset(guac_socket* socket, const guac_layer* layer) {
   1075
   1076    int ret_val;
   1077
   1078    guac_socket_instruction_begin(socket);
   1079    ret_val =
   1080           guac_socket_write_string(socket, "5.reset,")
   1081        || __guac_socket_write_length_int(socket, layer->index)
   1082        || guac_socket_write_string(socket, ";");
   1083
   1084    guac_socket_instruction_end(socket);
   1085    return ret_val;
   1086
   1087}
   1088
   1089int guac_protocol_send_set(guac_socket* socket, const guac_layer* layer,
   1090        const char* name, const char* value) {
   1091
   1092    int ret_val;
   1093
   1094    guac_socket_instruction_begin(socket);
   1095    ret_val =
   1096           guac_socket_write_string(socket, "3.set,")
   1097        || __guac_socket_write_length_int(socket, layer->index)
   1098        || guac_socket_write_string(socket, ",")
   1099        || __guac_socket_write_length_string(socket, name)
   1100        || guac_socket_write_string(socket, ",")
   1101        || __guac_socket_write_length_string(socket, value)
   1102        || guac_socket_write_string(socket, ";");
   1103
   1104    guac_socket_instruction_end(socket);
   1105    return ret_val;
   1106
   1107}
   1108
   1109int guac_protocol_send_set_int(guac_socket* socket, const guac_layer* layer,
   1110        const char* name, int value) {
   1111
   1112    int ret_val;
   1113
   1114    guac_socket_instruction_begin(socket);
   1115    ret_val =
   1116           guac_socket_write_string(socket, "3.set,")
   1117        || __guac_socket_write_length_int(socket, layer->index)
   1118        || guac_socket_write_string(socket, ",")
   1119        || __guac_socket_write_length_string(socket, name)
   1120        || guac_socket_write_string(socket, ",")
   1121        || __guac_socket_write_length_int(socket, value)
   1122        || guac_socket_write_string(socket, ";");
   1123
   1124    guac_socket_instruction_end(socket);
   1125    return ret_val;
   1126
   1127}
   1128
   1129int guac_protocol_send_select(guac_socket* socket, const char* protocol) {
   1130
   1131    int ret_val;
   1132
   1133    guac_socket_instruction_begin(socket);
   1134    ret_val =
   1135           guac_socket_write_string(socket, "6.select,")
   1136        || __guac_socket_write_length_string(socket, protocol)
   1137        || guac_socket_write_string(socket, ";");
   1138
   1139    guac_socket_instruction_end(socket);
   1140    return ret_val;
   1141
   1142}
   1143
   1144int guac_protocol_send_shade(guac_socket* socket, const guac_layer* layer,
   1145        int a) {
   1146
   1147    int ret_val;
   1148
   1149    guac_socket_instruction_begin(socket);
   1150    ret_val =
   1151           guac_socket_write_string(socket, "5.shade,")
   1152        || __guac_socket_write_length_int(socket, layer->index)
   1153        || guac_socket_write_string(socket, ",")
   1154        || __guac_socket_write_length_int(socket, a)
   1155        || guac_socket_write_string(socket, ";");
   1156
   1157    guac_socket_instruction_end(socket);
   1158    return ret_val;
   1159
   1160}
   1161
   1162int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer,
   1163        int w, int h) {
   1164
   1165    int ret_val;
   1166
   1167    guac_socket_instruction_begin(socket);
   1168    ret_val =
   1169           guac_socket_write_string(socket, "4.size,")
   1170        || __guac_socket_write_length_int(socket, layer->index)
   1171        || guac_socket_write_string(socket, ",")
   1172        || __guac_socket_write_length_int(socket, w)
   1173        || guac_socket_write_string(socket, ",")
   1174        || __guac_socket_write_length_int(socket, h)
   1175        || guac_socket_write_string(socket, ";");
   1176
   1177    guac_socket_instruction_end(socket);
   1178    return ret_val;
   1179
   1180}
   1181
   1182int guac_protocol_send_start(guac_socket* socket, const guac_layer* layer,
   1183        int x, int y) {
   1184
   1185    int ret_val;
   1186
   1187    guac_socket_instruction_begin(socket);
   1188    ret_val =
   1189           guac_socket_write_string(socket, "5.start,")
   1190        || __guac_socket_write_length_int(socket, layer->index)
   1191        || guac_socket_write_string(socket, ",")
   1192        || __guac_socket_write_length_int(socket, x)
   1193        || guac_socket_write_string(socket, ",")
   1194        || __guac_socket_write_length_int(socket, y)
   1195        || guac_socket_write_string(socket, ";");
   1196
   1197    guac_socket_instruction_end(socket);
   1198    return ret_val;
   1199
   1200}
   1201
   1202int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp) {
   1203
   1204    int ret_val;
   1205
   1206    guac_socket_instruction_begin(socket);
   1207    ret_val = 
   1208           guac_socket_write_string(socket, "4.sync,")
   1209        || __guac_socket_write_length_int(socket, timestamp)
   1210        || guac_socket_write_string(socket, ";");
   1211
   1212    guac_socket_instruction_end(socket);
   1213    return ret_val;
   1214
   1215}
   1216
   1217int guac_protocol_send_transfer(guac_socket* socket,
   1218        const guac_layer* srcl, int srcx, int srcy, int w, int h,
   1219        guac_transfer_function fn, const guac_layer* dstl, int dstx, int dsty) {
   1220
   1221    int ret_val;
   1222
   1223    guac_socket_instruction_begin(socket);
   1224    ret_val =
   1225           guac_socket_write_string(socket, "8.transfer,")
   1226        || __guac_socket_write_length_int(socket, srcl->index)
   1227        || guac_socket_write_string(socket, ",")
   1228        || __guac_socket_write_length_int(socket, srcx)
   1229        || guac_socket_write_string(socket, ",")
   1230        || __guac_socket_write_length_int(socket, srcy)
   1231        || guac_socket_write_string(socket, ",")
   1232        || __guac_socket_write_length_int(socket, w)
   1233        || guac_socket_write_string(socket, ",")
   1234        || __guac_socket_write_length_int(socket, h)
   1235        || guac_socket_write_string(socket, ",")
   1236        || __guac_socket_write_length_int(socket, fn)
   1237        || guac_socket_write_string(socket, ",")
   1238        || __guac_socket_write_length_int(socket, dstl->index)
   1239        || guac_socket_write_string(socket, ",")
   1240        || __guac_socket_write_length_int(socket, dstx)
   1241        || guac_socket_write_string(socket, ",")
   1242        || __guac_socket_write_length_int(socket, dsty)
   1243        || guac_socket_write_string(socket, ";");
   1244
   1245    guac_socket_instruction_end(socket);
   1246    return ret_val;
   1247
   1248}
   1249
   1250int guac_protocol_send_transform(guac_socket* socket, const guac_layer* layer,
   1251        double a, double b, double c,
   1252        double d, double e, double f) {
   1253
   1254    int ret_val;
   1255
   1256    guac_socket_instruction_begin(socket);
   1257    ret_val = 
   1258           guac_socket_write_string(socket, "9.transform,")
   1259        || __guac_socket_write_length_int(socket, layer->index)
   1260        || guac_socket_write_string(socket, ",")
   1261        || __guac_socket_write_length_double(socket, a)
   1262        || guac_socket_write_string(socket, ",")
   1263        || __guac_socket_write_length_double(socket, b)
   1264        || guac_socket_write_string(socket, ",")
   1265        || __guac_socket_write_length_double(socket, c)
   1266        || guac_socket_write_string(socket, ",")
   1267        || __guac_socket_write_length_double(socket, d)
   1268        || guac_socket_write_string(socket, ",")
   1269        || __guac_socket_write_length_double(socket, e)
   1270        || guac_socket_write_string(socket, ",")
   1271        || __guac_socket_write_length_double(socket, f)
   1272        || guac_socket_write_string(socket, ";");
   1273
   1274    guac_socket_instruction_end(socket);
   1275    return ret_val;
   1276
   1277}
   1278
   1279int guac_protocol_send_undefine(guac_socket* socket,
   1280        const guac_object* object) {
   1281
   1282    int ret_val;
   1283
   1284    guac_socket_instruction_begin(socket);
   1285    ret_val =
   1286           guac_socket_write_string(socket, "8.undefine,")
   1287        || __guac_socket_write_length_int(socket, object->index)
   1288        || guac_socket_write_string(socket, ";");
   1289
   1290    guac_socket_instruction_end(socket);
   1291    return ret_val;
   1292
   1293}
   1294
   1295int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
   1296        const guac_layer* layer, const char* mimetype) {
   1297
   1298    int ret_val;
   1299
   1300    guac_socket_instruction_begin(socket);
   1301    ret_val = 
   1302           guac_socket_write_string(socket, "5.video,")
   1303        || __guac_socket_write_length_int(socket, stream->index)
   1304        || guac_socket_write_string(socket, ",")
   1305        || __guac_socket_write_length_int(socket, layer->index)
   1306        || guac_socket_write_string(socket, ",")
   1307        || __guac_socket_write_length_string(socket, mimetype)
   1308        || guac_socket_write_string(socket, ";");
   1309    guac_socket_instruction_end(socket);
   1310
   1311    return ret_val;
   1312
   1313}
   1314
   1315/**
   1316 * Returns the value of a single base64 character.
   1317 */
   1318static int __guac_base64_value(char c) {
   1319
   1320    if (c >= 'A' && c <= 'Z')
   1321        return c - 'A';
   1322
   1323    if (c >= 'a' && c <= 'z')
   1324        return c - 'a' + 26;
   1325
   1326    if (c >= '0' && c <= '9')
   1327        return c - '0' + 52;
   1328
   1329    if (c == '+')
   1330        return 62;
   1331
   1332    if (c == '/')
   1333        return 63;
   1334
   1335    return 0;
   1336
   1337}
   1338
   1339int guac_protocol_decode_base64(char* base64) {
   1340
   1341    char* input = base64;
   1342    char* output = base64;
   1343
   1344    int length = 0;
   1345    int bits_read = 0;
   1346    int value = 0;
   1347    char current;
   1348
   1349    /* For all characters in string */
   1350    while ((current = *(input++)) != 0) {
   1351
   1352        /* If we've reached padding, then we're done */
   1353        if (current == '=')
   1354            break;
   1355
   1356        /* Otherwise, shift on the latest 6 bits */
   1357        value = (value << 6) | __guac_base64_value(current);
   1358        bits_read += 6;
   1359
   1360        /* If we have at least one byte, write out the latest whole byte */
   1361        if (bits_read >= 8) {
   1362            *(output++) = (value >> (bits_read % 8)) & 0xFF;
   1363            bits_read -= 8;
   1364            length++;
   1365        }
   1366
   1367    }
   1368
   1369    /* Return number of bytes written */
   1370    return length;
   1371
   1372}
   1373
   1374guac_protocol_version guac_protocol_string_to_version(const char* version_string) {
   1375    
   1376    guac_protocol_version_mapping* current = guac_protocol_version_table;
   1377    while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
   1378        
   1379        if (strcmp(current->version_string, version_string) == 0)
   1380            return current->version;
   1381        
   1382        current++;
   1383        
   1384    }
   1385    
   1386    return GUAC_PROTOCOL_VERSION_UNKNOWN;
   1387    
   1388}
   1389
   1390const char* guac_protocol_version_to_string(guac_protocol_version version) {
   1391    
   1392    guac_protocol_version_mapping* current = guac_protocol_version_table;
   1393    while (current->version != GUAC_PROTOCOL_VERSION_UNKNOWN) {
   1394        
   1395        if (current->version == version)
   1396            return (const char*) current->version_string;
   1397        
   1398        current++;
   1399        
   1400    }
   1401    
   1402    return NULL;
   1403    
   1404}
   1405