Compare commits

...

1 Commits

Author SHA1 Message Date
Virtually Nick
2cff6c6b73 [WIP]: Add support for links channel. 2022-04-11 14:27:07 -04:00
12 changed files with 319 additions and 2 deletions

View File

@ -717,3 +717,62 @@ int guac_client_supports_webp(guac_client* client) {
}
/**
* A callback that it is invokved by the call to guac_client_owner_send_uri
* which sends the 'uri" instruction and parameter to the specified user, who
* is the owner of the connection.
*
* @param user
* The user to send the "uri" instruction and parameter to, who owns the
* connection.
*
* @param data
* The URI to send to the owner, cast as a void*.
*
* @return
* Zero if the operation succeeds or non-zero on failure.
*/
static void* guac_client_owner_send_uri_callback(guac_user* user, void* data) {
const char* uri = (const char *) data;
/* Send uri parameter to owner. */
if (user != NULL)
return (void*) ((intptr_t) guac_protocol_send_uri(user->socket, uri));
return (void*) ((intptr_t) -1);
}
int guac_client_owner_send_uri(guac_client* client, const char* uri) {
/* Don't send the uri instruction if the client does not support it. */
if (!guac_client_owner_supports_uri(client))
return -1;
return (int) ((intptr_t) guac_client_for_owner(client, guac_client_owner_send_uri_callback, (void *) uri));
}
/**
* Callback function that is invoked by guac_client_owner_supports_uri to
* determine if the owner of a connection supports the "uri" instruction,
* so that Guacamole can pass URIs to the client browser.
*
* @param user
* The user that is being checked for URI support, which should be the
* owner of the connection.
*
* @param data
* Additional data - this will always be null.
*
* @return
* A non-zero integer if the user supports the "uri" instruction, otherwise
* zero to indicate a lack of URI support.
*/
static void* guac_owner_supports_uri_callback(guac_user* user, void* data) {
return (void*) ((intptr_t) guac_user_supports_uri(user));
}
int guac_client_owner_supports_uri(guac_client* client) {
return (int) ((intptr_t) guac_client_for_owner(client, guac_owner_supports_uri_callback, NULL));
}

View File

@ -565,6 +565,22 @@ int guac_client_get_processing_lag(guac_client* client);
*/
int guac_client_owner_send_required(guac_client* client, const char** required);
/**
* Sends the "uri" instruction to the given guac_client so that the remote
* client can process the URI, if the client supports it and has a handler
* configured for the URI.
*
* @param client
* The client to which to send the URI instruction.
*
* @param uri
* The URI to send to the remote client.
*
* @return
* Zero on success, non-zero on failure.
*/
int guac_client_owner_send_uri(guac_client* client, const char* uri);
/**
* Streams the given connection parameter value over an argument value stream
* ("argv" instruction), exposing the current value of the named connection
@ -736,6 +752,8 @@ int guac_client_owner_supports_required(guac_client* client);
*/
int guac_client_supports_webp(guac_client* client);
int guac_client_owner_supports_uri(guac_client* client);
/**
* The default Guacamole client layer, layer 0.
*/

View File

@ -38,7 +38,7 @@
* This version is passed by the __guac_protocol_send_args() function from the
* server to the client during the client/server handshake.
*/
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_3_0"
#define GUACAMOLE_PROTOCOL_VERSION "VERSION_1_6_0"
/**
* The maximum number of bytes that should be sent in any one blob instruction

View File

@ -306,7 +306,13 @@ typedef enum guac_protocol_version {
* allowing connections in guacd to request information from the client and
* await a response.
*/
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300
GUAC_PROTOCOL_VERSION_1_3_0 = 0x010300,
/**
* Protocol version 1.6.0, which supports the "uri" instruction, allowing
* remote systems to send
*/
GUAC_PROTOCOL_VERSION_1_6_0 = 0x010600
} guac_protocol_version;

View File

