GUACAMOLE-25: Abstract DVC management away from FreeRDP for sake of API compatibility.

This commit is contained in:
Michael Jumper 2016-05-09 18:20:04 -07:00
parent 46bdf0692f
commit 1393358469
8 changed files with 323 additions and 42 deletions

View File

@ -26,6 +26,7 @@ libguac_client_rdp_la_SOURCES = \
_generated_keymaps.c \ _generated_keymaps.c \
audio_input.c \ audio_input.c \
client.c \ client.c \
dvc.c \
input.c \ input.c \
ptr_string.c \ ptr_string.c \
rdp.c \ rdp.c \
@ -91,6 +92,7 @@ noinst_HEADERS = \
guac_svc/svc_service.h \ guac_svc/svc_service.h \
audio_input.h \ audio_input.h \
client.h \ client.h \
dvc.h \
input.h \ input.h \
ptr_string.h \ ptr_string.h \
rdp.h \ rdp.h \

View File

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "audio_input.h" #include "audio_input.h"
#include "dvc.h"
#include "ptr_string.h" #include "ptr_string.h"
#include "rdp.h" #include "rdp.h"
@ -71,17 +72,12 @@ int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) {
} }
void guac_rdp_audio_load_plugin(rdpContext* context) { void guac_rdp_audio_load_plugin(rdpContext* context, guac_rdp_dvc_list* list) {
guac_client* client = ((rdp_freerdp_context*) context)->client; guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Add "AUDIO_INPUT" channel */ /* Add "AUDIO_INPUT" channel */
ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV)); guac_rdp_dvc_list_add(list, "guacai", guac_rdp_ptr_to_string(client), NULL);
args->argc = 2;
args->argv = malloc(sizeof(char**) * 2);
args->argv[0] = strdup("guacai");
args->argv[1] = guac_rdp_ptr_to_string(client);
freerdp_dynamic_channel_collection_add(context->settings, args);
} }

View File

@ -21,6 +21,7 @@
#define GUAC_RDP_AUDIO_INPUT_H #define GUAC_RDP_AUDIO_INPUT_H
#include "config.h" #include "config.h"
#include "dvc.h"
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
@ -213,7 +214,7 @@ guac_user_end_handler guac_rdp_audio_end_handler;
* @param context * @param context
* The rdpContext associated with the active RDP session. * The rdpContext associated with the active RDP session.
*/ */
void guac_rdp_audio_load_plugin(rdpContext* context); void guac_rdp_audio_load_plugin(rdpContext* context, guac_rdp_dvc_list* list);
#endif #endif

154
src/protocols/rdp/dvc.c Normal file
View File

@ -0,0 +1,154 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "config.h"
#include "dvc.h"
#include "guac_list.h"
#include "rdp.h"
#include <freerdp/channels/channels.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <assert.h>
#include <stdarg.h>
guac_rdp_dvc_list* guac_rdp_dvc_list_alloc() {
guac_rdp_dvc_list* list = malloc(sizeof(guac_rdp_dvc_list));
/* Initialize with empty backing list */
list->channels = guac_common_list_alloc();
list->channel_count = 0;
return list;
}
void guac_rdp_dvc_list_add(guac_rdp_dvc_list* list, const char* name, ...) {
va_list args;
guac_rdp_dvc* dvc = malloc(sizeof(guac_rdp_dvc));
va_start(args, name);
/* Count number of arguments (excluding terminating NULL) */
dvc->argc = 1;
while (va_arg(args, char*) != NULL)
dvc->argc++;
/* Reset va_list */
va_end(args);
va_start(args, name);
/* Copy argument values into DVC entry */
dvc->argv = malloc(sizeof(char*) * dvc->argc);
dvc->argv[0] = strdup(name);
int i;
for (i = 1; i < dvc->argc; i++)
dvc->argv[i] = strdup(va_arg(args, char*));
va_end(args);
/* Add entry to DVC list */
guac_common_list_add(list->channels, dvc);
/* Update channel count */
list->channel_count++;
}
void guac_rdp_dvc_list_free(guac_rdp_dvc_list* list) {
/* For each channel */
guac_common_list_element* current = list->channels->head;
while (current != NULL) {
/* Free arguments declaration for current channel */
guac_rdp_dvc* dvc = (guac_rdp_dvc*) current->data;
/* Free the underlying arguments list if not delegated to FreeRDP */
if (dvc->argv != NULL) {
/* Free each argument value */
for (int i = 0; i < dvc->argc; i++)
free(dvc->argv[i]);
free(dvc->argv);
}
free(dvc);
current = current->next;
}
/* Free underlying list */
guac_common_list_free(list->channels);
/* Free the DVC list itself */
free(list);
}
int guac_rdp_load_drdynvc(rdpContext* context, guac_rdp_dvc_list* list) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
rdpChannels* channels = context->channels;
/* Skip if no channels will be loaded */
if (list->channel_count == 0)
return 0;
/* For each channel */
guac_common_list_element* current = list->channels->head;
while (current != NULL) {
/* Get channel arguments */
guac_rdp_dvc* dvc = (guac_rdp_dvc*) current->data;
current = current->next;
/* guac_rdp_dvc_list_add() guarantees at one argument */
assert(dvc->argc >= 1);
/* guac_rdp_load_drdynvc() MUST only be invoked once */
assert(dvc->argv != NULL);
/* Log registration of plugin for current channel */
guac_client_log(client, GUAC_LOG_DEBUG,
"Registering DVC plugin \"%s\"", dvc->argv[0]);
/* Register plugin with FreeRDP */
ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV));
args->argc = dvc->argc;
args->argv = dvc->argv;
freerdp_dynamic_channel_collection_add(context->settings, args);
/* Rely on FreeRDP to free argv storage */
dvc->argv = NULL;
}
/* Load virtual channel management plugin */
return freerdp_channels_load_plugin(channels, context->settings,
"drdynvc", context->settings);
}

