cscg24-guacamole

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

user.c (12598B)


      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 "encode-jpeg.h"
     23#include "encode-png.h"
     24#include "encode-webp.h"
     25#include "guacamole/mem.h"
     26#include "guacamole/client.h"
     27#include "guacamole/object.h"
     28#include "guacamole/pool.h"
     29#include "guacamole/protocol.h"
     30#include "guacamole/socket.h"
     31#include "guacamole/stream.h"
     32#include "guacamole/string.h"
     33#include "guacamole/timestamp.h"
     34#include "guacamole/user.h"
     35#include "id.h"
     36#include "user-handlers.h"
     37
     38#include <errno.h>
     39#include <limits.h>
     40#include <stdlib.h>
     41#include <string.h>
     42
     43guac_user* guac_user_alloc() {
     44
     45    guac_user* user = guac_mem_zalloc(sizeof(guac_user));
     46    int i;
     47
     48    /* Generate ID */
     49    user->user_id = guac_generate_id(GUAC_USER_ID_PREFIX);
     50    if (user->user_id == NULL) {
     51        guac_mem_free(user);
     52        return NULL;
     53    }
     54
     55    user->last_received_timestamp = guac_timestamp_current();
     56    user->last_frame_duration = 0;
     57    user->processing_lag = 0;
     58    user->active = 1;
     59
     60    /* Allocate stream pool */
     61    user->__stream_pool = guac_pool_alloc(0);
     62
     63    /* Initialze streams */
     64    user->__input_streams = guac_mem_alloc(sizeof(guac_stream), GUAC_USER_MAX_STREAMS);
     65    user->__output_streams = guac_mem_alloc(sizeof(guac_stream), GUAC_USER_MAX_STREAMS);
     66
     67    for (i=0; i<GUAC_USER_MAX_STREAMS; i++) {
     68        user->__input_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX;
     69        user->__output_streams[i].index = GUAC_USER_CLOSED_STREAM_INDEX;
     70    }
     71
     72    /* Allocate object pool */
     73    user->__object_pool = guac_pool_alloc(0);
     74
     75    /* Initialize objects */
     76    user->__objects = guac_mem_alloc(sizeof(guac_object), GUAC_USER_MAX_OBJECTS);
     77    for (i=0; i<GUAC_USER_MAX_OBJECTS; i++)
     78        user->__objects[i].index = GUAC_USER_UNDEFINED_OBJECT_INDEX;
     79
     80    return user;
     81
     82}
     83
     84void guac_user_free(guac_user* user) {
     85
     86    /* Free streams */
     87    guac_mem_free(user->__input_streams);
     88    guac_mem_free(user->__output_streams);
     89
     90    /* Free stream pool */
     91    guac_pool_free(user->__stream_pool);
     92
     93    /* Free objects */
     94    guac_mem_free(user->__objects);
     95
     96    /* Free object pool */
     97    guac_pool_free(user->__object_pool);
     98
     99    /* Clean up user */
    100    guac_mem_free(user->user_id);
    101    guac_mem_free(user);
    102
    103}
    104
    105guac_stream* guac_user_alloc_stream(guac_user* user) {
    106
    107    guac_stream* allocd_stream;
    108    int stream_index;
    109
    110    /* Refuse to allocate beyond maximum */
    111    if (user->__stream_pool->active == GUAC_USER_MAX_STREAMS)
    112        return NULL;
    113
    114    /* Allocate stream */
    115    stream_index = guac_pool_next_int(user->__stream_pool);
    116
    117    /* Initialize stream with even index (odd indices are client-level) */
    118    allocd_stream = &(user->__output_streams[stream_index]);
    119    allocd_stream->index = stream_index * 2;
    120    allocd_stream->data = NULL;
    121    allocd_stream->ack_handler = NULL;
    122    allocd_stream->blob_handler = NULL;
    123    allocd_stream->end_handler = NULL;
    124
    125    return allocd_stream;
    126
    127}
    128
    129void guac_user_free_stream(guac_user* user, guac_stream* stream) {
    130
    131    /* Release index to pool */
    132    guac_pool_free_int(user->__stream_pool, stream->index / 2);
    133
    134    /* Mark stream as closed */
    135    stream->index = GUAC_USER_CLOSED_STREAM_INDEX;
    136
    137}
    138
    139guac_object* guac_user_alloc_object(guac_user* user) {
    140
    141    guac_object* allocd_object;
    142    int object_index;
    143
    144    /* Refuse to allocate beyond maximum */
    145    if (user->__object_pool->active == GUAC_USER_MAX_OBJECTS)
    146        return NULL;
    147
    148    /* Allocate object */
    149    object_index = guac_pool_next_int(user->__object_pool);
    150
    151    /* Initialize object */
    152    allocd_object = &(user->__objects[object_index]);
    153    allocd_object->index = object_index;
    154    allocd_object->data = NULL;
    155    allocd_object->get_handler = NULL;
    156    allocd_object->put_handler = NULL;
    157
    158    return allocd_object;
    159
    160}
    161
    162void guac_user_free_object(guac_user* user, guac_object* object) {
    163
    164    /* Release index to pool */
    165    guac_pool_free_int(user->__object_pool, object->index);
    166
    167    /* Mark object as undefined */
    168    object->index = GUAC_USER_UNDEFINED_OBJECT_INDEX;
    169
    170}
    171
    172int guac_user_handle_instruction(guac_user* user, const char* opcode, int argc, char** argv) {
    173
    174    return __guac_user_call_opcode_handler(__guac_instruction_handler_map,
    175            user, opcode, argc, argv);
    176
    177}
    178
    179void guac_user_stop(guac_user* user) {
    180    user->active = 0;
    181}
    182
    183void vguac_user_abort(guac_user* user, guac_protocol_status status,
    184        const char* format, va_list ap) {
    185
    186    /* Only relevant if user is active */
    187    if (user->active) {
    188
    189        /* Log detail of error */
    190        vguac_user_log(user, GUAC_LOG_ERROR, format, ap);
    191
    192        /* Send error immediately, limit information given */
    193        guac_protocol_send_error(user->socket, "Aborted. See logs.", status);
    194        guac_socket_flush(user->socket);
    195
    196        /* Stop user */
    197        guac_user_stop(user);
    198
    199    }
    200
    201}
    202
    203void guac_user_abort(guac_user* user, guac_protocol_status status,
    204        const char* format, ...) {
    205
    206    va_list args;
    207    va_start(args, format);
    208
    209    vguac_user_abort(user, status, format, args);
    210
    211    va_end(args);
    212
    213}
    214
    215void vguac_user_log(guac_user* user, guac_client_log_level level,
    216        const char* format, va_list ap) {
    217
    218    vguac_client_log(user->client, level, format, ap);
    219
    220}
    221
    222void guac_user_log(guac_user* user, guac_client_log_level level,
    223        const char* format, ...) {
    224
    225    va_list args;
    226    va_start(args, format);
    227
    228    vguac_client_log(user->client, level, format, args);
    229
    230    va_end(args);
    231
    232}
    233
    234void guac_user_stream_argv(guac_user* user, guac_socket* socket,
    235        const char* mimetype, const char* name, const char* value) {
    236
    237    /* Allocate new stream for argument value */
    238    guac_stream* stream = guac_user_alloc_stream(user);
    239
    240    /* Declare stream as containing connection parameter data */
    241    guac_protocol_send_argv(socket, stream, mimetype, name);
    242
    243    /* Write parameter data */
    244    guac_protocol_send_blobs(socket, stream, value, strlen(value));
    245
    246    /* Terminate stream */
    247    guac_protocol_send_end(socket, stream);
    248
    249    /* Free allocated stream */
    250    guac_user_free_stream(user, stream);
    251
    252}
    253
    254void guac_user_stream_png(guac_user* user, guac_socket* socket,
    255        guac_composite_mode mode, const guac_layer* layer, int x, int y,
    256        cairo_surface_t* surface) {
    257
    258    /* Allocate new stream for image */
    259    guac_stream* stream = guac_user_alloc_stream(user);
    260
    261    /* Declare stream as containing image data */
    262    guac_protocol_send_img(socket, stream, mode, layer, "image/png", x, y);
    263
    264    /* Write PNG data */
    265    guac_png_write(socket, stream, surface);
    266
    267    /* Terminate stream */
    268    guac_protocol_send_end(socket, stream);
    269
    270    /* Free allocated stream */
    271    guac_user_free_stream(user, stream);
    272
    273}
    274
    275void guac_user_stream_jpeg(guac_user* user, guac_socket* socket,
    276        guac_composite_mode mode, const guac_layer* layer, int x, int y,
    277        cairo_surface_t* surface, int quality) {
    278
    279    /* Allocate new stream for image */
    280    guac_stream* stream = guac_user_alloc_stream(user);
    281
    282    /* Declare stream as containing image data */
    283    guac_protocol_send_img(socket, stream, mode, layer, "image/jpeg", x, y);
    284
    285    /* Write JPEG data */
    286    guac_jpeg_write(socket, stream, surface, quality);
    287
    288    /* Terminate stream */
    289    guac_protocol_send_end(socket, stream);
    290
    291    /* Free allocated stream */
    292    guac_user_free_stream(user, stream);
    293
    294}
    295
    296void guac_user_stream_webp(guac_user* user, guac_socket* socket,
    297        guac_composite_mode mode, const guac_layer* layer, int x, int y,
    298        cairo_surface_t* surface, int quality, int lossless) {
    299
    300#ifdef ENABLE_WEBP
    301    /* Allocate new stream for image */
    302    guac_stream* stream = guac_user_alloc_stream(user);
    303
    304    /* Declare stream as containing image data */
    305    guac_protocol_send_img(socket, stream, mode, layer, "image/webp", x, y);
    306
    307    /* Write WebP data */
    308    guac_webp_write(socket, stream, surface, quality, lossless);
    309
    310    /* Terminate stream */
    311    guac_protocol_send_end(socket, stream);
    312
    313    /* Free allocated stream */
    314    guac_user_free_stream(user, stream);
    315#else
    316    /* Do nothing if WebP support is not built in */
    317#endif
    318
    319}
    320
    321int guac_user_supports_msg(guac_user* user) {
    322
    323    if (user == NULL)
    324        return 0;
    325
    326    return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_5_0);
    327
    328}
    329
    330int guac_user_supports_required(guac_user* user) {
    331    
    332    if (user == NULL)
    333        return 0;
    334    
    335    return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_3_0);
    336    
    337}
    338
    339int guac_user_supports_webp(guac_user* user) {
    340
    341#ifdef ENABLE_WEBP
    342    const char** mimetype = user->info.image_mimetypes;
    343
    344    /* Search for WebP mimetype in list of supported image mimetypes */
    345    while (*mimetype != NULL) {
    346
    347        /* If WebP mimetype found, no need to search further */
    348        if (strcmp(*mimetype, "image/webp") == 0)
    349            return 1;
    350
    351        /* Next mimetype */
    352        mimetype++;
    353
    354    }
    355
    356    /* User does not support WebP */
    357    return 0;
    358#else
    359    /* Support for WebP is completely absent */
    360    return 0;
    361#endif
    362
    363}
    364
    365char* guac_user_parse_args_string(guac_user* user, const char** arg_names,
    366        const char** argv, int index, const char* default_value) {
    367
    368    /* Pull parameter value from argv */
    369    const char* value = argv[index];
    370
    371    /* Use default value if blank */
    372    if (value[0] == 0) {
    373
    374        /* NULL is a completely legal default value */
    375        if (default_value == NULL)
    376            return NULL;
    377
    378        /* Log use of default */
    379        guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
    380                "default value of \"%s\".", arg_names[index], default_value);
    381
    382        return guac_strdup(default_value);
    383
    384    }
    385
    386    /* Otherwise use provided value */
    387    return guac_strdup(value);
    388
    389}
    390
    391int guac_user_parse_args_int(guac_user* user, const char** arg_names,
    392        const char** argv, int index, int default_value) {
    393
    394    char* parse_end;
    395    long parsed_value;
    396
    397    /* Pull parameter value from argv */
    398    const char* value = argv[index];
    399
    400    /* Use default value if blank */
    401    if (value[0] == 0) {
    402
    403        /* Log use of default */
    404        guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
    405                "default value of %i.", arg_names[index], default_value);
    406
    407        return default_value;
    408
    409    }
    410
    411    /* Parse value, checking for errors */
    412    errno = 0;
    413    parsed_value = strtol(value, &parse_end, 10);
    414
    415    /* Ensure parsed value is within the legal range of an int */
    416    if (parsed_value < INT_MIN || parsed_value > INT_MAX)
    417        errno = ERANGE;
    418
    419    /* Resort to default if input is invalid */
    420    if (errno != 0 || *parse_end != '\0') {
    421
    422        /* Log use of default */
    423        guac_user_log(user, GUAC_LOG_WARNING, "Specified value \"%s\" for "
    424                "parameter \"%s\" is not a valid integer. Using default value "
    425                "of %i.", value, arg_names[index], default_value);
    426
    427        return default_value;
    428
    429    }
    430
    431    /* Parsed successfully */
    432    return parsed_value;
    433
    434}
    435
    436int guac_user_parse_args_boolean(guac_user* user, const char** arg_names,
    437        const char** argv, int index, int default_value) {
    438
    439    /* Pull parameter value from argv */
    440    const char* value = argv[index];
    441
    442    /* Use default value if blank */
    443    if (value[0] == 0) {
    444
    445        /* Log use of default */
    446        guac_user_log(user, GUAC_LOG_DEBUG, "Parameter \"%s\" omitted. Using "
    447                "default value of %i.", arg_names[index], default_value);
    448
    449        return default_value;
    450
    451    }
    452
    453    /* Parse string "true" as true */
    454    if (strcmp(value, "true") == 0)
    455        return 1;
    456
    457    /* Parse string "false" as false */
    458    if (strcmp(value, "false") == 0)
    459        return 0;
    460
    461    /* All other values are invalid */
    462    guac_user_log(user, GUAC_LOG_WARNING, "Parameter \"%s\" must be either "
    463            "\"true\" or \"false\". Using default value.", arg_names[index]);
    464
    465    return default_value;
    466
    467}
    468