@ -1037,6 +1037,21 @@ int guac_protocol_send_shade(guac_socket* socket, const guac_layer* layer,
int guac_protocol_send_size(guac_socket* socket, const guac_layer* layer,
int w, int h);
/**
* Send the uri instruction over the given guac_socket connection,
* providing a URI that the client should then process locally.
*
* @param socket
* The guac_socket connection to which to send the uri instruction.
*
* @param uri
* The URI that should be sent to the client.
*
* @return
* Zero on success, non-zero on error.
*/
int guac_protocol_send_uri(guac_socket* socket, const char* uri);
/* TEXT INSTRUCTIONS */
/**

View File

@ -861,6 +861,17 @@ void guac_user_stream_webp(guac_user* user, guac_socket* socket,
*/
int guac_user_supports_required(guac_user* user);
/**
* Returns non-zero if the user supports the "uri" instruction.
*
* @param user
* The Guacamole user to check for support of the "uri" instruction.
*
* @return
* Non-zero if the user supports the "uri" instruction, otherwise zero.
*/
int guac_user_supports_uri(guac_user* user);
/**
* Returns whether the given user supports WebP. If the user does not
* support WebP, or the server cannot encode WebP images, zero is returned.

View File

@ -65,6 +65,7 @@ guac_protocol_version_mapping guac_protocol_version_table[] = {
{ GUAC_PROTOCOL_VERSION_1_0_0, "VERSION_1_0_0" },
{ GUAC_PROTOCOL_VERSION_1_1_0, "VERSION_1_1_0" },
{ GUAC_PROTOCOL_VERSION_1_3_0, "VERSION_1_3_0" },
{ GUAC_PROTOCOL_VERSION_1_6_0, "VERSION_1_6_0" },
{ GUAC_PROTOCOL_VERSION_UNKNOWN, NULL }
};
@ -1274,6 +1275,21 @@ int guac_protocol_send_undefine(guac_socket* socket,
}
int guac_protocol_send_uri(guac_socket* socket, const char* uri) {
int ret_val;
guac_socket_instruction_begin(socket);
ret_val =
guac_socket_write_string(socket, "3.uri,")
|| __guac_socket_write_length_string(socket, uri)
|| guac_socket_write_string(socket, ";");
guac_socket_instruction_end(socket);
return ret_val;
}
int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
const guac_layer* layer, const char* mimetype) {

View File

@ -325,6 +325,14 @@ int guac_user_supports_required(guac_user* user) {
}
int guac_user_supports_uri(guac_user* user) {
if (user == NULL)
return 0;
return (user->info.protocol_version >= GUAC_PROTOCOL_VERSION_1_6_0);
}
int guac_user_supports_webp(guac_user* user) {
#ifdef ENABLE_WEBP

View File

@ -59,6 +59,7 @@ libguac_client_rdp_la_SOURCES = \
channels/rdpei.c \
channels/rdpsnd/rdpsnd-messages.c \
channels/rdpsnd/rdpsnd.c \
channels/rdpuri.c \
client.c \
color.c \
decompose.c \
@ -105,6 +106,7 @@ noinst_HEADERS = \
channels/rdpei.h \
channels/rdpsnd/rdpsnd-messages.h \
channels/rdpsnd/rdpsnd.h \
channels/rdpuri.h \
client.h \
color.h \
decompose.h \

View File

@ -0,0 +1,133 @@
/*
* 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 "channels/rdpuri.h"
#include "client.h"
#include "config.h"
#include "plugins/channels.h"
#include "rdp.h"
#include "unicode.h"
#include <freerdp/client/cliprdr.h>
#include <freerdp/event.h>
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <winpr/wtsapi.h>
#include <winpr/wtypes.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/**
* Callback which associates handlers specific to Guacamole with the
* CliprdrClientContext instance allocated by FreeRDP to deal with received
* CLIPRDR (clipboard redirection) messages.
*
* This function is called whenever a channel connects via the PubSub event
* system within FreeRDP, but only has any effect if the connected channel is
* the CLIPRDR channel. This specific callback is registered with the PubSub
* system of the relevant rdpContext when guac_rdp_clipboard_load_plugin() is
* called.
*
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* Event-specific arguments, mainly the name of the channel, and a
* reference to the associated plugin loaded for that channel by FreeRDP.
*/
static void guac_rdpuri_channel_connected(guac_rdp_common_svc* svc) {
guac_client_log(svc->client, GUAC_LOG_DEBUG, "RDPURI (URI redirection) "
"channel connected.");
}
/**
* Callback which disassociates Guacamole from the CliprdrClientContext
* instance that was originally allocated by FreeRDP and is about to be
* deallocated.
*
* This function is called whenever a channel disconnects via the PubSub event
* system within FreeRDP, but only has any effect if the disconnected channel
* is the CLIPRDR channel. This specific callback is registered with the PubSub
* system of the relevant rdpContext when guac_rdp_clipboard_load_plugin() is
* called.
*
* @param context
* The rdpContext associated with the active RDP session.
*
* @param e
* Event-specific arguments, mainly the name of the channel, and a
* reference to the associated plugin loaded for that channel by FreeRDP.
*/
static void guac_rdpuri_channel_disconnected(guac_rdp_common_svc* svc) {
guac_client_log(svc->client, GUAC_LOG_DEBUG, "RDPURI (URI redirection) "
"channel disconnected.");
}
static void guac_rdpuri_channel_receive(guac_rdp_common_svc* svc, wStream* input_stream) {
guac_client_log(svc->client, GUAC_LOG_DEBUG, "RDPURI (URI redirection) "
"channel received data.");
// int len = Stream_Length(input_stream);
int len = Stream_Length(input_stream);
guac_client_log(svc->client, GUAC_LOG_DEBUG, "Received URI %d in length.", len);
if (len < 1) {
guac_client_log(svc->client, GUAC_LOG_WARNING, "Received URI data is too "
"small.");
return;
}
/* Convert input stream to UTF8 and send it to the client. */
char uri[len/2];
guac_rdp_utf16_to_utf8(Stream_Pointer(input_stream), len/2 - 1, uri, sizeof(uri));
guac_client_log(svc->client, GUAC_LOG_DEBUG, "Received URI from server: %s", uri);
guac_client_owner_send_uri(svc->client, uri);
}
void guac_rdpuri_load_plugin(rdpContext* context) {
guac_client* client = ((rdp_freerdp_context*) context)->client;
/* Attempt to load FreeRDP support for the CLIPRDR channel */
if (guac_rdp_common_svc_load_plugin(context, "rdpuri", 0,
guac_rdpuri_channel_connected, guac_rdpuri_channel_receive,
guac_rdpuri_channel_disconnected)) {
guac_client_log(client, GUAC_LOG_WARNING,
"Support for the RDPURI channel (URI redirection) "
"could not be loaded. This support normally takes the form of "
"a plugin which is built into FreeRDP. Lacking this support, "
"URI redirection will not work.");
return;
}
guac_client_log(client, GUAC_LOG_DEBUG, "Support for RDPURI "
"(URI redirection) registered. Awaiting channel "
"connection.");
}

View File

@ -0,0 +1,45 @@
/*
* 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_CHANNELS_RDPURI_H
#define GUAC_RDP_CHANNELS_RDPURI_H
#include "channels/common-svc.h"
#include <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/user.h>
#include <winpr/stream.h>
#include <winpr/wtypes.h>
/**
* Initializes clipboard support for RDP and handling of the RDPURI channel.
* If failures occur, messages noting the specifics of those failures will be
* logged, and the RDP side of URI redirection will not be functional.
*
* This MUST be called within the PreConnect callback of the freerdp instance
* for RDPURI support to be loaded.
*
* @param context
* The rdpContext associated with the FreeRDP side of the RDP connection.
*/
void guac_rdpuri_load_plugin(rdpContext* context);
#endif

View File

@ -29,6 +29,7 @@
#include "channels/rdpdr/rdpdr.h"
#include "channels/rdpei.h"
#include "channels/rdpsnd/rdpsnd.h"
#include "channels/rdpuri.h"
#include "client.h"
#include "color.h"
#include "common/cursor.h"
@ -123,6 +124,9 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
guac_rdpsnd_load_plugin(context);
}
/* Load rdpuri service */
guac_rdpuri_load_plugin(context);
/* Load RAIL plugin if RemoteApp in use */
if (settings->remote_app != NULL)
guac_rdp_rail_load_plugin(context);