138
src/protocols/rdp/dvc.h Normal file
View File

@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef GUAC_RDP_DVC_H
#define GUAC_RDP_DVC_H
#include "config.h"
#include "guac_list.h"
#include <freerdp/freerdp.h>
/**
* The set of all arguments that should be passed to a given dynamic virtual
* channel plugin, including the name of that plugin.
*/
typedef struct guac_rdp_dvc {
/**
* The number of arguments in the argv array. This MUST be at least 1.
*/
int argc;
/**
* The argument values being passed to the dynamic virtual channel plugin.
* The first entry in this array is always the name of the plugin. If
* guac_rdp_load_drdynvc() has been invoked, and freeing the argument
* values is being delegated to FreeRDP, this will be NULL.
*/
char** argv;
} guac_rdp_dvc;
/**
* A list of dynamic virtual channels which should be provided to the DRDYNVC
* plugin once loaded via guac_rdp_load_drdynvc(). This interface exists purely
* to bridge incompatibilities between differing versions of FreeRDP and its
* DRDYNVC plugin. Any allocated guac_rdp_dvc_list is unlikely to be needed
* after the DRDYNVC plugin has been loaded.
*/
typedef struct guac_rdp_dvc_list {
/**
* Array of all dynamic virtual channels which should be registered with
* the DRDYNVC plugin once loaded. Each list element will point to a
* guac_rdp_dvc structure which must eventually be freed.
*/
guac_common_list* channels;
/**
* The number of channels within the list.
*/
int channel_count;
} guac_rdp_dvc_list;
/**
* Allocates a new, empty list of dynamic virtual channels. New channels may
* be added via guac_rdp_dvc_list_add(). The loading of those channels'
* associated plugins will be deferred until guac_rdp_load_drdynvc() is
* invoked.
*
* @return
* A newly-allocated, empty list of dynamic virtual channels.
*/
guac_rdp_dvc_list* guac_rdp_dvc_list_alloc();
/**
* Adds the given dynamic virtual channel plugin name and associated arguments
* to the list. The provied arguments list is NOT optional and MUST be
* NULL-terminated, even if there are no arguments for the named dynamic
* virtual channel plugin. Though FreeRDP requires that the arguments for a
* dynamic virtual channel plugin contain the name of the plugin itself as the
* first argument, the name must be excluded from the arguments provided here.
* This function will automatically take care of adding the plugin name to
* the arguments.
*
* @param list
* The guac_rdp_dvc_list to which the given plugin name and arguments
* should be added, for later bulk registration via
* guac_rdp_load_drdynvc().
*
* @param name
* The name of the dynamic virtual channel plugin that should be given
* the provided arguments when guac_rdp_load_drdynvc() is invoked.
*
* @param ...
* The string (char*) arguments which should be passed to the dynamic
* virtual channel plugin when it is loaded via guac_rdp_load_drdynvc(),
* excluding the plugin name itself.
*/
void guac_rdp_dvc_list_add(guac_rdp_dvc_list* list, const char* name, ...);
/**
* Frees the given list of dynamic virtual channels. Note that, while each
* individual entry within this list will be freed, it is partially up to
* FreeRDP to free the storage associated with the arguments passed to the
* virtual channels.
*
* @param list
* The list to free.
*/
void guac_rdp_dvc_list_free(guac_rdp_dvc_list* list);
/**
* Loads FreeRDP's DRDYNVC plugin and registers the dynamic virtual channel
* plugins described by the given guac_rdp_dvc_list. This function MUST be
* invoked no more than once per RDP connection. Invoking this function
* multiple times, even if the guac_rdp_dvc_list is different each time, will
* result in undefined behavior.
*
* @param context
* The rdpContext associated with the RDP connection for which the DRDYNVC
* plugin should be loaded.
*
* @param list
* A guac_rdp_dvc_list describing the dynamic virtual channel plugins that
* should be registered with the DRDYNVC plugin, along with any arguments.
*/
int guac_rdp_load_drdynvc(rdpContext* context, guac_rdp_dvc_list* list);
#endif

