Merge pull request #119 from glyptodon/fake-merge-screen-sharing-006-rdp
GUAC-1389: Add screen sharing support to RDP.
This commit is contained in:
commit
3c572501a9
@ -50,7 +50,7 @@ if ENABLE_TERMINAL
|
||||
endif
|
||||
|
||||
if ENABLE_RDP
|
||||
#SUBDIRS += src/protocols/rdp
|
||||
SUBDIRS += src/protocols/rdp
|
||||
endif
|
||||
|
||||
if ENABLE_SSH
|
||||
|
@ -28,7 +28,8 @@ lib_LTLIBRARIES = libguac-client-rdp.la
|
||||
libguac_client_rdp_la_SOURCES = \
|
||||
_generated_keymaps.c \
|
||||
client.c \
|
||||
guac_handlers.c \
|
||||
input.c \
|
||||
rdp.c \
|
||||
rdp_bitmap.c \
|
||||
rdp_cliprdr.c \
|
||||
rdp_color.c \
|
||||
@ -42,7 +43,8 @@ libguac_client_rdp_la_SOURCES = \
|
||||
rdp_stream.c \
|
||||
rdp_svc.c \
|
||||
resolution.c \
|
||||
unicode.c
|
||||
unicode.c \
|
||||
user.c
|
||||
|
||||
guacsvc_sources = \
|
||||
guac_svc/svc_service.c \
|
||||
@ -80,7 +82,8 @@ noinst_HEADERS = \
|
||||
guac_rdpsnd/rdpsnd_service.h \
|
||||
guac_svc/svc_service.h \
|
||||
client.h \
|
||||
guac_handlers.h \
|
||||
input.h \
|
||||
rdp.h \
|
||||
rdp_bitmap.h \
|
||||
rdp_cliprdr.h \
|
||||
rdp_color.h \
|
||||
@ -95,7 +98,8 @@ noinst_HEADERS = \
|
||||
rdp_stream.h \
|
||||
rdp_svc.h \
|
||||
resolution.h \
|
||||
unicode.h
|
||||
unicode.h \
|
||||
user.h
|
||||
|
||||
# Add compatibility layer for WinPR if not available
|
||||
if ! ENABLE_WINPR
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,37 +20,13 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _GUAC_RDP_CLIENT_H
|
||||
#define _GUAC_RDP_CLIENT_H
|
||||
#ifndef GUAC_RDP_CLIENT_H
|
||||
#define GUAC_RDP_CLIENT_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "guac_clipboard.h"
|
||||
#include "guac_list.h"
|
||||
#include "guac_surface.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_keymap.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "guac_sftp.h"
|
||||
#include "guac_ssh.h"
|
||||
#include "guac_ssh_user.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
#include "rdp_disp.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* The maximum duration of a frame in milliseconds.
|
||||
*/
|
||||
@ -63,6 +39,15 @@
|
||||
*/
|
||||
#define GUAC_RDP_FRAME_TIMEOUT 10
|
||||
|
||||
/**
|
||||
* The amount of time to wait for a new message from the RDP server when
|
||||
* beginning a new frame. This value must be kept reasonably small such that
|
||||
* a slow RDP server will not prevent external events from being handled (such
|
||||
* as the stop signal from guac_client_stop()), but large enough that the
|
||||
* message handling loop does not eat up CPU spinning.
|
||||
*/
|
||||
#define GUAC_RDP_FRAME_START_TIMEOUT 250000
|
||||
|
||||
/**
|
||||
* The native resolution of most RDP connections. As Windows and other systems
|
||||
* rely heavily on forced 96 DPI, we must assume 96 DPI.
|
||||
@ -107,152 +92,9 @@
|
||||
*/
|
||||
#define GUAC_RDP_AUDIO_BPS 16
|
||||
|
||||
|
||||
/**
|
||||
* Client data that will remain accessible through the guac_client.
|
||||
* This should generally include data commonly used by Guacamole handlers.
|
||||
* Handler which frees all data associated with the guac_client.
|
||||
*/
|
||||
typedef struct rdp_guac_client_data {
|
||||
|
||||
/**
|
||||
* Pointer to the FreeRDP client instance handling the current connection.
|
||||
*/
|
||||
freerdp* rdp_inst;
|
||||
|
||||
/**
|
||||
* All settings associated with the current or pending RDP connection.
|
||||
*/
|
||||
guac_rdp_settings settings;
|
||||
|
||||
/**
|
||||
* Button mask containing the OR'd value of all currently pressed buttons.
|
||||
*/
|
||||
int mouse_button_mask;
|
||||
|
||||
/**
|
||||
* Foreground color for any future glyphs.
|
||||
*/
|
||||
uint32_t glyph_color;
|
||||
|
||||
/**
|
||||
* The display.
|
||||
*/
|
||||
guac_common_surface* default_surface;
|
||||
|
||||
/**
|
||||
* The surface that GDI operations should draw to. RDP messages exist which
|
||||
* change this surface to allow drawing to occur off-screen.
|
||||
*/
|
||||
guac_common_surface* current_surface;
|
||||
|
||||
/**
|
||||
* The keymap to use when translating keysyms into scancodes or sequences
|
||||
* of scancodes for RDP.
|
||||
*/
|
||||
guac_rdp_static_keymap keymap;
|
||||
|
||||
/**
|
||||
* The state of all keys, based on whether events for pressing/releasing
|
||||
* particular keysyms have been received. This is necessary in order to
|
||||
* determine which keys must be released/pressed when a particular
|
||||
* keysym can only be typed through a sequence of scancodes (such as
|
||||
* an Alt-code) because the server-side keymap does not support that
|
||||
* keysym.
|
||||
*/
|
||||
guac_rdp_keysym_state_map keysym_state;
|
||||
|
||||
/**
|
||||
* The current clipboard contents.
|
||||
*/
|
||||
guac_common_clipboard* clipboard;
|
||||
|
||||
/**
|
||||
* The format of the clipboard which was requested. Data received from
|
||||
* the RDP server should conform to this format. This will be one of
|
||||
* several legal clipboard format values defined within FreeRDP, such as
|
||||
* CB_FORMAT_TEXT.
|
||||
*/
|
||||
int requested_clipboard_format;
|
||||
|
||||
/**
|
||||
* Audio output, if any.
|
||||
*/
|
||||
guac_audio_stream* audio;
|
||||
|
||||
/**
|
||||
* The filesystem being shared, if any.
|
||||
*/
|
||||
guac_rdp_fs* filesystem;
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/**
|
||||
* The user and credentials used to authenticate for SFTP.
|
||||
*/
|
||||
guac_common_ssh_user* sftp_user;
|
||||
|
||||
/**
|
||||
* The SSH session used for SFTP.
|
||||
*/
|
||||
guac_common_ssh_session* sftp_session;
|
||||
|
||||
/**
|
||||
* The exposed filesystem object, implemented with SFTP.
|
||||
*/
|
||||
guac_object* sftp_filesystem;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/**
|
||||
* Display size update module.
|
||||
*/
|
||||
guac_rdp_disp* disp;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* List of all available static virtual channels.
|
||||
*/
|
||||
guac_common_list* available_svc;
|
||||
|
||||
/**
|
||||
* Lock which is locked and unlocked for each RDP message.
|
||||
*/
|
||||
pthread_mutex_t rdp_lock;
|
||||
|
||||
/**
|
||||
* Common attributes for locks.
|
||||
*/
|
||||
pthread_mutexattr_t attributes;
|
||||
|
||||
} rdp_guac_client_data;
|
||||
|
||||
/**
|
||||
* Client data that will remain accessible through the RDP context.
|
||||
* This should generally include data commonly used by FreeRDP handlers.
|
||||
*/
|
||||
typedef struct rdp_freerdp_context {
|
||||
|
||||
/**
|
||||
* The parent context. THIS MUST BE THE FIRST ELEMENT.
|
||||
*/
|
||||
rdpContext _p;
|
||||
|
||||
/**
|
||||
* Pointer to the guac_client instance handling the RDP connection with
|
||||
* this context.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* Color conversion structure to be used to convert RDP images to PNGs.
|
||||
*/
|
||||
CLRCONV* clrconv;
|
||||
|
||||
/**
|
||||
* The current color palette, as received from the RDP server.
|
||||
*/
|
||||
UINT32 palette[256];
|
||||
|
||||
} rdp_freerdp_context;
|
||||
guac_client_free_handler guac_rdp_client_free_handler;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,525 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_clipboard.h"
|
||||
#include "guac_handlers.h"
|
||||
#include "guac_list.h"
|
||||
#include "guac_surface.h"
|
||||
#include "rdp_cliprdr.h"
|
||||
#include "rdp_keymap.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_rail.h"
|
||||
#include "rdp_stream.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include <guac_sftp.h>
|
||||
#include <guac_ssh.h>
|
||||
#include <guac_ssh_user.h>
|
||||
#endif
|
||||
|
||||
#include <freerdp/cache/cache.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <freerdp/utils/event.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/error.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
#include "rdp_disp.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#else
|
||||
#include "compat/client-cliprdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef LEGACY_FREERDP
|
||||
#include "compat/rail.h"
|
||||
#else
|
||||
#include <freerdp/rail.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to);
|
||||
int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed);
|
||||
|
||||
int rdp_guac_client_free_handler(guac_client* client) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data =
|
||||
(rdp_guac_client_data*) client->data;
|
||||
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
/* Clean up RDP client */
|
||||
freerdp_channels_close(channels, rdp_inst);
|
||||
freerdp_channels_free(channels);
|
||||
freerdp_disconnect(rdp_inst);
|
||||
freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv);
|
||||
cache_free(rdp_inst->context->cache);
|
||||
freerdp_free(rdp_inst);
|
||||
|
||||
/* Clean up filesystem, if allocated */
|
||||
if (guac_client_data->filesystem != NULL)
|
||||
guac_rdp_fs_free(guac_client_data->filesystem);
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/* Free SFTP filesystem, if loaded */
|
||||
if (guac_client_data->sftp_filesystem)
|
||||
guac_common_ssh_destroy_sftp_filesystem(guac_client_data->sftp_filesystem);
|
||||
|
||||
/* Free SFTP session */
|
||||
if (guac_client_data->sftp_session)
|
||||
guac_common_ssh_destroy_session(guac_client_data->sftp_session);
|
||||
|
||||
/* Free SFTP user */
|
||||
if (guac_client_data->sftp_user)
|
||||
guac_common_ssh_destroy_user(guac_client_data->sftp_user);
|
||||
|
||||
guac_common_ssh_uninit();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* Free display update module */
|
||||
guac_rdp_disp_free(guac_client_data->disp);
|
||||
#endif
|
||||
|
||||
/* Free SVC list */
|
||||
guac_common_list_free(guac_client_data->available_svc);
|
||||
|
||||
/* Free client data */
|
||||
guac_common_clipboard_free(guac_client_data->clipboard);
|
||||
guac_common_surface_free(guac_client_data->default_surface);
|
||||
free(guac_client_data);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int rdp_guac_client_wait_for_messages(guac_client* client, int timeout_usecs) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
int result;
|
||||
int index;
|
||||
int max_fd, fd;
|
||||
void* read_fds[32];
|
||||
void* write_fds[32];
|
||||
int read_count = 0;
|
||||
int write_count = 0;
|
||||
fd_set rfds, wfds;
|
||||
|
||||
struct timeval timeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = timeout_usecs
|
||||
};
|
||||
|
||||
/* Get RDP fds */
|
||||
if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get channel fds */
|
||||
if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds,
|
||||
&write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP channel file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Construct read fd_set */
|
||||
max_fd = 0;
|
||||
FD_ZERO(&rfds);
|
||||
for (index = 0; index < read_count; index++) {
|
||||
fd = (int)(long) (read_fds[index]);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
FD_SET(fd, &rfds);
|
||||
}
|
||||
|
||||
/* Construct write fd_set */
|
||||
FD_ZERO(&wfds);
|
||||
for (index = 0; index < write_count; index++) {
|
||||
fd = (int)(long) (write_fds[index]);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
FD_SET(fd, &wfds);
|
||||
}
|
||||
|
||||
/* If no file descriptors, error */
|
||||
if (max_fd == 0) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "No file descriptors associated with RDP connection.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for all RDP file descriptors */
|
||||
result = select(max_fd + 1, &rfds, &wfds, NULL, &timeout);
|
||||
if (result < 0) {
|
||||
|
||||
/* If error ignorable, pretend timout occurred */
|
||||
if (errno == EAGAIN
|
||||
|| errno == EWOULDBLOCK
|
||||
|| errno == EINPROGRESS
|
||||
|| errno == EINTR)
|
||||
return 0;
|
||||
|
||||
/* Otherwise, return as error */
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error waiting for file descriptor.");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Return wait result */
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
int rdp_guac_client_handle_messages(guac_client* client) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
wMessage* event;
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* Update remote display size */
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
guac_rdp_disp_update_size(guac_client_data->disp, rdp_inst->context);
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
#endif
|
||||
|
||||
/* Wait for messages */
|
||||
int wait_result = rdp_guac_client_wait_for_messages(client, 250000);
|
||||
guac_timestamp frame_start = guac_timestamp_current();
|
||||
while (wait_result > 0) {
|
||||
|
||||
guac_timestamp frame_end;
|
||||
int frame_remaining;
|
||||
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* Check the libfreerdp fds */
|
||||
if (!freerdp_check_fds(rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP file descriptors");
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check channel fds */
|
||||
if (!freerdp_channels_check_fds(channels, rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Error handling RDP channel file descriptors");
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Check for channel events */
|
||||
event = freerdp_channels_pop_event(channels);
|
||||
if (event) {
|
||||
|
||||
/* Handle channel events (clipboard and RAIL) */
|
||||
#ifdef LEGACY_EVENT
|
||||
if (event->event_class == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (event->event_class == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#else
|
||||
if (GetMessageClass(event->id) == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (GetMessageClass(event->id) == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#endif
|
||||
|
||||
freerdp_event_free(event);
|
||||
|
||||
}
|
||||
|
||||
/* Handle RDP disconnect */
|
||||
if (freerdp_shall_disconnect(rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "RDP server closed connection");
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
return 1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* Calculate time remaining in frame */
|
||||
frame_end = guac_timestamp_current();
|
||||
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION - frame_end;
|
||||
|
||||
/* Wait again if frame remaining */
|
||||
if (frame_remaining > 0)
|
||||
wait_result = rdp_guac_client_wait_for_messages(client,
|
||||
GUAC_RDP_FRAME_TIMEOUT*1000);
|
||||
else
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* If an error occurred, fail */
|
||||
if (wait_result < 0)
|
||||
return 1;
|
||||
|
||||
/* Success */
|
||||
guac_common_surface_flush(guac_client_data->default_surface);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* If button mask unchanged, just send move event */
|
||||
if (mask == guac_client_data->mouse_button_mask)
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
|
||||
|
||||
/* Otherwise, send events describing button change */
|
||||
else {
|
||||
|
||||
/* Mouse buttons which have JUST become released */
|
||||
int released_mask = guac_client_data->mouse_button_mask & ~mask;
|
||||
|
||||
/* Mouse buttons which have JUST become pressed */
|
||||
int pressed_mask = ~guac_client_data->mouse_button_mask & mask;
|
||||
|
||||
/* Release event */
|
||||
if (released_mask & 0x07) {
|
||||
|
||||
/* Calculate flags */
|
||||
int flags = 0;
|
||||
if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
|
||||
if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
|
||||
if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
|
||||
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
|
||||
|
||||
}
|
||||
|
||||
/* Press event */
|
||||
if (pressed_mask & 0x07) {
|
||||
|
||||
/* Calculate flags */
|
||||
int flags = PTR_FLAGS_DOWN;
|
||||
if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
|
||||
if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
|
||||
if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
|
||||
if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78;
|
||||
if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
|
||||
|
||||
/* Send event */
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
|
||||
|
||||
}
|
||||
|
||||
/* Scroll event */
|
||||
if (pressed_mask & 0x18) {
|
||||
|
||||
/* Down */
|
||||
if (pressed_mask & 0x08)
|
||||
rdp_inst->input->MouseEvent(
|
||||
rdp_inst->input,
|
||||
PTR_FLAGS_WHEEL | 0x78,
|
||||
x, y);
|
||||
|
||||
/* Up */
|
||||
if (pressed_mask & 0x10)
|
||||
rdp_inst->input->MouseEvent(
|
||||
rdp_inst->input,
|
||||
PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88,
|
||||
x, y);
|
||||
|
||||
}
|
||||
|
||||
guac_client_data->mouse_button_mask = mask;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
|
||||
/* If keysym can be in lookup table */
|
||||
if (GUAC_RDP_KEYSYM_STORABLE(keysym)) {
|
||||
|
||||
int pressed_flags;
|
||||
|
||||
/* Look up scancode mapping */
|
||||
const guac_rdp_keysym_desc* keysym_desc =
|
||||
&GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym);
|
||||
|
||||
/* If defined, send event */
|
||||
if (keysym_desc->scancode != 0) {
|
||||
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* If defined, send any prerequesite keys that must be set */
|
||||
if (keysym_desc->set_keysyms != NULL)
|
||||
__guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1);
|
||||
|
||||
/* If defined, release any keys that must be cleared */
|
||||
if (keysym_desc->clear_keysyms != NULL)
|
||||
__guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0);
|
||||
|
||||
/* Determine proper event flag for pressed state */
|
||||
if (pressed)
|
||||
pressed_flags = KBD_FLAGS_DOWN;
|
||||
else
|
||||
pressed_flags = KBD_FLAGS_RELEASE;
|
||||
|
||||
/* Send actual key */
|
||||
rdp_inst->input->KeyboardEvent(rdp_inst->input, keysym_desc->flags | pressed_flags,
|
||||
keysym_desc->scancode);
|
||||
|
||||
/* If defined, release any keys that were originally released */
|
||||
if (keysym_desc->set_keysyms != NULL)
|
||||
__guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0);
|
||||
|
||||
/* If defined, send any keys that were originally set */
|
||||
if (keysym_desc->clear_keysyms != NULL)
|
||||
__guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1);
|
||||
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall back to unicode events if undefined inside current keymap */
|
||||
|
||||
/* Only send when key pressed - Unicode events do not have
|
||||
* DOWN/RELEASE flags */
|
||||
if (pressed) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Sending keysym 0x%x as Unicode", keysym);
|
||||
|
||||
/* Translate keysym into codepoint */
|
||||
int codepoint;
|
||||
if (keysym <= 0xFF)
|
||||
codepoint = keysym;
|
||||
else if (keysym >= 0x1000000)
|
||||
codepoint = keysym & 0xFFFFFF;
|
||||
else {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Unmapped keysym has no equivalent unicode "
|
||||
"value: 0x%x", keysym);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* Send Unicode event */
|
||||
rdp_inst->input->UnicodeKeyboardEvent(
|
||||
rdp_inst->input,
|
||||
0, codepoint);
|
||||
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
int keysym;
|
||||
|
||||
/* Send all keysyms in string, NULL terminated */
|
||||
while ((keysym = *keysym_string) != 0) {
|
||||
|
||||
/* Get current keysym state */
|
||||
int current_state = GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym);
|
||||
|
||||
/* If key is currently in given state, send event for changing it to specified "to" state */
|
||||
if (current_state == from)
|
||||
__guac_rdp_send_keysym(client, *keysym_string, to);
|
||||
|
||||
/* Next keysym */
|
||||
keysym_string++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
|
||||
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
|
||||
/* Update keysym state */
|
||||
if (GUAC_RDP_KEYSYM_STORABLE(keysym))
|
||||
GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym) = pressed;
|
||||
|
||||
return __guac_rdp_send_keysym(client, keysym, pressed);
|
||||
|
||||
}
|
||||
|
||||
int rdp_guac_client_size_handler(guac_client* client, int width, int height) {
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
rdp_guac_client_data* guac_client_data =
|
||||
(rdp_guac_client_data*) client->data;
|
||||
|
||||
freerdp* rdp_inst = guac_client_data->rdp_inst;
|
||||
|
||||
/* Convert client pixels to remote pixels */
|
||||
width = width * guac_client_data->settings.resolution
|
||||
/ client->info.optimal_resolution;
|
||||
|
||||
height = height * guac_client_data->settings.resolution
|
||||
/ client->info.optimal_resolution;
|
||||
|
||||
/* Send display update */
|
||||
pthread_mutex_lock(&(guac_client_data->rdp_lock));
|
||||
guac_rdp_disp_set_size(guac_client_data->disp, rdp_inst->context,
|
||||
width, height);
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdpdr_fs_messages.h"
|
||||
#include "rdpdr_messages.h"
|
||||
#include "rdpdr_service.h"
|
||||
@ -135,7 +135,8 @@ static void guac_rdpdr_device_fs_free_handler(guac_rdpdr_device* device) {
|
||||
|
||||
void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr) {
|
||||
|
||||
rdp_guac_client_data* data = (rdp_guac_client_data*) rdpdr->client->data;
|
||||
guac_client* client = rdpdr->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
int id = rdpdr->devices_registered++;
|
||||
|
||||
/* Get new device */
|
||||
@ -152,12 +153,10 @@ void guac_rdpdr_register_fs(guac_rdpdrPlugin* rdpdr) {
|
||||
device->free_handler = guac_rdpdr_device_fs_free_handler;
|
||||
|
||||
/* Init data */
|
||||
device->data = data->filesystem;
|
||||
device->data = rdp_client->filesystem;
|
||||
|
||||
/* Announce filesystem to client */
|
||||
guac_protocol_send_filesystem(rdpdr->client->socket,
|
||||
data->filesystem->object, "Shared Drive");
|
||||
guac_socket_flush(rdpdr->client->socket);
|
||||
/* Announce filesystem to owner */
|
||||
guac_client_for_owner(client, guac_rdp_fs_expose, rdp_client->filesystem);
|
||||
|
||||
}
|
||||
|
||||
|
@ -282,9 +282,9 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
|
||||
(guac_rdpdr_printer_data*) device->data;
|
||||
|
||||
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
||||
completion_id, STATUS_SUCCESS, 1);
|
||||
completion_id, STATUS_SUCCESS, 4);
|
||||
|
||||
Stream_Write_UINT32(output_stream, 0); /* padding*/
|
||||
Stream_Write_UINT32(output_stream, 0); /* Padding */
|
||||
|
||||
/* Close input and wait for output thread to finish */
|
||||
close(printer_data->printer_input);
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "rdp_stream.h"
|
||||
@ -91,18 +91,18 @@ void guac_rdpdr_process_connect(rdpSvcPlugin* plugin) {
|
||||
plugin->channel_entry_points.pExtendedData = NULL;
|
||||
|
||||
/* Get data from client */
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Init plugin */
|
||||
rdpdr->client = client;
|
||||
rdpdr->devices_registered = 0;
|
||||
|
||||
/* Register printer if enabled */
|
||||
if (client_data->settings.printing_enabled)
|
||||
if (rdp_client->settings->printing_enabled)
|
||||
guac_rdpdr_register_printer(rdpdr);
|
||||
|
||||
/* Register drive if enabled */
|
||||
if (client_data->settings.drive_enabled)
|
||||
if (rdp_client->settings->drive_enabled)
|
||||
guac_rdpdr_register_fs(rdpdr);
|
||||
|
||||
/* Log that printing, etc. has been loaded */
|
||||
@ -222,12 +222,38 @@ wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path) {
|
||||
/**
|
||||
* Callback invoked on the current connection owner (if any) when a file
|
||||
* download is being initiated using the magic "Download" folder.
|
||||
*
|
||||
* @param owner
|
||||
* The guac_user that is the owner of the connection, or NULL if the
|
||||
* connection owner has left.
|
||||
*
|
||||
* @param data
|
||||
* The full absolute path to the file that should be downloaded.
|
||||
*
|
||||
* @return
|
||||
* The stream allocated for the file download, or NULL if the download has
|
||||
* failed to start.
|
||||
*/
|
||||
static void* guac_rdpdr_download_to_owner(guac_user* owner, void* data) {
|
||||
|
||||
/* Get client and stream */
|
||||
guac_client* client = device->rdpdr->client;
|
||||
/* Do not bother attempting the download if the owner has left */
|
||||
if (owner == NULL)
|
||||
return NULL;
|
||||
|
||||
int file_id = guac_rdp_fs_open((guac_rdp_fs*) device->data, path,
|
||||
guac_client* client = owner->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_fs* filesystem = rdp_client->filesystem;
|
||||
|
||||
/* Ignore download if filesystem has been unloaded */
|
||||
if (filesystem == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Attempt to open requested file */
|
||||
char* path = (char*) data;
|
||||
int file_id = guac_rdp_fs_open(filesystem, path,
|
||||
ACCESS_FILE_READ_DATA, 0, DISP_FILE_OPEN, 0);
|
||||
|
||||
/* If file opened successfully, start stream */
|
||||
@ -240,7 +266,7 @@ void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path) {
|
||||
char c;
|
||||
|
||||
/* Associate stream with transfer status */
|
||||
guac_stream* stream = guac_client_alloc_stream(client);
|
||||
guac_stream* stream = guac_user_alloc_stream(owner);
|
||||
stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream));
|
||||
stream->ack_handler = guac_rdp_download_ack_handler;
|
||||
rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM;
|
||||
@ -260,17 +286,31 @@ void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path) {
|
||||
|
||||
} while (c != '\0');
|
||||
|
||||
guac_client_log(device->rdpdr->client, GUAC_LOG_DEBUG,
|
||||
"%s: Initiating download of \"%s\"", __func__, path);
|
||||
guac_user_log(owner, GUAC_LOG_DEBUG, "%s: Initiating download "
|
||||
"of \"%s\"", __func__, path);
|
||||
|
||||
/* Begin stream */
|
||||
guac_protocol_send_file(client->socket, stream,
|
||||
guac_protocol_send_file(owner->socket, stream,
|
||||
"application/octet-stream", basename);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(owner->socket);
|
||||
|
||||
/* Download started successfully */
|
||||
return stream;
|
||||
|
||||
}
|
||||
else
|
||||
guac_client_log(client, GUAC_LOG_ERROR, "Unable to download \"%s\"", path);
|
||||
|
||||
/* Download failed */
|
||||
guac_user_log(owner, GUAC_LOG_ERROR, "Unable to download \"%s\"", path);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpdr_start_download(guac_rdpdr_device* device, char* path) {
|
||||
|
||||
guac_client* client = device->rdpdr->client;
|
||||
|
||||
/* Initiate download to the owner of the connection */
|
||||
guac_client_for_owner(client, guac_rdpdr_download_to_owner, path);
|
||||
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device,
|
||||
/**
|
||||
* Begins streaming the given file to the user via a Guacamole file stream.
|
||||
*/
|
||||
void guac_rdpdr_start_download(guac_rdpdr_device* device, const char* path);
|
||||
void guac_rdpdr_start_download(guac_rdpdr_device* device, char* path);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdpsnd_messages.h"
|
||||
#include "rdpsnd_service.h"
|
||||
|
||||
@ -56,10 +56,10 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_audio_stream* audio = client_data->audio;
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Format header */
|
||||
Stream_Seek(input_stream, 14);
|
||||
@ -188,7 +188,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_SetPointer(output_stream, output_stream_end);
|
||||
|
||||
/* Send accepted formats */
|
||||
pthread_mutex_lock(&(client_data->rdp_lock));
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
|
||||
|
||||
/* If version greater than 6, must send Quality Mode PDU */
|
||||
@ -205,7 +205,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
svc_plugin_send((rdpSvcPlugin*)rdpsnd, output_stream);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(client_data->rdp_lock));
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
@ -218,7 +218,7 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Read timestamp and data size */
|
||||
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
|
||||
@ -232,9 +232,9 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_Write_UINT16(output_stream, rdpsnd->server_timestamp);
|
||||
Stream_Write_UINT16(output_stream, data_size);
|
||||
|
||||
pthread_mutex_lock(&(client_data->rdp_lock));
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send((rdpSvcPlugin*) rdpsnd, output_stream);
|
||||
pthread_mutex_unlock(&(client_data->rdp_lock));
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
@ -245,10 +245,10 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_audio_stream* audio = client_data->audio;
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Read wave information */
|
||||
Stream_Read_UINT16(input_stream, rdpsnd->server_timestamp);
|
||||
@ -283,10 +283,10 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
|
||||
/* Get associated client data */
|
||||
guac_client* client = rdpsnd->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get audio stream from client data */
|
||||
guac_audio_stream* audio = client_data->audio;
|
||||
guac_audio_stream* audio = rdp_client->audio;
|
||||
|
||||
/* Wave Confirmation PDU */
|
||||
wStream* output_stream = Stream_New(NULL, 8);
|
||||
@ -313,9 +313,9 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
Stream_Write_UINT8(output_stream, 0);
|
||||
|
||||
/* Send Wave Confirmation PDU */
|
||||
pthread_mutex_lock(&(client_data->rdp_lock));
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
svc_plugin_send(plugin, output_stream);
|
||||
pthread_mutex_unlock(&(client_data->rdp_lock));
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* We no longer expect to receive wave data */
|
||||
rdpsnd->next_pdu_is_wave = FALSE;
|
||||
|
@ -97,8 +97,9 @@ void guac_svc_process_connect(rdpSvcPlugin* plugin) {
|
||||
|
||||
/* Create pipe */
|
||||
svc->output_pipe = guac_client_alloc_stream(svc->client);
|
||||
guac_protocol_send_pipe(svc->client->socket, svc->output_pipe,
|
||||
"application/octet-stream", svc->name);
|
||||
|
||||
/* Notify of pipe's existence */
|
||||
guac_rdp_svc_send_pipe(svc->client->socket, svc);
|
||||
|
||||
/* Log connection to static channel */
|
||||
guac_client_log(svc->client, GUAC_LOG_INFO,
|
||||
|
280
src/protocols/rdp/input.c
Normal file
280
src/protocols/rdp/input.c
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "input.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_keymap.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
#include "rdp_disp.h"
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
|
||||
/* Skip if not yet connected */
|
||||
if (rdp_inst == NULL)
|
||||
return 0;
|
||||
|
||||
/* If keysym can be in lookup table */
|
||||
if (GUAC_RDP_KEYSYM_STORABLE(keysym)) {
|
||||
|
||||
int pressed_flags;
|
||||
|
||||
/* Look up scancode mapping */
|
||||
const guac_rdp_keysym_desc* keysym_desc =
|
||||
&GUAC_RDP_KEYSYM_LOOKUP(rdp_client->keymap, keysym);
|
||||
|
||||
/* If defined, send event */
|
||||
if (keysym_desc->scancode != 0) {
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* If defined, send any prerequesite keys that must be set */
|
||||
if (keysym_desc->set_keysyms != NULL)
|
||||
guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1);
|
||||
|
||||
/* If defined, release any keys that must be cleared */
|
||||
if (keysym_desc->clear_keysyms != NULL)
|
||||
guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0);
|
||||
|
||||
/* Determine proper event flag for pressed state */
|
||||
if (pressed)
|
||||
pressed_flags = KBD_FLAGS_DOWN;
|
||||
else
|
||||
pressed_flags = KBD_FLAGS_RELEASE;
|
||||
|
||||
/* Send actual key */
|
||||
rdp_inst->input->KeyboardEvent(rdp_inst->input, keysym_desc->flags | pressed_flags,
|
||||
keysym_desc->scancode);
|
||||
|
||||
/* If defined, release any keys that were originally released */
|
||||
if (keysym_desc->set_keysyms != NULL)
|
||||
guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0);
|
||||
|
||||
/* If defined, send any keys that were originally set */
|
||||
if (keysym_desc->clear_keysyms != NULL)
|
||||
guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Fall back to unicode events if undefined inside current keymap */
|
||||
|
||||
/* Only send when key pressed - Unicode events do not have
|
||||
* DOWN/RELEASE flags */
|
||||
if (pressed) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Sending keysym 0x%x as Unicode", keysym);
|
||||
|
||||
/* Translate keysym into codepoint */
|
||||
int codepoint;
|
||||
if (keysym <= 0xFF)
|
||||
codepoint = keysym;
|
||||
else if (keysym >= 0x1000000)
|
||||
codepoint = keysym & 0xFFFFFF;
|
||||
else {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Unmapped keysym has no equivalent unicode "
|
||||
"value: 0x%x", keysym);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Send Unicode event */
|
||||
rdp_inst->input->UnicodeKeyboardEvent(
|
||||
rdp_inst->input,
|
||||
0, codepoint);
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
int keysym;
|
||||
|
||||
/* Send all keysyms in string, NULL terminated */
|
||||
while ((keysym = *keysym_string) != 0) {
|
||||
|
||||
/* Get current keysym state */
|
||||
int current_state = GUAC_RDP_KEYSYM_LOOKUP(rdp_client->keysym_state, keysym);
|
||||
|
||||
/* If key is currently in given state, send event for changing it to specified "to" state */
|
||||
if (current_state == from)
|
||||
guac_rdp_send_keysym(client, *keysym_string, to);
|
||||
|
||||
/* Next keysym */
|
||||
keysym_string++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_user_mouse_handler(guac_user* user, int x, int y, int mask) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
|
||||
/* Store current mouse location */
|
||||
guac_common_cursor_move(rdp_client->display->cursor, user, x, y);
|
||||
|
||||
/* Skip if not yet connected */
|
||||
if (rdp_inst == NULL)
|
||||
return 0;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* If button mask unchanged, just send move event */
|
||||
if (mask == rdp_client->mouse_button_mask)
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
|
||||
|
||||
/* Otherwise, send events describing button change */
|
||||
else {
|
||||
|
||||
/* Mouse buttons which have JUST become released */
|
||||
int released_mask = rdp_client->mouse_button_mask & ~mask;
|
||||
|
||||
/* Mouse buttons which have JUST become pressed */
|
||||
int pressed_mask = ~rdp_client->mouse_button_mask & mask;
|
||||
|
||||
/* Release event */
|
||||
if (released_mask & 0x07) {
|
||||
|
||||
/* Calculate flags */
|
||||
int flags = 0;
|
||||
if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
|
||||
if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
|
||||
if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
|
||||
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
|
||||
|
||||
}
|
||||
|
||||
/* Press event */
|
||||
if (pressed_mask & 0x07) {
|
||||
|
||||
/* Calculate flags */
|
||||
int flags = PTR_FLAGS_DOWN;
|
||||
if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
|
||||
if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
|
||||
if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
|
||||
if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78;
|
||||
if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
|
||||
|
||||
/* Send event */
|
||||
rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
|
||||
|
||||
}
|
||||
|
||||
/* Scroll event */
|
||||
if (pressed_mask & 0x18) {
|
||||
|
||||
/* Down */
|
||||
if (pressed_mask & 0x08)
|
||||
rdp_inst->input->MouseEvent(
|
||||
rdp_inst->input,
|
||||
PTR_FLAGS_WHEEL | 0x78,
|
||||
x, y);
|
||||
|
||||
/* Up */
|
||||
if (pressed_mask & 0x10)
|
||||
rdp_inst->input->MouseEvent(
|
||||
rdp_inst->input,
|
||||
PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88,
|
||||
x, y);
|
||||
|
||||
}
|
||||
|
||||
rdp_client->mouse_button_mask = mask;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int guac_rdp_user_key_handler(guac_user* user, int keysym, int pressed) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Update keysym state */
|
||||
if (GUAC_RDP_KEYSYM_STORABLE(keysym))
|
||||
GUAC_RDP_KEYSYM_LOOKUP(rdp_client->keysym_state, keysym) = pressed;
|
||||
|
||||
return guac_rdp_send_keysym(client, keysym, pressed);
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_user_size_handler(guac_user* user, int width, int height) {
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
|
||||
/* Skip if not yet connected */
|
||||
if (rdp_inst == NULL)
|
||||
return 0;
|
||||
|
||||
/* Convert client pixels to remote pixels */
|
||||
width = width * rdp_client->settings->resolution
|
||||
/ user->info.optimal_resolution;
|
||||
|
||||
height = height * rdp_client->settings->resolution
|
||||
/ user->info.optimal_resolution;
|
||||
|
||||
/* Send display update */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_disp_set_size(rdp_client->disp, rdp_inst->context, width, height);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
87
src/protocols/rdp/input.h
Normal file
87
src/protocols/rdp/input.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_INPUT_H
|
||||
#define GUAC_RDP_INPUT_H
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
/**
|
||||
* Presses or releases the given keysym, sending an appropriate set of key
|
||||
* events to the RDP server. The key events sent will depend on the current
|
||||
* keymap.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param keysym
|
||||
* The keysym being pressed or released.
|
||||
*
|
||||
* @param pressed
|
||||
* Zero if the keysym is being released, non-zero otherwise.
|
||||
*
|
||||
* @return
|
||||
* Zero if the keys were successfully sent, non-zero otherwise.
|
||||
*/
|
||||
int guac_rdp_send_keysym(guac_client* client, int keysym, int pressed);
|
||||
|
||||
/**
|
||||
* For every keysym in the given NULL-terminated array of keysyms, update
|
||||
* the current state of that key conditionally. For each key in the "from"
|
||||
* state (0 being released and 1 being pressed), that key will be updated
|
||||
* to the "to" state.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param keysym_string
|
||||
* A NULL-terminated array of keysyms, each of which will be updated.
|
||||
*
|
||||
* @param from
|
||||
* 0 if the state of currently-released keys should be updated, or 1 if
|
||||
* the state of currently-pressed keys should be updated.
|
||||
*
|
||||
* @param to
|
||||
* 0 if the keys being updated should be marked as released, or 1 if
|
||||
* the keys being updated should be marked as pressed.
|
||||
*/
|
||||
void guac_rdp_update_keysyms(guac_client* client, const int* keysym_string,
|
||||
int from, int to);
|
||||
|
||||
/**
|
||||
* Handler for Guacamole user mouse events.
|
||||
*/
|
||||
guac_user_mouse_handler guac_rdp_user_mouse_handler;
|
||||
|
||||
/**
|
||||
* Handler for Guacamole user key events.
|
||||
*/
|
||||
guac_user_key_handler guac_rdp_user_key_handler;
|
||||
|
||||
/**
|
||||
* Handler for Guacamole user size events.
|
||||
*/
|
||||
guac_user_size_handler guac_rdp_user_size_handler;
|
||||
|
||||
#endif
|
||||
|
932
src/protocols/rdp/rdp.c
Normal file
932
src/protocols/rdp/rdp.c
Normal file
@ -0,0 +1,932 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_cursor.h"
|
||||
#include "guac_display.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_cliprdr.h"
|
||||
#include "rdp_gdi.h"
|
||||
#include "rdp_glyph.h"
|
||||
#include "rdp_keymap.h"
|
||||
#include "rdp_pointer.h"
|
||||
#include "rdp_rail.h"
|
||||
#include "rdp_stream.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include <guac_sftp.h>
|
||||
#include <guac_ssh.h>
|
||||
#include <guac_ssh_user.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
#include "rdp_disp.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/cache/bitmap.h>
|
||||
#include <freerdp/cache/brush.h>
|
||||
#include <freerdp/cache/glyph.h>
|
||||
#include <freerdp/cache/offscreen.h>
|
||||
#include <freerdp/cache/palette.h>
|
||||
#include <freerdp/cache/pointer.h>
|
||||
#include <freerdp/channels/channels.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/timestamp.h>
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CLIPRDR_H
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#else
|
||||
#include "compat/client-cliprdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_DISP_H
|
||||
#include <freerdp/client/disp.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
#include <freerdp/event.h>
|
||||
#endif
|
||||
|
||||
#ifdef LEGACY_FREERDP
|
||||
#include "compat/rail.h"
|
||||
#else
|
||||
#include <freerdp/rail.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
#else
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_ADDIN_H
|
||||
#include <freerdp/addin.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_CLIENT_CHANNELS_H
|
||||
#include <freerdp/client/channels.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_VERSION_H
|
||||
#include <freerdp/version.h>
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP for data received along a channel. This is the
|
||||
* most recent version of the callback and uses a 16-bit unsigned integer for
|
||||
* the channel ID, as well as different type naming for the datatype of the
|
||||
* data itself. This function does nothing more than invoke
|
||||
* freerdp_channels_data() with the given arguments. The prototypes of these
|
||||
* functions are compatible in 1.2 and later, but not necessarily prior to
|
||||
* that, hence the conditional compilation of differing prototypes.
|
||||
*
|
||||
* Beware that the official purpose of these parameters is an undocumented
|
||||
* mystery. The meanings below are derived from looking at how the function is
|
||||
* used within FreeRDP.
|
||||
*
|
||||
* @param rdp_inst
|
||||
* The RDP client instance associated with the channel receiving the data.
|
||||
*
|
||||
* @param channelId
|
||||
* The integer ID of the channel that received the data.
|
||||
*
|
||||
* @param data
|
||||
* A buffer containing the received data.
|
||||
*
|
||||
* @param size
|
||||
* The number of bytes received and contained in the given buffer (the
|
||||
* number of bytes received within the PDU that resulted in this function
|
||||
* being inboked).
|
||||
*
|
||||
* @param flags
|
||||
* Channel control flags, as defined by the CHANNEL_PDU_HEADER in the RDP
|
||||
* specification.
|
||||
*
|
||||
* @param total_size
|
||||
* The total length of the chanel data being received, which may span
|
||||
* multiple PDUs (see the "length" field of CHANNEL_PDU_HEADER).
|
||||
*
|
||||
* @return
|
||||
* Zero if the received channel data was successfully handled, non-zero
|
||||
* otherwise. Note that this return value is discarded in practice.
|
||||
*/
|
||||
#if defined(FREERDP_VERSION_MAJOR) \
|
||||
&& (FREERDP_VERSION_MAJOR > 1 || FREERDP_VERSION_MINOR >= 2)
|
||||
static int __guac_receive_channel_data(freerdp* rdp_inst, UINT16 channelId,
|
||||
BYTE* data, int size, int flags, int total_size) {
|
||||
#else
|
||||
static int __guac_receive_channel_data(freerdp* rdp_inst, int channelId,
|
||||
UINT8* data, int size, int flags, int total_size) {
|
||||
#endif
|
||||
return freerdp_channels_data(rdp_inst, channelId,
|
||||
data, size, flags, total_size);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
/**
|
||||
* Called whenever a channel connects via the PubSub event system within
|
||||
* FreeRDP.
|
||||
*
|
||||
* @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_rdp_channel_connected(rdpContext* context,
|
||||
ChannelConnectedEventArgs* e) {
|
||||
|
||||
#ifdef HAVE_RDPSETTINGS_SUPPORTDISPLAYCONTROL
|
||||
/* Store reference to the display update plugin once it's connected */
|
||||
if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) {
|
||||
|
||||
DispClientContext* disp = (DispClientContext*) e->pInterface;
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Init module with current display size */
|
||||
guac_rdp_disp_set_size(rdp_client->disp, context,
|
||||
guac_rdp_get_width(context->instance),
|
||||
guac_rdp_get_height(context->instance));
|
||||
|
||||
/* Store connected channel */
|
||||
guac_rdp_disp_connect(rdp_client->disp, disp);
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Display update channel connected.");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdpChannels* channels = context->channels;
|
||||
rdpBitmap* bitmap;
|
||||
rdpGlyph* glyph;
|
||||
rdpPointer* pointer;
|
||||
rdpPrimaryUpdate* primary;
|
||||
CLRCONV* clrconv;
|
||||
|
||||
guac_rdp_client* rdp_client =
|
||||
(guac_rdp_client*) client->data;
|
||||
|
||||
#ifdef HAVE_FREERDP_REGISTER_ADDIN_PROVIDER
|
||||
/* Init FreeRDP add-in provider */
|
||||
freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_EVENT_PUBSUB
|
||||
/* Subscribe to and handle channel connected events */
|
||||
PubSub_SubscribeChannelConnected(context->pubSub,
|
||||
(pChannelConnectedEventHandler) guac_rdp_channel_connected);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* 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.");
|
||||
|
||||
/* Init display update plugin */
|
||||
rdp_client->disp = guac_rdp_disp_alloc();
|
||||
guac_rdp_disp_load_plugin(instance->context);
|
||||
#endif
|
||||
|
||||
/* Load clipboard plugin */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"cliprdr", NULL))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load cliprdr plugin. Clipboard will not work.");
|
||||
|
||||
/* If audio enabled, choose an encoder */
|
||||
if (rdp_client->settings->audio_enabled) {
|
||||
|
||||
rdp_client->audio = guac_audio_stream_alloc(client, NULL,
|
||||
GUAC_RDP_AUDIO_RATE,
|
||||
GUAC_RDP_AUDIO_CHANNELS,
|
||||
GUAC_RDP_AUDIO_BPS);
|
||||
|
||||
/* Warn if no audio encoding is available */
|
||||
if (rdp_client->audio == NULL)
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"No available audio encoding. Sound disabled.");
|
||||
|
||||
} /* end if audio enabled */
|
||||
|
||||
/* Load filesystem if drive enabled */
|
||||
if (rdp_client->settings->drive_enabled)
|
||||
rdp_client->filesystem =
|
||||
guac_rdp_fs_alloc(client, rdp_client->settings->drive_path,
|
||||
rdp_client->settings->create_drive_path);
|
||||
|
||||
/* If RDPSND/RDPDR required, load them */
|
||||
if (rdp_client->settings->printing_enabled
|
||||
|| rdp_client->settings->drive_enabled
|
||||
|| rdp_client->settings->audio_enabled) {
|
||||
|
||||
/* Load RDPDR plugin */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacdr", client))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load guacdr plugin. Drive redirection and "
|
||||
"printing will not work. Sound MAY not work.");
|
||||
|
||||
/* Load RDPSND plugin */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacsnd", client))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load guacsnd alongside guacdr plugin. Sound "
|
||||
"will not work. Drive redirection and printing MAY not "
|
||||
"work.");
|
||||
|
||||
}
|
||||
|
||||
/* Load RAIL plugin if RemoteApp in use */
|
||||
if (rdp_client->settings->remote_app != NULL) {
|
||||
|
||||
#ifdef LEGACY_FREERDP
|
||||
RDP_PLUGIN_DATA* plugin_data = malloc(sizeof(RDP_PLUGIN_DATA) * 2);
|
||||
|
||||
plugin_data[0].size = sizeof(RDP_PLUGIN_DATA);
|
||||
plugin_data[0].data[0] = rdp_client->settings->remote_app;
|
||||
plugin_data[0].data[1] = rdp_client->settings->remote_app_dir;
|
||||
plugin_data[0].data[2] = rdp_client->settings->remote_app_args;
|
||||
plugin_data[0].data[3] = NULL;
|
||||
|
||||
plugin_data[1].size = 0;
|
||||
|
||||
/* Attempt to load rail */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"rail", plugin_data))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load rail plugin. RemoteApp will not work.");
|
||||
#else
|
||||
/* Attempt to load rail */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"rail", instance->settings))
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Failed to load rail plugin. RemoteApp will not work.");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/* Load SVC plugin instances for all static channels */
|
||||
if (rdp_client->settings->svc_names != NULL) {
|
||||
|
||||
char** current = rdp_client->settings->svc_names;
|
||||
do {
|
||||
|
||||
guac_rdp_svc* svc = guac_rdp_alloc_svc(client, *current);
|
||||
|
||||
/* Attempt to load guacsvc plugin for new static channel */
|
||||
if (freerdp_channels_load_plugin(channels, instance->settings,
|
||||
"guacsvc", svc)) {
|
||||
guac_client_log(client, GUAC_LOG_WARNING,
|
||||
"Cannot create static channel \"%s\": failed to load guacsvc plugin.",
|
||||
svc->name);
|
||||
guac_rdp_free_svc(svc);
|
||||
}
|
||||
|
||||
/* Store and log on success */
|
||||
else {
|
||||
guac_rdp_add_svc(client, svc);
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Created static channel \"%s\"...",
|
||||
svc->name);
|
||||
}
|
||||
|
||||
} while (*(++current) != NULL);
|
||||
|
||||
}
|
||||
|
||||
/* Init color conversion structure */
|
||||
clrconv = calloc(1, sizeof(CLRCONV));
|
||||
clrconv->alpha = 1;
|
||||
clrconv->invert = 0;
|
||||
clrconv->rgb555 = 0;
|
||||
clrconv->palette = calloc(1, sizeof(rdpPalette));
|
||||
((rdp_freerdp_context*) context)->clrconv = clrconv;
|
||||
|
||||
/* Init FreeRDP cache */
|
||||
instance->context->cache = cache_new(instance->settings);
|
||||
|
||||
/* Set up bitmap handling */
|
||||
bitmap = calloc(1, sizeof(rdpBitmap));
|
||||
bitmap->size = sizeof(guac_rdp_bitmap);
|
||||
bitmap->New = guac_rdp_bitmap_new;
|
||||
bitmap->Free = guac_rdp_bitmap_free;
|
||||
bitmap->Paint = guac_rdp_bitmap_paint;
|
||||
bitmap->Decompress = guac_rdp_bitmap_decompress;
|
||||
bitmap->SetSurface = guac_rdp_bitmap_setsurface;
|
||||
graphics_register_bitmap(context->graphics, bitmap);
|
||||
free(bitmap);
|
||||
|
||||
/* Set up glyph handling */
|
||||
glyph = calloc(1, sizeof(rdpGlyph));
|
||||
glyph->size = sizeof(guac_rdp_glyph);
|
||||
glyph->New = guac_rdp_glyph_new;
|
||||
glyph->Free = guac_rdp_glyph_free;
|
||||
glyph->Draw = guac_rdp_glyph_draw;
|
||||
glyph->BeginDraw = guac_rdp_glyph_begindraw;
|
||||
glyph->EndDraw = guac_rdp_glyph_enddraw;
|
||||
graphics_register_glyph(context->graphics, glyph);
|
||||
free(glyph);
|
||||
|
||||
/* Set up pointer handling */
|
||||
pointer = calloc(1, sizeof(rdpPointer));
|
||||
pointer->size = sizeof(guac_rdp_pointer);
|
||||
pointer->New = guac_rdp_pointer_new;
|
||||
pointer->Free = guac_rdp_pointer_free;
|
||||
pointer->Set = guac_rdp_pointer_set;
|
||||
#ifdef HAVE_RDPPOINTER_SETNULL
|
||||
pointer->SetNull = guac_rdp_pointer_set_null;
|
||||
#endif
|
||||
#ifdef HAVE_RDPPOINTER_SETDEFAULT
|
||||
pointer->SetDefault = guac_rdp_pointer_set_default;
|
||||
#endif
|
||||
graphics_register_pointer(context->graphics, pointer);
|
||||
free(pointer);
|
||||
|
||||
/* Set up GDI */
|
||||
instance->update->DesktopResize = guac_rdp_gdi_desktop_resize;
|
||||
instance->update->EndPaint = guac_rdp_gdi_end_paint;
|
||||
instance->update->Palette = guac_rdp_gdi_palette_update;
|
||||
instance->update->SetBounds = guac_rdp_gdi_set_bounds;
|
||||
|
||||
primary = instance->update->primary;
|
||||
primary->DstBlt = guac_rdp_gdi_dstblt;
|
||||
primary->PatBlt = guac_rdp_gdi_patblt;
|
||||
primary->ScrBlt = guac_rdp_gdi_scrblt;
|
||||
primary->MemBlt = guac_rdp_gdi_memblt;
|
||||
primary->OpaqueRect = guac_rdp_gdi_opaquerect;
|
||||
|
||||
pointer_cache_register_callbacks(instance->update);
|
||||
glyph_cache_register_callbacks(instance->update);
|
||||
brush_cache_register_callbacks(instance->update);
|
||||
bitmap_cache_register_callbacks(instance->update);
|
||||
offscreen_cache_register_callbacks(instance->update);
|
||||
palette_cache_register_callbacks(instance->update);
|
||||
|
||||
/* Init channels (pre-connect) */
|
||||
if (freerdp_channels_pre_connect(channels, instance)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP just after the connection is established with
|
||||
* the RDP server. Implementations are required to manually invoke
|
||||
* freerdp_channels_post_connect().
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance that has just connected.
|
||||
*
|
||||
* @return
|
||||
* TRUE if successful, FALSE if an error occurs.
|
||||
*/
|
||||
static BOOL rdp_freerdp_post_connect(freerdp* instance) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdpChannels* channels = instance->context->channels;
|
||||
|
||||
/* Init channels (post-connect) */
|
||||
if (freerdp_channels_post_connect(channels, instance)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error initializing RDP client channel manager");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP when authentication is required but a username
|
||||
* and password has not already been given. In the case of Guacamole, this
|
||||
* function always succeeds but does not populate the usename or password. The
|
||||
* username/password must be given within the connection parameters.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance associated with the RDP session requesting
|
||||
* credentials.
|
||||
*
|
||||
* @param username
|
||||
* Pointer to a string which will receive the user's username.
|
||||
*
|
||||
* @param password
|
||||
* Pointer to a string which will receive the user's password.
|
||||
*
|
||||
* @param domain
|
||||
* Pointer to a string which will receive the domain associated with the
|
||||
* user's account.
|
||||
*
|
||||
* @return
|
||||
* Always TRUE.
|
||||
*/
|
||||
static BOOL rdp_freerdp_authenticate(freerdp* instance, char** username,
|
||||
char** password, char** domain) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
|
||||
/* Warn if connection is likely to fail due to lack of credentials */
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"Authentication requested but username or password not given");
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP when the SSL/TLS certificate of the RDP server
|
||||
* needs to be verified. If this ever happens, this function implementation
|
||||
* will always fail unless the connection has been configured to ignore
|
||||
* certificate validity.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance associated with the RDP session whose SSL/TLS
|
||||
* certificate needs to be verified.
|
||||
*
|
||||
* @param subject
|
||||
* The subject to whom the certificate was issued.
|
||||
*
|
||||
* @param issuer
|
||||
* The authority that issued the certificate,
|
||||
*
|
||||
* @param fingerprint
|
||||
* The cryptographic fingerprint of the certificate.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the certificate passes verification, FALSE otherwise.
|
||||
*/
|
||||
static BOOL rdp_freerdp_verify_certificate(freerdp* instance, char* subject,
|
||||
char* issuer, char* fingerprint) {
|
||||
|
||||
rdpContext* context = instance->context;
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_rdp_client* rdp_client =
|
||||
(guac_rdp_client*) client->data;
|
||||
|
||||
/* Bypass validation if ignore_certificate given */
|
||||
if (rdp_client->settings->ignore_certificate) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation bypassed");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Certificate validation failed");
|
||||
return FALSE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP after a new rdpContext has been allocated and
|
||||
* associated with the current FreeRDP instance. Implementations are required
|
||||
* to manually invoke freerdp_channels_new() at this point.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance whose context has just been allocated.
|
||||
*
|
||||
* @param context
|
||||
* The newly-allocated FreeRDP context.
|
||||
*/
|
||||
static void rdp_freerdp_context_new(freerdp* instance, rdpContext* context) {
|
||||
context->channels = freerdp_channels_new();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by FreeRDP when the rdpContext is being freed. This must be
|
||||
* provided, but there is no Guacamole-specific data associated with the
|
||||
* FreeRDP context, so nothing is done here.
|
||||
*
|
||||
* @param instance
|
||||
* The FreeRDP instance whose context is being freed.
|
||||
*
|
||||
* @param context
|
||||
* The FreeRDP context being freed.
|
||||
*/
|
||||
static void rdp_freerdp_context_free(freerdp* instance, rdpContext* context) {
|
||||
/* EMPTY */
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all keysym/scancode mappings declared within the given keymap and its
|
||||
* parent keymap, if any. These mappings are stored within the guac_rdp_client
|
||||
* structure associated with the given guac_client for future use in
|
||||
* translating keysyms to the scancodes required by RDP key events.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client whose associated guac_rdp_client should be initialized
|
||||
* with the keysym/scancode mapping defined in the given keymap.
|
||||
*
|
||||
* @param keymap
|
||||
* The keymap to use to populate the given client's keysym/scancode
|
||||
* mapping.
|
||||
*/
|
||||
static void __guac_rdp_client_load_keymap(guac_client* client,
|
||||
const guac_rdp_keymap* keymap) {
|
||||
|
||||
guac_rdp_client* rdp_client =
|
||||
(guac_rdp_client*) client->data;
|
||||
|
||||
/* Get mapping */
|
||||
const guac_rdp_keysym_desc* mapping = keymap->mapping;
|
||||
|
||||
/* If parent exists, load parent first */
|
||||
if (keymap->parent != NULL)
|
||||
__guac_rdp_client_load_keymap(client, keymap->parent);
|
||||
|
||||
/* Log load */
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Loading keymap \"%s\"", keymap->name);
|
||||
|
||||
/* Load mapping into keymap */
|
||||
while (mapping->keysym != 0) {
|
||||
|
||||
/* Copy mapping */
|
||||
GUAC_RDP_KEYSYM_LOOKUP(rdp_client->keymap, mapping->keysym) =
|
||||
*mapping;
|
||||
|
||||
/* Next keysym */
|
||||
mapping++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for messages from the RDP server for the given number of microseconds.
|
||||
*
|
||||
* @param client
|
||||
* The client associated with the current RDP session.
|
||||
*
|
||||
* @param timeout_usecs
|
||||
* The maximum amount of time to wait, in microseconds.
|
||||
*
|
||||
* @return
|
||||
* A positive value if messages are ready, zero if the specified timeout
|
||||
* period elapsed, or a negative value if an error occurs.
|
||||
*/
|
||||
static int rdp_guac_client_wait_for_messages(guac_client* client,
|
||||
int timeout_usecs) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
freerdp* rdp_inst = rdp_client->rdp_inst;
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
int result;
|
||||
int index;
|
||||
int max_fd, fd;
|
||||
void* read_fds[32];
|
||||
void* write_fds[32];
|
||||
int read_count = 0;
|
||||
int write_count = 0;
|
||||
fd_set rfds, wfds;
|
||||
|
||||
struct timeval timeout = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = timeout_usecs
|
||||
};
|
||||
|
||||
/* Get RDP fds */
|
||||
if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get channel fds */
|
||||
if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds,
|
||||
&write_count)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Unable to read RDP channel file descriptors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Construct read fd_set */
|
||||
max_fd = 0;
|
||||
FD_ZERO(&rfds);
|
||||
for (index = 0; index < read_count; index++) {
|
||||
fd = (int)(long) (read_fds[index]);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
FD_SET(fd, &rfds);
|
||||
}
|
||||
|
||||
/* Construct write fd_set */
|
||||
FD_ZERO(&wfds);
|
||||
for (index = 0; index < write_count; index++) {
|
||||
fd = (int)(long) (write_fds[index]);
|
||||
if (fd > max_fd)
|
||||
max_fd = fd;
|
||||
FD_SET(fd, &wfds);
|
||||
}
|
||||
|
||||
/* If no file descriptors, error */
|
||||
if (max_fd == 0) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "No file descriptors associated with RDP connection.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Wait for all RDP file descriptors */
|
||||
result = select(max_fd + 1, &rfds, &wfds, NULL, &timeout);
|
||||
if (result < 0) {
|
||||
|
||||
/* If error ignorable, pretend timout occurred */
|
||||
if (errno == EAGAIN
|
||||
|| errno == EWOULDBLOCK
|
||||
|| errno == EINPROGRESS
|
||||
|| errno == EINTR)
|
||||
return 0;
|
||||
|
||||
/* Otherwise, return as error */
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Error waiting for file descriptor.");
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/* Return wait result */
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void* guac_rdp_client_thread(void* data) {
|
||||
|
||||
guac_client* client = (guac_client*) data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
/* Init random number generator */
|
||||
srandom(time(NULL));
|
||||
|
||||
/* Create display */
|
||||
rdp_client->display = guac_common_display_alloc(client,
|
||||
rdp_client->settings->width,
|
||||
rdp_client->settings->height);
|
||||
|
||||
rdp_client->current_surface = rdp_client->display->default_surface;
|
||||
|
||||
#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
|
||||
freerdp_channels_global_init();
|
||||
#endif
|
||||
|
||||
/* Init client */
|
||||
freerdp* rdp_inst = freerdp_new();
|
||||
rdp_inst->PreConnect = rdp_freerdp_pre_connect;
|
||||
rdp_inst->PostConnect = rdp_freerdp_post_connect;
|
||||
rdp_inst->Authenticate = rdp_freerdp_authenticate;
|
||||
rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
|
||||
rdp_inst->ReceiveChannelData = __guac_receive_channel_data;
|
||||
|
||||
/* Allocate FreeRDP context */
|
||||
#ifdef LEGACY_FREERDP
|
||||
rdp_inst->context_size = sizeof(rdp_freerdp_context);
|
||||
#else
|
||||
rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
|
||||
#endif
|
||||
rdp_inst->ContextNew = (pContextNew) rdp_freerdp_context_new;
|
||||
rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free;
|
||||
|
||||
freerdp_context_new(rdp_inst);
|
||||
((rdp_freerdp_context*) rdp_inst->context)->client = client;
|
||||
|
||||
/* Load keymap into client */
|
||||
__guac_rdp_client_load_keymap(client, settings->server_layout);
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
guac_common_ssh_init(client);
|
||||
|
||||
/* Connect via SSH if SFTP is enabled */
|
||||
if (settings->enable_sftp) {
|
||||
|
||||
/* Abort if username is missing */
|
||||
if (settings->sftp_username == NULL)
|
||||
return NULL;
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Connecting via SSH for SFTP filesystem access.");
|
||||
|
||||
rdp_client->sftp_user =
|
||||
guac_common_ssh_create_user(settings->sftp_username);
|
||||
|
||||
/* Import private key, if given */
|
||||
if (settings->sftp_private_key != NULL) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Authenticating with private key.");
|
||||
|
||||
/* Abort if private key cannot be read */
|
||||
if (guac_common_ssh_user_import_key(rdp_client->sftp_user,
|
||||
settings->sftp_private_key,
|
||||
settings->sftp_passphrase)) {
|
||||
guac_common_ssh_destroy_user(rdp_client->sftp_user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise, use specified password */
|
||||
else {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Authenticating with password.");
|
||||
|
||||
guac_common_ssh_user_set_password(rdp_client->sftp_user,
|
||||
settings->sftp_password);
|
||||
|
||||
}
|
||||
|
||||
/* Attempt SSH connection */
|
||||
rdp_client->sftp_session =
|
||||
guac_common_ssh_create_session(client, settings->sftp_hostname,
|
||||
settings->sftp_port, rdp_client->sftp_user);
|
||||
|
||||
/* Fail if SSH connection does not succeed */
|
||||
if (rdp_client->sftp_session == NULL) {
|
||||
/* Already aborted within guac_common_ssh_create_session() */
|
||||
guac_common_ssh_destroy_user(rdp_client->sftp_user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load and expose filesystem */
|
||||
rdp_client->sftp_filesystem =
|
||||
guac_common_ssh_create_sftp_filesystem(
|
||||
rdp_client->sftp_session, "/");
|
||||
|
||||
/* Expose filesystem to connection owner */
|
||||
guac_client_for_owner(client,
|
||||
guac_common_ssh_expose_sftp_filesystem,
|
||||
rdp_client->sftp_filesystem);
|
||||
|
||||
/* Abort if SFTP connection fails */
|
||||
if (rdp_client->sftp_filesystem == NULL) {
|
||||
guac_common_ssh_destroy_session(rdp_client->sftp_session);
|
||||
guac_common_ssh_destroy_user(rdp_client->sftp_user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"SFTP connection succeeded.");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Send connection name */
|
||||
guac_protocol_send_name(client->socket, settings->hostname);
|
||||
|
||||
/* Set default pointer */
|
||||
guac_common_cursor_set_pointer(rdp_client->display->cursor);
|
||||
|
||||
/* Push desired settings to FreeRDP */
|
||||
guac_rdp_push_settings(settings, rdp_inst);
|
||||
|
||||
/* Connect to RDP server */
|
||||
if (!freerdp_connect(rdp_inst)) {
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||
"Error connecting to RDP server");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Connection complete */
|
||||
rdp_client->rdp_inst = rdp_inst;
|
||||
|
||||
rdpChannels* channels = rdp_inst->context->channels;
|
||||
|
||||
/* Handle messages from RDP server while client is running */
|
||||
while (client->state == GUAC_CLIENT_RUNNING) {
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/* Update remote display size */
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
guac_rdp_disp_update_size(rdp_client->disp, rdp_inst->context);
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
#endif
|
||||
|
||||
/* Wait for data and construct a reasonable frame */
|
||||
int wait_result = rdp_guac_client_wait_for_messages(client,
|
||||
GUAC_RDP_FRAME_START_TIMEOUT);
|
||||
guac_timestamp frame_start = guac_timestamp_current();
|
||||
while (wait_result > 0) {
|
||||
|
||||
guac_timestamp frame_end;
|
||||
int frame_remaining;
|
||||
|
||||
pthread_mutex_lock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Check the libfreerdp fds */
|
||||
if (!freerdp_check_fds(rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Error handling RDP file descriptors");
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check channel fds */
|
||||
if (!freerdp_channels_check_fds(channels, rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
"Error handling RDP channel file descriptors");
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check for channel events */
|
||||
wMessage* event = freerdp_channels_pop_event(channels);
|
||||
if (event) {
|
||||
|
||||
/* Handle channel events (clipboard and RAIL) */
|
||||
#ifdef LEGACY_EVENT
|
||||
if (event->event_class == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (event->event_class == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#else
|
||||
if (GetMessageClass(event->id) == CliprdrChannel_Class)
|
||||
guac_rdp_process_cliprdr_event(client, event);
|
||||
else if (GetMessageClass(event->id) == RailChannel_Class)
|
||||
guac_rdp_process_rail_event(client, event);
|
||||
#endif
|
||||
|
||||
freerdp_event_free(event);
|
||||
|
||||
}
|
||||
|
||||
/* Handle RDP disconnect */
|
||||
if (freerdp_shall_disconnect(rdp_inst)) {
|
||||
guac_client_log(client, GUAC_LOG_INFO,
|
||||
"RDP server closed connection");
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(rdp_client->rdp_lock));
|
||||
|
||||
/* Calculate time remaining in frame */
|
||||
frame_end = guac_timestamp_current();
|
||||
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION - frame_end;
|
||||
|
||||
/* Wait again if frame remaining */
|
||||
if (frame_remaining > 0)
|
||||
wait_result = rdp_guac_client_wait_for_messages(client,
|
||||
GUAC_RDP_FRAME_TIMEOUT*1000);
|
||||
else
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* If an error occurred, fail */
|
||||
if (wait_result < 0)
|
||||
guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR,
|
||||
"Connection closed.");
|
||||
|
||||
/* End of frame */
|
||||
guac_common_display_flush(rdp_client->display);
|
||||
guac_client_end_frame(client);
|
||||
guac_socket_flush(client->socket);
|
||||
|
||||
}
|
||||
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");
|
||||
return NULL;
|
||||
|
||||
}
|
||||
|
219
src/protocols/rdp/rdp.h
Normal file
219
src/protocols/rdp/rdp.h
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_H
|
||||
#define GUAC_RDP_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "guac_clipboard.h"
|
||||
#include "guac_display.h"
|
||||
#include "guac_surface.h"
|
||||
#include "guac_list.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_keymap.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "guac_sftp.h"
|
||||
#include "guac_ssh.h"
|
||||
#include "guac_ssh_user.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
#include "rdp_disp.h"
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* RDP-specific client data.
|
||||
*/
|
||||
typedef struct guac_rdp_client {
|
||||
|
||||
/**
|
||||
* The RDP client thread.
|
||||
*/
|
||||
pthread_t client_thread;
|
||||
|
||||
/**
|
||||
* Pointer to the FreeRDP client instance handling the current connection.
|
||||
*/
|
||||
freerdp* rdp_inst;
|
||||
|
||||
/**
|
||||
* All settings associated with the current or pending RDP connection.
|
||||
*/
|
||||
guac_rdp_settings* settings;
|
||||
|
||||
/**
|
||||
* Button mask containing the OR'd value of all currently pressed buttons.
|
||||
*/
|
||||
int mouse_button_mask;
|
||||
|
||||
/**
|
||||
* Foreground color for any future glyphs.
|
||||
*/
|
||||
uint32_t glyph_color;
|
||||
|
||||
/**
|
||||
* The display.
|
||||
*/
|
||||
guac_common_display* display;
|
||||
|
||||
/**
|
||||
* The surface that GDI operations should draw to. RDP messages exist which
|
||||
* change this surface to allow drawing to occur off-screen.
|
||||
*/
|
||||
guac_common_surface* current_surface;
|
||||
|
||||
/**
|
||||
* The keymap to use when translating keysyms into scancodes or sequences
|
||||
* of scancodes for RDP.
|
||||
*/
|
||||
guac_rdp_static_keymap keymap;
|
||||
|
||||
/**
|
||||
* The state of all keys, based on whether events for pressing/releasing
|
||||
* particular keysyms have been received. This is necessary in order to
|
||||
* determine which keys must be released/pressed when a particular
|
||||
* keysym can only be typed through a sequence of scancodes (such as
|
||||
* an Alt-code) because the server-side keymap does not support that
|
||||
* keysym.
|
||||
*/
|
||||
guac_rdp_keysym_state_map keysym_state;
|
||||
|
||||
/**
|
||||
* The current clipboard contents.
|
||||
*/
|
||||
guac_common_clipboard* clipboard;
|
||||
|
||||
/**
|
||||
* The format of the clipboard which was requested. Data received from
|
||||
* the RDP server should conform to this format. This will be one of
|
||||
* several legal clipboard format values defined within FreeRDP, such as
|
||||
* CB_FORMAT_TEXT.
|
||||
*/
|
||||
int requested_clipboard_format;
|
||||
|
||||
/**
|
||||
* Audio output, if any.
|
||||
*/
|
||||
guac_audio_stream* audio;
|
||||
|
||||
/**
|
||||
* The filesystem being shared, if any.
|
||||
*/
|
||||
guac_rdp_fs* filesystem;
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/**
|
||||
* The user and credentials used to authenticate for SFTP.
|
||||
*/
|
||||
guac_common_ssh_user* sftp_user;
|
||||
|
||||
/**
|
||||
* The SSH session used for SFTP.
|
||||
*/
|
||||
guac_common_ssh_session* sftp_session;
|
||||
|
||||
/**
|
||||
* An SFTP-based filesystem.
|
||||
*/
|
||||
guac_common_ssh_sftp_filesystem* sftp_filesystem;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT
|
||||
/**
|
||||
* Display size update module.
|
||||
*/
|
||||
guac_rdp_disp* disp;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* List of all available static virtual channels.
|
||||
*/
|
||||
guac_common_list* available_svc;
|
||||
|
||||
/**
|
||||
* Lock which is locked and unlocked for each RDP message.
|
||||
*/
|
||||
pthread_mutex_t rdp_lock;
|
||||
|
||||
/**
|
||||
* Common attributes for locks.
|
||||
*/
|
||||
pthread_mutexattr_t attributes;
|
||||
|
||||
} guac_rdp_client;
|
||||
|
||||
/**
|
||||
* Client data that will remain accessible through the RDP context.
|
||||
* This should generally include data commonly used by FreeRDP handlers.
|
||||
*/
|
||||
typedef struct rdp_freerdp_context {
|
||||
|
||||
/**
|
||||
* The parent context. THIS MUST BE THE FIRST ELEMENT.
|
||||
*/
|
||||
rdpContext _p;
|
||||
|
||||
/**
|
||||
* Pointer to the guac_client instance handling the RDP connection with
|
||||
* this context.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* Color conversion structure to be used to convert RDP images to PNGs.
|
||||
*/
|
||||
CLRCONV* clrconv;
|
||||
|
||||
/**
|
||||
* The current color palette, as received from the RDP server.
|
||||
*/
|
||||
UINT32 palette[256];
|
||||
|
||||
} rdp_freerdp_context;
|
||||
|
||||
/**
|
||||
* RDP client thread. This thread runs throughout the duration of the client,
|
||||
* existing as a single instance, shared by all users.
|
||||
*
|
||||
* @param data
|
||||
* The guac_client to associate with an RDP session, once the RDP
|
||||
* connection succeeds.
|
||||
*
|
||||
* @return
|
||||
* NULL in all cases. The return value of this thread is expected to be
|
||||
* ignored.
|
||||
*/
|
||||
void* guac_rdp_client_thread(void* data);
|
||||
|
||||
#endif
|
||||
|
@ -23,7 +23,9 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_display.h"
|
||||
#include "guac_surface.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
@ -46,12 +48,11 @@
|
||||
void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_socket* socket = client->socket;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Allocate surface */
|
||||
guac_layer* buffer = guac_client_alloc_buffer(client);
|
||||
guac_common_surface* surface = guac_common_surface_alloc(client, socket,
|
||||
buffer, bitmap->width, bitmap->height);
|
||||
/* Allocate buffer */
|
||||
guac_common_display_layer* buffer = guac_common_display_alloc_buffer(
|
||||
rdp_client->display, bitmap->width, bitmap->height);
|
||||
|
||||
/* Cache image data if present */
|
||||
if (bitmap->data != NULL) {
|
||||
@ -62,7 +63,7 @@ void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
|
||||
bitmap->width, bitmap->height, 4*bitmap->width);
|
||||
|
||||
/* Send surface to buffer */
|
||||
guac_common_surface_draw(surface, 0, 0, image);
|
||||
guac_common_surface_draw(buffer->surface, 0, 0, image);
|
||||
|
||||
/* Free surface */
|
||||
cairo_surface_destroy(image);
|
||||
@ -70,8 +71,7 @@ void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap) {
|
||||
}
|
||||
|
||||
/* Store buffer reference in bitmap */
|
||||
((guac_rdp_bitmap*) bitmap)->buffer = buffer;
|
||||
((guac_rdp_bitmap*) bitmap)->surface = surface;
|
||||
((guac_rdp_bitmap*) bitmap)->layer = buffer;
|
||||
|
||||
}
|
||||
|
||||
@ -101,8 +101,7 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
||||
}
|
||||
|
||||
/* No corresponding surface yet - caching is deferred. */
|
||||
((guac_rdp_bitmap*) bitmap)->buffer = NULL;
|
||||
((guac_rdp_bitmap*) bitmap)->surface = NULL;
|
||||
((guac_rdp_bitmap*) bitmap)->layer = NULL;
|
||||
|
||||
/* Start at zero usage */
|
||||
((guac_rdp_bitmap*) bitmap)->used = 0;
|
||||
@ -112,21 +111,22 @@ void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap) {
|
||||
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_common_surface* surface = ((guac_rdp_bitmap*) bitmap)->surface;
|
||||
guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
|
||||
|
||||
int width = bitmap->right - bitmap->left + 1;
|
||||
int height = bitmap->bottom - bitmap->top + 1;
|
||||
|
||||
/* If not cached, cache if necessary */
|
||||
if (surface == NULL && ((guac_rdp_bitmap*) bitmap)->used >= 1)
|
||||
if (buffer == NULL && ((guac_rdp_bitmap*) bitmap)->used >= 1)
|
||||
guac_rdp_cache_bitmap(context, bitmap);
|
||||
|
||||
/* If cached, retrieve from cache */
|
||||
if (surface != NULL)
|
||||
guac_common_surface_copy(surface, 0, 0, width, height,
|
||||
client_data->default_surface, bitmap->left, bitmap->top);
|
||||
if (buffer != NULL)
|
||||
guac_common_surface_copy(buffer->surface, 0, 0, width, height,
|
||||
rdp_client->display->default_surface,
|
||||
bitmap->left, bitmap->top);
|
||||
|
||||
/* Otherwise, draw with stored image data */
|
||||
else if (bitmap->data != NULL) {
|
||||
@ -137,7 +137,8 @@ void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
||||
width, height, 4*bitmap->width);
|
||||
|
||||
/* Draw image on default surface */
|
||||
guac_common_surface_draw(client_data->default_surface, bitmap->left, bitmap->top, image);
|
||||
guac_common_surface_draw(rdp_client->display->default_surface,
|
||||
bitmap->left, bitmap->top, image);
|
||||
|
||||
/* Free surface */
|
||||
cairo_surface_destroy(image);
|
||||
@ -152,26 +153,22 @@ void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap) {
|
||||
void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_layer* buffer = ((guac_rdp_bitmap*) bitmap)->buffer;
|
||||
guac_common_surface* surface = ((guac_rdp_bitmap*) bitmap)->surface;
|
||||
|
||||
/* If cached, free surface */
|
||||
if (surface != NULL)
|
||||
guac_common_surface_free(surface);
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_display_layer* buffer = ((guac_rdp_bitmap*) bitmap)->layer;
|
||||
|
||||
/* If cached, free buffer */
|
||||
if (buffer != NULL)
|
||||
guac_client_free_buffer(client, buffer);
|
||||
guac_common_display_free_buffer(rdp_client->display, buffer);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
if (primary)
|
||||
client_data->current_surface = client_data->default_surface;
|
||||
rdp_client->current_surface = rdp_client->display->default_surface;
|
||||
|
||||
else {
|
||||
|
||||
@ -182,10 +179,11 @@ void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL pri
|
||||
}
|
||||
|
||||
/* If not available as a surface, make available. */
|
||||
if (((guac_rdp_bitmap*) bitmap)->surface == NULL)
|
||||
if (((guac_rdp_bitmap*) bitmap)->layer == NULL)
|
||||
guac_rdp_cache_bitmap(context, bitmap);
|
||||
|
||||
client_data->current_surface = ((guac_rdp_bitmap*) bitmap)->surface;
|
||||
rdp_client->current_surface =
|
||||
((guac_rdp_bitmap*) bitmap)->layer->surface;
|
||||
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
#define _GUAC_RDP_RDP_BITMAP_H
|
||||
|
||||
#include "config.h"
|
||||
#include "guac_surface.h"
|
||||
#include "guac_display.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/layer.h>
|
||||
@ -36,6 +36,9 @@
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Guacamole-specific rdpBitmap data.
|
||||
*/
|
||||
typedef struct guac_rdp_bitmap {
|
||||
|
||||
/**
|
||||
@ -44,14 +47,9 @@ typedef struct guac_rdp_bitmap {
|
||||
rdpBitmap bitmap;
|
||||
|
||||
/**
|
||||
* The allocated buffer which backs this bitmap.
|
||||
* Layer containing cached image data.
|
||||
*/
|
||||
guac_layer* buffer;
|
||||
|
||||
/**
|
||||
* Surface containing cached image data.
|
||||
*/
|
||||
guac_common_surface* surface;
|
||||
guac_common_display_layer* layer;
|
||||
|
||||
/**
|
||||
* The number of times a bitmap has been used.
|
||||
@ -60,18 +58,149 @@ typedef struct guac_rdp_bitmap {
|
||||
|
||||
} guac_rdp_bitmap;
|
||||
|
||||
/**
|
||||
* Caches the given bitmap immediately, storing its data in a remote Guacamole
|
||||
* buffer. As RDP bitmaps are frequently created, used once, and immediately
|
||||
* destroyed, we defer actual remote-side caching of RDP bitmaps until they are
|
||||
* used at least once.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap to cache.
|
||||
*/
|
||||
void guac_rdp_cache_bitmap(rdpContext* context, rdpBitmap* bitmap);
|
||||
|
||||
/**
|
||||
* Initializes the given newly-created rdpBitmap.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap to initialize.
|
||||
*/
|
||||
void guac_rdp_bitmap_new(rdpContext* context, rdpBitmap* bitmap);
|
||||
|
||||
/**
|
||||
* Paints the given rdpBitmap on the primary display surface. Note that this
|
||||
* operation does NOT draw to the "current" surface set by calls to
|
||||
* guac_rdp_bitmap_setsurface().
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap to paint. This structure will also contain the specifics of
|
||||
* the paint operation to perform, including the destination X/Y
|
||||
* coordinates.
|
||||
*/
|
||||
void guac_rdp_bitmap_paint(rdpContext* context, rdpBitmap* bitmap);
|
||||
|
||||
/**
|
||||
* Frees any Guacamole-specific data associated with the given rdpBitmap.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap whose Guacamole-specific data is to be freed.
|
||||
*/
|
||||
void guac_rdp_bitmap_free(rdpContext* context, rdpBitmap* bitmap);
|
||||
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary);
|
||||
|
||||
/**
|
||||
* Sets the given rdpBitmap as the drawing surface for future operations or,
|
||||
* if the primary flag is set, resets the current drawing surface to the
|
||||
* primary drawing surface of the remote display.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The rdpBitmap to set as the current drawing surface. This parameter is
|
||||
* only valid if the primary flag is FALSE.
|
||||
*
|
||||
* @param primary
|
||||
* TRUE if the bitmap parameter should be ignored, and the current drawing
|
||||
* surface should be reset to the primary drawing surface of the remote
|
||||
* display, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_bitmap_setsurface(rdpContext* context, rdpBitmap* bitmap,
|
||||
BOOL primary);
|
||||
|
||||
#ifdef LEGACY_RDPBITMAP
|
||||
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, UINT8* data,
|
||||
int width, int height, int bpp, int length, BOOL compressed);
|
||||
/**
|
||||
* Decompresses or copies the given image data, storing the result within the
|
||||
* given bitmap, depending on the compressed flag. Note that even if the
|
||||
* received data is not compressed, it is the duty of this function to also
|
||||
* flip received data, if the row order is backwards.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap in which the decompressed/copied data should be stored.
|
||||
*
|
||||
* @param data
|
||||
* Possibly-compressed image data.
|
||||
*
|
||||
* @param width
|
||||
* The width of the image data, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The height of the image data, in pixels.
|
||||
*
|
||||
* @param bpp
|
||||
* The number of bits per pixel in the image data.
|
||||
*
|
||||
* @param length
|
||||
* The length of the image data, in bytes.
|
||||
*
|
||||
* @param compressed
|
||||
* TRUE if the image data is compressed, FALSE otherwise.
|
||||
*/
|
||||
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap,
|
||||
UINT8* data, int width, int height, int bpp, int length,
|
||||
BOOL compressed);
|
||||
#else
|
||||
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap, UINT8* data,
|
||||
int width, int height, int bpp, int length, BOOL compressed, int codec_id);
|
||||
/**
|
||||
* Decompresses or copies the given image data, storing the result within the
|
||||
* given bitmap, depending on the compressed flag. Note that even if the
|
||||
* received data is not compressed, it is the duty of this function to also
|
||||
* flip received data, if the row order is backwards.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bitmap
|
||||
* The bitmap in which the decompressed/copied data should be stored.
|
||||
*
|
||||
* @param data
|
||||
* Possibly-compressed image data.
|
||||
*
|
||||
* @param width
|
||||
* The width of the image data, in pixels.
|
||||
*
|
||||
* @param height
|
||||
* The height of the image data, in pixels.
|
||||
*
|
||||
* @param bpp
|
||||
* The number of bits per pixel in the image data.
|
||||
*
|
||||
* @param length
|
||||
* The length of the image data, in bytes.
|
||||
*
|
||||
* @param compressed
|
||||
* TRUE if the image data is compressed, FALSE otherwise.
|
||||
*
|
||||
* @param codec_id
|
||||
* The ID of the codec used to compress the image data. This parameter is
|
||||
* currently ignored.
|
||||
*/
|
||||
void guac_rdp_bitmap_decompress(rdpContext* context, rdpBitmap* bitmap,
|
||||
UINT8* data, int width, int height, int bpp, int length,
|
||||
BOOL compressed, int codec_id);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_cliprdr.h"
|
||||
#include "guac_clipboard.h"
|
||||
#include "guac_iconv.h"
|
||||
@ -92,7 +93,7 @@ void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event) {
|
||||
void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) {
|
||||
|
||||
rdpChannels* channels =
|
||||
((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
|
||||
((guac_rdp_client*) client->data)->rdp_inst->context->channels;
|
||||
|
||||
RDP_CB_FORMAT_LIST_EVENT* format_list =
|
||||
(RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
|
||||
@ -114,11 +115,18 @@ void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event) {
|
||||
|
||||
/**
|
||||
* Sends a clipboard data request for the given format.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param format
|
||||
* The clipboard format to request. This format must be one of the
|
||||
* documented values used by the CLIPRDR channel for clipboard format IDs.
|
||||
*/
|
||||
static void __guac_rdp_cb_request_format(guac_client* client, int format) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
rdpChannels* channels = client_data->rdp_inst->context->channels;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
|
||||
|
||||
/* Create new data request */
|
||||
RDP_CB_DATA_REQUEST_EVENT* data_request =
|
||||
@ -128,7 +136,7 @@ static void __guac_rdp_cb_request_format(guac_client* client, int format) {
|
||||
NULL, NULL);
|
||||
|
||||
/* Set to requested format */
|
||||
client_data->requested_clipboard_format = format;
|
||||
rdp_client->requested_clipboard_format = format;
|
||||
data_request->format = format;
|
||||
|
||||
/* Send request */
|
||||
@ -174,11 +182,11 @@ void guac_rdp_process_cb_format_list(guac_client* client,
|
||||
void guac_rdp_process_cb_data_request(guac_client* client,
|
||||
RDP_CB_DATA_REQUEST_EVENT* event) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
rdpChannels* channels = client_data->rdp_inst->context->channels;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
|
||||
|
||||
guac_iconv_write* writer;
|
||||
const char* input = client_data->clipboard->buffer;
|
||||
const char* input = rdp_client->clipboard->buffer;
|
||||
char* output = malloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
|
||||
|
||||
RDP_CB_DATA_RESPONSE_EVENT* data_response;
|
||||
@ -209,7 +217,7 @@ void guac_rdp_process_cb_data_request(guac_client* client,
|
||||
|
||||
/* Set data and size */
|
||||
data_response->data = (BYTE*) output;
|
||||
guac_iconv(GUAC_READ_UTF8, &input, client_data->clipboard->length,
|
||||
guac_iconv(GUAC_READ_UTF8, &input, rdp_client->clipboard->length,
|
||||
writer, &output, GUAC_RDP_CLIPBOARD_MAX_LENGTH);
|
||||
data_response->size = ((BYTE*) output) - data_response->data;
|
||||
|
||||
@ -221,7 +229,7 @@ void guac_rdp_process_cb_data_request(guac_client* client,
|
||||
void guac_rdp_process_cb_data_response(guac_client* client,
|
||||
RDP_CB_DATA_RESPONSE_EVENT* event) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
char received_data[GUAC_RDP_CLIPBOARD_MAX_LENGTH];
|
||||
|
||||
guac_iconv_read* reader;
|
||||
@ -229,7 +237,7 @@ void guac_rdp_process_cb_data_response(guac_client* client,
|
||||
char* output = received_data;
|
||||
|
||||
/* Find correct source encoding */
|
||||
switch (client_data->requested_clipboard_format) {
|
||||
switch (rdp_client->requested_clipboard_format) {
|
||||
|
||||
/* Non-Unicode */
|
||||
case CB_FORMAT_TEXT:
|
||||
@ -244,7 +252,7 @@ void guac_rdp_process_cb_data_response(guac_client* client,
|
||||
default:
|
||||
guac_client_log(client, GUAC_LOG_ERROR, "Requested clipboard data in "
|
||||
"unsupported format %i",
|
||||
client_data->requested_clipboard_format);
|
||||
rdp_client->requested_clipboard_format);
|
||||
return;
|
||||
|
||||
}
|
||||
@ -254,9 +262,9 @@ void guac_rdp_process_cb_data_response(guac_client* client,
|
||||
GUAC_WRITE_UTF8, &output, sizeof(received_data))) {
|
||||
|
||||
int length = strnlen(received_data, sizeof(received_data));
|
||||
guac_common_clipboard_reset(client_data->clipboard, "text/plain");
|
||||
guac_common_clipboard_append(client_data->clipboard, received_data, length);
|
||||
guac_common_clipboard_send(client_data->clipboard, client);
|
||||
guac_common_clipboard_reset(rdp_client->clipboard, "text/plain");
|
||||
guac_common_clipboard_append(rdp_client->clipboard, received_data, length);
|
||||
guac_common_clipboard_send(rdp_client->clipboard, client);
|
||||
|
||||
}
|
||||
|
||||
|
@ -50,15 +50,72 @@
|
||||
*/
|
||||
#define GUAC_RDP_CLIPBOARD_FORMAT_UTF16 2
|
||||
|
||||
/**
|
||||
* Called within the main RDP connection thread whenever a CLIPRDR message is
|
||||
* received. This function will dispatch that message to an appropriate
|
||||
* function, specific to that message type.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The received CLIPRDR message.
|
||||
*/
|
||||
void guac_rdp_process_cliprdr_event(guac_client* client, wMessage* event);
|
||||
|
||||
/**
|
||||
* Handles the given CLIPRDR event, which MUST be a Monitor Ready event. It
|
||||
* is the responsibility of this function to respond to the Monitor Ready
|
||||
* event with a list of supported clipboard formats.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The received CLIPRDR message, which must be a Monitor Ready event.
|
||||
*/
|
||||
void guac_rdp_process_cb_monitor_ready(guac_client* client, wMessage* event);
|
||||
|
||||
/**
|
||||
* Handles the given CLIPRDR event, which MUST be a Format List event. It
|
||||
* is the responsibility of this function to respond to the Format List
|
||||
* event with a request for clipboard data in one of the enumerated formats.
|
||||
* This event is fired whenever remote clipboard data is available.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The received CLIPRDR message, which must be a Format List event.
|
||||
*/
|
||||
void guac_rdp_process_cb_format_list(guac_client* client,
|
||||
RDP_CB_FORMAT_LIST_EVENT* event);
|
||||
|
||||
/**
|
||||
* Handles the given CLIPRDR event, which MUST be a Data Request event. It
|
||||
* is the responsibility of this function to respond to the Data Request
|
||||
* event with a data response containing the current clipoard contents.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The received CLIPRDR message, which must be a Data Request event.
|
||||
*/
|
||||
void guac_rdp_process_cb_data_request(guac_client* client,
|
||||
RDP_CB_DATA_REQUEST_EVENT* event);
|
||||
|
||||
/**
|
||||
* Handles the given CLIPRDR event, which MUST be a Data Response event. It
|
||||
* is the responsibility of this function to read and forward the received
|
||||
* clipboard data to connected clients.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The received CLIPRDR message, which must be a Data Response event.
|
||||
*/
|
||||
void guac_rdp_process_cb_data_response(guac_client* client,
|
||||
RDP_CB_DATA_RESPONSE_EVENT* event);
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
#include <freerdp/codec/color.h>
|
||||
|
@ -35,6 +35,16 @@
|
||||
* Converts the given color to ARGB32. The color given may be an index
|
||||
* referring to the palette, a 16-bit or 32-bit color, etc. all depending on
|
||||
* the current color depth of the RDP session.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param color
|
||||
* A color value in the format of the current RDP session.
|
||||
*
|
||||
* @return
|
||||
* A 32-bit ARGB color, where the low 8 bits are the blue component and
|
||||
* the high 8 bits are alpha.
|
||||
*/
|
||||
UINT32 guac_rdp_convert_color(rdpContext* context, UINT32 color);
|
||||
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_disp.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/client/disp.h>
|
||||
|
@ -38,8 +38,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/object.h>
|
||||
#include <guacamole/pool.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||
int create_drive_path) {
|
||||
@ -61,10 +64,6 @@ guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||
guac_rdp_fs* fs = malloc(sizeof(guac_rdp_fs));
|
||||
|
||||
fs->client = client;
|
||||
fs->object = guac_client_alloc_object(client);
|
||||
fs->object->get_handler = guac_rdp_download_get_handler;
|
||||
fs->object->put_handler = guac_rdp_upload_put_handler;
|
||||
|
||||
fs->drive_path = strdup(drive_path);
|
||||
fs->file_id_pool = guac_pool_alloc(0);
|
||||
fs->open_files = 0;
|
||||
@ -74,16 +73,56 @@ guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||
}
|
||||
|
||||
void guac_rdp_fs_free(guac_rdp_fs* fs) {
|
||||
guac_client_free_object(fs->client, fs->object);
|
||||
guac_pool_free(fs->file_id_pool);
|
||||
free(fs->drive_path);
|
||||
free(fs);
|
||||
}
|
||||
|
||||
guac_object* guac_rdp_fs_alloc_object(guac_rdp_fs* fs, guac_user* user) {
|
||||
|
||||
/* Init filesystem */
|
||||
guac_object* fs_object = guac_user_alloc_object(user);
|
||||
fs_object->get_handler = guac_rdp_download_get_handler;
|
||||
fs_object->put_handler = guac_rdp_upload_put_handler;
|
||||
fs_object->data = fs;
|
||||
|
||||
/* Send filesystem to user */
|
||||
guac_protocol_send_filesystem(user->socket, fs_object, "Shared Drive");
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
return fs_object;
|
||||
|
||||
}
|
||||
|
||||
void* guac_rdp_fs_expose(guac_user* user, void* data) {
|
||||
|
||||
guac_rdp_fs* fs = (guac_rdp_fs*) data;
|
||||
|
||||
/* No need to expose if there is no filesystem or the user has left */
|
||||
if (user == NULL || fs == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Allocate and expose filesystem object for user */
|
||||
return guac_rdp_fs_alloc_object(fs, user);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates an absolute Windows virtual_path to an absolute virtual_path
|
||||
* which is within the "drive virtual_path" specified in the connection
|
||||
* settings.
|
||||
* Translates an absolute Windows path to an absolute path which is within the
|
||||
* "drive path" specified in the connection settings. No checking is performed
|
||||
* on the path provided, which is assumed to have already been normalized and
|
||||
* validated as absolute.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file whose path is being translated.
|
||||
*
|
||||
* @param virtual_path
|
||||
* The absolute path to the file on the simulated filesystem, relative to
|
||||
* the simulated filesystem root.
|
||||
*
|
||||
* @param real_path
|
||||
* The buffer in which to store the absolute path to the real file on the
|
||||
* local filesystem.
|
||||
*/
|
||||
static void __guac_rdp_fs_translate_path(guac_rdp_fs* fs,
|
||||
const char* virtual_path, char* real_path) {
|
||||
@ -701,7 +740,7 @@ int guac_rdp_fs_get_info(guac_rdp_fs* fs, guac_rdp_fs_info* info) {
|
||||
/* Read FS information */
|
||||
struct statvfs fs_stat;
|
||||
if (statvfs(fs->drive_path, &fs_stat))
|
||||
return guac_rdp_fs_get_status(errno);
|
||||
return guac_rdp_fs_get_errorcode(errno);
|
||||
|
||||
/* Assign to structure */
|
||||
info->blocks_available = fs_stat.f_bfree;
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/object.h>
|
||||
#include <guacamole/pool.h>
|
||||
|
||||
#include <dirent.h>
|
||||
@ -269,15 +268,10 @@ typedef struct guac_rdp_fs_file {
|
||||
typedef struct guac_rdp_fs {
|
||||
|
||||
/**
|
||||
* The controlling client.
|
||||
* The Guacamole client associated with the RDP session.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* The underlying filesystem object.
|
||||
*/
|
||||
guac_object* object;
|
||||
|
||||
/**
|
||||
* The root of the filesystem.
|
||||
*/
|
||||
@ -323,34 +317,155 @@ typedef struct guac_rdp_fs_info {
|
||||
} guac_rdp_fs_info;
|
||||
|
||||
/**
|
||||
* Allocates a new filesystem given a root path.
|
||||
* Allocates a new filesystem given a root path. This filesystem will behave
|
||||
* as if it were a network drive.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param drive_path
|
||||
* The local directory to use as the root directory of the emulated
|
||||
* network drive.
|
||||
*
|
||||
* @param create_drive_path
|
||||
* Non-zero if the drive path specified should be automatically created if
|
||||
* it does not yet exist, zero otherwise.
|
||||
*
|
||||
* @return
|
||||
* The newly-allocated filesystem.
|
||||
*/
|
||||
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path, int create_drive_path);
|
||||
guac_rdp_fs* guac_rdp_fs_alloc(guac_client* client, const char* drive_path,
|
||||
int create_drive_path);
|
||||
|
||||
/**
|
||||
* Frees the given filesystem.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem to free.
|
||||
*/
|
||||
void guac_rdp_fs_free(guac_rdp_fs* fs);
|
||||
|
||||
/**
|
||||
* Creates and exposes a new filesystem guac_object to the given user,
|
||||
* providing access to the files within the given RDP filesystem. The
|
||||
* allocated guac_object must eventually be freed via guac_user_free_object().
|
||||
*
|
||||
* @param fs
|
||||
* The RDP filesystem object to expose.
|
||||
*
|
||||
* @param user
|
||||
* The user that the RDP filesystem should be exposed to.
|
||||
*
|
||||
* @return
|
||||
* A new Guacamole filesystem object, configured to use RDP for uploading
|
||||
* and downloading files.
|
||||
*/
|
||||
guac_object* guac_rdp_fs_alloc_object(guac_rdp_fs* fs, guac_user* user);
|
||||
|
||||
/**
|
||||
* Allocates a new filesystem guac_object for the given user, returning the
|
||||
* resulting guac_object. This function is provided for convenience, as it is
|
||||
* can be used as the callback for guac_client_foreach_user() or
|
||||
* guac_client_for_owner(). Note that this guac_object will be tracked
|
||||
* internally by libguac, will be provided to us in the parameters of handlers
|
||||
* related to that guac_object, and will automatically be freed when the
|
||||
* associated guac_user is freed, so the return value of this function can
|
||||
* safely be ignored.
|
||||
*
|
||||
* If either the given user or the given filesystem are NULL, then this
|
||||
* function has no effect.
|
||||
*
|
||||
* @param user
|
||||
* The use to expose the filesystem to, or NULL if nothing should be
|
||||
* exposed.
|
||||
*
|
||||
* @param data
|
||||
* A pointer to the guac_rdp_fs instance to expose to the given user, or
|
||||
* NULL if nothing should be exposed.
|
||||
*
|
||||
* @return
|
||||
* The guac_object allocated for the newly-exposed filesystem, or NULL if
|
||||
* no filesystem object could be allocated.
|
||||
*/
|
||||
void* guac_rdp_fs_expose(guac_user* user, void* data);
|
||||
|
||||
/**
|
||||
* Converts the given relative path to an absolute path based on the given
|
||||
* parent path. If the path cannot be converted, non-zero is returned.
|
||||
*
|
||||
* @param parent
|
||||
* The parent directory of the relative path.
|
||||
*
|
||||
* @param rel_path
|
||||
* The relative path to convert.
|
||||
*
|
||||
* @return
|
||||
* Zero if the path was converted successfully, non-zero otherwise.
|
||||
*/
|
||||
int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path);
|
||||
int guac_rdp_fs_convert_path(const char* parent, const char* rel_path,
|
||||
char* abs_path);
|
||||
|
||||
/**
|
||||
* Translates the given errno error code to a GUAC_RDP_FS error code.
|
||||
*
|
||||
* @param err
|
||||
* The error code, as returned within errno by a system call.
|
||||
*
|
||||
* @return
|
||||
* A GUAC_RDP_FS error code, such as GUAC_RDP_FS_ENFILE,
|
||||
* GUAC_RDP_FS_ENOENT, etc.
|
||||
*/
|
||||
int guac_rdp_fs_get_errorcode(int err);
|
||||
|
||||
/**
|
||||
* Teanslates the given GUAC_RDP_FS error code to an RDPDR status code.
|
||||
* Translates the given GUAC_RDP_FS error code to an RDPDR status code.
|
||||
*
|
||||
* @param err
|
||||
* A GUAC_RDP_FS error code, such as GUAC_RDP_FS_ENFILE,
|
||||
* GUAC_RDP_FS_ENOENT, etc.
|
||||
*
|
||||
* @return
|
||||
* A status code corresponding to the given error code that an
|
||||
* implementation of the RDPDR channel can understand.
|
||||
*/
|
||||
int guac_rdp_fs_get_status(int err);
|
||||
|
||||
/**
|
||||
* Returns the next available file ID, or an error code less than zero
|
||||
* if an error occurs.
|
||||
* Opens the given file, returning the a new file ID, or an error code less
|
||||
* than zero if an error occurs. The given path MUST be absolute, and will be
|
||||
* translated to be relative to the drive path of the simulated filesystem.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem to use when opening the file.
|
||||
*
|
||||
* @param path
|
||||
* The absolute path to the file within the simulated filesystem.
|
||||
*
|
||||
* @param access
|
||||
* A bitwise-OR of various RDPDR access flags, such as ACCESS_GENERIC_ALL
|
||||
* or ACCESS_GENERIC_WRITE. This value will ultimately be translated to a
|
||||
* standard O_RDWR, O_WRONLY, etc. value when opening the real file on the
|
||||
* local filesystem.
|
||||
*
|
||||
* @param file_attributes
|
||||
* The attributes to apply to the file, if created. This parameter is
|
||||
* currently ignored, and has no effect.
|
||||
*
|
||||
* @param create_disposition
|
||||
* Any one of several RDPDR file creation dispositions, such as
|
||||
* DISP_FILE_CREATE, DISP_FILE_OPEN_IF, etc. The creation disposition
|
||||
* dictates whether a new file should be created, whether the file can
|
||||
* already exist, whether existing contents should be truncated, etc.
|
||||
*
|
||||
* @param create_options
|
||||
* A bitwise-OR of various RDPDR options dictating how a file is to be
|
||||
* created. Currently only one option is implemented, FILE_DIRECTORY_FILE,
|
||||
* which specifies that the new file must be a directory.
|
||||
*
|
||||
* @return
|
||||
* A new file ID, which will always be a positive value, or an error code
|
||||
* if an error occurs. All error codes are negative values and correspond
|
||||
* to GUAC_RDP_FS constants, such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
||||
int access, int file_attributes, int create_disposition,
|
||||
@ -360,6 +475,26 @@ int guac_rdp_fs_open(guac_rdp_fs* fs, const char* path,
|
||||
* Reads up to the given length of bytes from the given offset within the
|
||||
* file having the given ID. Returns the number of bytes read, zero on EOF,
|
||||
* and an error code if an error occurs.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file from which data is to be read.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to read data from, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @param offset
|
||||
* The byte offset within the file to start reading from.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer to fill with data from the file.
|
||||
*
|
||||
* @param length
|
||||
* The maximum number of bytes to read from the file.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes actually read, zero on EOF, or an error code if an
|
||||
* error occurs. All error codes are negative values and correspond to
|
||||
* GUAC_RDP_FS constants, such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, int offset,
|
||||
void* buffer, int length);
|
||||
@ -368,6 +503,26 @@ int guac_rdp_fs_read(guac_rdp_fs* fs, int file_id, int offset,
|
||||
* Writes up to the given length of bytes from the given offset within the
|
||||
* file having the given ID. Returns the number of bytes written, and an
|
||||
* error code if an error occurs.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to which data is to be written.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to write data to, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @param offset
|
||||
* The byte offset within the file to start writinging at.
|
||||
*
|
||||
* @param buffer
|
||||
* The buffer containing the data to write.
|
||||
*
|
||||
* @param length
|
||||
* The maximum number of bytes to write to the file.
|
||||
*
|
||||
* @return
|
||||
* The number of bytes actually written, or an error code if an error
|
||||
* occurs. All error codes are negative values and correspond to
|
||||
* GUAC_RDP_FS constants, such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, int offset,
|
||||
void* buffer, int length);
|
||||
@ -375,56 +530,172 @@ int guac_rdp_fs_write(guac_rdp_fs* fs, int file_id, int offset,
|
||||
/**
|
||||
* Renames (moves) the file with the given ID to the new path specified.
|
||||
* Returns zero on success, or an error code if an error occurs.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to rename.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to rename, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @param new_path
|
||||
* The absolute path to move the file to.
|
||||
*
|
||||
* @return
|
||||
* Zero if the rename succeeded, or an error code if an error occurs. All
|
||||
* error codes are negative values and correspond to GUAC_RDP_FS constants,
|
||||
* such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_rename(guac_rdp_fs* fs, int file_id,
|
||||
const char* new_path);
|
||||
|
||||
/**
|
||||
* Deletes the file with the given ID.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to delete.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to delete, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @return
|
||||
* Zero if deletion succeeded, or an error code if an error occurs. All
|
||||
* error codes are negative values and correspond to GUAC_RDP_FS constants,
|
||||
* such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_delete(guac_rdp_fs* fs, int file_id);
|
||||
|
||||
/**
|
||||
* Truncates the file with the given ID to the given length (in bytes), which
|
||||
* may be larger.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to truncate.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to truncate, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @param length
|
||||
* The new length of the file, in bytes. Despite being named "truncate",
|
||||
* this new length may be larger.
|
||||
*
|
||||
* @return
|
||||
* Zero if truncation succeeded, or an error code if an error occurs. All
|
||||
* error codes are negative values and correspond to GUAC_RDP_FS constants,
|
||||
* such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_truncate(guac_rdp_fs* fs, int file_id, int length);
|
||||
|
||||
/**
|
||||
* Frees the given file ID, allowing future open operations to reuse it.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to close.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to close, as returned by guac_rdp_fs_open().
|
||||
*/
|
||||
void guac_rdp_fs_close(guac_rdp_fs* fs, int file_id);
|
||||
|
||||
/**
|
||||
* Given an arbitrary path, which may contain ".." and ".", creates an
|
||||
* absolute path which does NOT contain ".." or ".".
|
||||
* absolute path which does NOT contain ".." or ".". The given path MUST
|
||||
* be absolute.
|
||||
*
|
||||
* @param path
|
||||
* The absolute path to normalize.
|
||||
*
|
||||
* @param abs_path
|
||||
* The buffer to populate with the normalized path. The normalized path
|
||||
* will not contain relative path components like ".." or ".".
|
||||
*
|
||||
* @return
|
||||
* Zero if normalization succeeded, non-zero otherwise.
|
||||
*/
|
||||
int guac_rdp_fs_normalize_path(const char* path, char* abs_path);
|
||||
|
||||
/**
|
||||
* Given a parent path and a relative path, produces a normalized absolute path.
|
||||
* Given a parent path and a relative path, produces a normalized absolute
|
||||
* path.
|
||||
*
|
||||
* @param parent
|
||||
* The absolute path of the parent directory of the relative path.
|
||||
*
|
||||
* @param rel_path
|
||||
* The relative path to convert.
|
||||
*
|
||||
* @param abs_path
|
||||
* The buffer to populate with the absolute, normalized path. The
|
||||
* normalized path will not contain relative path components like ".." or
|
||||
* ".".
|
||||
*
|
||||
* @return
|
||||
* Zero if conversion succeeded, non-zero otherwise.
|
||||
*/
|
||||
int guac_rdp_fs_convert_path(const char* parent, const char* rel_path, char* abs_path);
|
||||
int guac_rdp_fs_convert_path(const char* parent, const char* rel_path,
|
||||
char* abs_path);
|
||||
|
||||
/**
|
||||
* Returns the next filename within the directory having the given file ID,
|
||||
* or NULL if no more files.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the file to read directory entries from.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the file to read directory entries from, as returned by
|
||||
* guac_rdp_fs_open().
|
||||
*
|
||||
* @return
|
||||
* The name of the next filename within the directory, or NULL if the last
|
||||
* file in the directory has already been returned by a previous call.
|
||||
*/
|
||||
const char* guac_rdp_fs_read_dir(guac_rdp_fs* fs, int file_id);
|
||||
|
||||
/**
|
||||
* Returns the file having the given ID, or NULL if no such file exists.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem containing the desired file.
|
||||
*
|
||||
* @param file_id
|
||||
* The ID of the desired, as returned by guac_rdp_fs_open().
|
||||
*
|
||||
* @return
|
||||
* The file having the given ID, or NULL is no such file exists.
|
||||
*/
|
||||
guac_rdp_fs_file* guac_rdp_fs_get_file(guac_rdp_fs* fs, int file_id);
|
||||
|
||||
/**
|
||||
* Returns whether the given filename matches the given pattern.
|
||||
* Returns whether the given filename matches the given pattern. The pattern
|
||||
* given is a shell wildcard pattern as accepted by the POSIX fnmatch()
|
||||
* function. Backslashes will be interpreted as literal backslashes, not
|
||||
* escape characters.
|
||||
*
|
||||
* @param filename
|
||||
* The filename to check
|
||||
*
|
||||
* @param pattern
|
||||
* The pattern to check the filename against.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the pattern matches, zero otherwise.
|
||||
*/
|
||||
int guac_rdp_fs_matches(const char* filename, const char* pattern);
|
||||
|
||||
/**
|
||||
* Populates the given structure with information about the filesystem,
|
||||
* particularly the amount of space available.
|
||||
*
|
||||
* @param fs
|
||||
* The filesystem to obtain information from.
|
||||
*
|
||||
* @param info
|
||||
* The guac_rdp_fs_info structure to populate.
|
||||
*
|
||||
* @return
|
||||
* Zero if information retrieval succeeded, or an error code if an error
|
||||
* occurs. All error codes are negative values and correspond to
|
||||
* GUAC_RDP_FS constants, such as GUAC_RDP_FS_ENOENT.
|
||||
*/
|
||||
int guac_rdp_fs_get_info(guac_rdp_fs* fs, guac_rdp_fs_info* info);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_surface.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_bitmap.h"
|
||||
#include "rdp_color.h"
|
||||
#include "rdp_settings.h"
|
||||
@ -102,7 +103,7 @@ guac_transfer_function guac_rdp_rop3_transfer_function(guac_client* client,
|
||||
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = dstblt->nLeftRect;
|
||||
int y = dstblt->nTopRect;
|
||||
@ -158,7 +159,7 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
|
||||
/* Get client and current layer */
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface =
|
||||
((rdp_guac_client_data*) client->data)->current_surface;
|
||||
((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = patblt->nLeftRect;
|
||||
int y = patblt->nTopRect;
|
||||
@ -210,7 +211,7 @@ void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) {
|
||||
void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = scrblt->nLeftRect;
|
||||
int y = scrblt->nTopRect;
|
||||
@ -220,18 +221,18 @@ void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt) {
|
||||
int x_src = scrblt->nXSrc;
|
||||
int y_src = scrblt->nYSrc;
|
||||
|
||||
rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Copy screen rect to current surface */
|
||||
guac_common_surface_copy(data->default_surface, x_src, y_src, w, h,
|
||||
current_surface, x, y);
|
||||
guac_common_surface_copy(rdp_client->display->default_surface,
|
||||
x_src, y_src, w, h, current_surface, x, y);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
guac_rdp_bitmap* bitmap = (guac_rdp_bitmap*) memblt->bitmap;
|
||||
|
||||
int x = memblt->nLeftRect;
|
||||
@ -263,11 +264,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
case 0xCC:
|
||||
|
||||
/* If not cached, cache if necessary */
|
||||
if (bitmap->surface == NULL && bitmap->used >= 1)
|
||||
if (bitmap->layer == NULL && bitmap->used >= 1)
|
||||
guac_rdp_cache_bitmap(context, memblt->bitmap);
|
||||
|
||||
/* If not cached, send as PNG */
|
||||
if (bitmap->surface == NULL) {
|
||||
if (bitmap->layer == NULL) {
|
||||
if (memblt->bitmap->data != NULL) {
|
||||
|
||||
/* Create surface from image data */
|
||||
@ -286,8 +287,8 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
|
||||
/* Otherwise, copy */
|
||||
else
|
||||
guac_common_surface_copy(bitmap->surface, x_src, y_src, w, h,
|
||||
current_surface, x, y);
|
||||
guac_common_surface_copy(bitmap->layer->surface,
|
||||
x_src, y_src, w, h, current_surface, x, y);
|
||||
|
||||
/* Increment usage counter */
|
||||
((guac_rdp_bitmap*) bitmap)->used++;
|
||||
@ -303,10 +304,11 @@ void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) {
|
||||
default:
|
||||
|
||||
/* If not available as a surface, make available. */
|
||||
if (bitmap->surface == NULL)
|
||||
if (bitmap->layer == NULL)
|
||||
guac_rdp_cache_bitmap(context, memblt->bitmap);
|
||||
|
||||
guac_common_surface_transfer(bitmap->surface, x_src, y_src, w, h,
|
||||
guac_common_surface_transfer(bitmap->layer->surface,
|
||||
x_src, y_src, w, h,
|
||||
guac_rdp_rop3_transfer_function(client, memblt->bRop),
|
||||
current_surface, x, y);
|
||||
|
||||
@ -324,7 +326,7 @@ void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect
|
||||
|
||||
UINT32 color = guac_rdp_convert_color(context, opaque_rect->color);
|
||||
|
||||
guac_common_surface* current_surface = ((rdp_guac_client_data*) client->data)->current_surface;
|
||||
guac_common_surface* current_surface = ((guac_rdp_client*) client->data)->current_surface;
|
||||
|
||||
int x = opaque_rect->nLeftRect;
|
||||
int y = opaque_rect->nTopRect;
|
||||
@ -341,6 +343,13 @@ void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect
|
||||
/**
|
||||
* Updates the palette within a FreeRDP CLRCONV object using the new palette
|
||||
* entries provided by an RDP palette update.
|
||||
*
|
||||
* @param clrconv
|
||||
* The FreeRDP CLRCONV object to update.
|
||||
*
|
||||
* @param palette
|
||||
* An RDP palette update message containing the palette to store within the
|
||||
* given CLRCONV object.
|
||||
*/
|
||||
static void guac_rdp_update_clrconv(CLRCONV* clrconv,
|
||||
PALETTE_UPDATE* palette) {
|
||||
@ -358,6 +367,14 @@ static void guac_rdp_update_clrconv(CLRCONV* clrconv,
|
||||
/**
|
||||
* Updates a raw ARGB32 palette using the new palette entries provided by an
|
||||
* RDP palette update.
|
||||
*
|
||||
* @param guac_palette
|
||||
* An array of 256 ARGB32 colors, with each entry corresponding to an
|
||||
* entry in the color palette.
|
||||
*
|
||||
* @param palette
|
||||
* An RDP palette update message containing the palette to store within the
|
||||
* given array of ARGB32 colors.
|
||||
*/
|
||||
static void guac_rdp_update_palette(UINT32* guac_palette,
|
||||
PALETTE_UPDATE* palette) {
|
||||
@ -393,16 +410,18 @@ void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette) {
|
||||
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* If no bounds given, clear bounding rect */
|
||||
if (bounds == NULL)
|
||||
guac_common_surface_reset_clip(data->default_surface);
|
||||
guac_common_surface_reset_clip(rdp_client->display->default_surface);
|
||||
|
||||
/* Otherwise, set bounding rectangle */
|
||||
else
|
||||
guac_common_surface_clip(data->default_surface, bounds->left, bounds->top,
|
||||
bounds->right - bounds->left + 1, bounds->bottom - bounds->top + 1);
|
||||
guac_common_surface_clip(rdp_client->display->default_surface,
|
||||
bounds->left, bounds->top,
|
||||
bounds->right - bounds->left + 1,
|
||||
bounds->bottom - bounds->top + 1);
|
||||
|
||||
}
|
||||
|
||||
@ -413,13 +432,13 @@ void guac_rdp_gdi_end_paint(rdpContext* context) {
|
||||
void guac_rdp_gdi_desktop_resize(rdpContext* context) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_common_surface_resize(data->default_surface,
|
||||
guac_common_surface_resize(rdp_client->display->default_surface,
|
||||
guac_rdp_get_width(context->instance),
|
||||
guac_rdp_get_height(context->instance));
|
||||
|
||||
guac_common_surface_reset_clip(data->default_surface);
|
||||
guac_common_surface_reset_clip(rdp_client->display->default_surface);
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG, "Server resized display to %ix%i",
|
||||
guac_rdp_get_width(context->instance),
|
||||
|
@ -30,49 +30,112 @@
|
||||
#include <guacamole/protocol.h>
|
||||
|
||||
/**
|
||||
* Translates a standard RDP ROP3 value into a guac_composite_mode.
|
||||
* Translates a standard RDP ROP3 value into a guac_composite_mode. Valid
|
||||
* ROP3 operations indexes are listed in the RDP protocol specifications:
|
||||
*
|
||||
* http://msdn.microsoft.com/en-us/library/cc241583.aspx
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param rop3
|
||||
* The ROP3 operation index to translate.
|
||||
*
|
||||
* @return
|
||||
* The guac_composite_mode that equates to, or most closely approximates,
|
||||
* the given ROP3 operation.
|
||||
*/
|
||||
guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client, int rop3);
|
||||
guac_composite_mode guac_rdp_rop3_transfer_function(guac_client* client,
|
||||
int rop3);
|
||||
|
||||
/**
|
||||
* Handler for RDP DSTBLT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param dstblt
|
||||
* The DSTBLT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_dstblt(rdpContext* context, DSTBLT_ORDER* dstblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP PATBLT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param patblt
|
||||
* The PATBLT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP SCRBLT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param scrblt
|
||||
* The SCRBLT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_scrblt(rdpContext* context, SCRBLT_ORDER* scrblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP MEMBLT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param memblt
|
||||
* The MEMBLT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt);
|
||||
|
||||
/**
|
||||
* Handler for RDP OPAQUE_RECT update.
|
||||
* Handler for RDP OPAQUE RECT update.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param opaque_rect
|
||||
* The OPAQUE RECT update to handle.
|
||||
*/
|
||||
void guac_rdp_gdi_opaquerect(rdpContext* context, OPAQUE_RECT_ORDER* opaque_rect);
|
||||
void guac_rdp_gdi_opaquerect(rdpContext* context,
|
||||
OPAQUE_RECT_ORDER* opaque_rect);
|
||||
|
||||
/**
|
||||
* Handler called when the remote color palette is changing.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param palette
|
||||
* The PALETTE update containing the new palette.
|
||||
*/
|
||||
void guac_rdp_gdi_palette_update(rdpContext* context, PALETTE_UPDATE* palette);
|
||||
|
||||
/**
|
||||
* Handler called prior to calling the handlers for specific updates when
|
||||
* those updatese are clipped by a bounding rectangle.
|
||||
* those updates are clipped by a bounding rectangle. This is not a true RDP
|
||||
* update, but is called by FreeRDP before and after any update involving
|
||||
* clipping.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param bounds
|
||||
* The clipping rectangle to set, or NULL to remove any applied clipping
|
||||
* rectangle.
|
||||
*/
|
||||
void guac_rdp_gdi_set_bounds(rdpContext* context, rdpBounds* bounds);
|
||||
|
||||
/**
|
||||
* Handler called when a paint operation is complete. We don't actually
|
||||
* use this, but FreeRDP requires it.
|
||||
* use this, but FreeRDP requires it. Calling this function has no effect.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*/
|
||||
void guac_rdp_gdi_end_paint(rdpContext* context);
|
||||
|
||||
@ -81,6 +144,12 @@ void guac_rdp_gdi_end_paint(rdpContext* context);
|
||||
* true desktop resize event received by the RDP client, or due to
|
||||
* a revised size given by the server during initial connection
|
||||
* negotiation.
|
||||
*
|
||||
* The new screen size will be made available within the settings associated
|
||||
* with the given context.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*/
|
||||
void guac_rdp_gdi_desktop_resize(rdpContext* context);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_surface.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_color.h"
|
||||
#include "rdp_glyph.h"
|
||||
#include "rdp_settings.h"
|
||||
@ -102,9 +103,9 @@ void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph) {
|
||||
void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_common_surface* current_surface = guac_client_data->current_surface;
|
||||
uint32_t fgcolor = guac_client_data->glyph_color;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_surface* current_surface = rdp_client->current_surface;
|
||||
uint32_t fgcolor = rdp_client->glyph_color;
|
||||
|
||||
/* Paint with glyph as mask */
|
||||
guac_common_surface_paint(current_surface, x, y, ((guac_rdp_glyph*) glyph)->surface,
|
||||
@ -129,8 +130,8 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
rdp_guac_client_data* guac_client_data =
|
||||
(rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client =
|
||||
(guac_rdp_client*) client->data;
|
||||
|
||||
/* Fill background with color if specified */
|
||||
if (width != 0 && height != 0) {
|
||||
@ -138,7 +139,7 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
/* Convert background color */
|
||||
bgcolor = guac_rdp_convert_color(context, bgcolor);
|
||||
|
||||
guac_common_surface_rect(guac_client_data->current_surface, x, y, width, height,
|
||||
guac_common_surface_rect(rdp_client->current_surface, x, y, width, height,
|
||||
(bgcolor & 0xFF0000) >> 16,
|
||||
(bgcolor & 0x00FF00) >> 8,
|
||||
bgcolor & 0x0000FF);
|
||||
@ -146,7 +147,7 @@ void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
}
|
||||
|
||||
/* Convert foreground color */
|
||||
guac_client_data->glyph_color = guac_rdp_convert_color(context, fgcolor);
|
||||
rdp_client->glyph_color = guac_rdp_convert_color(context, fgcolor);
|
||||
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,9 @@
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Guacamole-specific rdpGlyph data.
|
||||
*/
|
||||
typedef struct guac_rdp_glyph {
|
||||
|
||||
/**
|
||||
@ -49,11 +52,120 @@ typedef struct guac_rdp_glyph {
|
||||
|
||||
} guac_rdp_glyph;
|
||||
|
||||
/**
|
||||
* Caches the given glyph. Note that this caching currently only occurs server-
|
||||
* side, as it is more efficient to transmit the text as PNG.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param glyph
|
||||
* The glyph to cache.
|
||||
*/
|
||||
void guac_rdp_glyph_new(rdpContext* context, rdpGlyph* glyph);
|
||||
|
||||
/**
|
||||
* Draws a previously-cached glyph at the given coordinates within the current
|
||||
* drawing surface.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param glyph
|
||||
* The cached glyph to draw.
|
||||
*
|
||||
* @param x
|
||||
* The destination X coordinate of the upper-left corner of the glyph.
|
||||
*
|
||||
* @param y
|
||||
* The destination Y coordinate of the upper-left corner of the glyph.
|
||||
*/
|
||||
void guac_rdp_glyph_draw(rdpContext* context, rdpGlyph* glyph, int x, int y);
|
||||
|
||||
/**
|
||||
* Frees any Guacamole-specific data associated with the given glyph, such that
|
||||
* it can be safely freed by FreeRDP.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param glyph
|
||||
* The cached glyph to free.
|
||||
*/
|
||||
void guac_rdp_glyph_free(rdpContext* context, rdpGlyph* glyph);
|
||||
|
||||
/**
|
||||
* Called just prior to rendering a series of glyphs. After this function is
|
||||
* called, the glyphs will be individually rendered by calls to
|
||||
* guac_rdp_glyph_draw().
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the background rectangle of
|
||||
* the drawing operation, or 0 if the background is transparent.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the background rectangle of
|
||||
* the drawing operation, or 0 if the background is transparent.
|
||||
*
|
||||
* @param width
|
||||
* The width of the background rectangle of the drawing operation, or 0 if
|
||||
* the background is transparent.
|
||||
*
|
||||
* @param height
|
||||
* The height of the background rectangle of the drawing operation, or 0 if
|
||||
* the background is transparent.
|
||||
*
|
||||
* @param fgcolor
|
||||
* The foreground color of each glyph. This color will be in the colorspace
|
||||
* of the RDP session, and may even be a palette index, and must be
|
||||
* translated via guac_rdp_convert_color().
|
||||
*
|
||||
* @param bgcolor
|
||||
* The background color of the drawing area. This color will be in the
|
||||
* colorspace of the RDP session, and may even be a palette index, and must
|
||||
* be translated via guac_rdp_convert_color(). If the background is
|
||||
* transparent, this value is undefined.
|
||||
*/
|
||||
void guac_rdp_glyph_begindraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
||||
|
||||
/**
|
||||
* Called immediately after rendering a series of glyphs. Unlike
|
||||
* guac_rdp_glyph_begindraw(), there is no way to detect through any invocation
|
||||
* of this function whether the background color is opaque or transparent. We
|
||||
* currently do NOT implement this function.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param x
|
||||
* The X coordinate of the upper-left corner of the background rectangle of
|
||||
* the drawing operation.
|
||||
*
|
||||
* @param y
|
||||
* The Y coordinate of the upper-left corner of the background rectangle of
|
||||
* the drawing operation.
|
||||
*
|
||||
* @param width
|
||||
* The width of the background rectangle of the drawing operation.
|
||||
*
|
||||
* @param height
|
||||
* The height of the background rectangle of the drawing operation.
|
||||
*
|
||||
* @param fgcolor
|
||||
* The foreground color of each glyph. This color will be in the colorspace
|
||||
* of the RDP session, and may even be a palette index, and must be
|
||||
* translated via guac_rdp_convert_color().
|
||||
*
|
||||
* @param bgcolor
|
||||
* The background color of the drawing area. This color will be in the
|
||||
* colorspace of the RDP session, and may even be a palette index, and must
|
||||
* be translated via guac_rdp_convert_color(). If the background is
|
||||
* transparent, this value is undefined.
|
||||
*/
|
||||
void guac_rdp_glyph_enddraw(rdpContext* context,
|
||||
int x, int y, int width, int height, UINT32 fgcolor, UINT32 bgcolor);
|
||||
|
||||
|
@ -112,6 +112,12 @@ typedef int guac_rdp_keysym_state_map[0x200][0x100];
|
||||
/**
|
||||
* Simple macro for determing whether a keysym can be stored (or retrieved)
|
||||
* from any keymap.
|
||||
*
|
||||
* @param keysym
|
||||
* The keysym to check.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the keysym can be stored or retrieved, zero otherwise.
|
||||
*/
|
||||
#define GUAC_RDP_KEYSYM_STORABLE(keysym) ((keysym) <= 0xFFFF || ((keysym) & 0xFFFF0000) == 0x01000000)
|
||||
|
||||
@ -120,6 +126,13 @@ typedef int guac_rdp_keysym_state_map[0x200][0x100];
|
||||
* keysym. The idea here is that a keysym of the form 0xABCD will map to
|
||||
* mapping[0xAB][0xCD] while a keysym of the form 0x100ABCD will map to
|
||||
* mapping[0x1AB][0xCD].
|
||||
*
|
||||
* @param keysym_mapping
|
||||
* A 512-entry array of 256-entry arrays of arbitrary values, where the
|
||||
* location of each array and value is determined by the given keysym.
|
||||
*
|
||||
* @param keysym
|
||||
* The keysym of the entry to look up.
|
||||
*/
|
||||
#define GUAC_RDP_KEYSYM_LOOKUP(keysym_mapping, keysym) ( \
|
||||
(keysym_mapping) \
|
||||
@ -196,6 +209,12 @@ extern const guac_rdp_keymap* GUAC_KEYMAPS[];
|
||||
|
||||
/**
|
||||
* Return the keymap having the given name, if any, or NULL otherwise.
|
||||
*
|
||||
* @param name
|
||||
* The name of the keymap to find.
|
||||
*
|
||||
* @return
|
||||
* The keymap having the given name, or NULL if no such keymap exists.
|
||||
*/
|
||||
const guac_rdp_keymap* guac_rdp_keymap_find(const char* name);
|
||||
|
||||
|
@ -23,28 +23,30 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_cursor.h"
|
||||
#include "guac_display.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_pointer.h"
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_socket* socket = client->socket;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Allocate buffer */
|
||||
guac_common_display_layer* buffer = guac_common_display_alloc_buffer(
|
||||
rdp_client->display, pointer->width, pointer->height);
|
||||
|
||||
/* Allocate data for image */
|
||||
unsigned char* data =
|
||||
(unsigned char*) malloc(pointer->width * pointer->height * 4);
|
||||
|
||||
/* Allocate layer */
|
||||
guac_layer* buffer = guac_client_alloc_buffer(client);
|
||||
|
||||
cairo_surface_t* surface;
|
||||
|
||||
/* Convert to alpha cursor if mask data present */
|
||||
@ -60,8 +62,7 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
pointer->width, pointer->height, 4*pointer->width);
|
||||
|
||||
/* Send surface to buffer */
|
||||
guac_client_stream_png(client, socket, GUAC_COMP_SRC, buffer,
|
||||
0, 0, surface);
|
||||
guac_common_surface_draw(buffer->surface, 0, 0, surface);
|
||||
|
||||
/* Free surface */
|
||||
cairo_surface_destroy(surface);
|
||||
@ -75,19 +76,23 @@ void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer) {
|
||||
void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_socket* socket = client->socket;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Set cursor */
|
||||
guac_protocol_send_cursor(socket, pointer->xPos, pointer->yPos,
|
||||
((guac_rdp_pointer*) pointer)->layer,
|
||||
0, 0, pointer->width, pointer->height);
|
||||
guac_common_cursor_set_surface(rdp_client->display->cursor,
|
||||
pointer->xPos, pointer->yPos,
|
||||
((guac_rdp_pointer*) pointer)->layer->surface);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer) {
|
||||
|
||||
guac_client* client = ((rdp_freerdp_context*) context)->client;
|
||||
guac_client_free_buffer(client, ((guac_rdp_pointer*) pointer)->layer);
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_display_layer* buffer = ((guac_rdp_pointer*) pointer)->layer;
|
||||
|
||||
/* Free buffer */
|
||||
guac_common_display_free_buffer(rdp_client->display, buffer);
|
||||
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,13 @@
|
||||
#define _GUAC_RDP_RDP_POINTER_H
|
||||
|
||||
#include "config.h"
|
||||
#include "guac_display.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <guacamole/layer.h>
|
||||
|
||||
/**
|
||||
* Guacamole-specific rdpPointer data.
|
||||
*/
|
||||
typedef struct guac_rdp_pointer {
|
||||
|
||||
/**
|
||||
@ -37,16 +40,63 @@ typedef struct guac_rdp_pointer {
|
||||
rdpPointer pointer;
|
||||
|
||||
/**
|
||||
* Guacamole layer containing cached image data.
|
||||
* The display layer containing cached image data.
|
||||
*/
|
||||
guac_layer* layer;
|
||||
guac_common_display_layer* layer;
|
||||
|
||||
} guac_rdp_pointer;
|
||||
|
||||
/**
|
||||
* Caches a new pointer, which can later be set via guac_rdp_pointer_set() as
|
||||
* the current mouse pointer.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param pointer
|
||||
* The pointer to cache.
|
||||
*/
|
||||
void guac_rdp_pointer_new(rdpContext* context, rdpPointer* pointer);
|
||||
|
||||
/**
|
||||
* Sets the given cached pointer as the current pointer. The given pointer must
|
||||
* have already been initialized through a call to guac_rdp_pointer_new().
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param pointer
|
||||
* The pointer to set as the current mouse pointer.
|
||||
*/
|
||||
void guac_rdp_pointer_set(rdpContext* context, rdpPointer* pointer);
|
||||
|
||||
/**
|
||||
* Frees all Guacamole-related data associated with the given pointer, allowing
|
||||
* FreeRDP to free the rest safely.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*
|
||||
* @param pointer
|
||||
* The pointer to free.
|
||||
*/
|
||||
void guac_rdp_pointer_free(rdpContext* context, rdpPointer* pointer);
|
||||
|
||||
/**
|
||||
* Hides the current mouse pointer.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*/
|
||||
void guac_rdp_pointer_set_null(rdpContext* context);
|
||||
|
||||
/**
|
||||
* Sets the system-dependent (as in dependent on the client system) default
|
||||
* pointer as the current pointer, rather than a cached pointer.
|
||||
*
|
||||
* @param context
|
||||
* The rdpContext associated with the current RDP session.
|
||||
*/
|
||||
void guac_rdp_pointer_set_default(rdpContext* context);
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_rail.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
@ -88,8 +89,8 @@ void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event) {
|
||||
RAIL_SYSPARAM_ORDER* sysparam;
|
||||
|
||||
/* Get channels */
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
rdpChannels* channels = client_data->rdp_inst->context->channels;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
|
||||
|
||||
/* Get sysparam structure */
|
||||
#ifdef LEGACY_EVENT
|
||||
@ -106,8 +107,8 @@ void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event) {
|
||||
/* Work area */
|
||||
sysparam->workArea.left = 0;
|
||||
sysparam->workArea.top = 0;
|
||||
sysparam->workArea.right = client_data->settings.width;
|
||||
sysparam->workArea.bottom = client_data->settings.height;
|
||||
sysparam->workArea.right = rdp_client->settings->width;
|
||||
sysparam->workArea.bottom = rdp_client->settings->height;
|
||||
|
||||
/* Taskbar */
|
||||
sysparam->taskbarPos.left = 0;
|
||||
|
@ -36,11 +36,24 @@
|
||||
|
||||
/**
|
||||
* Dispatches a given RAIL event to the appropriate handler.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The RAIL event to process.
|
||||
*/
|
||||
void guac_rdp_process_rail_event(guac_client* client, wMessage* event);
|
||||
|
||||
/**
|
||||
* Handles the event sent when updating system parameters.
|
||||
* Handles the event sent when updating system parameters. The event given
|
||||
* MUST be a SYSPARAM event.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param event
|
||||
* The system parameter event to process.
|
||||
*/
|
||||
void guac_rdp_process_rail_get_sysparam(guac_client* client, wMessage* event);
|
||||
|
||||
|
@ -22,10 +22,15 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_string.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "resolution.h"
|
||||
|
||||
#include <freerdp/constants.h>
|
||||
#include <freerdp/settings.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#ifdef ENABLE_WINPR
|
||||
#include <winpr/wtypes.h>
|
||||
@ -36,6 +41,677 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Client plugin arguments */
|
||||
const char* GUAC_RDP_CLIENT_ARGS[] = {
|
||||
"hostname",
|
||||
"port",
|
||||
"domain",
|
||||
"username",
|
||||
"password",
|
||||
"width",
|
||||
"height",
|
||||
"dpi",
|
||||
"initial-program",
|
||||
"color-depth",
|
||||
"disable-audio",
|
||||
"enable-printing",
|
||||
"enable-drive",
|
||||
"drive-path",
|
||||
"create-drive-path",
|
||||
"console",
|
||||
"console-audio",
|
||||
"server-layout",
|
||||
"security",
|
||||
"ignore-cert",
|
||||
"disable-auth",
|
||||
"remote-app",
|
||||
"remote-app-dir",
|
||||
"remote-app-args",
|
||||
"static-channels",
|
||||
"client-name",
|
||||
"enable-wallpaper",
|
||||
"enable-theming",
|
||||
"enable-font-smoothing",
|
||||
"enable-full-window-drag",
|
||||
"enable-desktop-composition",
|
||||
"enable-menu-animations",
|
||||
"preconnection-id",
|
||||
"preconnection-blob",
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
"enable-sftp",
|
||||
"sftp-hostname",
|
||||
"sftp-port",
|
||||
"sftp-username",
|
||||
"sftp-password",
|
||||
"sftp-private-key",
|
||||
"sftp-passphrase",
|
||||
"sftp-directory",
|
||||
#endif
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
enum RDP_ARGS_IDX {
|
||||
|
||||
/**
|
||||
* The hostname to connect to.
|
||||
*/
|
||||
IDX_HOSTNAME,
|
||||
|
||||
/**
|
||||
* The port to connect to. If omitted, the default RDP port of 3389 will be
|
||||
* used.
|
||||
*/
|
||||
IDX_PORT,
|
||||
|
||||
/**
|
||||
* The domain of the user logging in.
|
||||
*/
|
||||
IDX_DOMAIN,
|
||||
|
||||
/**
|
||||
* The username of the user logging in.
|
||||
*/
|
||||
IDX_USERNAME,
|
||||
|
||||
/**
|
||||
* The password of the user logging in.
|
||||
*/
|
||||
IDX_PASSWORD,
|
||||
|
||||
/**
|
||||
* The width of the display to request, in pixels. If omitted, a reasonable
|
||||
* value will be calculated based on the user's own display size and
|
||||
* resolution.
|
||||
*/
|
||||
IDX_WIDTH,
|
||||
|
||||
/**
|
||||
* The height of the display to request, in pixels. If omitted, a
|
||||
* reasonable value will be calculated based on the user's own display
|
||||
* size and resolution.
|
||||
*/
|
||||
IDX_HEIGHT,
|
||||
|
||||
/**
|
||||
* The resolution of the display to request, in DPI. If omitted, a
|
||||
* reasonable value will be calculated based on the user's own display
|
||||
* size and resolution.
|
||||
*/
|
||||
IDX_DPI,
|
||||
|
||||
/**
|
||||
* The initial program to run, if any.
|
||||
*/
|
||||
IDX_INITIAL_PROGRAM,
|
||||
|
||||
/**
|
||||
* The color depth of the display to request, in bits.
|
||||
*/
|
||||
IDX_COLOR_DEPTH,
|
||||
|
||||
/**
|
||||
* "true" if audio should be disabled, "false" or blank to leave audio
|
||||
* enabled.
|
||||
*/
|
||||
IDX_DISABLE_AUDIO,
|
||||
|
||||
/**
|
||||
* "true" if printing should be enabled, "false" or blank otherwise.
|
||||
*/
|
||||
IDX_ENABLE_PRINTING,
|
||||
|
||||
/**
|
||||
* "true" if the virtual drive should be enabled, "false" or blank
|
||||
* otherwise.
|
||||
*/
|
||||
IDX_ENABLE_DRIVE,
|
||||
|
||||
/**
|
||||
* The local system path which will be used to persist the
|
||||
* virtual drive. This must be specified if the virtual drive is enabled.
|
||||
*/
|
||||
IDX_DRIVE_PATH,
|
||||
|
||||
/**
|
||||
* "true" to automatically create the local system path used by the virtual
|
||||
* drive if it does not yet exist, "false" or blank otherwise.
|
||||
*/
|
||||
IDX_CREATE_DRIVE_PATH,
|
||||
|
||||
/**
|
||||
* "true" if this session is a console session, "false" or blank otherwise.
|
||||
*/
|
||||
IDX_CONSOLE,
|
||||
|
||||
/**
|
||||
* "true" if audio should be allowed in console sessions, "false" or blank
|
||||
* otherwise.
|
||||
*/
|
||||
IDX_CONSOLE_AUDIO,
|
||||
|
||||
/**
|
||||
* The name of the keymap chosen as the layout of the server. Legal names
|
||||
* are defined within the *.keymap files in the "keymaps" directory of the
|
||||
* source for Guacamole's RDP support.
|
||||
*/
|
||||
IDX_SERVER_LAYOUT,
|
||||
|
||||
/**
|
||||
* The type of security to use for the connection. Valid values are "rdp",
|
||||
* "tls", "nla", or "any". By default, "rdp" security is used.
|
||||
*/
|
||||
IDX_SECURITY,
|
||||
|
||||
/**
|
||||
* "true" if validity of the RDP server's certificate should be ignored,
|
||||
* "false" or blank if invalid certificates should result in a failure to
|
||||
* connect.
|
||||
*/
|
||||
IDX_IGNORE_CERT,
|
||||
|
||||
/**
|
||||
* "true" if authentication should be disabled, "false" or blank otherwise.
|
||||
* This is different from the authentication that takes place when a user
|
||||
* provides their username and password. Authentication is required by
|
||||
* definition for NLA.
|
||||
*/
|
||||
IDX_DISABLE_AUTH,
|
||||
|
||||
/**
|
||||
* The application to launch, if RemoteApp is in use.
|
||||
*/
|
||||
IDX_REMOTE_APP,
|
||||
|
||||
/**
|
||||
* The working directory of the remote application, if RemoteApp is in use.
|
||||
*/
|
||||
IDX_REMOTE_APP_DIR,
|
||||
|
||||
/**
|
||||
* The arguments to pass to the remote application, if RemoteApp is in use.
|
||||
*/
|
||||
IDX_REMOTE_APP_ARGS,
|
||||
|
||||
/**
|
||||
* Comma-separated list of the names of all static virtual channels that
|
||||
* should be connected to and exposed as Guacamole pipe streams, or blank
|
||||
* if no static virtual channels should be used.
|
||||
*/
|
||||
IDX_STATIC_CHANNELS,
|
||||
|
||||
/**
|
||||
* The name of the client to submit to the RDP server upon connection.
|
||||
*/
|
||||
IDX_CLIENT_NAME,
|
||||
|
||||
/**
|
||||
* "true" if the desktop wallpaper should be visible, "false" or blank if
|
||||
* the desktop wallpaper should be hidden.
|
||||
*/
|
||||
IDX_ENABLE_WALLPAPER,
|
||||
|
||||
/**
|
||||
* "true" if desktop and window theming should be allowed, "false" or blank
|
||||
* if theming should be temporarily disabled on the desktop of the RDP
|
||||
* server for the sake of performance.
|
||||
*/
|
||||
IDX_ENABLE_THEMING,
|
||||
|
||||
/**
|
||||
* "true" if glyphs should be smoothed with antialiasing (ClearType),
|
||||
* "false" or blank if glyphs should be rendered with sharp edges and using
|
||||
* single colors, effectively 1-bit images.
|
||||
*/
|
||||
IDX_ENABLE_FONT_SMOOTHING,
|
||||
|
||||
/**
|
||||
* "true" if windows' contents should be shown as they are moved, "false"
|
||||
* or blank if only a window border should be shown during window move
|
||||
* operations.
|
||||
*/
|
||||
IDX_ENABLE_FULL_WINDOW_DRAG,
|
||||
|
||||
/**
|
||||
* "true" if desktop composition (Aero) should be enabled during the
|
||||
* session, "false" or blank otherwise. As desktop composition provides
|
||||
* alpha blending and other special effects, this increases the amount of
|
||||
* bandwidth used.
|
||||
*/
|
||||
IDX_ENABLE_DESKTOP_COMPOSITION,
|
||||
|
||||
/**
|
||||
* "true" if menu animations should be shown, "false" or blank menus should
|
||||
* not be animated.
|
||||
*/
|
||||
IDX_ENABLE_MENU_ANIMATIONS,
|
||||
|
||||
/**
|
||||
* The preconnection ID to send within the preconnection PDU when
|
||||
* initiating an RDP connection, if any.
|
||||
*/
|
||||
IDX_PRECONNECTION_ID,
|
||||
|
||||
/**
|
||||
* The preconnection BLOB (PCB) to send to the RDP server prior to full RDP
|
||||
* connection negotiation. This value is used by Hyper-V to select the
|
||||
* destination VM.
|
||||
*/
|
||||
IDX_PRECONNECTION_BLOB,
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/**
|
||||
* "true" if SFTP should be enabled for the RDP connection, "false" or
|
||||
* blank otherwise.
|
||||
*/
|
||||
IDX_ENABLE_SFTP,
|
||||
|
||||
/**
|
||||
* The hostname of the SSH server to connect to for SFTP. If blank, the
|
||||
* hostname of the RDP server will be used.
|
||||
*/
|
||||
IDX_SFTP_HOSTNAME,
|
||||
|
||||
/**
|
||||
* The port of the SSH server to connect to for SFTP. If blank, the default
|
||||
* SSH port of "22" will be used.
|
||||
*/
|
||||
IDX_SFTP_PORT,
|
||||
|
||||
/**
|
||||
* The username to provide when authenticating with the SSH server for
|
||||
* SFTP. If blank, the username provided for the RDP user will be used.
|
||||
*/
|
||||
IDX_SFTP_USERNAME,
|
||||
|
||||
/**
|
||||
* The password to provide when authenticating with the SSH server for
|
||||
* SFTP (if not using a private key).
|
||||
*/
|
||||
IDX_SFTP_PASSWORD,
|
||||
|
||||
/**
|
||||
* The base64-encoded private key to use when authenticating with the SSH
|
||||
* server for SFTP (if not using a password).
|
||||
*/
|
||||
IDX_SFTP_PRIVATE_KEY,
|
||||
|
||||
/**
|
||||
* The passphrase to use to decrypt the provided base64-encoded private
|
||||
* key.
|
||||
*/
|
||||
IDX_SFTP_PASSPHRASE,
|
||||
|
||||
/**
|
||||
* The default location for file uploads within the SSH server. This will
|
||||
* apply only to uploads which do not use the filesystem guac_object (where
|
||||
* the destination directory is otherwise ambiguous).
|
||||
*/
|
||||
IDX_SFTP_DIRECTORY,
|
||||
|
||||
#endif
|
||||
|
||||
RDP_ARGS_COUNT
|
||||
};
|
||||
|
||||
guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
||||
int argc, const char** argv) {
|
||||
|
||||
/* Validate arg count */
|
||||
if (argc != RDP_ARGS_COUNT) {
|
||||
guac_user_log(user, GUAC_LOG_WARNING, "Incorrect number of connection "
|
||||
"parameters provided: expected %i, got %i.",
|
||||
RDP_ARGS_COUNT, argc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guac_rdp_settings* settings = calloc(1, sizeof(guac_rdp_settings));
|
||||
|
||||
/* Use console */
|
||||
settings->console =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_CONSOLE, 0);
|
||||
|
||||
/* Enable/disable console audio */
|
||||
settings->console_audio =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_CONSOLE_AUDIO, 0);
|
||||
|
||||
/* Ignore SSL/TLS certificate */
|
||||
settings->ignore_certificate =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_IGNORE_CERT, 0);
|
||||
|
||||
/* Disable authentication */
|
||||
settings->disable_authentication =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_DISABLE_AUTH, 0);
|
||||
|
||||
/* NLA security */
|
||||
if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
|
||||
guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA");
|
||||
settings->security_mode = GUAC_SECURITY_NLA;
|
||||
}
|
||||
|
||||
/* TLS security */
|
||||
else if (strcmp(argv[IDX_SECURITY], "tls") == 0) {
|
||||
guac_user_log(user, GUAC_LOG_INFO, "Security mode: TLS");
|
||||
settings->security_mode = GUAC_SECURITY_TLS;
|
||||
}
|
||||
|
||||
/* RDP security */
|
||||
else if (strcmp(argv[IDX_SECURITY], "rdp") == 0) {
|
||||
guac_user_log(user, GUAC_LOG_INFO, "Security mode: RDP");
|
||||
settings->security_mode = GUAC_SECURITY_RDP;
|
||||
}
|
||||
|
||||
/* ANY security (allow server to choose) */
|
||||
else if (strcmp(argv[IDX_SECURITY], "any") == 0) {
|
||||
guac_user_log(user, GUAC_LOG_INFO, "Security mode: ANY");
|
||||
settings->security_mode = GUAC_SECURITY_ANY;
|
||||
}
|
||||
|
||||
/* If nothing given, default to RDP */
|
||||
else {
|
||||
guac_user_log(user, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP.");
|
||||
settings->security_mode = GUAC_SECURITY_RDP;
|
||||
}
|
||||
|
||||
/* Set hostname */
|
||||
settings->hostname =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_HOSTNAME, "");
|
||||
|
||||
/* If port specified, use it */
|
||||
settings->port =
|
||||
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_PORT, RDP_DEFAULT_PORT);
|
||||
|
||||
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||
"User resolution is %ix%i at %i DPI",
|
||||
user->info.optimal_width,
|
||||
user->info.optimal_height,
|
||||
user->info.optimal_resolution);
|
||||
|
||||
/* Use suggested resolution unless overridden */
|
||||
settings->resolution =
|
||||
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_DPI, guac_rdp_suggest_resolution(user));
|
||||
|
||||
/* Use optimal width unless overridden */
|
||||
settings->width = user->info.optimal_width
|
||||
* settings->resolution
|
||||
/ user->info.optimal_resolution;
|
||||
|
||||
if (argv[IDX_WIDTH][0] != '\0')
|
||||
settings->width = atoi(argv[IDX_WIDTH]);
|
||||
|
||||
/* Use default width if given width is invalid. */
|
||||
if (settings->width <= 0) {
|
||||
settings->width = RDP_DEFAULT_WIDTH;
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Invalid width: \"%s\". Using default of %i.",
|
||||
argv[IDX_WIDTH], settings->width);
|
||||
}
|
||||
|
||||
/* Round width down to nearest multiple of 4 */
|
||||
settings->width = settings->width & ~0x3;
|
||||
|
||||
/* Use optimal height unless overridden */
|
||||
settings->height = user->info.optimal_height
|
||||
* settings->resolution
|
||||
/ user->info.optimal_resolution;
|
||||
|
||||
if (argv[IDX_HEIGHT][0] != '\0')
|
||||
settings->height = atoi(argv[IDX_HEIGHT]);
|
||||
|
||||
/* Use default height if given height is invalid. */
|
||||
if (settings->height <= 0) {
|
||||
settings->height = RDP_DEFAULT_HEIGHT;
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Invalid height: \"%s\". Using default of %i.",
|
||||
argv[IDX_WIDTH], settings->height);
|
||||
}
|
||||
|
||||
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||
"Using resolution of %ix%i at %i DPI",
|
||||
settings->width,
|
||||
settings->height,
|
||||
settings->resolution);
|
||||
|
||||
/* Domain */
|
||||
settings->domain =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_DOMAIN, NULL);
|
||||
|
||||
/* Username */
|
||||
settings->username =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_USERNAME, NULL);
|
||||
|
||||
/* Password */
|
||||
settings->password =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_PASSWORD, NULL);
|
||||
|
||||
/* Client name */
|
||||
settings->client_name =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_CLIENT_NAME, NULL);
|
||||
|
||||
/* Initial program */
|
||||
settings->initial_program =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_INITIAL_PROGRAM, NULL);
|
||||
|
||||
/* RemoteApp program */
|
||||
settings->remote_app =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_REMOTE_APP, NULL);
|
||||
|
||||
/* RemoteApp working directory */
|
||||
settings->remote_app_dir =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_REMOTE_APP_DIR, NULL);
|
||||
|
||||
/* RemoteApp arguments */
|
||||
settings->remote_app_args =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_REMOTE_APP_ARGS, NULL);
|
||||
|
||||
/* Static virtual channels */
|
||||
settings->svc_names = NULL;
|
||||
if (argv[IDX_STATIC_CHANNELS][0] != '\0')
|
||||
settings->svc_names = guac_split(argv[IDX_STATIC_CHANNELS], ',');
|
||||
|
||||
/*
|
||||
* Performance flags
|
||||
*/
|
||||
|
||||
settings->wallpaper_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_WALLPAPER, 0);
|
||||
|
||||
settings->theming_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_THEMING, 0);
|
||||
|
||||
settings->font_smoothing_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_FONT_SMOOTHING, 0);
|
||||
|
||||
settings->full_window_drag_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_FULL_WINDOW_DRAG, 0);
|
||||
|
||||
settings->desktop_composition_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_DESKTOP_COMPOSITION, 0);
|
||||
|
||||
settings->menu_animations_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_MENU_ANIMATIONS, 0);
|
||||
|
||||
/* Session color depth */
|
||||
settings->color_depth =
|
||||
guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_COLOR_DEPTH, RDP_DEFAULT_DEPTH);
|
||||
|
||||
/* Preconnection ID */
|
||||
settings->preconnection_id = -1;
|
||||
if (argv[IDX_PRECONNECTION_ID][0] != '\0') {
|
||||
|
||||
/* Parse preconnection ID, warn if invalid */
|
||||
int preconnection_id = atoi(argv[IDX_PRECONNECTION_ID]);
|
||||
if (preconnection_id < 0)
|
||||
guac_user_log(user, GUAC_LOG_WARNING,
|
||||
"Ignoring invalid preconnection ID: %i",
|
||||
preconnection_id);
|
||||
|
||||
/* Otherwise, assign specified ID */
|
||||
else {
|
||||
settings->preconnection_id = preconnection_id;
|
||||
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||
"Preconnection ID: %i", settings->preconnection_id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Preconnection BLOB */
|
||||
settings->preconnection_blob = NULL;
|
||||
if (argv[IDX_PRECONNECTION_BLOB][0] != '\0') {
|
||||
settings->preconnection_blob = strdup(argv[IDX_PRECONNECTION_BLOB]);
|
||||
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||
"Preconnection BLOB: \"%s\"", settings->preconnection_blob);
|
||||
}
|
||||
|
||||
#ifndef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
|
||||
/* Warn if support for the preconnection BLOB / ID is absent */
|
||||
if (settings->preconnection_blob != NULL
|
||||
|| settings->preconnection_id != -1) {
|
||||
guac_user_log(user, GUAC_LOG_WARNING,
|
||||
"Installed version of FreeRDP lacks support for the "
|
||||
"preconnection PDU. The specified preconnection BLOB and/or "
|
||||
"ID will be ignored.");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Audio enable/disable */
|
||||
settings->audio_enabled =
|
||||
!guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_DISABLE_AUDIO, 0);
|
||||
|
||||
/* Printing enable/disable */
|
||||
settings->printing_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_PRINTING, 0);
|
||||
|
||||
/* Drive enable/disable */
|
||||
settings->drive_enabled =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_DRIVE, 0);
|
||||
|
||||
settings->drive_path =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_DRIVE_PATH, "");
|
||||
|
||||
settings->create_drive_path =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_CREATE_DRIVE_PATH, 0);
|
||||
|
||||
/* Pick keymap based on argument */
|
||||
settings->server_layout = NULL;
|
||||
if (argv[IDX_SERVER_LAYOUT][0] != '\0')
|
||||
settings->server_layout =
|
||||
guac_rdp_keymap_find(argv[IDX_SERVER_LAYOUT]);
|
||||
|
||||
/* If no keymap requested, use default */
|
||||
if (settings->server_layout == NULL)
|
||||
settings->server_layout = guac_rdp_keymap_find(GUAC_DEFAULT_KEYMAP);
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/* SFTP enable/disable */
|
||||
settings->enable_sftp =
|
||||
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_ENABLE_SFTP, 0);
|
||||
|
||||
/* Hostname for SFTP connection */
|
||||
settings->sftp_hostname =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_HOSTNAME, settings->hostname);
|
||||
|
||||
/* Port for SFTP connection */
|
||||
settings->sftp_port =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_PORT, "22");
|
||||
|
||||
/* Username for SSH/SFTP authentication */
|
||||
settings->sftp_username =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_USERNAME,
|
||||
settings->username != NULL ? settings->username : "");
|
||||
|
||||
/* Password for SFTP (if not using private key) */
|
||||
settings->sftp_password =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_PASSWORD, "");
|
||||
|
||||
/* Private key for SFTP (if not using password) */
|
||||
settings->sftp_private_key =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_PRIVATE_KEY, NULL);
|
||||
|
||||
/* Passphrase for decrypting the SFTP private key (if applicable */
|
||||
settings->sftp_passphrase =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_PASSPHRASE, "");
|
||||
|
||||
/* Default upload directory */
|
||||
settings->sftp_directory =
|
||||
guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
|
||||
IDX_SFTP_DIRECTORY, NULL);
|
||||
#endif
|
||||
|
||||
/* Success */
|
||||
return settings;
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_settings_free(guac_rdp_settings* settings) {
|
||||
|
||||
/* Free settings strings */
|
||||
free(settings->client_name);
|
||||
free(settings->domain);
|
||||
free(settings->drive_path);
|
||||
free(settings->hostname);
|
||||
free(settings->initial_program);
|
||||
free(settings->password);
|
||||
free(settings->preconnection_blob);
|
||||
free(settings->remote_app);
|
||||
free(settings->remote_app_args);
|
||||
free(settings->remote_app_dir);
|
||||
free(settings->username);
|
||||
|
||||
/* Free channel name array */
|
||||
free(settings->svc_names);
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/* Free SFTP settings */
|
||||
free(settings->sftp_directory);
|
||||
free(settings->sftp_hostname);
|
||||
free(settings->sftp_passphrase);
|
||||
free(settings->sftp_password);
|
||||
free(settings->sftp_port);
|
||||
free(settings->sftp_private_key);
|
||||
free(settings->sftp_username);
|
||||
#endif
|
||||
|
||||
/* Free settings structure */
|
||||
free(settings);
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_get_width(freerdp* rdp) {
|
||||
#ifdef LEGACY_RDPSETTINGS
|
||||
return rdp->settings->width;
|
||||
|
@ -280,25 +280,133 @@ typedef struct guac_rdp_settings {
|
||||
*/
|
||||
char* preconnection_blob;
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
/**
|
||||
* Whether SFTP should be enabled for the VNC connection.
|
||||
*/
|
||||
int enable_sftp;
|
||||
|
||||
/**
|
||||
* The hostname of the SSH server to connect to for SFTP.
|
||||
*/
|
||||
char* sftp_hostname;
|
||||
|
||||
/**
|
||||
* The port of the SSH server to connect to for SFTP.
|
||||
*/
|
||||
char* sftp_port;
|
||||
|
||||
/**
|
||||
* The username to provide when authenticating with the SSH server for
|
||||
* SFTP.
|
||||
*/
|
||||
char* sftp_username;
|
||||
|
||||
/**
|
||||
* The password to provide when authenticating with the SSH server for
|
||||
* SFTP (if not using a private key).
|
||||
*/
|
||||
char* sftp_password;
|
||||
|
||||
/**
|
||||
* The base64-encoded private key to use when authenticating with the SSH
|
||||
* server for SFTP (if not using a password).
|
||||
*/
|
||||
char* sftp_private_key;
|
||||
|
||||
/**
|
||||
* The passphrase to use to decrypt the provided base64-encoded private
|
||||
* key.
|
||||
*/
|
||||
char* sftp_passphrase;
|
||||
|
||||
/**
|
||||
* The default location for file uploads within the SSH server. This will
|
||||
* apply only to uploads which do not use the filesystem guac_object (where
|
||||
* the destination directory is otherwise ambiguous).
|
||||
*/
|
||||
char* sftp_directory;
|
||||
#endif
|
||||
|
||||
} guac_rdp_settings;
|
||||
|
||||
/**
|
||||
* Parses all given args, storing them in a newly-allocated settings object. If
|
||||
* the args fail to parse, NULL is returned.
|
||||
*
|
||||
* @param user
|
||||
* The user who submitted the given arguments while joining the
|
||||
* connection.
|
||||
*
|
||||
* @param argc
|
||||
* The number of arguments within the argv array.
|
||||
*
|
||||
* @param argv
|
||||
* The values of all arguments provided by the user.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated settings object which must be freed with
|
||||
* guac_rdp_settings_free() when no longer needed. If the arguments fail
|
||||
* to parse, NULL is returned.
|
||||
*/
|
||||
guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
|
||||
int argc, const char** argv);
|
||||
|
||||
/**
|
||||
* Frees the given guac_rdp_settings object, having been previously allocated
|
||||
* via guac_rdp_parse_args().
|
||||
*
|
||||
* @param settings
|
||||
* The settings object to free.
|
||||
*/
|
||||
void guac_rdp_settings_free(guac_rdp_settings* settings);
|
||||
|
||||
/**
|
||||
* NULL-terminated array of accepted client args.
|
||||
*/
|
||||
extern const char* GUAC_RDP_CLIENT_ARGS[];
|
||||
|
||||
/**
|
||||
* Save all given settings to the given freerdp instance.
|
||||
*
|
||||
* @param guac_settings
|
||||
* The guac_rdp_settings object to save.
|
||||
*
|
||||
* @param rdp
|
||||
* The RDP instance to save settings to.
|
||||
*/
|
||||
void guac_rdp_push_settings(guac_rdp_settings* guac_settings, freerdp* rdp);
|
||||
|
||||
/**
|
||||
* Returns the width of the RDP session display.
|
||||
*
|
||||
* @param rdp
|
||||
* The RDP instance to retrieve the width from.
|
||||
*
|
||||
* @return
|
||||
* The current width of the RDP display, in pixels.
|
||||
*/
|
||||
int guac_rdp_get_width(freerdp* rdp);
|
||||
|
||||
/**
|
||||
* Returns the height of the RDP session display.
|
||||
*
|
||||
* @param rdp
|
||||
* The RDP instance to retrieve the height from.
|
||||
*
|
||||
* @return
|
||||
* The current height of the RDP display, in pixels.
|
||||
*/
|
||||
int guac_rdp_get_height(freerdp* rdp);
|
||||
|
||||
/**
|
||||
* Returns the depth of the RDP session display.
|
||||
*
|
||||
* @param rdp
|
||||
* The RDP instance to retrieve the depth from.
|
||||
*
|
||||
* @return
|
||||
* The current depth of the RDP display, in bits per pixel.
|
||||
*/
|
||||
int guac_rdp_get_depth(freerdp* rdp);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "config.h"
|
||||
#include "client.h"
|
||||
#include "guac_clipboard.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_fs.h"
|
||||
#include "rdp_svc.h"
|
||||
#include "rdp_stream.h"
|
||||
@ -82,19 +83,22 @@ static void __generate_upload_path(const char* filename, char* path) {
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_upload_file_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* filename) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
int file_id;
|
||||
guac_rdp_stream* rdp_stream;
|
||||
char file_path[GUAC_RDP_FS_MAX_PATH];
|
||||
|
||||
/* Get filesystem, return error if no filesystem */
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
|
||||
GUAC_PROTOCOL_STATUS_SERVER_ERROR);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -105,9 +109,9 @@ int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
|
||||
file_id = guac_rdp_fs_open(fs, file_path, ACCESS_GENERIC_WRITE, 0,
|
||||
DISP_FILE_OVERWRITE_IF, 0);
|
||||
if (file_id < 0) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -120,31 +124,31 @@ int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
|
||||
stream->blob_handler = guac_rdp_upload_blob_handler;
|
||||
stream->end_handler = guac_rdp_upload_end_handler;
|
||||
|
||||
guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)",
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_svc_pipe_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* name) {
|
||||
|
||||
guac_rdp_stream* rdp_stream;
|
||||
guac_rdp_svc* svc = guac_rdp_get_svc(client, name);
|
||||
guac_rdp_svc* svc = guac_rdp_get_svc(user->client, name);
|
||||
|
||||
/* Fail if no such SVC */
|
||||
if (svc == NULL) {
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Requested non-existent pipe: \"%s\".",
|
||||
name);
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO SUCH PIPE)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO SUCH PIPE)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Inbound half of channel \"%s\" connected.",
|
||||
name);
|
||||
|
||||
@ -153,16 +157,16 @@ int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream,
|
||||
stream->blob_handler = guac_rdp_svc_blob_handler;
|
||||
rdp_stream->type = GUAC_RDP_INBOUND_SVC_STREAM;
|
||||
rdp_stream->svc = svc;
|
||||
svc->input_pipe = stream;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_clipboard_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_stream* rdp_stream;
|
||||
|
||||
/* Init stream data */
|
||||
@ -171,23 +175,25 @@ int guac_rdp_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
stream->end_handler = guac_rdp_clipboard_end_handler;
|
||||
rdp_stream->type = GUAC_RDP_INBOUND_CLIPBOARD_STREAM;
|
||||
|
||||
guac_common_clipboard_reset(client_data->clipboard, mimetype);
|
||||
guac_common_clipboard_reset(rdp_client->clipboard, mimetype);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
int bytes_written;
|
||||
guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
|
||||
|
||||
/* Get filesystem, return error if no filesystem 0*/
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
|
||||
GUAC_PROTOCOL_STATUS_SERVER_ERROR);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -202,10 +208,10 @@ int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
|
||||
|
||||
/* On error, abort */
|
||||
if (bytes_written < 0) {
|
||||
guac_protocol_send_ack(client->socket, stream,
|
||||
guac_protocol_send_ack(user->socket, stream,
|
||||
"FAIL (BAD WRITE)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -216,14 +222,14 @@ int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
|
||||
|
||||
}
|
||||
|
||||
guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)",
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_svc_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
|
||||
@ -231,32 +237,35 @@ int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream,
|
||||
/* Write blob data to SVC directly */
|
||||
guac_rdp_svc_write(rdp_stream->svc, data, length);
|
||||
|
||||
guac_protocol_send_ack(client->socket, stream, "OK (DATA RECEIVED)",
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_clipboard_blob_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_clipboard_blob_handler(guac_user* user, guac_stream* stream,
|
||||
void* data, int length) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_common_clipboard_append(client_data->clipboard, (char*) data, length);
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_clipboard_append(rdp_client->clipboard, (char*) data, length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream) {
|
||||
int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
|
||||
|
||||
/* Get filesystem, return error if no filesystem */
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
|
||||
GUAC_PROTOCOL_STATUS_SERVER_ERROR);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -264,19 +273,20 @@ int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream) {
|
||||
guac_rdp_fs_close(fs, rdp_stream->upload_status.file_id);
|
||||
|
||||
/* Acknowledge stream end */
|
||||
guac_protocol_send_ack(client->socket, stream, "OK (STREAM END)",
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
free(rdp_stream);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream) {
|
||||
int guac_rdp_clipboard_end_handler(guac_user* user, guac_stream* stream) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
rdpChannels* channels = client_data->rdp_inst->context->channels;
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
rdpChannels* channels = rdp_client->rdp_inst->context->channels;
|
||||
|
||||
RDP_CB_FORMAT_LIST_EVENT* format_list =
|
||||
(RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
|
||||
@ -285,10 +295,10 @@ int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream) {
|
||||
NULL, NULL);
|
||||
|
||||
/* Terminate clipboard data with NULL */
|
||||
guac_common_clipboard_append(client_data->clipboard, "", 1);
|
||||
guac_common_clipboard_append(rdp_client->clipboard, "", 1);
|
||||
|
||||
/* Notify server that text data is now available */
|
||||
format_list->formats = (UINT32*) malloc(sizeof(UINT32));
|
||||
format_list->formats = (UINT32*) malloc(sizeof(UINT32) * 2);
|
||||
format_list->formats[0] = CB_FORMAT_TEXT;
|
||||
format_list->formats[1] = CB_FORMAT_UNICODETEXT;
|
||||
format_list->num_formats = 2;
|
||||
@ -298,17 +308,19 @@ int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream,
|
||||
char* message, guac_protocol_status status) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data;
|
||||
|
||||
/* Get filesystem, return error if no filesystem */
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
|
||||
GUAC_PROTOCOL_STATUS_SERVER_ERROR);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -324,39 +336,39 @@ int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream,
|
||||
/* If bytes read, send as blob */
|
||||
if (bytes_read > 0) {
|
||||
rdp_stream->download_status.offset += bytes_read;
|
||||
guac_protocol_send_blob(client->socket, stream,
|
||||
guac_protocol_send_blob(user->socket, stream,
|
||||
buffer, bytes_read);
|
||||
}
|
||||
|
||||
/* If EOF, send end */
|
||||
else if (bytes_read == 0) {
|
||||
guac_protocol_send_end(client->socket, stream);
|
||||
guac_client_free_stream(client, stream);
|
||||
guac_protocol_send_end(user->socket, stream);
|
||||
guac_user_free_stream(user, stream);
|
||||
free(rdp_stream);
|
||||
}
|
||||
|
||||
/* Otherwise, fail stream */
|
||||
else {
|
||||
guac_client_log(client, GUAC_LOG_ERROR,
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Error reading file for download");
|
||||
guac_protocol_send_end(client->socket, stream);
|
||||
guac_client_free_stream(client, stream);
|
||||
guac_protocol_send_end(user->socket, stream);
|
||||
guac_user_free_stream(user, stream);
|
||||
free(rdp_stream);
|
||||
}
|
||||
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
}
|
||||
|
||||
/* Otherwise, return stream to client */
|
||||
/* Otherwise, return stream to user */
|
||||
else
|
||||
guac_client_free_stream(client, stream);
|
||||
guac_user_free_stream(user, stream);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_ls_ack_handler(guac_user* user, guac_stream* stream,
|
||||
char* message, guac_protocol_status status) {
|
||||
|
||||
int blob_written = 0;
|
||||
@ -368,7 +380,7 @@ int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
if (status != GUAC_PROTOCOL_STATUS_SUCCESS) {
|
||||
guac_rdp_fs_close(rdp_stream->ls_status.fs,
|
||||
rdp_stream->ls_status.file_id);
|
||||
guac_client_free_stream(client, stream);
|
||||
guac_user_free_stream(user, stream);
|
||||
free(rdp_stream);
|
||||
return 0;
|
||||
}
|
||||
@ -388,7 +400,7 @@ int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
if (!guac_rdp_fs_append_filename(absolute_path,
|
||||
rdp_stream->ls_status.directory_name, filename)) {
|
||||
|
||||
guac_client_log(client, GUAC_LOG_DEBUG,
|
||||
guac_user_log(user, GUAC_LOG_DEBUG,
|
||||
"Skipping filename \"%s\" - filename is invalid or "
|
||||
"resulting path is too long", filename);
|
||||
|
||||
@ -414,12 +426,12 @@ int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
/* Determine mimetype */
|
||||
const char* mimetype;
|
||||
if (file->attributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
mimetype = GUAC_CLIENT_STREAM_INDEX_MIMETYPE;
|
||||
mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE;
|
||||
else
|
||||
mimetype = "application/octet-stream";
|
||||
|
||||
/* Write entry */
|
||||
blob_written |= guac_common_json_write_property(client, stream,
|
||||
blob_written |= guac_common_json_write_property(user, stream,
|
||||
&rdp_stream->ls_status.json_state, absolute_path, mimetype);
|
||||
|
||||
guac_rdp_fs_close(rdp_stream->ls_status.fs, file_id);
|
||||
@ -430,9 +442,9 @@ int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
if (filename == NULL) {
|
||||
|
||||
/* Complete JSON object */
|
||||
guac_common_json_end_object(client, stream,
|
||||
guac_common_json_end_object(user, stream,
|
||||
&rdp_stream->ls_status.json_state);
|
||||
guac_common_json_flush(client, stream,
|
||||
guac_common_json_flush(user, stream,
|
||||
&rdp_stream->ls_status.json_state);
|
||||
|
||||
/* Clean up resources */
|
||||
@ -441,21 +453,24 @@ int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
free(rdp_stream);
|
||||
|
||||
/* Signal of stream */
|
||||
guac_protocol_send_end(client->socket, stream);
|
||||
guac_client_free_stream(client, stream);
|
||||
guac_protocol_send_end(user->socket, stream);
|
||||
guac_user_free_stream(user, stream);
|
||||
|
||||
}
|
||||
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
|
||||
int guac_rdp_download_get_handler(guac_user* user, guac_object* object,
|
||||
char* name) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get filesystem, ignore request if no filesystem */
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL)
|
||||
return 0;
|
||||
|
||||
@ -463,7 +478,7 @@ int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
|
||||
int file_id = guac_rdp_fs_open(fs, name, ACCESS_GENERIC_READ, 0,
|
||||
DISP_FILE_OPEN, 0);
|
||||
if (file_id < 0) {
|
||||
guac_client_log(client, GUAC_LOG_INFO, "Unable to read file \"%s\"",
|
||||
guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"",
|
||||
name);
|
||||
return 0;
|
||||
}
|
||||
@ -489,17 +504,17 @@ int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
|
||||
sizeof(rdp_stream->ls_status.directory_name) - 1);
|
||||
|
||||
/* Allocate stream for body */
|
||||
guac_stream* stream = guac_client_alloc_stream(client);
|
||||
guac_stream* stream = guac_user_alloc_stream(user);
|
||||
stream->ack_handler = guac_rdp_ls_ack_handler;
|
||||
stream->data = rdp_stream;
|
||||
|
||||
/* Init JSON object state */
|
||||
guac_common_json_begin_object(client, stream,
|
||||
guac_common_json_begin_object(user, stream,
|
||||
&rdp_stream->ls_status.json_state);
|
||||
|
||||
/* Associate new stream with get request */
|
||||
guac_protocol_send_body(client->socket, object, stream,
|
||||
GUAC_CLIENT_STREAM_INDEX_MIMETYPE, name);
|
||||
guac_protocol_send_body(user->socket, object, stream,
|
||||
GUAC_USER_STREAM_INDEX_MIMETYPE, name);
|
||||
|
||||
}
|
||||
|
||||
@ -513,29 +528,32 @@ int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
|
||||
rdp_stream->download_status.offset = 0;
|
||||
|
||||
/* Allocate stream for body */
|
||||
guac_stream* stream = guac_client_alloc_stream(client);
|
||||
guac_stream* stream = guac_user_alloc_stream(user);
|
||||
stream->data = rdp_stream;
|
||||
stream->ack_handler = guac_rdp_download_ack_handler;
|
||||
|
||||
/* Associate new stream with get request */
|
||||
guac_protocol_send_body(client->socket, object, stream,
|
||||
guac_protocol_send_body(user->socket, object, stream,
|
||||
"application/octet-stream", name);
|
||||
|
||||
}
|
||||
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
|
||||
int guac_rdp_upload_put_handler(guac_user* user, guac_object* object,
|
||||
guac_stream* stream, char* mimetype, char* name) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Get filesystem, return error if no filesystem */
|
||||
guac_rdp_fs* fs = ((rdp_guac_client_data*) client->data)->filesystem;
|
||||
guac_rdp_fs* fs = rdp_client->filesystem;
|
||||
if (fs == NULL) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (NO FS)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)",
|
||||
GUAC_PROTOCOL_STATUS_SERVER_ERROR);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -545,9 +563,9 @@ int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
|
||||
|
||||
/* Abort on failure */
|
||||
if (file_id < 0) {
|
||||
guac_protocol_send_ack(client->socket, stream, "FAIL (CANNOT OPEN)",
|
||||
guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)",
|
||||
GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -563,9 +581,9 @@ int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
|
||||
stream->end_handler = guac_rdp_upload_end_handler;
|
||||
|
||||
/* Acknowledge stream creation */
|
||||
guac_protocol_send_ack(client->socket, stream, "OK (STREAM BEGIN)",
|
||||
guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)",
|
||||
GUAC_PROTOCOL_STATUS_SUCCESS);
|
||||
guac_socket_flush(client->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "guac_json.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/user.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/stream.h>
|
||||
|
||||
@ -163,125 +163,67 @@ typedef struct guac_rdp_stream {
|
||||
/**
|
||||
* Handler for inbound files related to file uploads.
|
||||
*/
|
||||
int guac_rdp_upload_file_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype, char* filename);
|
||||
guac_user_file_handler guac_rdp_upload_file_handler;
|
||||
|
||||
/**
|
||||
* Handler for inbound pipes related to static virtual channels.
|
||||
*/
|
||||
int guac_rdp_svc_pipe_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype, char* name);
|
||||
guac_user_pipe_handler guac_rdp_svc_pipe_handler;
|
||||
|
||||
/**
|
||||
* Handler for inbound clipboard data.
|
||||
*/
|
||||
int guac_rdp_clipboard_handler(guac_client* client, guac_stream* stream,
|
||||
char* mimetype);
|
||||
guac_user_clipboard_handler guac_rdp_clipboard_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream data related to file uploads.
|
||||
*/
|
||||
int guac_rdp_upload_blob_handler(guac_client* client, guac_stream* stream,
|
||||
void* data, int length);
|
||||
guac_user_blob_handler guac_rdp_upload_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream data related to static virtual channels.
|
||||
*/
|
||||
int guac_rdp_svc_blob_handler(guac_client* client, guac_stream* stream,
|
||||
void* data, int length);
|
||||
guac_user_blob_handler guac_rdp_svc_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for stream data related to clipboard.
|
||||
*/
|
||||
int guac_rdp_clipboard_blob_handler(guac_client* client, guac_stream* stream,
|
||||
void* data, int length);
|
||||
guac_user_blob_handler guac_rdp_clipboard_blob_handler;
|
||||
|
||||
/**
|
||||
* Handler for end-of-stream related to file uploads.
|
||||
*/
|
||||
int guac_rdp_upload_end_handler(guac_client* client, guac_stream* stream);
|
||||
guac_user_end_handler guac_rdp_upload_end_handler;
|
||||
|
||||
/**
|
||||
* Handler for end-of-stream related to clipboard.
|
||||
*/
|
||||
int guac_rdp_clipboard_end_handler(guac_client* client, guac_stream* stream);
|
||||
guac_user_end_handler guac_rdp_clipboard_end_handler;
|
||||
|
||||
/**
|
||||
* Handler for acknowledgements of receipt of data related to file downloads.
|
||||
*/
|
||||
int guac_rdp_download_ack_handler(guac_client* client, guac_stream* stream,
|
||||
char* message, guac_protocol_status status);
|
||||
guac_user_ack_handler guac_rdp_download_ack_handler;
|
||||
|
||||
/**
|
||||
* Handler for ack messages received due to receipt of a "body" or "blob"
|
||||
* instruction associated with a directory list operation.
|
||||
*
|
||||
* @param client
|
||||
* The client receiving the ack message.
|
||||
*
|
||||
* @param stream
|
||||
* The Guacamole protocol stream associated with the received ack message.
|
||||
*
|
||||
* @param message
|
||||
* An arbitrary human-readable message describing the nature of the
|
||||
* success or failure denoted by this ack message.
|
||||
*
|
||||
* @param status
|
||||
* The status code associated with this ack message, which may indicate
|
||||
* success or an error.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_rdp_ls_ack_handler(guac_client* client, guac_stream* stream,
|
||||
char* message, guac_protocol_status status);
|
||||
guac_user_ack_handler guac_rdp_ls_ack_handler;
|
||||
|
||||
/**
|
||||
* Handler for get messages. In context of downloads and the filesystem exposed
|
||||
* via the Guacamole protocol, get messages request the body of a file within
|
||||
* the filesystem.
|
||||
*
|
||||
* @param client
|
||||
* The client receiving the get message.
|
||||
*
|
||||
* @param object
|
||||
* The Guacamole protocol object associated with the get request itself.
|
||||
*
|
||||
* @param name
|
||||
* The name of the input stream (file) being requested.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_rdp_download_get_handler(guac_client* client, guac_object* object,
|
||||
char* name);
|
||||
guac_user_get_handler guac_rdp_download_get_handler;
|
||||
|
||||
/**
|
||||
* Handler for put messages. In context of uploads and the filesystem exposed
|
||||
* via the Guacamole protocol, put messages request write access to a file
|
||||
* within the filesystem.
|
||||
*
|
||||
* @param client
|
||||
* The client receiving the put message.
|
||||
*
|
||||
* @param object
|
||||
* The Guacamole protocol object associated with the put request itself.
|
||||
*
|
||||
* @param stream
|
||||
* The Guacamole protocol stream along which the client will be sending
|
||||
* file data.
|
||||
*
|
||||
* @param mimetype
|
||||
* The mimetype of the data being send along the stream.
|
||||
*
|
||||
* @param name
|
||||
* The name of the input stream (file) being requested.
|
||||
*
|
||||
* @return
|
||||
* Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_rdp_upload_put_handler(guac_client* client, guac_object* object,
|
||||
guac_stream* stream, char* mimetype, char* name);
|
||||
guac_user_put_handler guac_rdp_upload_put_handler;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "config.h"
|
||||
#include "client.h"
|
||||
#include "guac_list.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#include <freerdp/utils/svc_plugin.h>
|
||||
@ -44,7 +45,6 @@ guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name) {
|
||||
/* Init SVC */
|
||||
svc->client = client;
|
||||
svc->plugin = NULL;
|
||||
svc->input_pipe = NULL;
|
||||
svc->output_pipe = NULL;
|
||||
|
||||
/* Warn about name length */
|
||||
@ -65,26 +65,52 @@ void guac_rdp_free_svc(guac_rdp_svc* svc) {
|
||||
free(svc);
|
||||
}
|
||||
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc) {
|
||||
|
||||
/* Send pipe instruction for the SVC's output stream */
|
||||
guac_protocol_send_pipe(socket, svc->output_pipe,
|
||||
"application/octet-stream", svc->name);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_svc_send_pipes(guac_user* user) {
|
||||
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
|
||||
/* Send pipe for each allocated SVC's output stream */
|
||||
guac_common_list_element* current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
guac_rdp_svc_send_pipe(user->socket, (guac_rdp_svc*) current->data);
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
|
||||
/* Add to list of available SVC */
|
||||
guac_common_list_lock(client_data->available_svc);
|
||||
guac_common_list_add(client_data->available_svc, svc);
|
||||
guac_common_list_unlock(client_data->available_svc);
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
guac_common_list_add(rdp_client->available_svc, svc);
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
}
|
||||
|
||||
guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(client_data->available_svc);
|
||||
current = client_data->available_svc->head;
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, found */
|
||||
@ -97,7 +123,7 @@ guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(client_data->available_svc);
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
return found;
|
||||
|
||||
@ -105,19 +131,19 @@ guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name) {
|
||||
|
||||
guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_list_element* current;
|
||||
guac_rdp_svc* found = NULL;
|
||||
|
||||
/* For each available SVC */
|
||||
guac_common_list_lock(client_data->available_svc);
|
||||
current = client_data->available_svc->head;
|
||||
guac_common_list_lock(rdp_client->available_svc);
|
||||
current = rdp_client->available_svc->head;
|
||||
while (current != NULL) {
|
||||
|
||||
/* If name matches, remove entry */
|
||||
guac_rdp_svc* current_svc = (guac_rdp_svc*) current->data;
|
||||
if (strcmp(current_svc->name, name) == 0) {
|
||||
guac_common_list_remove(client_data->available_svc, current);
|
||||
guac_common_list_remove(rdp_client->available_svc, current);
|
||||
found = current_svc;
|
||||
break;
|
||||
}
|
||||
@ -125,7 +151,7 @@ guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name) {
|
||||
current = current->next;
|
||||
|
||||
}
|
||||
guac_common_list_unlock(client_data->available_svc);
|
||||
guac_common_list_unlock(rdp_client->available_svc);
|
||||
|
||||
/* Return removed entry, if any */
|
||||
return found;
|
||||
|
@ -55,12 +55,6 @@ typedef struct guac_rdp_svc {
|
||||
*/
|
||||
char name[GUAC_RDP_SVC_MAX_LENGTH+1];
|
||||
|
||||
/**
|
||||
* The pipe opened by the Guacamole client, if any. This should be
|
||||
* opened in response to the output pipe.
|
||||
*/
|
||||
guac_stream* input_pipe;
|
||||
|
||||
/**
|
||||
* The output pipe, opened when the RDP server receives a connection to
|
||||
* the static channel.
|
||||
@ -71,31 +65,104 @@ typedef struct guac_rdp_svc {
|
||||
|
||||
/**
|
||||
* Allocate a new SVC with the given name.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the virtual channel to allocate.
|
||||
*
|
||||
* @return
|
||||
* A newly-allocated static virtual channel.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_alloc_svc(guac_client* client, char* name);
|
||||
|
||||
/**
|
||||
* Free the given SVC.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to free.
|
||||
*/
|
||||
void guac_rdp_free_svc(guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instruction describing the given static virtual channel
|
||||
* along the given socket. This pipe instruction will relate the SVC's
|
||||
* underlying output stream with the SVC's name and the mimetype
|
||||
* "application/octet-stream".
|
||||
*
|
||||
* @param socket
|
||||
* The socket along which the "pipe" instruction should be sent.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel that the "pipe" instruction should describe.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipe(guac_socket* socket, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Sends the "pipe" instructions describing all static virtual channels
|
||||
* available to the given user along that user's socket. Each pipe instruction
|
||||
* will relate the associated SVC's underlying output stream with the SVC's
|
||||
* name and the mimetype "application/octet-stream".
|
||||
*
|
||||
* @param user
|
||||
* The user to send the "pipe" instructions to.
|
||||
*/
|
||||
void guac_rdp_svc_send_pipes(guac_user* user);
|
||||
|
||||
/**
|
||||
* Add the given SVC to the list of all available SVCs.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to add to the list of all such channels
|
||||
* available.
|
||||
*/
|
||||
void guac_rdp_add_svc(guac_client* client, guac_rdp_svc* svc);
|
||||
|
||||
/**
|
||||
* Retrieve the SVC with the given name from the list stored in the client.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to retrieve.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel with the given name, or NULL if no such
|
||||
* virtual channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_get_svc(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Remove the SVC with the given name from the list stored in the client.
|
||||
*
|
||||
* @param client
|
||||
* The guac_client associated with the current RDP session.
|
||||
*
|
||||
* @param name
|
||||
* The name of the static virtual channel to remove.
|
||||
*
|
||||
* @return
|
||||
* The static virtual channel that was removed, or NULL if no such virtual
|
||||
* channel exists.
|
||||
*/
|
||||
guac_rdp_svc* guac_rdp_remove_svc(guac_client* client, const char* name);
|
||||
|
||||
/**
|
||||
* Write the given blob of data to the virtual channel.
|
||||
*
|
||||
* @param svc
|
||||
* The static virtual channel to write data to.
|
||||
*
|
||||
* @param data
|
||||
* The data to write.
|
||||
*
|
||||
* @param length
|
||||
* The number of bytes to write.
|
||||
*/
|
||||
void guac_rdp_svc_write(guac_rdp_svc* svc, void* data, int length);
|
||||
|
||||
|
@ -23,38 +23,38 @@
|
||||
#include "client.h"
|
||||
#include "resolution.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
int guac_rdp_resolution_reasonable(guac_client* client, int resolution) {
|
||||
int guac_rdp_resolution_reasonable(guac_user* user, int resolution) {
|
||||
|
||||
int width = client->info.optimal_width;
|
||||
int height = client->info.optimal_height;
|
||||
int width = user->info.optimal_width;
|
||||
int height = user->info.optimal_height;
|
||||
|
||||
/* Convert client pixels to remote pixels */
|
||||
width = width * resolution / client->info.optimal_resolution;
|
||||
height = height * resolution / client->info.optimal_resolution;
|
||||
/* Convert user pixels to remote pixels */
|
||||
width = width * resolution / user->info.optimal_resolution;
|
||||
height = height * resolution / user->info.optimal_resolution;
|
||||
|
||||
/*
|
||||
* Resolution is reasonable if the same as the client optimal resolution
|
||||
* Resolution is reasonable if the same as the user optimal resolution
|
||||
* OR if the resulting display area is reasonable
|
||||
*/
|
||||
return client->info.optimal_resolution == resolution
|
||||
return user->info.optimal_resolution == resolution
|
||||
|| width*height >= GUAC_RDP_REASONABLE_AREA;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_suggest_resolution(guac_client* client) {
|
||||
int guac_rdp_suggest_resolution(guac_user* user) {
|
||||
|
||||
/* Prefer RDP's native resolution */
|
||||
if (guac_rdp_resolution_reasonable(client, GUAC_RDP_NATIVE_RESOLUTION))
|
||||
if (guac_rdp_resolution_reasonable(user, GUAC_RDP_NATIVE_RESOLUTION))
|
||||
return GUAC_RDP_NATIVE_RESOLUTION;
|
||||
|
||||
/* If native resolution is too tiny, try higher resolution */
|
||||
if (guac_rdp_resolution_reasonable(client, GUAC_RDP_HIGH_RESOLUTION))
|
||||
if (guac_rdp_resolution_reasonable(user, GUAC_RDP_HIGH_RESOLUTION))
|
||||
return GUAC_RDP_HIGH_RESOLUTION;
|
||||
|
||||
/* Fallback to client-suggested resolution */
|
||||
return client->info.optimal_resolution;
|
||||
/* Fallback to user-suggested resolution */
|
||||
return user->info.optimal_resolution;
|
||||
|
||||
}
|
||||
|
||||
|
@ -23,27 +23,35 @@
|
||||
#ifndef GUAC_RDP_RESOLUTION_H
|
||||
#define GUAC_RDP_RESOLUTION_H
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
/**
|
||||
* Returns whether the given resolution is reasonable for the given client,
|
||||
* Returns whether the given resolution is reasonable for the given user,
|
||||
* based on arbitrary criteria for reasonability.
|
||||
*
|
||||
* @param client The guac_client to test the given resolution against.
|
||||
* @param resolution The resolution to test, in DPI.
|
||||
* @return Non-zero if the resolution is reasonable, zero otherwise.
|
||||
* @param user
|
||||
* The guac_user to test the given resolution against.
|
||||
*
|
||||
* @param resolution
|
||||
* The resolution to test, in DPI.
|
||||
*
|
||||
* @return
|
||||
* Non-zero if the resolution is reasonable, zero otherwise.
|
||||
*/
|
||||
int guac_rdp_resolution_reasonable(guac_client* client, int resolution);
|
||||
int guac_rdp_resolution_reasonable(guac_user* user, int resolution);
|
||||
|
||||
/**
|
||||
* Returns a reasonable resolution for the remote display, given the size and
|
||||
* resolution of a guac_client.
|
||||
* resolution of a guac_user.
|
||||
*
|
||||
* @param client The guac_client whose size and resolution shall be used to
|
||||
* determine an appropriate remote display resolution.
|
||||
* @return A reasonable resolution for the remote display, in DPI.
|
||||
* @param user
|
||||
* The guac_user whose size and resolution shall be used to determine an
|
||||
* appropriate remote display resolution.
|
||||
*
|
||||
* @return
|
||||
* A reasonable resolution for the remote display, in DPI.
|
||||
*/
|
||||
int guac_rdp_suggest_resolution(guac_client* client);
|
||||
int guac_rdp_suggest_resolution(guac_user* user);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -22,21 +22,23 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_sftp.h"
|
||||
#include "rdp.h"
|
||||
#include "sftp.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_sftp_file_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* filename) {
|
||||
|
||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) client->data;
|
||||
guac_object* filesystem = client_data->sftp_filesystem;
|
||||
guac_client* client = user->client;
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
|
||||
guac_common_ssh_sftp_filesystem* filesystem = rdp_client->sftp_filesystem;
|
||||
|
||||
/* Handle file upload */
|
||||
return guac_common_ssh_sftp_handle_file_stream(filesystem, stream,
|
||||
return guac_common_ssh_sftp_handle_file_stream(filesystem, user, stream,
|
||||
mimetype, filename);
|
||||
|
||||
}
|
||||
|
@ -25,15 +25,15 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
/**
|
||||
* Handles an incoming stream from a Guacamole "file" instruction, saving the
|
||||
* contents of that stream to the file having the given name.
|
||||
*
|
||||
* @param client
|
||||
* The client receiving the uploaded file.
|
||||
* @param user
|
||||
* The user uploading the file.
|
||||
*
|
||||
* @param stream
|
||||
* The stream through which the uploaded file data will be received.
|
||||
@ -48,7 +48,7 @@
|
||||
* Zero if the incoming stream has been handled successfully, non-zero on
|
||||
* failure.
|
||||
*/
|
||||
int guac_rdp_sftp_file_handler(guac_client* client, guac_stream* stream,
|
||||
int guac_rdp_sftp_file_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* filename);
|
||||
|
||||
#endif
|
||||
|
@ -20,18 +20,44 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#ifndef GUAC_RDP_UNICODE_H
|
||||
#define GUAC_RDP_UNICODE_H
|
||||
|
||||
/**
|
||||
* Convert the given number of UTF-16 characters to UTF-8 characters.
|
||||
*
|
||||
* @param utf16
|
||||
* Arbitrary UTF-16 data.
|
||||
*
|
||||
* @param length
|
||||
* The length of the UTF-16 data, in characters.
|
||||
*
|
||||
* @param utf8
|
||||
* Buffer to which the converted UTF-8 data will be written.
|
||||
*
|
||||
* @param size
|
||||
* The maximum number of bytes available in the UTF-8 buffer.
|
||||
*/
|
||||
void guac_rdp_utf16_to_utf8(const unsigned char* utf16, int length,
|
||||
char* utf8, int size);
|
||||
|
||||
/**
|
||||
* Convert the given number of UTF-8 characters to UTF-16 characters.
|
||||
*
|
||||
* @param utf8
|
||||
* Arbitrary UTF-8 data.
|
||||
*
|
||||
* @param length
|
||||
* The length of the UTF-8 data, in characters.
|
||||
*
|
||||
* @param utf16
|
||||
* Buffer to which the converted UTF-16 data will be written.
|
||||
*
|
||||
* @param size
|
||||
* The maximum number of bytes available in the UTF-16 buffer.
|
||||
*/
|
||||
void guac_rdp_utf8_to_utf16(const unsigned char* utf8, int length,
|
||||
char* utf16, int size);
|
||||
|
||||
#endif
|
||||
|
||||
|
127
src/protocols/rdp/user.c
Normal file
127
src/protocols/rdp/user.c
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "input.h"
|
||||
#include "guac_display.h"
|
||||
#include "user.h"
|
||||
#include "rdp.h"
|
||||
#include "rdp_settings.h"
|
||||
#include "rdp_stream.h"
|
||||
#include "rdp_svc.h"
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
#include "sftp.h"
|
||||
#endif
|
||||
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data;
|
||||
|
||||
/* Connect via RDP if owner */
|
||||
if (user->owner) {
|
||||
|
||||
/* Parse arguments into client */
|
||||
guac_rdp_settings* settings = rdp_client->settings =
|
||||
guac_rdp_parse_args(user, argc, (const char**) argv);
|
||||
|
||||
/* Fail if settings cannot be parsed */
|
||||
if (settings == NULL) {
|
||||
guac_user_log(user, GUAC_LOG_INFO,
|
||||
"Badly formatted client arguments.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Start client thread */
|
||||
if (pthread_create(&rdp_client->client_thread, NULL,
|
||||
guac_rdp_client_thread, user->client)) {
|
||||
guac_user_log(user, GUAC_LOG_ERROR,
|
||||
"Unable to start VNC client thread.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* If not owner, synchronize with current state */
|
||||
else {
|
||||
|
||||
/* Synchronize any audio stream */
|
||||
if (rdp_client->audio)
|
||||
guac_audio_stream_add_user(rdp_client->audio, user);
|
||||
|
||||
/* Bring user up to date with any registered static channels */
|
||||
guac_rdp_svc_send_pipes(user);
|
||||
|
||||
/* Synchronize with current display */
|
||||
guac_common_display_dup(rdp_client->display, user, user->socket);
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
}
|
||||
|
||||
user->file_handler = guac_rdp_user_file_handler;
|
||||
user->mouse_handler = guac_rdp_user_mouse_handler;
|
||||
user->key_handler = guac_rdp_user_key_handler;
|
||||
user->size_handler = guac_rdp_user_size_handler;
|
||||
user->pipe_handler = guac_rdp_svc_pipe_handler;
|
||||
user->clipboard_handler = guac_rdp_clipboard_handler;
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int guac_rdp_user_file_handler(guac_user* user, guac_stream* stream,
|
||||
char* mimetype, char* filename) {
|
||||
|
||||
guac_rdp_client* rdp_client = (guac_rdp_client*) user->client->data;
|
||||
|
||||
#ifdef ENABLE_COMMON_SSH
|
||||
guac_rdp_settings* settings = rdp_client->settings;
|
||||
|
||||
/* If SFTP is enabled, it should be used for default uploads only if RDPDR
|
||||
* is not enabled or its upload directory has been set */
|
||||
if (rdp_client->sftp_filesystem != NULL) {
|
||||
if (!settings->drive_enabled || settings->sftp_directory != NULL)
|
||||
return guac_rdp_sftp_file_handler(user, stream, mimetype, filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Default to using RDPDR uploads (if enabled) */
|
||||
if (rdp_client->filesystem != NULL)
|
||||
return guac_rdp_upload_file_handler(user, stream, mimetype, filename);
|
||||
|
||||
/* File transfer not enabled */
|
||||
guac_protocol_send_ack(user->socket, stream, "File transfer disabled",
|
||||
GUAC_PROTOCOL_STATUS_UNSUPPORTED);
|
||||
guac_socket_flush(user->socket);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Glyptodon LLC
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -20,19 +20,22 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GUAC_RDP_USER_H
|
||||
#define GUAC_RDP_USER_H
|
||||
|
||||
#ifndef _GUAC_RDP_GUAC_HANDLERS_H
|
||||
#define _GUAC_RDP_GUAC_HANDLERS_H
|
||||
#include <guacamole/user.h>
|
||||
|
||||
#include "config.h"
|
||||
/**
|
||||
* Handler for joining users.
|
||||
*/
|
||||
guac_user_join_handler guac_rdp_user_join_handler;
|
||||
|
||||
#include <guacamole/client.h>
|
||||
|
||||
int rdp_guac_client_free_handler(guac_client* client);
|
||||
int rdp_guac_client_handle_messages(guac_client* client);
|
||||
int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask);
|
||||
int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed);
|
||||
int rdp_guac_client_size_handler(guac_client* client, int width, int height);
|
||||
/**
|
||||
* Handler for received simple file uploads. This handler will automatically
|
||||
* select between RDPDR and SFTP depending on which is available and which has
|
||||
* priority given associated settings.
|
||||
*/
|
||||
guac_user_file_handler guac_rdp_user_file_handler;
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user