cscg24-guacamole

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

guacai.c (10320B)


      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 "channels/audio-input/audio-buffer.h"
     21#include "plugins/guacai/guacai.h"
     22#include "plugins/guacai/guacai-messages.h"
     23#include "plugins/ptr-string.h"
     24#include "rdp.h"
     25
     26#include <freerdp/dvc.h>
     27#include <freerdp/settings.h>
     28#include <guacamole/client.h>
     29#include <guacamole/mem.h>
     30#include <winpr/stream.h>
     31#include <winpr/wtsapi.h>
     32#include <winpr/wtypes.h>
     33
     34#include <stdlib.h>
     35
     36/**
     37 * Handles the given data received along the AUDIO_INPUT channel of the RDP
     38 * connection associated with the given guac_client. This handler is
     39 * API-independent and is invoked by API-dependent guac_rdp_ai_data callback
     40 * specific to the version of FreeRDP installed.
     41 *
     42 * @param client
     43 *     The guac_client associated with RDP connection having the AUDIO_INPUT
     44 *     connection along which the given data was received.
     45 *
     46 * @param channel
     47 *     A reference to the IWTSVirtualChannel instance along which responses
     48 *     should be sent.
     49 *
     50 * @param stream
     51 *     The data received along the AUDIO_INPUT channel.
     52 */
     53static void guac_rdp_ai_handle_data(guac_client* client,
     54        IWTSVirtualChannel* channel, wStream* stream) {
     55
     56    /* Verify we have at least 1 byte in the stream (UINT8) */
     57    if (Stream_GetRemainingLength(stream) < 1) {
     58        guac_client_log(client, GUAC_LOG_WARNING, "Audio input PDU header does "
     59                "not contain the expected number of bytes. Audio input "
     60                "redirection may not work as expected.");
     61        return;
     62    }
     63    
     64    /* Read message ID from received PDU */
     65    BYTE message_id;
     66    Stream_Read_UINT8(stream, message_id);
     67    
     68    /* Invoke appropriate message processor based on ID */
     69    switch (message_id) {
     70
     71        /* Version PDU */
     72        case GUAC_RDP_MSG_SNDIN_VERSION:
     73            guac_rdp_ai_process_version(client, channel, stream);
     74            break;
     75
     76        /* Sound Formats PDU */
     77        case GUAC_RDP_MSG_SNDIN_FORMATS:
     78            guac_rdp_ai_process_formats(client, channel, stream);
     79            break;
     80
     81        /* Open PDU */
     82        case GUAC_RDP_MSG_SNDIN_OPEN:
     83            guac_rdp_ai_process_open(client, channel, stream);
     84            break;
     85
     86        /* Format Change PDU */
     87        case GUAC_RDP_MSG_SNDIN_FORMATCHANGE:
     88            guac_rdp_ai_process_formatchange(client, channel, stream);
     89            break;
     90
     91        /* Log unknown message IDs */
     92        default:
     93            guac_client_log(client, GUAC_LOG_DEBUG,
     94                    "Unknown AUDIO_INPUT message ID: 0x%x", message_id);
     95
     96    }
     97
     98}
     99
    100/**
    101 * Callback which is invoked when data is received along a connection to the
    102 * AUDIO_INPUT plugin.
    103 *
    104 * @param channel_callback
    105 *     The IWTSVirtualChannelCallback structure to which this callback was
    106 *     originally assigned.
    107 *
    108 * @param stream
    109 *     The data received.
    110 *
    111 * @return
    112 *     Always zero.
    113 */
    114static UINT guac_rdp_ai_data(IWTSVirtualChannelCallback* channel_callback,
    115        wStream* stream) {
    116
    117    guac_rdp_ai_channel_callback* ai_channel_callback =
    118        (guac_rdp_ai_channel_callback*) channel_callback;
    119    IWTSVirtualChannel* channel = ai_channel_callback->channel;
    120
    121    /* Invoke generalized (API-independent) data handler */
    122    guac_rdp_ai_handle_data(ai_channel_callback->client, channel, stream);
    123
    124    return CHANNEL_RC_OK;
    125
    126}
    127
    128/**
    129 * Callback which is invoked when a connection to the AUDIO_INPUT plugin is
    130 * closed.
    131 *
    132 * @param channel_callback
    133 *     The IWTSVirtualChannelCallback structure to which this callback was
    134 *     originally assigned.
    135 *
    136 * @return
    137 *     Always zero.
    138 */
    139static UINT guac_rdp_ai_close(IWTSVirtualChannelCallback* channel_callback) {
    140
    141    guac_rdp_ai_channel_callback* ai_channel_callback =
    142        (guac_rdp_ai_channel_callback*) channel_callback;
    143
    144    guac_client* client = ai_channel_callback->client;
    145    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    146    guac_rdp_audio_buffer* audio_buffer = rdp_client->audio_input;
    147
    148    /* Log closure of AUDIO_INPUT channel */
    149    guac_client_log(client, GUAC_LOG_DEBUG,
    150            "AUDIO_INPUT channel connection closed");
    151
    152    guac_rdp_audio_buffer_end(audio_buffer);
    153    guac_mem_free(ai_channel_callback);
    154    return CHANNEL_RC_OK;
    155
    156}
    157
    158/**
    159 * Callback which is invoked when a new connection is received by the
    160 * AUDIO_INPUT plugin. Additional callbacks required to handle received data
    161 * and closure of the connection must be installed at this point.
    162 *
    163 * @param listener_callback
    164 *     The IWTSListenerCallback structure associated with the AUDIO_INPUT
    165 *     plugin receiving the new connection.
    166 *
    167 * @param channel
    168 *     A reference to the IWTSVirtualChannel instance along which data related
    169 *     to the AUDIO_INPUT channel should be sent.
    170 *
    171 * @param data
    172 *     Absolutely no idea. According to Microsoft's documentation for the
    173 *     function prototype on which FreeRDP's API appears to be based: "This
    174 *     parameter is not implemented and is reserved for future use."
    175 *
    176 * @param accept
    177 *     Pointer to a flag which should be set to TRUE if the connection should
    178 *     be accepted or FALSE otherwise. In the case of FreeRDP, this value
    179 *     defaults to TRUE, and TRUE absolutely MUST be identically 1 or it will
    180 *     be interpreted as FALSE.
    181 *
    182 * @param channel_callback
    183 *     A pointer to the location that the new IWTSVirtualChannelCallback
    184 *     structure containing the required callbacks should be assigned.
    185 *
    186 * @return
    187 *     Always zero.
    188 */
    189static UINT guac_rdp_ai_new_connection(
    190        IWTSListenerCallback* listener_callback, IWTSVirtualChannel* channel,
    191        BYTE* data, int* accept,
    192        IWTSVirtualChannelCallback** channel_callback) {
    193
    194    guac_rdp_ai_listener_callback* ai_listener_callback =
    195        (guac_rdp_ai_listener_callback*) listener_callback;
    196
    197    /* Log new AUDIO_INPUT connection */
    198    guac_client_log(ai_listener_callback->client, GUAC_LOG_DEBUG,
    199            "New AUDIO_INPUT channel connection");
    200
    201    /* Allocate new channel callback */
    202    guac_rdp_ai_channel_callback* ai_channel_callback =
    203        guac_mem_zalloc(sizeof(guac_rdp_ai_channel_callback));
    204
    205    /* Init listener callback with data from plugin */
    206    ai_channel_callback->client = ai_listener_callback->client;
    207    ai_channel_callback->channel = channel;
    208    ai_channel_callback->parent.OnDataReceived = guac_rdp_ai_data;
    209    ai_channel_callback->parent.OnClose = guac_rdp_ai_close;
    210
    211    /* Return callback through pointer */
    212    *channel_callback = (IWTSVirtualChannelCallback*) ai_channel_callback;
    213
    214    return CHANNEL_RC_OK;
    215
    216}
    217
    218/**
    219 * Callback which is invoked when the AUDIO_INPUT plugin has been loaded and
    220 * needs to be initialized with other callbacks and data.
    221 *
    222 * @param plugin
    223 *     The AUDIO_INPUT plugin that needs to be initialied.
    224 *
    225 * @param manager
    226 *     The IWTSVirtualChannelManager instance with which the AUDIO_INPUT plugin
    227 *     must be registered.
    228 *
    229 * @return
    230 *     Always zero.
    231 */
    232static UINT guac_rdp_ai_initialize(IWTSPlugin* plugin,
    233        IWTSVirtualChannelManager* manager) {
    234
    235    /* Allocate new listener callback */
    236    guac_rdp_ai_listener_callback* ai_listener_callback =
    237        guac_mem_zalloc(sizeof(guac_rdp_ai_listener_callback));
    238
    239    /* Ensure listener callback is freed when plugin is terminated */
    240    guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*) plugin;
    241    ai_plugin->listener_callback = ai_listener_callback;
    242
    243    /* Init listener callback with data from plugin */
    244    ai_listener_callback->client = ai_plugin->client;
    245    ai_listener_callback->parent.OnNewChannelConnection =
    246        guac_rdp_ai_new_connection;
    247
    248    /* Register listener for "AUDIO_INPUT" channel */
    249    manager->CreateListener(manager, "AUDIO_INPUT", 0,
    250            (IWTSListenerCallback*) ai_listener_callback, NULL);
    251
    252    return CHANNEL_RC_OK;
    253
    254}
    255
    256/**
    257 * Callback which is invoked when all connections to the AUDIO_INPUT plugin
    258 * have closed and the plugin is being unloaded.
    259 *
    260 * @param plugin
    261 *     The AUDIO_INPUT plugin being unloaded.
    262 *
    263 * @return
    264 *     Always zero.
    265 */
    266static UINT guac_rdp_ai_terminated(IWTSPlugin* plugin) {
    267
    268    guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*) plugin;
    269    guac_client* client = ai_plugin->client;
    270
    271    /* Free all non-FreeRDP data */
    272    guac_mem_free(ai_plugin->listener_callback);
    273    guac_mem_free(ai_plugin);
    274
    275    guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin unloaded.");
    276    return CHANNEL_RC_OK;
    277
    278}
    279
    280/**
    281 * Entry point for AUDIO_INPUT dynamic virtual channel.
    282 */
    283UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) {
    284
    285    /* Pull guac_client from arguments */
    286    ADDIN_ARGV* args = pEntryPoints->GetPluginData(pEntryPoints);
    287    guac_client* client = (guac_client*) guac_rdp_string_to_ptr(args->argv[1]);
    288
    289    /* Pull previously-allocated plugin */
    290    guac_rdp_ai_plugin* ai_plugin = (guac_rdp_ai_plugin*)
    291        pEntryPoints->GetPlugin(pEntryPoints, "guacai");
    292
    293    /* If no such plugin allocated, allocate and register it now */
    294    if (ai_plugin == NULL) {
    295
    296        /* Init plugin callbacks and data */
    297        ai_plugin = guac_mem_zalloc(sizeof(guac_rdp_ai_plugin));
    298        ai_plugin->parent.Initialize = guac_rdp_ai_initialize;
    299        ai_plugin->parent.Terminated = guac_rdp_ai_terminated;
    300        ai_plugin->client = client;
    301
    302        /* Register plugin as "guacai" for later retrieval */
    303        pEntryPoints->RegisterPlugin(pEntryPoints, "guacai",
    304                (IWTSPlugin*) ai_plugin);
    305
    306        guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT plugin loaded.");
    307    }
    308
    309    return CHANNEL_RC_OK;
    310
    311}
    312