Skip to content

Commit

Permalink
GUACAMOLE-1931: Allow writing to existing recording files.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmuehlner committed Mar 16, 2024
1 parent 3782339 commit 2844a7d
Show file tree
Hide file tree
Showing 17 changed files with 126 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/libguac/guacamole/recording.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ typedef struct guac_recording {
* caution. Key events can easily contain sensitive information, such as
* passwords, credit card numbers, etc.
*
* @param allow_write_existing
* Non-zero if writing to an existing file should be allowed, or zero
* otherwise.
*
* @return
* A new guac_recording structure representing the in-progress
* recording if the recording file has been successfully created and a
Expand All @@ -156,7 +160,7 @@ typedef struct guac_recording {
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_touch,
int include_keys);
int include_keys, int allow_write_existing);

/**
* Frees the resources associated with the given in-progress recording. Note
Expand Down
35 changes: 21 additions & 14 deletions src/libguac/recording.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@

/**
* Attempts to open a new recording within the given path and having the given
* name. If such a file already exists, sequential numeric suffixes (.1, .2,
* .3, etc.) are appended until a filename is found which does not exist (or
* until the maximum number of numeric suffixes has been tried). If the file
* absolutely cannot be opened due to an error, -1 is returned and errno is set
* appropriately.
* name. If opening the file fails for any reason, or if such a file already
* exists and allow_write_existing is not set, sequential numeric suffixes
* (.1, .2, .3, etc.) are appended until a filename is found which does not
* exist (or until the maximum number of numeric suffixes has been tried).
* If the file exists and allow_write_existing is set, the recording will be
* appended to any existing file contents. If the file absolutely cannot be
* opened due to an error, -1 is returned and errno is set appropriately.
*
* @param path
* The full path to the directory in which the data file should be created.
Expand All @@ -60,12 +62,17 @@
* @param basename_size
* The number of bytes available within the provided basename buffer.
*
* @param allow_write_existing
* Non-zero if writing to an existing file should be allowed, or zero
* otherwise.
*
* @return
* The file descriptor of the open data file if open succeeded, or -1 on
* failure.
*/
static int guac_recording_open(const char* path,
const char* name, char* basename, int basename_size) {
const char* name, char* basename, int basename_size,
int allow_write_existing) {

int i;

Expand All @@ -81,10 +88,11 @@ static int guac_recording_open(const char* path,
return -1;
}

/* Require the file not exist already if allow_write_existing not set */
int flags = O_CREAT | O_WRONLY | (allow_write_existing ? 0 : O_EXCL);

/* Attempt to open recording */
int fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP);
int fd = open(basename, flags, S_IRUSR | S_IWUSR | S_IRGRP);

