cscg24-guacamole

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

client.c (10022B)


      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 "client.h"
     21#include "channels/audio-input/audio-buffer.h"
     22#include "channels/cliprdr.h"
     23#include "channels/disp.h"
     24#include "channels/pipe-svc.h"
     25#include "config.h"
     26#include "fs.h"
     27#include "log.h"
     28#include "rdp.h"
     29#include "settings.h"
     30#include "user.h"
     31
     32#ifdef ENABLE_COMMON_SSH
     33#include "common-ssh/sftp.h"
     34#include "common-ssh/ssh.h"
     35#include "common-ssh/user.h"
     36#endif
     37
     38#include <guacamole/audio.h>
     39#include <guacamole/client.h>
     40#include <guacamole/mem.h>
     41#include <guacamole/recording.h>
     42
     43#include <dirent.h>
     44#include <errno.h>
     45#include <fcntl.h>
     46#include <pthread.h>
     47#include <pwd.h>
     48#include <stdlib.h>
     49#include <string.h>
     50#include <sys/types.h>
     51#include <unistd.h>
     52
     53/**
     54 * Tests whether the given path refers to a directory which the current user
     55 * can write to. If the given path is not a directory, is not writable, or is
     56 * not a link pointing to a writable directory, this test will fail, and
     57 * errno will be set appropriately.
     58 *
     59 * @param path
     60 *     The path to test.
     61 *
     62 * @return
     63 *     Non-zero if the given path is (or points to) a writable directory, zero
     64 *     otherwise.
     65 */
     66static int is_writable_directory(const char* path) {
     67
     68    /* Verify path is writable */
     69    if (faccessat(AT_FDCWD, path, W_OK, 0))
     70        return 0;
     71
     72    /* If writable, verify path is actually a directory */
     73    DIR* dir = opendir(path);
     74    if (!dir)
     75        return 0;
     76
     77    /* Path is both writable and a directory */
     78    closedir(dir);
     79    return 1;
     80
     81}
     82
     83/**
     84 * Add the provided user to the provided audio stream.
     85 *
     86 * @param user
     87 *    The pending user who should be added to the audio stream.
     88 *
     89 * @param data
     90 *    The audio stream that the user should be added to.
     91 *
     92 * @return
     93 *     Always NULL.
     94 */
     95static void* guac_rdp_sync_pending_user_audio(guac_user* user, void* data) {
     96
     97    /* Add the user to the stream */
     98    guac_audio_stream* audio = (guac_audio_stream*) data;
     99    guac_audio_stream_add_user(audio, user);
    100
    101    return NULL;
    102
    103}
    104
    105/**
    106 * A pending join handler implementation that will synchronize the connection
    107 * state for all pending users prior to them being promoted to full user.
    108 *
    109 * @param client
    110 *     The client whose pending users are about to be promoted.
    111 *
    112 * @return
    113 *     Always zero.
    114 */
    115static int guac_rdp_join_pending_handler(guac_client* client) {
    116
    117    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    118    guac_socket* broadcast_socket = client->pending_socket;
    119
    120    /* Synchronize any audio stream for each pending user */
    121    if (rdp_client->audio)
    122        guac_client_foreach_pending_user(
    123            client, guac_rdp_sync_pending_user_audio, rdp_client->audio);
    124
    125    /* Bring user up to date with any registered static channels */
    126    guac_rdp_pipe_svc_send_pipes(client, broadcast_socket);
    127
    128    /* Synchronize with current display */
    129    if (rdp_client->display != NULL) {
    130        guac_common_display_dup(rdp_client->display, client, broadcast_socket);
    131        guac_socket_flush(broadcast_socket);
    132    }
    133
    134    return 0;
    135
    136}
    137
    138int guac_client_init(guac_client* client, int argc, char** argv) {
    139
    140    /* Automatically set HOME environment variable if unset (FreeRDP's
    141     * initialization process will fail within freerdp_settings_new() if this
    142     * is unset) */
    143    const char* current_home = getenv("HOME");
    144    if (current_home == NULL) {
    145
    146        /* Warn if the correct home directory cannot be determined */
    147        struct passwd* passwd = getpwuid(getuid());
    148        if (passwd == NULL)
    149            guac_client_log(client, GUAC_LOG_WARNING, "FreeRDP initialization "
    150                    "may fail: The \"HOME\" environment variable is unset and "
    151                    "its correct value could not be automatically determined: "
    152                    "%s", strerror(errno));
    153
    154        /* Warn if the correct home directory could be determined but can't be
    155         * assigned */
    156        else if (setenv("HOME", passwd->pw_dir, 1))
    157            guac_client_log(client, GUAC_LOG_WARNING, "FreeRDP initialization "
    158                    "may fail: The \"HOME\" environment variable is unset "
    159                    "and its correct value (detected as \"%s\") could not be "
    160                    "assigned: %s", passwd->pw_dir, strerror(errno));
    161
    162        /* HOME has been successfully set */
    163        else {
    164            guac_client_log(client, GUAC_LOG_DEBUG, "\"HOME\" "
    165                    "environment variable was unset and has been "
    166                    "automatically set to \"%s\"", passwd->pw_dir);
    167            current_home = passwd->pw_dir;
    168        }
    169
    170    }
    171
    172    /* Verify that detected home directory is actually writable and actually a
    173     * directory, as FreeRDP initialization will mysteriously fail otherwise */
    174    if (current_home != NULL && !is_writable_directory(current_home)) {
    175        if (errno == EACCES)
    176            guac_client_log(client, GUAC_LOG_WARNING, "FreeRDP initialization "
    177                    "may fail: The current user's home directory (\"%s\") is "
    178                    "not writable, but FreeRDP generally requires a writable "
    179                    "home directory for storage of configuration files and "
    180                    "certificates.", current_home);
    181        else if (errno == ENOTDIR)
    182            guac_client_log(client, GUAC_LOG_WARNING, "FreeRDP initialization "
    183                    "may fail: The current user's home directory (\"%s\") is "
    184                    "not actually a directory, but FreeRDP generally requires "
    185                    "a writable home directory for storage of configuration "
    186                    "files and certificates.", current_home);
    187        else
    188            guac_client_log(client, GUAC_LOG_WARNING, "FreeRDP initialization "
    189                    "may fail: Writability of the current user's home "
    190                    "directory (\"%s\") could not be determined: %s",
    191                    current_home, strerror(errno));
    192    }
    193
    194    /* Set client args */
    195    client->args = GUAC_RDP_CLIENT_ARGS;
    196
    197    /* Alloc client data */
    198    guac_rdp_client* rdp_client = guac_mem_zalloc(sizeof(guac_rdp_client));
    199    client->data = rdp_client;
    200
    201    /* Init clipboard */
    202    rdp_client->clipboard = guac_rdp_clipboard_alloc(client);
    203
    204    /* Init display update module */
    205    rdp_client->disp = guac_rdp_disp_alloc(client);
    206
    207    /* Init multi-touch support module (RDPEI) */
    208    rdp_client->rdpei = guac_rdp_rdpei_alloc(client);
    209
    210    /* Redirect FreeRDP log messages to guac_client_log() */
    211    guac_rdp_redirect_wlog(client);
    212
    213    /* Recursive attribute for locks */
    214    pthread_mutexattr_init(&(rdp_client->attributes));
    215    pthread_mutexattr_settype(&(rdp_client->attributes),
    216            PTHREAD_MUTEX_RECURSIVE);
    217
    218    /* Init required locks */
    219    pthread_rwlock_init(&(rdp_client->lock), NULL);
    220    pthread_mutex_init(&(rdp_client->message_lock), &(rdp_client->attributes));
    221
    222    /* Set handlers */
    223    client->join_handler = guac_rdp_user_join_handler;
    224    client->join_pending_handler = guac_rdp_join_pending_handler;
    225    client->free_handler = guac_rdp_client_free_handler;
    226    client->leave_handler = guac_rdp_user_leave_handler;
    227
    228#ifdef ENABLE_COMMON_SSH
    229    guac_common_ssh_init(client);
    230#endif
    231
    232    return 0;
    233
    234}
    235
    236int guac_rdp_client_free_handler(guac_client* client) {
    237
    238    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    239
    240    /* Wait for client thread */
    241    pthread_join(rdp_client->client_thread, NULL);
    242
    243    /* Free parsed settings */
    244    if (rdp_client->settings != NULL)
    245        guac_rdp_settings_free(rdp_client->settings);
    246
    247    /* Clean up clipboard */
    248    guac_rdp_clipboard_free(rdp_client->clipboard);
    249
    250    /* Free display update module */
    251    guac_rdp_disp_free(rdp_client->disp);
    252
    253    /* Free multi-touch support module (RDPEI) */
    254    guac_rdp_rdpei_free(rdp_client->rdpei);
    255
    256    /* Clean up filesystem, if allocated */
    257    if (rdp_client->filesystem != NULL)
    258        guac_rdp_fs_free(rdp_client->filesystem);
    259
    260    /* End active print job, if any */
    261    guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
    262    if (job != NULL) {
    263        guac_rdp_print_job_kill(job);
    264        guac_rdp_print_job_free(job);
    265        rdp_client->active_job = NULL;
    266    }
    267
    268#ifdef ENABLE_COMMON_SSH
    269    /* Free SFTP filesystem, if loaded */
    270    if (rdp_client->sftp_filesystem)
    271        guac_common_ssh_destroy_sftp_filesystem(rdp_client->sftp_filesystem);
    272
    273    /* Free SFTP session */
    274    if (rdp_client->sftp_session)
    275        guac_common_ssh_destroy_session(rdp_client->sftp_session);
    276
    277    /* Free SFTP user */
    278    if (rdp_client->sftp_user)
    279        guac_common_ssh_destroy_user(rdp_client->sftp_user);
    280
    281    guac_common_ssh_uninit();
    282#endif
    283
    284    /* Clean up recording, if in progress */
    285    if (rdp_client->recording != NULL)
    286        guac_recording_free(rdp_client->recording);
    287
    288    /* Clean up audio stream, if allocated */
    289    if (rdp_client->audio != NULL)
    290        guac_audio_stream_free(rdp_client->audio);
    291
    292    /* Clean up audio input buffer, if allocated */
    293    if (rdp_client->audio_input != NULL)
    294        guac_rdp_audio_buffer_free(rdp_client->audio_input);
    295
    296    pthread_rwlock_destroy(&(rdp_client->lock));
    297    pthread_mutex_destroy(&(rdp_client->message_lock));
    298
    299    /* Free client data */
    300    guac_mem_free(rdp_client);
    301
    302    return 0;
    303
    304}
    305