View File

@ -21,6 +21,7 @@
#include "audio_input.h" #include "audio_input.h"
#include "client.h" #include "client.h"
#include "dvc.h"
#include "guac_cursor.h" #include "guac_cursor.h"
#include "guac_display.h" #include "guac_display.h"
#include "guac_recording.h" #include "guac_recording.h"
@ -212,6 +213,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
rdpPrimaryUpdate* primary; rdpPrimaryUpdate* primary;
CLRCONV* clrconv; CLRCONV* clrconv;
guac_rdp_dvc_list* dvc_list = guac_rdp_dvc_list_alloc();
#ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER #ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
/* Init FreeRDP add-in provider */ /* Init FreeRDP add-in provider */
@ -224,35 +226,18 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
(pChannelConnectedEventHandler) guac_rdp_channel_connected); (pChannelConnectedEventHandler) guac_rdp_channel_connected);
#endif #endif
/* Load DRDYNVC plugin if required */
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE
|| settings->enable_audio_input) {
/* Load virtual channel management plugin */
if (freerdp_channels_load_plugin(channels, instance->settings,
"drdynvc", instance->settings))
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load drdynvc plugin. Display update and audio "
"input support will be disabled.");
/* Init display update plugin if "drdynvc" was loaded successfully */
else {
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/* Load "disp" plugin for display update */ /* Load "disp" plugin for display update */
if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE)
guac_rdp_disp_load_plugin(instance->context); guac_rdp_disp_load_plugin(instance->context, dvc_list);
#endif #endif
/* Load "AUDIO_INPUT" plugin for audio input*/ /* Load "AUDIO_INPUT" plugin for audio input*/
if (settings->enable_audio_input) { if (settings->enable_audio_input) {
rdp_client->audio_input = guac_rdp_audio_buffer_alloc(); rdp_client->audio_input = guac_rdp_audio_buffer_alloc();
guac_rdp_audio_load_plugin(instance->context); guac_rdp_audio_load_plugin(instance->context, dvc_list);
} }
}
} /* end if drdynvc required */
/* Load clipboard plugin */ /* Load clipboard plugin */
if (freerdp_channels_load_plugin(channels, instance->settings, if (freerdp_channels_load_plugin(channels, instance->settings,
"cliprdr", NULL)) "cliprdr", NULL))
@ -338,6 +323,15 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
} }
/* Load DRDYNVC plugin if required */
if (guac_rdp_load_drdynvc(instance->context, dvc_list))
guac_client_log(client, GUAC_LOG_WARNING,
"Failed to load drdynvc plugin. Display update and audio "
"input support will be disabled.");
/* Dynamic virtual channel list is no longer needed */
guac_rdp_dvc_list_free(dvc_list);
/* Init color conversion structure */ /* Init color conversion structure */
clrconv = calloc(1, sizeof(CLRCONV)); clrconv = calloc(1, sizeof(CLRCONV));
clrconv->alpha = 1; clrconv->alpha = 1;

View File

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include "client.h" #include "client.h"
#include "dvc.h"
#include "rdp.h" #include "rdp.h"
#include "rdp_disp.h" #include "rdp_disp.h"
#include "rdp_settings.h" #include "rdp_settings.h"
@ -54,20 +55,14 @@ void guac_rdp_disp_free(guac_rdp_disp* disp) {
free(disp); free(disp);
} }
void guac_rdp_disp_load_plugin(rdpContext* context) { void guac_rdp_disp_load_plugin(rdpContext* context, guac_rdp_dvc_list* list) {
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL #ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
context->settings->SupportDisplayControl = TRUE; context->settings->SupportDisplayControl = TRUE;
#endif #endif
/* Add "disp" channel */ /* Add "disp" channel */
ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV)); guac_rdp_dvc_list_add(list, "disp", NULL);
args->argc = 1;
args->argv = malloc(sizeof(char**) * 1);
args->argv[0] = strdup("disp");
freerdp_dynamic_channel_collection_add(context->settings, args);
#endif
} }

View File

@ -20,6 +20,7 @@
#ifndef GUAC_RDP_DISP_H #ifndef GUAC_RDP_DISP_H
#define GUAC_RDP_DISP_H #define GUAC_RDP_DISP_H
#include "dvc.h"
#include "rdp_settings.h" #include "rdp_settings.h"
#include <freerdp/freerdp.h> #include <freerdp/freerdp.h>
@ -102,7 +103,7 @@ void guac_rdp_disp_free(guac_rdp_disp* disp);
* *
* @param context The rdpContext associated with the active RDP session. * @param context The rdpContext associated with the active RDP session.
*/ */
void guac_rdp_disp_load_plugin(rdpContext* context); void guac_rdp_disp_load_plugin(rdpContext* context, guac_rdp_dvc_list* list);
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT #ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
/** /**