/* Continuously retry with alternate names on failure */
if (fd == -1) {
Expand All @@ -101,9 +109,7 @@ static int guac_recording_open(const char* path,
sprintf(suffix, "%i", i);

/* Retry with newly-suffixed filename */
fd = open(basename,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP);
fd = open(basename, flags, S_IRUSR | S_IWUSR | S_IRGRP);

}

Expand Down Expand Up @@ -138,7 +144,7 @@ static int guac_recording_open(const char* path,
guac_recording* guac_recording_create(guac_client* client,
const char* path, const char* name, int create_path,
int include_output, int include_mouse, int include_touch,
int include_keys) {
int include_keys, int allow_write_existing) {

char filename[GUAC_COMMON_RECORDING_MAX_NAME_LENGTH];

Expand All @@ -155,7 +161,8 @@ guac_recording* guac_recording_create(guac_client* client,
}

/* Attempt to open recording file */
int fd = guac_recording_open(path, name, filename, sizeof(filename));
int fd = guac_recording_open(
path, name, filename, sizeof(filename), allow_write_existing);
if (fd == -1) {
guac_client_log(client, GUAC_LOG_ERROR,
"Creation of recording failed: %s", strerror(errno));
Expand Down
3 changes: 2 additions & 1 deletion src/protocols/kubernetes/kubernetes.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@ void* guac_kubernetes_client_thread(void* data) {
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys);
settings->recording_include_keys,
settings->recording_write_existing);
}

/* Create terminal options with required parameters */
Expand Down
12 changes: 12 additions & 0 deletions src/protocols/kubernetes/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const char* GUAC_KUBERNETES_CLIENT_ARGS[] = {
"recording-exclude-mouse",
"recording-include-keys",
"create-recording-path",
"recording-write-existing",
"read-only",
"backspace",
"scrollback",
Expand Down Expand Up @@ -210,6 +211,12 @@ enum KUBERNETES_ARGS_IDX {
*/
IDX_CREATE_RECORDING_PATH,

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
IDX_RECORDING_WRITE_EXISTING,

/**
* "true" if this connection should be read-only (user input should be
* dropped), "false" or blank otherwise.
Expand Down Expand Up @@ -389,6 +396,11 @@ guac_kubernetes_settings* guac_kubernetes_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false);

/* Parse allow write existing file flag */
settings->recording_write_existing =
guac_user_parse_args_boolean(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
IDX_RECORDING_WRITE_EXISTING, false);

/* Parse backspace key code */
settings->backspace =
guac_user_parse_args_int(user, GUAC_KUBERNETES_CLIENT_ARGS, argv,
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/kubernetes/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ typedef struct guac_kubernetes_settings {
*/
bool recording_include_keys;

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
bool recording_write_existing;

/**
* The ASCII code, as an integer, that the Kubernetes client will use when
* the backspace key is pressed. By default, this is 127, ASCII delete, if
Expand Down
3 changes: 2 additions & 1 deletion src/protocols/rdp/rdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,8 @@ void* guac_rdp_client_thread(void* data) {
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
!settings->recording_exclude_touch,
settings->recording_include_keys);
settings->recording_include_keys,
settings->recording_write_existing);
}

/* Continue handling connections until error or client disconnect */
Expand Down
12 changes: 12 additions & 0 deletions src/protocols/rdp/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = {
"recording-exclude-touch",
"recording-include-keys",
"create-recording-path",
"recording-write-existing",
"resize-method",
"enable-audio-input",
"enable-touch",
Expand Down Expand Up @@ -564,6 +565,12 @@ enum RDP_ARGS_IDX {
*/
IDX_CREATE_RECORDING_PATH,

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
IDX_RECORDING_WRITE_EXISTING,

/**
* The method to use to apply screen size changes requested by the user.
* Valid values are blank, "display-update", and "reconnect".
Expand Down Expand Up @@ -1161,6 +1168,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, 0);

/* Parse allow write existing file flag */
settings->recording_write_existing =
guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
IDX_RECORDING_WRITE_EXISTING, 0);

/* No resize method */
if (strcmp(argv[IDX_RESIZE_METHOD], "") == 0) {
guac_user_log(user, GUAC_LOG_INFO, "Resize method: none");
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/rdp/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ typedef struct guac_rdp_settings {
int recording_include_keys;

/**
* Non-zero if existing files should be appended to when creating a new
* recording. Disabled by default.
*/
int recording_write_existing;

/**
* The method to apply when the user's display changes size.
*/
guac_rdp_resize_method resize_method;
Expand Down
12 changes: 12 additions & 0 deletions src/protocols/ssh/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const char* GUAC_SSH_CLIENT_ARGS[] = {
"recording-exclude-mouse",
"recording-include-keys",
"create-recording-path",
"recording-write-existing",
"read-only",
"server-alive-interval",
"backspace",
Expand Down Expand Up @@ -241,6 +242,12 @@ enum SSH_ARGS_IDX {
*/
IDX_CREATE_RECORDING_PATH,

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
IDX_RECORDING_WRITE_EXISTING,

/**
* "true" if this connection should be read-only (user input should be
* dropped), "false" or blank otherwise.
Expand Down Expand Up @@ -495,6 +502,11 @@ guac_ssh_settings* guac_ssh_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false);

/* Parse allow write existing file flag */
settings->recording_write_existing =
guac_user_parse_args_boolean(user, GUAC_SSH_CLIENT_ARGS, argv,
IDX_RECORDING_WRITE_EXISTING, false);

/* Parse server alive interval */
settings->server_alive_interval =
guac_user_parse_args_int(user, GUAC_SSH_CLIENT_ARGS, argv,
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/ssh/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ typedef struct guac_ssh_settings {
*/
bool recording_include_keys;

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
bool recording_write_existing;

/**
* The number of seconds between sending server alive messages.
*/
Expand Down
3 changes: 2 additions & 1 deletion src/protocols/ssh/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ void* ssh_client_thread(void* data) {
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys);
settings->recording_include_keys,
settings->recording_write_existing);
}

/* Create terminal options with required parameters */
Expand Down
12 changes: 12 additions & 0 deletions src/protocols/telnet/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const char* GUAC_TELNET_CLIENT_ARGS[] = {
"recording-exclude-mouse",
"recording-include-keys",
"create-recording-path",
"recording-write-existing",
"read-only",
"backspace",
"terminal-type",
Expand Down Expand Up @@ -193,6 +194,12 @@ enum TELNET_ARGS_IDX {
*/
IDX_CREATE_RECORDING_PATH,

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
IDX_RECORDING_WRITE_EXISTING,

/**
* "true" if this connection should be read-only (user input should be
* dropped), "false" or blank otherwise.
Expand Down Expand Up @@ -486,6 +493,11 @@ guac_telnet_settings* guac_telnet_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false);

/* Parse allow write existing file flag */
settings->recording_write_existing =
guac_user_parse_args_boolean(user, GUAC_TELNET_CLIENT_ARGS, argv,
IDX_RECORDING_WRITE_EXISTING, false);

/* Parse backspace key code */
settings->backspace =
guac_user_parse_args_int(user, GUAC_TELNET_CLIENT_ARGS, argv,
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/telnet/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ typedef struct guac_telnet_settings {
*/
bool recording_include_keys;

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
bool recording_write_existing;

/**
* The ASCII code, as an integer, that the telnet client will use when the
* backspace key is pressed. By default, this is 127, ASCII delete, if
Expand Down
3 changes: 2 additions & 1 deletion src/protocols/telnet/telnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,8 @@ void* guac_telnet_client_thread(void* data) {
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys);
settings->recording_include_keys,
settings->recording_write_existing);
}

/* Create terminal options with required parameters */
Expand Down
12 changes: 12 additions & 0 deletions src/protocols/vnc/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const char* GUAC_VNC_CLIENT_ARGS[] = {
"recording-exclude-mouse",
"recording-include-keys",
"create-recording-path",
"recording-write-existing",
"disable-copy",
"disable-paste",

Expand Down Expand Up @@ -331,6 +332,12 @@ enum VNC_ARGS_IDX {
*/
IDX_CREATE_RECORDING_PATH,

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
IDX_RECORDING_WRITE_EXISTING,

/**
* Whether outbound clipboard access should be blocked. If set to "true",
* it will not be possible to copy data from the remote desktop to the
Expand Down Expand Up @@ -594,6 +601,11 @@ guac_vnc_settings* guac_vnc_parse_args(guac_user* user,
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_CREATE_RECORDING_PATH, false);

/* Parse allow write existing file flag */
settings->recording_write_existing =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
IDX_RECORDING_WRITE_EXISTING, false);

/* Parse clipboard copy disable flag */
settings->disable_copy =
guac_user_parse_args_boolean(user, GUAC_VNC_CLIENT_ARGS, argv,
Expand Down
6 changes: 6 additions & 0 deletions src/protocols/vnc/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@ typedef struct guac_vnc_settings {
* as passwords, credit card numbers, etc.
*/
bool recording_include_keys;

/**
* Whether existing files should be appended to when creating a new recording.
* Disabled by default.
*/
bool recording_write_existing;

/**
* Whether or not to send the magic Wake-on-LAN (WoL) packet prior to
Expand Down
3 changes: 2 additions & 1 deletion src/protocols/vnc/vnc.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,8 @@ void* guac_vnc_client_thread(void* data) {
!settings->recording_exclude_output,
!settings->recording_exclude_mouse,
0, /* Touch events not supported */
settings->recording_include_keys);
settings->recording_include_keys,
settings->recording_write_existing);
}

/* Create display */
Expand Down

0 comments on commit 2844a7d

Please sign in to comment.