You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2017/02/16 04:52:50 UTC
[1/9] incubator-guacamole-server git commit: GUACAMOLE-200: Clean up
PostScript document title search logic.
Repository: incubator-guacamole-server
Updated Branches:
refs/heads/staging/0.9.12-incubating 4da4ce727 -> a7ee5edd8
GUACAMOLE-200: Clean up PostScript document title search logic.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/d23a22b7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/d23a22b7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/d23a22b7
Branch: refs/heads/staging/0.9.12-incubating
Commit: d23a22b7c6e966cde6a175b38cc81f005263483d
Parents: 3fc43fb
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Feb 13 18:42:03 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Feb 13 18:42:28 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c | 130 +++++++++++++++-----
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h | 6 +
2 files changed, 106 insertions(+), 30 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d23a22b7/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
index 8beaac7..c3cb03c 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
@@ -498,54 +498,124 @@ void* guac_rdpdr_print_job_alloc(guac_user* user, void* data) {
}
-int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
+/**
+ * Attempts to parse the given PostScript "%%Title:" header, storing the
+ * contents within the filename of the given print job. If the given buffer
+ * does not immediately begin with the "%%Title:" header, this function has no
+ * effect.
+ *
+ * @param job
+ * The job whose filename should be set if the "%%Title:" header is
+ * successfully parsed.
+ *
+ * @param buffer
+ * The buffer to parse as the "%%Title:" header.
+ *
+ * @param length
+ * The number of bytes within the buffer.
+ *
+ * @return
+ * Non-zero if the given buffer began with the "%%Title:" header and this
+ * header was successfully parsed, zero otherwise.
+ */
+static int guac_rdpdr_print_job_parse_title_header(guac_rdpdr_print_job* job,
void* buffer, int length) {
- /* Create print job, if not yet created */
- if (job->bytes_received == 0) {
+ int i;
+ char* current = buffer;
+ char* filename = job->filename;
- char* filename = job->filename;
- unsigned char* search = buffer;
- int i;
+ /* Verify that the buffer begins with "%%Title: " */
+ if (strncmp(current, "%%Title: ", 9) != 0)
+ return 0;
- /* Search for filename within buffer */
- for (i=0; i<length-9 && i < 2048; i++) {
+ /* Skip past "%%Title: " */
+ current += 9;
+ length -= 9;
- /* If title. use as filename */
- if (memcmp(search, "%%Title: ", 9) == 0) {
+ /* Calculate space remaining in filename */
+ int remaining = sizeof(job->filename) - 5 /* ".pdf\0" */;
- /* Skip past "%%Title: " */
- search += 9;
+ /* Do not exceed bounds of provided buffer */
+ if (length < remaining)
+ remaining = length;
- /* Copy as much of title as reasonable */
- int j;
- for (j=0; j < GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH - 5 /* extension + 1 */ && i<length; i++, j++) {
+ /* Copy as much of title as reasonable */
+ for (i = 0; i < remaining; i++) {
- /* Get character, stop at EOL */
- char c = *(search++);
- if (c == '\r' || c == '\n')
- break;
+ /* Get character, stop at EOL */
+ char c = *(current++);
+ if (c == '\r' || c == '\n')
+ break;
- /* Copy to filename */
- filename[j] = c;
+ /* Copy to filename */
+ *(filename++) = c;
- }
+ }
- /* Append filename with extension */
- strcpy(&(filename[j]), ".pdf");
- break;
- }
+ /* Append extension to filename */
+ strcpy(filename, ".pdf");
- /* Next character */
- search++;
+ /* Title successfully parsed */
+ return 1;
- }
+}
+
+/**
+ * Searches through the given buffer for PostScript headers denoting the title
+ * of the document, assigning the filename of the given print job using the
+ * discovered title. If no title can be found within
+ * GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH bytes, this function has no effect.
+ *
+ * @param job
+ * The job whose filename should be set if the document title can be found
+ * within the given buffer.
+ *
+ * @param buffer
+ * The buffer to search for the document title.
+ *
+ * @param length
+ * The number of bytes within the buffer.
+ */
+static void guac_rdpdr_print_job_read_filename(guac_rdpdr_print_job* job,
+ void* buffer, int length) {
+
+ char* current = buffer;
+ int i;
+
+ /* Restrict search area */
+ if (length > GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH)
+ length = GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH;
+
+ /* Search for document title within buffer */
+ for (i = 0; i < length; i++) {
+
+ /* If document title has been found, we're done */
+ if (guac_rdpdr_print_job_parse_title_header(job, current, length))
+ break;
+
+ /* Advance to next character */
+ length--;
+ current++;
+
+ }
+
+}
+
+int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
+ void* buffer, int length) {
+
+ /* Create print job, if not yet created */
+ if (job->bytes_received == 0) {
+
+ /* Attempt to read document title from first buffer of data */
+ guac_rdpdr_print_job_read_filename(job, buffer, length);
/* Begin print stream */
guac_client_for_user(job->client, job->user,
guac_rdpdr_print_job_begin_stream, job);
- } /* end if print job beginning */
+ }
/* Update counter of bytes received */
job->bytes_received += length;
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/d23a22b7/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
index 81b1863..97e3d62 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
@@ -42,6 +42,12 @@
#define GUAC_RDPDR_PRINT_JOB_DEFAULT_FILENAME "guacamole-print.pdf"
/**
+ * The maximum number of bytes to search through at the beginning of a
+ * PostScript document when locating the document title.
+ */
+#define GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH 2048
+
+/**
* The current state of an RDPDR print job.
*/
typedef enum guac_rdpdr_print_job_state {
[2/9] incubator-guacamole-server git commit: GUACAMOLE-200: Refactor
RDPDR printer such that the "ack" handler cannot block.
Posted by jm...@apache.org.
GUACAMOLE-200: Refactor RDPDR printer such that the "ack" handler cannot block.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/3fc43fba
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/3fc43fba
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/3fc43fba
Branch: refs/heads/staging/0.9.12-incubating
Commit: 3fc43fba37043cb1f00bde0cccbf194c87a8dbc7
Parents: 4da4ce7
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Feb 13 14:44:06 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Feb 13 18:42:28 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/Makefile.am | 2 +
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c | 581 ++++++++++++++++++++
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h | 226 ++++++++
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 359 ++----------
src/protocols/rdp/guac_rdpdr/rdpdr_printer.h | 31 --
5 files changed, 848 insertions(+), 351 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/3fc43fba/src/protocols/rdp/Makefile.am
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index a190003..7daf0e6 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -72,6 +72,7 @@ guacdr_sources = \
guac_rdpdr/rdpdr_fs_service.c \
guac_rdpdr/rdpdr_messages.c \
guac_rdpdr/rdpdr_printer.c \
+ guac_rdpdr/rdpdr_print_job.c \
guac_rdpdr/rdpdr_service.c \
rdp_fs.c \
rdp_stream.c \
@@ -89,6 +90,7 @@ noinst_HEADERS = \
guac_rdpdr/rdpdr_fs_service.h \
guac_rdpdr/rdpdr_messages.h \
guac_rdpdr/rdpdr_printer.h \
+ guac_rdpdr/rdpdr_print_job.h \
guac_rdpdr/rdpdr_service.h \
guac_rdpsnd/rdpsnd_messages.h \
guac_rdpsnd/rdpsnd_service.h \
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/3fc43fba/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
new file mode 100644
index 0000000..8beaac7
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
@@ -0,0 +1,581 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#include "config.h"
+#include "rdpdr_print_job.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * The command to run when filtering postscript to produce PDF. This must be
+ * a NULL-terminated array of arguments, where the first argument is the name
+ * of the file to run.
+ */
+char* const guac_rdpdr_pdf_filter_command[] = {
+ "gs",
+ "-q",
+ "-dNOPAUSE",
+ "-dBATCH",
+ "-dSAFER",
+ "-dPARANOIDSAFER",
+ "-sDEVICE=pdfwrite",
+ "-sOutputFile=-",
+ "-c",
+ ".setpdfwrite",
+ "-sstdout=/dev/null",
+ "-f",
+ "-",
+ NULL
+};
+
+/**
+ * Updates the state of the given print job. Any threads currently blocked by a
+ * call to guac_rdpdr_print_job_wait_for_ack() will be unblocked.
+ *
+ * @param job
+ * The print job whose state should be updated.
+ *
+ * @param state
+ * The new state to assign to the given print job.
+ */
+static void guac_rdpdr_print_job_set_state(guac_rdpdr_print_job* job,
+ guac_rdpdr_print_job_state state) {
+
+ pthread_mutex_lock(&(job->state_lock));
+
+ /* Update stream state, signalling modification */
+ job->state = state;
+ pthread_cond_signal(&(job->state_modified));
+
+ pthread_mutex_unlock(&(job->state_lock));
+
+}
+
+/**
+ * Suspends execution of the current thread until the state of the given print
+ * job is not GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK. If the state of the print
+ * job is GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED, the state is automatically reset
+ * back to GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK prior to returning.
+ *
+ * @param job
+ * The print job to wait for.
+ *
+ * @return
+ * Zero if the state of the print job is GUAC_RDPDR_PRINT_JOB_CLOSED,
+ * non-zero if the state was GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED and has been
+ * automatically reset to GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK.
+ */
+static int guac_rdpdr_print_job_wait_for_ack(guac_rdpdr_print_job* job) {
+
+ /* Wait for ack if stream open and not yet received */
+ pthread_mutex_lock(&(job->state_lock));
+ if (job->state == GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK)
+ pthread_cond_wait(&job->state_modified, &job->state_lock);
+
+ /* Reset state if ack received */
+ int got_ack = (job->state == GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED);
+ if (got_ack)
+ job->state = GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK;
+
+ /* Return whether ack was successfully received */
+ pthread_mutex_unlock(&(job->state_lock));
+ return got_ack;
+
+}
+
+/**
+ * Sends a "file" instruction to the given user describing the PDF file that
+ * will be sent using the output of the given print job. If the given user no
+ * longer exists, the print stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "file" instruction.
+ *
+ * @param data
+ * A pointer to the guac_rdpdr_print_job representing the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdpdr_print_job_begin_stream(guac_user* user, void* data) {
+
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Beginning print stream: %s",
+ job->filename);
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdpdr_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Send document as a PDF file stream */
+ guac_protocol_send_file(user->socket, job->stream,
+ "application/pdf", job->filename);
+
+ guac_socket_flush(user->socket);
+ return NULL;
+
+}
+
+/**
+ * Sends a "blob" instruction to the given user containing the provided data
+ * along the stream associated with the provided print job. If the given user
+ * no longer exists, the print stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "blob" instruction.
+ *
+ * @param data
+ * A pointer to an guac_rdpdr_print_blob structure containing the data to
+ * be written, the number of bytes being written, and the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdpdr_print_job_send_blob(guac_user* user, void* data) {
+
+ guac_rdpdr_print_blob* blob = (guac_rdpdr_print_blob*) data;
+ guac_rdpdr_print_job* job = blob->job;
+
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Sending %i byte(s) "
+ "of filtered output.", blob->length);
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdpdr_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Send single blob of print data */
+ guac_protocol_send_blob(user->socket, job->stream,
+ blob->buffer, blob->length);
+
+ guac_socket_flush(user->socket);
+ return NULL;
+
+}
+
+/**
+ * Sends an "end" instruction to the given user, closing the stream associated
+ * with the given print job. If the given user no longer exists, the print
+ * stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "end" instruction.
+ *
+ * @param data
+ * A pointer to the guac_rdpdr_print_job representing the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdpdr_print_job_end_stream(guac_user* user, void* data) {
+
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "End of print stream.");
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdpdr_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Explicitly close down stream */
+ guac_protocol_send_end(user->socket, job->stream);
+ guac_socket_flush(user->socket);
+
+ /* Clean up our end of the stream */
+ guac_user_free_stream(job->user, job->stream);
+
+ return NULL;
+
+}
+
+/**
+ * Handler for "ack" messages received in response to printed data. Additional
+ * data will be sent as a result or, if no data remains, the stream will be
+ * terminated. It is required that the data pointer of the provided stream be
+ * set to the file descriptor from which the printed data should be read.
+ *
+ * @param user
+ * The user to whom the printed data is being sent.
+ *
+ * @param stream
+ * The stream along which the printed data is to be sent. The data pointer
+ * of this stream MUST be set to the file descriptor from which the data
+ * being sent is to be read.
+ *
+ * @param message
+ * An arbitrary, human-readable message describing the success/failure of
+ * the operation being acknowledged (either stream creation or receipt of
+ * a blob).
+ *
+ * @param status
+ * The status code describing the success/failure of the operation being
+ * acknowledged (either stream creation or receipt of a blob).
+ *
+ * @return
+ * Always zero.
+ */
+static int guac_rdpdr_print_filter_ack_handler(guac_user* user,
+ guac_stream* stream, char* message, guac_protocol_status status) {
+
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) stream->data;
+
+ /* Update state for successful acks */
+ if (status == GUAC_PROTOCOL_STATUS_SUCCESS)
+ guac_rdpdr_print_job_set_state(job, GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED);
+
+ /* Terminate stream if ack signals an error */
+ else {
+
+ /* Note that the stream was aborted by the user */
+ guac_client_log(job->client, GUAC_LOG_INFO, "User explicitly aborted "
+ "print stream.");
+
+ /* Kill job (the results will no longer be received) */
+ guac_rdpdr_print_job_kill(job);
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * Forks a new print filtering process which accepts PostScript input and
+ * produces PDF output. File descriptors for writing input and reading output
+ * will automatically be allocated and must be manually closed when processing
+ * is complete.
+ *
+ * @param client
+ * The guac_client associated with the print job for which this filter
+ * process is being created.
+ *
+ * @param input_fd
+ * A pointer to an int which should receive the input file descriptor of
+ * the filter process. PostScript input for the filter process should be
+ * written to this file descriptor.
+ *
+ * @param output_fd
+ * A pointer to an int which should receive the output file descriptor of
+ * the filter process. PDF output from the filter process must be
+ * continuously read from this file descriptor or the pipeline may block.
+ *
+ * @return
+ * The PID of the filter process, or -1 if the filter process could not be
+ * created. If the filter process could not be created, the values assigned
+ * through input_fd and output_fd are undefined.
+ */
+static pid_t guac_rdpdr_create_filter_process(guac_client* client,
+ int* input_fd, int* output_fd) {
+
+ int child_pid;
+ int stdin_pipe[2];
+ int stdout_pipe[2];
+
+ /* Create STDIN pipe */
+ if (pipe(stdin_pipe)) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDIN "
+ "pipe for PDF filter process: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Create STDOUT pipe */
+ if (pipe(stdout_pipe)) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDOUT "
+ "pipe for PDF filter process: %s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+ return -1;
+ }
+
+ /* Store parent side of stdin/stdout */
+ *input_fd = stdin_pipe[1];
+ *output_fd = stdout_pipe[0];
+
+ /* Fork child process */
+ child_pid = fork();
+
+ /* Log fork errors */
+ if (child_pid == -1) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to fork PDF filter "
+ "process: %s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ return -1;
+ }
+
+ /* Child process */
+ if (child_pid == 0) {
+
+ /* Close unneeded ends of pipe */
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+
+ /* Reassign file descriptors as STDIN/STDOUT */
+ dup2(stdin_pipe[0], STDIN_FILENO);
+ dup2(stdout_pipe[1], STDOUT_FILENO);
+
+ /* Run PDF filter */
+ guac_client_log(client, GUAC_LOG_INFO, "Running %s",
+ guac_rdpdr_pdf_filter_command[0]);
+ if (execvp(guac_rdpdr_pdf_filter_command[0],
+ guac_rdpdr_pdf_filter_command) < 0)
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
+ "filter command: %s", strerror(errno));
+ else
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
+ "filter command, but no error given");
+
+ /* Terminate child process */
+ exit(1);
+
+ }
+
+ /* Log fork success */
+ guac_client_log(client, GUAC_LOG_INFO, "Created PDF filter process "
+ "PID=%i", child_pid);
+
+ /* Close unneeded ends of pipe */
+ close(stdin_pipe[0]);
+ close(stdout_pipe[1]);
+ return child_pid;
+
+}
+
+/**
+ * Thread which continuously reads from the output file descriptor associated
+ * with the given print job, writing filtered PDF output to the associated
+ * Guacamole stream, and terminating only after the print job has completed
+ * processing or the associated Guacamole stream has closed.
+ *
+ * @param data
+ * A pointer to the guac_rdpdr_print_job representing the print job that
+ * should be read.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdpdr_print_job_output_thread(void* data) {
+
+ int length;
+ char buffer[6048];
+
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Reading output from filter "
+ "process...");
+
+ /* Read continuously while data remains */
+ while ((length = read(job->output_fd, buffer, sizeof(buffer))) > 0) {
+
+ /* Wait for client to be ready for blob */
+ if (guac_rdpdr_print_job_wait_for_ack(job)) {
+
+ guac_rdpdr_print_blob blob = {
+ .job = job,
+ .buffer = buffer,
+ .length = length
+ };
+
+ /* Write a single blob of output */
+ guac_client_for_user(job->client, job->user,
+ guac_rdpdr_print_job_send_blob, &blob);
+
+ }
+
+ /* Abort if stream is closed */
+ else {
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Print stream "
+ "explicitly aborted.");
+ break;
+ }
+
+ }
+
+ /* Warn of read errors */
+ if (length < 0)
+ guac_client_log(job->client, GUAC_LOG_ERROR,
+ "Error reading from filter: %s", strerror(errno));
+
+ /* Terminate stream */
+ guac_client_for_user(job->client, job->user,
+ guac_rdpdr_print_job_end_stream, job);
+
+ /* Ensure all associated file descriptors are closed */
+ close(job->input_fd);
+ close(job->output_fd);
+
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Print job completed.");
+ return NULL;
+
+}
+
+void* guac_rdpdr_print_job_alloc(guac_user* user, void* data) {
+
+ /* Allocate nothing if user does not exist */
+ if (user == NULL)
+ return NULL;
+
+ /* Allocate stream for print job output */
+ guac_stream* stream = guac_user_alloc_stream(user);
+ if (stream == NULL)
+ return NULL;
+
+ /* Bail early if allocation fails */
+ guac_rdpdr_print_job* job = malloc(sizeof(guac_rdpdr_print_job));
+ if (job == NULL)
+ return NULL;
+
+ /* Associate job with stream and dependent data */
+ job->client = user->client;
+ job->user = user;
+ job->stream = stream;
+ job->bytes_received = 0;
+
+ /* Set default filename for job */
+ strcpy(job->filename, GUAC_RDPDR_PRINT_JOB_DEFAULT_FILENAME);
+
+ /* Prepare stream for receipt of acks */
+ stream->ack_handler = guac_rdpdr_print_filter_ack_handler;
+ stream->data = job;
+
+ /* Create print filter process */
+ job->filter_pid = guac_rdpdr_create_filter_process(job->client,
+ &job->input_fd, &job->output_fd);
+
+ /* Abort if print filter process cannot be created */
+ if (job->filter_pid == -1) {
+ guac_user_free_stream(user, stream);
+ free(job);
+ return NULL;
+ }
+
+ /* Init stream state signal and lock */
+ job->state = GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK;
+ pthread_cond_init(&job->state_modified, NULL);
+ pthread_mutex_init(&job->state_lock, NULL);
+
+ /* Start output thread */
+ pthread_create(&job->output_thread, NULL,
+ guac_rdpdr_print_job_output_thread, job);
+
+ /* Print job allocated successfully */
+ return job;
+
+}
+
+int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
+ void* buffer, int length) {
+
+ /* Create print job, if not yet created */
+ if (job->bytes_received == 0) {
+
+ char* filename = job->filename;
+ unsigned char* search = buffer;
+ int i;
+
+ /* Search for filename within buffer */
+ for (i=0; i<length-9 && i < 2048; i++) {
+
+ /* If title. use as filename */
+ if (memcmp(search, "%%Title: ", 9) == 0) {
+
+ /* Skip past "%%Title: " */
+ search += 9;
+
+ /* Copy as much of title as reasonable */
+ int j;
+ for (j=0; j < GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH - 5 /* extension + 1 */ && i<length; i++, j++) {
+
+ /* Get character, stop at EOL */
+ char c = *(search++);
+ if (c == '\r' || c == '\n')
+ break;
+
+ /* Copy to filename */
+ filename[j] = c;
+
+ }
+
+ /* Append filename with extension */
+ strcpy(&(filename[j]), ".pdf");
+ break;
+ }
+
+ /* Next character */
+ search++;
+
+ }
+
+ /* Begin print stream */
+ guac_client_for_user(job->client, job->user,
+ guac_rdpdr_print_job_begin_stream, job);
+
+ } /* end if print job beginning */
+
+ /* Update counter of bytes received */
+ job->bytes_received += length;
+
+ /* Write data to filter process */
+ return write(job->input_fd, buffer, length);
+
+}
+
+void guac_rdpdr_print_job_free(guac_rdpdr_print_job* job) {
+
+ /* No more input will be provided */
+ close(job->input_fd);
+
+ /* Wait for job to terminate */
+ pthread_join(job->output_thread, NULL);
+
+ /* Free base structure */
+ free(job);
+
+}
+
+void guac_rdpdr_print_job_kill(guac_rdpdr_print_job* job) {
+
+ /* Stop all handling of I/O */
+ close(job->input_fd);
+ close(job->output_fd);
+
+ /* Mark stream as closed */
+ guac_rdpdr_print_job_set_state(job, GUAC_RDPDR_PRINT_JOB_CLOSED);
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/3fc43fba/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
new file mode 100644
index 0000000..81b1863
--- /dev/null
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDPDR_PRINT_JOB_H
+#define GUAC_RDPDR_PRINT_JOB_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <pthread.h>
+#include <unistd.h>
+
+/**
+ * The maximum number of bytes in the filename of an RDPDR print job sent as a
+ * file over the Guacamole protocol, including NULL terminator.
+ */
+#define GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH 1024
+
+/**
+ * The default filename to use for the PDF output of an RDPDR print job if no
+ * document title can be found within the printed data.
+ */
+#define GUAC_RDPDR_PRINT_JOB_DEFAULT_FILENAME "guacamole-print.pdf"
+
+/**
+ * The current state of an RDPDR print job.
+ */
+typedef enum guac_rdpdr_print_job_state {
+
+ /**
+ * The print stream has been opened with the Guacamole client, but the
+ * client has not yet confirmed that it is ready to receive data.
+ */
+ GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK,
+
+ /**
+ * The print stream has been opened with the Guacamole client, and the
+ * client has responded with an "ack", confirming that it is ready to
+ * receive data (or that data has been received and it is ready to receive
+ * more).
+ */
+ GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED,
+
+ /**
+ * The print stream has been closed or the printer is terminating, and no
+ * further data should be sent to the client.
+ */
+ GUAC_RDPDR_PRINT_JOB_CLOSED
+
+} guac_rdpdr_print_job_state;
+
+/**
+ * Data specific to an instance of the printer device.
+ */
+typedef struct guac_rdpdr_print_job {
+
+ guac_client* client;
+
+ /**
+ * The user receiving the output from the print job.
+ */
+ guac_user* user;
+
+ /**
+ * The stream along which the print job output should be sent.
+ */
+ guac_stream* stream;
+
+ /**
+ * The PID of the print filter process converting PostScript data into PDF.
+ */
+ pid_t filter_pid;
+
+ /**
+ * The filename that should be used when the converted PDF output is
+ * streamed to the Guacamole user. This value will be automatically
+ * determined based on the contents of the printed document.
+ */
+ char filename[GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH];
+
+ /**
+ * File descriptor that should be written to when sending documents to the
+ * printer.
+ */
+ int input_fd;
+
+ /**
+ * File descriptor that should be read from when receiving output from the
+ * printer.
+ */
+ int output_fd;
+
+ /**
+ * The current state of the print stream, dependent on whether the client
+ * has acknowledged creation of the stream, whether the client has
+ * acknowledged receipt of data along the steam, and whether the print
+ * stream itself has closed.
+ */
+ guac_rdpdr_print_job_state state;
+
+ /**
+ * Lock which is acquired prior to modifying the state property or waiting
+ * on the state_modified conditional.
+ */
+ pthread_mutex_t state_lock;
+
+ /**
+ * Conditional which signals modification to the state property of this
+ * structure.
+ */
+ pthread_cond_t state_modified;
+
+ /**
+ * Thread which transfers data from the printer to the Guacamole client.
+ */
+ pthread_t output_thread;
+
+ /**
+ * The number of bytes received in the current print job.
+ */
+ int bytes_received;
+
+} guac_rdpdr_print_job;
+
+/**
+ * A blob of print data being sent to the Guacamole user.
+ */
+typedef struct guac_rdpdr_print_blob {
+
+ /**
+ * The print job which generated the data being sent.
+ */
+ guac_rdpdr_print_job* job;
+
+ /**
+ * The data being sent.
+ */
+ void* buffer;
+
+ /**
+ * The number of bytes of data being sent.
+ */
+ int length;
+
+} guac_rdpdr_print_blob;
+
+/**
+ * Allocates a new print job for the given user. It is expected that this
+ * function will be invoked via a call to guac_client_for_user() or
+ * guac_client_for_owner().
+ *
+ * @param user
+ * The user that should receive the output from the print job.
+ *
+ * @param data
+ * An arbitrary data parameter required by guac_client_for_user() and
+ * guac_client_for_owner() but ignored by this function. This should
+ * always be NULL.
+ *
+ * @return
+ * A pointer to a newly-allocated guac_rdpdr_print_job, or NULL if the
+ * print job could not be created.
+ */
+void* guac_rdpdr_print_job_alloc(guac_user* user, void* data);
+
+/**
+ * Writes PostScript print data to the given active print job. The print job
+ * will automatically convert this data to PDF, streaming the result to the
+ * Guacamole user associated with the print job. This function may block if
+ * the print job is not yet ready for more data.
+ *
+ * @param buffer
+ * The PostScript print data to write to the given print job.
+ *
+ * @param length
+ * The number of bytes of PostScript print data to write.
+ *
+ * @return
+ * The number of bytes written, or -1 if an error occurs which prevents
+ * further writes.
+ */
+int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
+ void* buffer, int length);
+
+/**
+ * Frees the memory associated with the given print job, closing all underlying
+ * file descriptors, and ending the file transfer to the associated Guacamole
+ * user. This function may block if the print filter process has not yet
+ * finished processing the received data.
+ *
+ * @param job
+ * The print job to free.
+ */
+void guac_rdpdr_print_job_free(guac_rdpdr_print_job* job);
+
+/**
+ * Forcibly kills the given print job, stopping all associated processing and
+ * streaming. The memory associated with the print job will still need to be
+ * reclaimed via guac_rdpdr_print_job_free().
+ *
+ * @param job
+ * The print job to kill.
+ */
+void guac_rdpdr_print_job_kill(guac_rdpdr_print_job* job);
+
+#endif
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/3fc43fba/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index 41c3663..cd7c517 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -21,6 +21,7 @@
#include "rdpdr_messages.h"
#include "rdpdr_printer.h"
+#include "rdpdr_print_job.h"
#include "rdpdr_service.h"
#include "rdp_status.h"
@@ -38,330 +39,60 @@
#endif
#include <errno.h>
+#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-/* Command to run GhostScript safely as a filter writing PDF */
-char* const guac_rdpdr_pdf_filter_command[] = {
- "gs",
- "-q",
- "-dNOPAUSE",
- "-dBATCH",
- "-dSAFER",
- "-dPARANOIDSAFER",
- "-sDEVICE=pdfwrite",
- "-sOutputFile=-",
- "-c",
- ".setpdfwrite",
- "-sstdout=/dev/null",
- "-f",
- "-",
- NULL
-};
-
-/**
- * Handler for "ack" messages received in response to printed data. Additional
- * data will be sent as a result or, if no data remains, the stream will be
- * terminated. It is required that the data pointer of the provided stream be
- * set to the file descriptor from which the printed data should be read.
- *
- * @param user
- * The user to whom the printed data is being sent.
- *
- * @param stream
- * The stream along which the printed data is to be sent. The data pointer
- * of this stream MUST be set to the file descriptor from which the data
- * being sent is to be read.
- *
- * @param message
- * An arbitrary, human-readable message describing the success/failure of
- * the operation being acknowledged (either stream creation or receipt of
- * a blob).
- *
- * @param status
- * The status code describing the success/failure of the operation being
- * acknowledged (either stream creation or receipt of a blob).
- *
- * @return
- * Always zero.
- */
-static int guac_rdpdr_print_filter_ack_handler(guac_user* user,
- guac_stream* stream, char* message, guac_protocol_status status) {
-
- char buffer[6048];
-
- /* Pull file descriptor from stream data */
- int fd = (intptr_t) stream->data;
-
- /* Reading only if ack reports success */
- if (status == GUAC_PROTOCOL_STATUS_SUCCESS) {
-
- /* Write a single blob of output */
- int length = read(fd, buffer, sizeof(buffer));
- if (length > 0) {
- guac_protocol_send_blob(user->socket, stream, buffer, length);
- guac_socket_flush(user->socket);
- return 0;
- }
-
- /* Warn of read errors, fall through to termination */
- else if (length < 0)
- guac_user_log(user, GUAC_LOG_ERROR,
- "Error reading from filter: %s", strerror(errno));
-
- }
-
- /* Note if stream aborted by user, fall through to termination */
- else
- guac_user_log(user, GUAC_LOG_INFO, "Print stream aborted.");
-
- /* Explicitly close down stream */
- guac_protocol_send_end(user->socket, stream);
- guac_socket_flush(user->socket);
-
- /* Clean up our end of the stream */
- guac_user_free_stream(user, stream);
- close(fd);
-
- return 0;
-
-}
-
-static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
-
- guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
-
- int child_pid;
- int stdin_pipe[2];
- int stdout_pipe[2];
-
- /* Create STDIN pipe */
- if (pipe(stdin_pipe)) {
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
- "Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
- return 1;
- }
-
- /* Create STDOUT pipe */
- if (pipe(stdout_pipe)) {
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
- "Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- return 1;
- }
-
- /* Store our side of stdin/stdout */
- printer_data->printer_input = stdin_pipe[1];
- printer_data->printer_output = stdout_pipe[0];
-
- /* Fork child process */
- child_pid = fork();
-
- /* Log fork errors */
- if (child_pid == -1) {
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
- "Unable to fork PDF filter process: %s", strerror(errno));
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- close(stdout_pipe[0]);
- close(stdout_pipe[1]);
- return 1;
- }
-
- /* Child process */
- if (child_pid == 0) {
-
- /* Close unneeded ends of pipe */
- close(stdin_pipe[1]);
- close(stdout_pipe[0]);
-
- /* Reassign file descriptors as STDIN/STDOUT */
- dup2(stdin_pipe[0], STDIN_FILENO);
- dup2(stdout_pipe[1], STDOUT_FILENO);
-
- /* Run PDF filter */
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Running %s", guac_rdpdr_pdf_filter_command[0]);
- if (execvp(guac_rdpdr_pdf_filter_command[0], guac_rdpdr_pdf_filter_command) < 0)
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command: %s", strerror(errno));
- else
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command, but no error given");
-
- /* Terminate child process */
- exit(1);
-
- }
-
- /* Log fork success */
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Created PDF filter process PID=%i", child_pid);
-
- /* Close unneeded ends of pipe */
- close(stdin_pipe[0]);
- close(stdout_pipe[1]);
- return 0;
-
-}
-
void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
- guac_rdpdr_printer_data* printer_data =
- (guac_rdpdr_printer_data*) device->data;
+ /* Log creation of print job */
+ guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job created");
+ /* Create print job */
+ device->data = guac_client_for_owner(device->rdpdr->client,
+ guac_rdpdr_print_job_alloc, NULL);
+
+ /* Respond with success */
wStream* output_stream = guac_rdpdr_new_io_completion(device,
completion_id, STATUS_SUCCESS, 4);
- /* No bytes received yet */
- printer_data->bytes_received = 0;
Stream_Write_UINT32(output_stream, 0); /* fileId */
-
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
}
-/**
- * Given data representing a print device with a pending pring job, allocates a
- * new stream for the given user, associating it with the provided data and
- * returning the resulting guac_stream. The stream will be pre-configured to
- * send blobs of print data in response to "ack" messages received from the
- * user. If the given user is NULL, no stream will be allocated, and the
- * print job will be immediately aborted.
- *
- * @param user
- * The user to whom the print job is being sent, or NULL if no stream
- * should be allocated.
- *
- * @param data
- * A pointer to the guac_rdpdr_device instance associated with the new
- * print job.
- *
- * @return
- * The guac_stream allocated for the new print job, or NULL if no stream
- * could be allocated.
- */
-static void* guac_rdpdr_alloc_printer_stream(guac_user* owner, void* data) {
-
- guac_rdpdr_device* device = (guac_rdpdr_device*) data;
- guac_rdpdr_printer_data* printer_data =
- (guac_rdpdr_printer_data*) device->data;
-
- /* Abort immediately if there is no owner */
- if (owner == NULL) {
- close(printer_data->printer_output);
- close(printer_data->printer_input);
- printer_data->printer_output = -1;
- printer_data->printer_input = -1;
- return NULL;
- }
-
- /* Allocate stream for owner */
- guac_stream* stream = guac_user_alloc_stream(owner);
- stream->ack_handler = guac_rdpdr_print_filter_ack_handler;
- stream->data = (void*) (intptr_t) printer_data->printer_output;
-
- return stream;
-
-}
-
void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
- guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
- int status=0, length;
- unsigned char* buffer;
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
- wStream* output_stream;
+ unsigned char* buffer;
+ int length;
+ int status;
+ /* Read buffer of print data */
Stream_Read_UINT32(input_stream, length);
Stream_Seek(input_stream, 8); /* Offset */
Stream_Seek(input_stream, 20); /* Padding */
buffer = Stream_Pointer(input_stream);
- /* Create print job, if not yet created */
- if (printer_data->bytes_received == 0) {
-
- char filename[1024] = "guacamole-print.pdf";
- unsigned char* search = buffer;
- int i;
-
- /* Search for filename within buffer */
- for (i=0; i<length-9 && i < 2048; i++) {
-
- /* If title. use as filename */
- if (memcmp(search, "%%Title: ", 9) == 0) {
-
- /* Skip past "%%Title: " */
- search += 9;
-
- /* Copy as much of title as reasonable */
- int j;
- for (j=0; j<sizeof(filename) - 5 /* extension + 1 */ && i<length; i++, j++) {
-
- /* Get character, stop at EOL */
- char c = *(search++);
- if (c == '\r' || c == '\n')
- break;
-
- /* Copy to filename */
- filename[j] = c;
-
- }
-
- /* Append filename with extension */
- strcpy(&(filename[j]), ".pdf");
- break;
- }
-
- /* Next character */
- search++;
-
- }
-
- /* Start print process */
- if (guac_rdpdr_create_print_process(device) != 0) {
- status = STATUS_DEVICE_OFF_LINE;
- length = 0;
- }
-
- /* If print started successfully, create outbound stream */
- else {
-
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO,
- "Print job created");
-
- /* Allocate stream */
- guac_stream* stream = (guac_stream*) guac_client_for_owner(
- device->rdpdr->client, guac_rdpdr_alloc_printer_stream,
- device);
-
- /* Begin file if stream allocation was successful */
- if (stream != NULL)
- guac_protocol_send_file(device->rdpdr->client->socket, stream,
- "application/pdf", filename);
-
- }
-
- } /* end if print job beginning */
-
- printer_data->bytes_received += length;
-
- /* If not yet failed, write received data */
- if (status == 0) {
-
- /* Write data to printer, translate output for RDP */
- length = write(printer_data->printer_input, buffer, length);
- if (length == -1) {
- guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Error writing to printer: %s", strerror(errno));
- status = STATUS_DEVICE_OFF_LINE;
- length = 0;
- }
+ /* Write data only if job exists, translating status for RDP */
+ if (job != NULL && (length = guac_rdpdr_print_job_write(job,
+ buffer, length)) >= 0) {
+ status = STATUS_SUCCESS;
+ }
+ /* Report device offline if write fails */
+ else {
+ status = STATUS_DEVICE_OFF_LINE;
+ length = 0;
}
- output_stream = guac_rdpdr_new_io_completion(device, completion_id,
- status, 5);
+ wStream* output_stream = guac_rdpdr_new_io_completion(device,
+ completion_id, status, 5);
Stream_Write_UINT32(output_stream, length);
Stream_Write_UINT8(output_stream, 0); /* Padding */
@@ -373,20 +104,21 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
- guac_rdpdr_printer_data* printer_data =
- (guac_rdpdr_printer_data*) device->data;
+ /* End print job */
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
+ if (job != NULL) {
+ guac_rdpdr_print_job_free(job);
+ device->data = NULL;
+ }
wStream* output_stream = guac_rdpdr_new_io_completion(device,
completion_id, STATUS_SUCCESS, 4);
Stream_Write_UINT32(output_stream, 0); /* Padding */
+ svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
- /* Close input - the Guacamole stream will continue while output remains */
- close(printer_data->printer_input);
- printer_data->printer_input = -1;
-
+ /* Log end of print job */
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job closed");
- svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
}
@@ -447,19 +179,10 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
- guac_rdpdr_printer_data* printer_data =
- (guac_rdpdr_printer_data*) device->data;
-
- /* Close print job input (STDIN for filter process) if open */
- if (printer_data->printer_input != -1)
- close(printer_data->printer_input);
-
- /* Close print job output (STDOUT for filter process) if open */
- if (printer_data->printer_output != -1)
- close(printer_data->printer_output);
-
- /* Free underlying data */
- free(device->data);
+ /* Close print job if open */
+ guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
+ if (job != NULL)
+ guac_rdpdr_print_job_free(job);
}
@@ -469,7 +192,6 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
/* Get new device */
guac_rdpdr_device* device = &(rdpdr->devices[id]);
- guac_rdpdr_printer_data* printer_data;
/* Init device */
device->rdpdr = rdpdr;
@@ -481,11 +203,8 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
device->iorequest_handler = guac_rdpdr_device_printer_iorequest_handler;
device->free_handler = guac_rdpdr_device_printer_free_handler;
- /* Init data */
- printer_data = malloc(sizeof(guac_rdpdr_printer_data));
- printer_data->printer_input = -1;
- printer_data->printer_output = -1;
- device->data = printer_data;
+ /* No active print job yet */
+ device->data = NULL;
}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/3fc43fba/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
index 72afa7a..6f203d6 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.h
@@ -32,41 +32,10 @@
#endif
/**
- * Data specific to an instance of the printer device.
- */
-typedef struct guac_rdpdr_printer_data {
-
- /**
- * File descriptor that should be written to when sending documents to the
- * printer. If no print job is in progress, this will be -1.
- */
- int printer_input;
-
- /**
- * File descriptor that should be read from when receiving output from the
- * printer. If no print job is in progress, this will be -1.
- */
- int printer_output;
-
- /**
- * The number of bytes received in the current print job.
- */
- int bytes_received;
-
-} guac_rdpdr_printer_data;
-
-/**
* Registers a new printer device within the RDPDR plugin. This must be done
* before RDPDR connection finishes.
*/
void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr);
-/**
- * The command to run when filtering postscript to produce PDF. This must be
- * a NULL-terminated array of arguments, where the first argument is the name
- * of the file to run.
- */
-extern char* const guac_rdpdr_pdf_filter_command[];
-
#endif
[9/9] incubator-guacamole-server git commit: GUACAMOLE-200: Merge fix
for print blocking input handling.
Posted by jm...@apache.org.
GUACAMOLE-200: Merge fix for print blocking input handling.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/a7ee5edd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/a7ee5edd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/a7ee5edd
Branch: refs/heads/staging/0.9.12-incubating
Commit: a7ee5edd8bb7e84893e30df918a64d54130b7fb2
Parents: 4da4ce7 12cde5d
Author: James Muehlner <ja...@guac-dev.org>
Authored: Wed Feb 15 20:50:11 2017 -0800
Committer: James Muehlner <ja...@guac-dev.org>
Committed: Wed Feb 15 20:50:11 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/Makefile.am | 3 +
src/protocols/rdp/client.c | 1 +
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 368 ++----------
src/protocols/rdp/guac_rdpdr/rdpdr_printer.h | 31 --
src/protocols/rdp/rdp.c | 7 +
src/protocols/rdp/rdp.h | 6 +
src/protocols/rdp/rdp_print_job.c | 651 ++++++++++++++++++++++
src/protocols/rdp/rdp_print_job.h | 235 ++++++++
8 files changed, 947 insertions(+), 355 deletions(-)
----------------------------------------------------------------------
[6/9] incubator-guacamole-server git commit: GUACAMOLE-200: Move
print job cleanup into main RDP client thread.
Posted by jm...@apache.org.
GUACAMOLE-200: Move print job cleanup into main RDP client thread.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/bf2a5885
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/bf2a5885
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/bf2a5885
Branch: refs/heads/staging/0.9.12-incubating
Commit: bf2a5885d05dca09ee4b8d73130e25e70d66a512
Parents: 5a68f93
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Feb 14 00:09:24 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Tue Feb 14 00:09:24 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/client.c | 7 -------
src/protocols/rdp/rdp.c | 7 +++++++
2 files changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/bf2a5885/src/protocols/rdp/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c
index 203bdb0..2dcb401 100644
--- a/src/protocols/rdp/client.c
+++ b/src/protocols/rdp/client.c
@@ -24,7 +24,6 @@
#include "rdp.h"
#include "rdp_disp.h"
#include "rdp_fs.h"
-#include "rdp_print_job.h"
#include "user.h"
#ifdef ENABLE_COMMON_SSH
@@ -107,12 +106,6 @@ int guac_rdp_client_free_handler(guac_client* client) {
if (rdp_client->filesystem != NULL)
guac_rdp_fs_free(rdp_client->filesystem);
- /* Clean up print job, if active */
- if (rdp_client->active_job != NULL) {
- guac_rdp_print_job_kill(rdp_client->active_job);
- guac_rdp_print_job_free(rdp_client->active_job);
- }
-
#ifdef ENABLE_COMMON_SSH
/* Free SFTP filesystem, if loaded */
if (rdp_client->sftp_filesystem)
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/bf2a5885/src/protocols/rdp/rdp.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c
index 389c704..a48230a 100644
--- a/src/protocols/rdp/rdp.c
+++ b/src/protocols/rdp/rdp.c
@@ -31,6 +31,7 @@
#include "rdp_cliprdr.h"
#include "rdp_disp.h"
#include "rdp_fs.h"
+#include "rdp_print_job.h"
#include "rdp_gdi.h"
#include "rdp_glyph.h"
#include "rdp_pointer.h"
@@ -853,6 +854,12 @@ static int guac_rdp_handle_connection(guac_client* client) {
}
+ /* Clean up print job, if active */
+ if (rdp_client->active_job != NULL) {
+ guac_rdp_print_job_kill(rdp_client->active_job);
+ guac_rdp_print_job_free(rdp_client->active_job);
+ }
+
pthread_mutex_lock(&(rdp_client->rdp_lock));
/* Disconnect client and channels */
[8/9] incubator-guacamole-server git commit: GUACAMOLE-200: Clear
reference to active job after the job has been freed.
Posted by jm...@apache.org.
GUACAMOLE-200: Clear reference to active job after the job has been freed.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/12cde5d5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/12cde5d5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/12cde5d5
Branch: refs/heads/staging/0.9.12-incubating
Commit: 12cde5d520a9b1cf9deedf6ad10696cb1720b235
Parents: 7a4b737
Author: Michael Jumper <mj...@apache.org>
Authored: Wed Feb 15 20:11:26 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Wed Feb 15 20:11:26 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/12cde5d5/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index 3733b01..40ac36f 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -117,7 +117,7 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
/* End print job */
if (job != NULL) {
guac_rdp_print_job_free(job);
- device->data = NULL;
+ rdp_client->active_job = NULL;
}
wStream* output_stream = guac_rdpdr_new_io_completion(device,
[5/9] incubator-guacamole-server git commit: GUACAMOLE-200: Maintain
print jobs at top level. Do not rely on proper free of RDPDR plugin.
Posted by jm...@apache.org.
GUACAMOLE-200: Maintain print jobs at top level. Do not rely on proper free of RDPDR plugin.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/5a68f932
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/5a68f932
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/5a68f932
Branch: refs/heads/staging/0.9.12-incubating
Commit: 5a68f932d6cc048c3ec9df8c48e64cb4e7a36202
Parents: 1537e47
Author: Michael Jumper <mj...@apache.org>
Authored: Tue Feb 14 00:02:17 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Tue Feb 14 00:02:17 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/client.c | 8 ++++++
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 31 +++++++++++------------
src/protocols/rdp/rdp.h | 6 +++++
3 files changed, 29 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/5a68f932/src/protocols/rdp/client.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/client.c b/src/protocols/rdp/client.c
index 2a9a690..203bdb0 100644
--- a/src/protocols/rdp/client.c
+++ b/src/protocols/rdp/client.c
@@ -23,6 +23,8 @@
#include "client.h"
#include "rdp.h"
#include "rdp_disp.h"
+#include "rdp_fs.h"
+#include "rdp_print_job.h"
#include "user.h"
#ifdef ENABLE_COMMON_SSH
@@ -105,6 +107,12 @@ int guac_rdp_client_free_handler(guac_client* client) {
if (rdp_client->filesystem != NULL)
guac_rdp_fs_free(rdp_client->filesystem);
+ /* Clean up print job, if active */
+ if (rdp_client->active_job != NULL) {
+ guac_rdp_print_job_kill(rdp_client->active_job);
+ guac_rdp_print_job_free(rdp_client->active_job);
+ }
+
#ifdef ENABLE_COMMON_SSH
/* Free SFTP filesystem, if loaded */
if (rdp_client->sftp_filesystem)
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/5a68f932/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index 812eaf0..3733b01 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -22,6 +22,7 @@
#include "rdpdr_messages.h"
#include "rdpdr_printer.h"
#include "rdpdr_service.h"
+#include "rdp.h"
#include "rdp_print_job.h"
#include "rdp_status.h"
@@ -48,11 +49,14 @@
void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
+ guac_client* client = device->rdpdr->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+
/* Log creation of print job */
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job created");
+ guac_client_log(client, GUAC_LOG_INFO, "Print job created");
/* Create print job */
- device->data = guac_client_for_owner(device->rdpdr->client,
+ rdp_client->active_job = guac_client_for_owner(client,
guac_rdp_print_job_alloc, NULL);
/* Respond with success */
@@ -67,7 +71,9 @@ void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
- guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
+ guac_client* client = device->rdpdr->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
unsigned char* buffer;
int length;
@@ -104,8 +110,11 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
+ guac_client* client = device->rdpdr->client;
+ guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
+ guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
+
/* End print job */
- guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
if (job != NULL) {
guac_rdp_print_job_free(job);
device->data = NULL;
@@ -118,7 +127,7 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
/* Log end of print job */
- guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job closed");
+ guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
}
@@ -178,14 +187,7 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
}
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
-
- /* Terminate and free print job if open */
- guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
- if (job != NULL) {
- guac_rdp_print_job_kill(job);
- guac_rdp_print_job_free(job);
- }
-
+ /* Do nothing */
}
void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
@@ -205,8 +207,5 @@ void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
device->iorequest_handler = guac_rdpdr_device_printer_iorequest_handler;
device->free_handler = guac_rdpdr_device_printer_free_handler;
- /* No active print job yet */
- device->data = NULL;
-
}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/5a68f932/src/protocols/rdp/rdp.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h
index af26408..e3e3311 100644
--- a/src/protocols/rdp/rdp.h
+++ b/src/protocols/rdp/rdp.h
@@ -30,6 +30,7 @@
#include "keyboard.h"
#include "rdp_disp.h"
#include "rdp_fs.h"
+#include "rdp_print_job.h"
#include "rdp_settings.h"
#include <freerdp/freerdp.h>
@@ -120,6 +121,11 @@ typedef struct guac_rdp_client {
*/
guac_rdp_fs* filesystem;
+ /**
+ * The currently-active print job, or NULL if no print job is active.
+ */
+ guac_rdp_print_job* active_job;
+
#ifdef ENABLE_COMMON_SSH
/**
* The user and credentials used to authenticate for SFTP.
[7/9] incubator-guacamole-server git commit: GUACAMOLE-200: Document
all parameters of guac_rdp_print_job_write().
Posted by jm...@apache.org.
GUACAMOLE-200: Document all parameters of guac_rdp_print_job_write().
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/7a4b737a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/7a4b737a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/7a4b737a
Branch: refs/heads/staging/0.9.12-incubating
Commit: 7a4b737a3ab07f43fd183625f5fdb8a58285376b
Parents: bf2a588
Author: Michael Jumper <mj...@apache.org>
Authored: Wed Feb 15 19:59:50 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Wed Feb 15 19:59:50 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/rdp_print_job.h | 3 +++
1 file changed, 3 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/7a4b737a/src/protocols/rdp/rdp_print_job.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp_print_job.h b/src/protocols/rdp/rdp_print_job.h
index aa879d9..5682f18 100644
--- a/src/protocols/rdp/rdp_print_job.h
+++ b/src/protocols/rdp/rdp_print_job.h
@@ -194,6 +194,9 @@ void* guac_rdp_print_job_alloc(guac_user* user, void* data);
* Guacamole user associated with the print job. This function may block if
* the print job is not yet ready for more data.
*
+ * @param job
+ * The print job to write to.
+ *
* @param buffer
* The PostScript print data to write to the given print job.
*
[4/9] incubator-guacamole-server git commit: GUACAMOLE-200: Refactor
RDPDR print job object to top-level.
Posted by jm...@apache.org.
GUACAMOLE-200: Refactor RDPDR print job object to top-level.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/1537e475
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/1537e475
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/1537e475
Branch: refs/heads/staging/0.9.12-incubating
Commit: 1537e475af475ff4e5388c573f9f83b4ef0a13ba
Parents: 17093a8
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Feb 13 23:51:33 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Feb 13 23:51:33 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/Makefile.am | 5 +-
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c | 651 --------------------
src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h | 232 -------
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 18 +-
src/protocols/rdp/rdp_print_job.c | 651 ++++++++++++++++++++
src/protocols/rdp/rdp_print_job.h | 232 +++++++
6 files changed, 895 insertions(+), 894 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/Makefile.am
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am
index 7daf0e6..c49afea 100644
--- a/src/protocols/rdp/Makefile.am
+++ b/src/protocols/rdp/Makefile.am
@@ -41,6 +41,7 @@ libguac_client_rdp_la_SOURCES = \
rdp_gdi.c \
rdp_glyph.c \
rdp_keymap.c \
+ rdp_print_job.c \
rdp_pointer.c \
rdp_rail.c \
rdp_settings.c \
@@ -72,9 +73,9 @@ guacdr_sources = \
guac_rdpdr/rdpdr_fs_service.c \
guac_rdpdr/rdpdr_messages.c \
guac_rdpdr/rdpdr_printer.c \
- guac_rdpdr/rdpdr_print_job.c \
guac_rdpdr/rdpdr_service.c \
rdp_fs.c \
+ rdp_print_job.c \
rdp_stream.c \
unicode.c
@@ -90,7 +91,6 @@ noinst_HEADERS = \
guac_rdpdr/rdpdr_fs_service.h \
guac_rdpdr/rdpdr_messages.h \
guac_rdpdr/rdpdr_printer.h \
- guac_rdpdr/rdpdr_print_job.h \
guac_rdpdr/rdpdr_service.h \
guac_rdpsnd/rdpsnd_messages.h \
guac_rdpsnd/rdpsnd_service.h \
@@ -111,6 +111,7 @@ noinst_HEADERS = \
rdp_glyph.h \
rdp_keymap.h \
rdp_pointer.h \
+ rdp_print_job.h \
rdp_rail.h \
rdp_settings.h \
rdp_status.h \
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
deleted file mode 100644
index c3cb03c..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.c
+++ /dev/null
@@ -1,651 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-
-#include "config.h"
-#include "rdpdr_print_job.h"
-
-#include <guacamole/client.h>
-#include <guacamole/protocol.h>
-#include <guacamole/socket.h>
-#include <guacamole/stream.h>
-#include <guacamole/user.h>
-
-#include <errno.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-/**
- * The command to run when filtering postscript to produce PDF. This must be
- * a NULL-terminated array of arguments, where the first argument is the name
- * of the file to run.
- */
-char* const guac_rdpdr_pdf_filter_command[] = {
- "gs",
- "-q",
- "-dNOPAUSE",
- "-dBATCH",
- "-dSAFER",
- "-dPARANOIDSAFER",
- "-sDEVICE=pdfwrite",
- "-sOutputFile=-",
- "-c",
- ".setpdfwrite",
- "-sstdout=/dev/null",
- "-f",
- "-",
- NULL
-};
-
-/**
- * Updates the state of the given print job. Any threads currently blocked by a
- * call to guac_rdpdr_print_job_wait_for_ack() will be unblocked.
- *
- * @param job
- * The print job whose state should be updated.
- *
- * @param state
- * The new state to assign to the given print job.
- */
-static void guac_rdpdr_print_job_set_state(guac_rdpdr_print_job* job,
- guac_rdpdr_print_job_state state) {
-
- pthread_mutex_lock(&(job->state_lock));
-
- /* Update stream state, signalling modification */
- job->state = state;
- pthread_cond_signal(&(job->state_modified));
-
- pthread_mutex_unlock(&(job->state_lock));
-
-}
-
-/**
- * Suspends execution of the current thread until the state of the given print
- * job is not GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK. If the state of the print
- * job is GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED, the state is automatically reset
- * back to GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK prior to returning.
- *
- * @param job
- * The print job to wait for.
- *
- * @return
- * Zero if the state of the print job is GUAC_RDPDR_PRINT_JOB_CLOSED,
- * non-zero if the state was GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED and has been
- * automatically reset to GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK.
- */
-static int guac_rdpdr_print_job_wait_for_ack(guac_rdpdr_print_job* job) {
-
- /* Wait for ack if stream open and not yet received */
- pthread_mutex_lock(&(job->state_lock));
- if (job->state == GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK)
- pthread_cond_wait(&job->state_modified, &job->state_lock);
-
- /* Reset state if ack received */
- int got_ack = (job->state == GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED);
- if (got_ack)
- job->state = GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK;
-
- /* Return whether ack was successfully received */
- pthread_mutex_unlock(&(job->state_lock));
- return got_ack;
-
-}
-
-/**
- * Sends a "file" instruction to the given user describing the PDF file that
- * will be sent using the output of the given print job. If the given user no
- * longer exists, the print stream will be automatically terminated.
- *
- * @param user
- * The user receiving the "file" instruction.
- *
- * @param data
- * A pointer to the guac_rdpdr_print_job representing the print job being
- * streamed.
- *
- * @return
- * Always NULL.
- */
-static void* guac_rdpdr_print_job_begin_stream(guac_user* user, void* data) {
-
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
- guac_client_log(job->client, GUAC_LOG_DEBUG, "Beginning print stream: %s",
- job->filename);
-
- /* Kill job and do nothing if user no longer exists */
- if (user == NULL) {
- guac_rdpdr_print_job_kill(job);
- return NULL;
- }
-
- /* Send document as a PDF file stream */
- guac_protocol_send_file(user->socket, job->stream,
- "application/pdf", job->filename);
-
- guac_socket_flush(user->socket);
- return NULL;
-
-}
-
-/**
- * Sends a "blob" instruction to the given user containing the provided data
- * along the stream associated with the provided print job. If the given user
- * no longer exists, the print stream will be automatically terminated.
- *
- * @param user
- * The user receiving the "blob" instruction.
- *
- * @param data
- * A pointer to an guac_rdpdr_print_blob structure containing the data to
- * be written, the number of bytes being written, and the print job being
- * streamed.
- *
- * @return
- * Always NULL.
- */
-static void* guac_rdpdr_print_job_send_blob(guac_user* user, void* data) {
-
- guac_rdpdr_print_blob* blob = (guac_rdpdr_print_blob*) data;
- guac_rdpdr_print_job* job = blob->job;
-
- guac_client_log(job->client, GUAC_LOG_DEBUG, "Sending %i byte(s) "
- "of filtered output.", blob->length);
-
- /* Kill job and do nothing if user no longer exists */
- if (user == NULL) {
- guac_rdpdr_print_job_kill(job);
- return NULL;
- }
-
- /* Send single blob of print data */
- guac_protocol_send_blob(user->socket, job->stream,
- blob->buffer, blob->length);
-
- guac_socket_flush(user->socket);
- return NULL;
-
-}
-
-/**
- * Sends an "end" instruction to the given user, closing the stream associated
- * with the given print job. If the given user no longer exists, the print
- * stream will be automatically terminated.
- *
- * @param user
- * The user receiving the "end" instruction.
- *
- * @param data
- * A pointer to the guac_rdpdr_print_job representing the print job being
- * streamed.
- *
- * @return
- * Always NULL.
- */
-static void* guac_rdpdr_print_job_end_stream(guac_user* user, void* data) {
-
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
- guac_client_log(job->client, GUAC_LOG_DEBUG, "End of print stream.");
-
- /* Kill job and do nothing if user no longer exists */
- if (user == NULL) {
- guac_rdpdr_print_job_kill(job);
- return NULL;
- }
-
- /* Explicitly close down stream */
- guac_protocol_send_end(user->socket, job->stream);
- guac_socket_flush(user->socket);
-
- /* Clean up our end of the stream */
- guac_user_free_stream(job->user, job->stream);
-
- return NULL;
-
-}
-
-/**
- * Handler for "ack" messages received in response to printed data. Additional
- * data will be sent as a result or, if no data remains, the stream will be
- * terminated. It is required that the data pointer of the provided stream be
- * set to the file descriptor from which the printed data should be read.
- *
- * @param user
- * The user to whom the printed data is being sent.
- *
- * @param stream
- * The stream along which the printed data is to be sent. The data pointer
- * of this stream MUST be set to the file descriptor from which the data
- * being sent is to be read.
- *
- * @param message
- * An arbitrary, human-readable message describing the success/failure of
- * the operation being acknowledged (either stream creation or receipt of
- * a blob).
- *
- * @param status
- * The status code describing the success/failure of the operation being
- * acknowledged (either stream creation or receipt of a blob).
- *
- * @return
- * Always zero.
- */
-static int guac_rdpdr_print_filter_ack_handler(guac_user* user,
- guac_stream* stream, char* message, guac_protocol_status status) {
-
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) stream->data;
-
- /* Update state for successful acks */
- if (status == GUAC_PROTOCOL_STATUS_SUCCESS)
- guac_rdpdr_print_job_set_state(job, GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED);
-
- /* Terminate stream if ack signals an error */
- else {
-
- /* Note that the stream was aborted by the user */
- guac_client_log(job->client, GUAC_LOG_INFO, "User explicitly aborted "
- "print stream.");
-
- /* Kill job (the results will no longer be received) */
- guac_rdpdr_print_job_kill(job);
-
- }
-
- return 0;
-
-}
-
-/**
- * Forks a new print filtering process which accepts PostScript input and
- * produces PDF output. File descriptors for writing input and reading output
- * will automatically be allocated and must be manually closed when processing
- * is complete.
- *
- * @param client
- * The guac_client associated with the print job for which this filter
- * process is being created.
- *
- * @param input_fd
- * A pointer to an int which should receive the input file descriptor of
- * the filter process. PostScript input for the filter process should be
- * written to this file descriptor.
- *
- * @param output_fd
- * A pointer to an int which should receive the output file descriptor of
- * the filter process. PDF output from the filter process must be
- * continuously read from this file descriptor or the pipeline may block.
- *
- * @return
- * The PID of the filter process, or -1 if the filter process could not be
- * created. If the filter process could not be created, the values assigned
- * through input_fd and output_fd are undefined.
- */
-static pid_t guac_rdpdr_create_filter_process(guac_client* client,
- int* input_fd, int* output_fd) {
-
- int child_pid;
- int stdin_pipe[2];
- int stdout_pipe[2];
-
- /* Create STDIN pipe */
- if (pipe(stdin_pipe)) {
- guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDIN "
- "pipe for PDF filter process: %s", strerror(errno));
- return -1;
- }
-
- /* Create STDOUT pipe */
- if (pipe(stdout_pipe)) {
- guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDOUT "
- "pipe for PDF filter process: %s", strerror(errno));
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- return -1;
- }
-
- /* Store parent side of stdin/stdout */
- *input_fd = stdin_pipe[1];
- *output_fd = stdout_pipe[0];
-
- /* Fork child process */
- child_pid = fork();
-
- /* Log fork errors */
- if (child_pid == -1) {
- guac_client_log(client, GUAC_LOG_ERROR, "Unable to fork PDF filter "
- "process: %s", strerror(errno));
- close(stdin_pipe[0]);
- close(stdin_pipe[1]);
- close(stdout_pipe[0]);
- close(stdout_pipe[1]);
- return -1;
- }
-
- /* Child process */
- if (child_pid == 0) {
-
- /* Close unneeded ends of pipe */
- close(stdin_pipe[1]);
- close(stdout_pipe[0]);
-
- /* Reassign file descriptors as STDIN/STDOUT */
- dup2(stdin_pipe[0], STDIN_FILENO);
- dup2(stdout_pipe[1], STDOUT_FILENO);
-
- /* Run PDF filter */
- guac_client_log(client, GUAC_LOG_INFO, "Running %s",
- guac_rdpdr_pdf_filter_command[0]);
- if (execvp(guac_rdpdr_pdf_filter_command[0],
- guac_rdpdr_pdf_filter_command) < 0)
- guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
- "filter command: %s", strerror(errno));
- else
- guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
- "filter command, but no error given");
-
- /* Terminate child process */
- exit(1);
-
- }
-
- /* Log fork success */
- guac_client_log(client, GUAC_LOG_INFO, "Created PDF filter process "
- "PID=%i", child_pid);
-
- /* Close unneeded ends of pipe */
- close(stdin_pipe[0]);
- close(stdout_pipe[1]);
- return child_pid;
-
-}
-
-/**
- * Thread which continuously reads from the output file descriptor associated
- * with the given print job, writing filtered PDF output to the associated
- * Guacamole stream, and terminating only after the print job has completed
- * processing or the associated Guacamole stream has closed.
- *
- * @param data
- * A pointer to the guac_rdpdr_print_job representing the print job that
- * should be read.
- *
- * @return
- * Always NULL.
- */
-static void* guac_rdpdr_print_job_output_thread(void* data) {
-
- int length;
- char buffer[6048];
-
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) data;
- guac_client_log(job->client, GUAC_LOG_DEBUG, "Reading output from filter "
- "process...");
-
- /* Read continuously while data remains */
- while ((length = read(job->output_fd, buffer, sizeof(buffer))) > 0) {
-
- /* Wait for client to be ready for blob */
- if (guac_rdpdr_print_job_wait_for_ack(job)) {
-
- guac_rdpdr_print_blob blob = {
- .job = job,
- .buffer = buffer,
- .length = length
- };
-
- /* Write a single blob of output */
- guac_client_for_user(job->client, job->user,
- guac_rdpdr_print_job_send_blob, &blob);
-
- }
-
- /* Abort if stream is closed */
- else {
- guac_client_log(job->client, GUAC_LOG_DEBUG, "Print stream "
- "explicitly aborted.");
- break;
- }
-
- }
-
- /* Warn of read errors */
- if (length < 0)
- guac_client_log(job->client, GUAC_LOG_ERROR,
- "Error reading from filter: %s", strerror(errno));
-
- /* Terminate stream */
- guac_client_for_user(job->client, job->user,
- guac_rdpdr_print_job_end_stream, job);
-
- /* Ensure all associated file descriptors are closed */
- close(job->input_fd);
- close(job->output_fd);
-
- guac_client_log(job->client, GUAC_LOG_DEBUG, "Print job completed.");
- return NULL;
-
-}
-
-void* guac_rdpdr_print_job_alloc(guac_user* user, void* data) {
-
- /* Allocate nothing if user does not exist */
- if (user == NULL)
- return NULL;
-
- /* Allocate stream for print job output */
- guac_stream* stream = guac_user_alloc_stream(user);
- if (stream == NULL)
- return NULL;
-
- /* Bail early if allocation fails */
- guac_rdpdr_print_job* job = malloc(sizeof(guac_rdpdr_print_job));
- if (job == NULL)
- return NULL;
-
- /* Associate job with stream and dependent data */
- job->client = user->client;
- job->user = user;
- job->stream = stream;
- job->bytes_received = 0;
-
- /* Set default filename for job */
- strcpy(job->filename, GUAC_RDPDR_PRINT_JOB_DEFAULT_FILENAME);
-
- /* Prepare stream for receipt of acks */
- stream->ack_handler = guac_rdpdr_print_filter_ack_handler;
- stream->data = job;
-
- /* Create print filter process */
- job->filter_pid = guac_rdpdr_create_filter_process(job->client,
- &job->input_fd, &job->output_fd);
-
- /* Abort if print filter process cannot be created */
- if (job->filter_pid == -1) {
- guac_user_free_stream(user, stream);
- free(job);
- return NULL;
- }
-
- /* Init stream state signal and lock */
- job->state = GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK;
- pthread_cond_init(&job->state_modified, NULL);
- pthread_mutex_init(&job->state_lock, NULL);
-
- /* Start output thread */
- pthread_create(&job->output_thread, NULL,
- guac_rdpdr_print_job_output_thread, job);
-
- /* Print job allocated successfully */
- return job;
-
-}
-
-/**
- * Attempts to parse the given PostScript "%%Title:" header, storing the
- * contents within the filename of the given print job. If the given buffer
- * does not immediately begin with the "%%Title:" header, this function has no
- * effect.
- *
- * @param job
- * The job whose filename should be set if the "%%Title:" header is
- * successfully parsed.
- *
- * @param buffer
- * The buffer to parse as the "%%Title:" header.
- *
- * @param length
- * The number of bytes within the buffer.
- *
- * @return
- * Non-zero if the given buffer began with the "%%Title:" header and this
- * header was successfully parsed, zero otherwise.
- */
-static int guac_rdpdr_print_job_parse_title_header(guac_rdpdr_print_job* job,
- void* buffer, int length) {
-
- int i;
- char* current = buffer;
- char* filename = job->filename;
-
- /* Verify that the buffer begins with "%%Title: " */
- if (strncmp(current, "%%Title: ", 9) != 0)
- return 0;
-
- /* Skip past "%%Title: " */
- current += 9;
- length -= 9;
-
- /* Calculate space remaining in filename */
- int remaining = sizeof(job->filename) - 5 /* ".pdf\0" */;
-
- /* Do not exceed bounds of provided buffer */
- if (length < remaining)
- remaining = length;
-
- /* Copy as much of title as reasonable */
- for (i = 0; i < remaining; i++) {
-
- /* Get character, stop at EOL */
- char c = *(current++);
- if (c == '\r' || c == '\n')
- break;
-
- /* Copy to filename */
- *(filename++) = c;
-
- }
-
- /* Append extension to filename */
- strcpy(filename, ".pdf");
-
- /* Title successfully parsed */
- return 1;
-
-}
-
-/**
- * Searches through the given buffer for PostScript headers denoting the title
- * of the document, assigning the filename of the given print job using the
- * discovered title. If no title can be found within
- * GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH bytes, this function has no effect.
- *
- * @param job
- * The job whose filename should be set if the document title can be found
- * within the given buffer.
- *
- * @param buffer
- * The buffer to search for the document title.
- *
- * @param length
- * The number of bytes within the buffer.
- */
-static void guac_rdpdr_print_job_read_filename(guac_rdpdr_print_job* job,
- void* buffer, int length) {
-
- char* current = buffer;
- int i;
-
- /* Restrict search area */
- if (length > GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH)
- length = GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH;
-
- /* Search for document title within buffer */
- for (i = 0; i < length; i++) {
-
- /* If document title has been found, we're done */
- if (guac_rdpdr_print_job_parse_title_header(job, current, length))
- break;
-
- /* Advance to next character */
- length--;
- current++;
-
- }
-
-}
-
-int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
- void* buffer, int length) {
-
- /* Create print job, if not yet created */
- if (job->bytes_received == 0) {
-
- /* Attempt to read document title from first buffer of data */
- guac_rdpdr_print_job_read_filename(job, buffer, length);
-
- /* Begin print stream */
- guac_client_for_user(job->client, job->user,
- guac_rdpdr_print_job_begin_stream, job);
-
- }
-
- /* Update counter of bytes received */
- job->bytes_received += length;
-
- /* Write data to filter process */
- return write(job->input_fd, buffer, length);
-
-}
-
-void guac_rdpdr_print_job_free(guac_rdpdr_print_job* job) {
-
- /* No more input will be provided */
- close(job->input_fd);
-
- /* Wait for job to terminate */
- pthread_join(job->output_thread, NULL);
-
- /* Free base structure */
- free(job);
-
-}
-
-void guac_rdpdr_print_job_kill(guac_rdpdr_print_job* job) {
-
- /* Stop all handling of I/O */
- close(job->input_fd);
- close(job->output_fd);
-
- /* Mark stream as closed */
- guac_rdpdr_print_job_set_state(job, GUAC_RDPDR_PRINT_JOB_CLOSED);
-
-}
-
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h b/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
deleted file mode 100644
index 97e3d62..0000000
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_print_job.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-#ifndef GUAC_RDPDR_PRINT_JOB_H
-#define GUAC_RDPDR_PRINT_JOB_H
-
-#include "config.h"
-
-#include <guacamole/client.h>
-#include <guacamole/stream.h>
-#include <guacamole/user.h>
-
-#include <pthread.h>
-#include <unistd.h>
-
-/**
- * The maximum number of bytes in the filename of an RDPDR print job sent as a
- * file over the Guacamole protocol, including NULL terminator.
- */
-#define GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH 1024
-
-/**
- * The default filename to use for the PDF output of an RDPDR print job if no
- * document title can be found within the printed data.
- */
-#define GUAC_RDPDR_PRINT_JOB_DEFAULT_FILENAME "guacamole-print.pdf"
-
-/**
- * The maximum number of bytes to search through at the beginning of a
- * PostScript document when locating the document title.
- */
-#define GUAC_RDPDR_PRINT_JOB_TITLE_SEARCH_LENGTH 2048
-
-/**
- * The current state of an RDPDR print job.
- */
-typedef enum guac_rdpdr_print_job_state {
-
- /**
- * The print stream has been opened with the Guacamole client, but the
- * client has not yet confirmed that it is ready to receive data.
- */
- GUAC_RDPDR_PRINT_JOB_WAITING_FOR_ACK,
-
- /**
- * The print stream has been opened with the Guacamole client, and the
- * client has responded with an "ack", confirming that it is ready to
- * receive data (or that data has been received and it is ready to receive
- * more).
- */
- GUAC_RDPDR_PRINT_JOB_ACK_RECEIVED,
-
- /**
- * The print stream has been closed or the printer is terminating, and no
- * further data should be sent to the client.
- */
- GUAC_RDPDR_PRINT_JOB_CLOSED
-
-} guac_rdpdr_print_job_state;
-
-/**
- * Data specific to an instance of the printer device.
- */
-typedef struct guac_rdpdr_print_job {
-
- guac_client* client;
-
- /**
- * The user receiving the output from the print job.
- */
- guac_user* user;
-
- /**
- * The stream along which the print job output should be sent.
- */
- guac_stream* stream;
-
- /**
- * The PID of the print filter process converting PostScript data into PDF.
- */
- pid_t filter_pid;
-
- /**
- * The filename that should be used when the converted PDF output is
- * streamed to the Guacamole user. This value will be automatically
- * determined based on the contents of the printed document.
- */
- char filename[GUAC_RDPDR_PRINT_JOB_FILENAME_MAX_LENGTH];
-
- /**
- * File descriptor that should be written to when sending documents to the
- * printer.
- */
- int input_fd;
-
- /**
- * File descriptor that should be read from when receiving output from the
- * printer.
- */
- int output_fd;
-
- /**
- * The current state of the print stream, dependent on whether the client
- * has acknowledged creation of the stream, whether the client has
- * acknowledged receipt of data along the steam, and whether the print
- * stream itself has closed.
- */
- guac_rdpdr_print_job_state state;
-
- /**
- * Lock which is acquired prior to modifying the state property or waiting
- * on the state_modified conditional.
- */
- pthread_mutex_t state_lock;
-
- /**
- * Conditional which signals modification to the state property of this
- * structure.
- */
- pthread_cond_t state_modified;
-
- /**
- * Thread which transfers data from the printer to the Guacamole client.
- */
- pthread_t output_thread;
-
- /**
- * The number of bytes received in the current print job.
- */
- int bytes_received;
-
-} guac_rdpdr_print_job;
-
-/**
- * A blob of print data being sent to the Guacamole user.
- */
-typedef struct guac_rdpdr_print_blob {
-
- /**
- * The print job which generated the data being sent.
- */
- guac_rdpdr_print_job* job;
-
- /**
- * The data being sent.
- */
- void* buffer;
-
- /**
- * The number of bytes of data being sent.
- */
- int length;
-
-} guac_rdpdr_print_blob;
-
-/**
- * Allocates a new print job for the given user. It is expected that this
- * function will be invoked via a call to guac_client_for_user() or
- * guac_client_for_owner().
- *
- * @param user
- * The user that should receive the output from the print job.
- *
- * @param data
- * An arbitrary data parameter required by guac_client_for_user() and
- * guac_client_for_owner() but ignored by this function. This should
- * always be NULL.
- *
- * @return
- * A pointer to a newly-allocated guac_rdpdr_print_job, or NULL if the
- * print job could not be created.
- */
-void* guac_rdpdr_print_job_alloc(guac_user* user, void* data);
-
-/**
- * Writes PostScript print data to the given active print job. The print job
- * will automatically convert this data to PDF, streaming the result to the
- * Guacamole user associated with the print job. This function may block if
- * the print job is not yet ready for more data.
- *
- * @param buffer
- * The PostScript print data to write to the given print job.
- *
- * @param length
- * The number of bytes of PostScript print data to write.
- *
- * @return
- * The number of bytes written, or -1 if an error occurs which prevents
- * further writes.
- */
-int guac_rdpdr_print_job_write(guac_rdpdr_print_job* job,
- void* buffer, int length);
-
-/**
- * Frees the memory associated with the given print job, closing all underlying
- * file descriptors, and ending the file transfer to the associated Guacamole
- * user. This function may block if the print filter process has not yet
- * finished processing the received data.
- *
- * @param job
- * The print job to free.
- */
-void guac_rdpdr_print_job_free(guac_rdpdr_print_job* job);
-
-/**
- * Forcibly kills the given print job, stopping all associated processing and
- * streaming. The memory associated with the print job will still need to be
- * reclaimed via guac_rdpdr_print_job_free().
- *
- * @param job
- * The print job to kill.
- */
-void guac_rdpdr_print_job_kill(guac_rdpdr_print_job* job);
-
-#endif
-
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index 0545f89..812eaf0 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -21,8 +21,8 @@
#include "rdpdr_messages.h"
#include "rdpdr_printer.h"
-#include "rdpdr_print_job.h"
#include "rdpdr_service.h"
+#include "rdp_print_job.h"
#include "rdp_status.h"
#include <freerdp/utils/svc_plugin.h>
@@ -53,7 +53,7 @@ void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
/* Create print job */
device->data = guac_client_for_owner(device->rdpdr->client,
- guac_rdpdr_print_job_alloc, NULL);
+ guac_rdp_print_job_alloc, NULL);
/* Respond with success */
wStream* output_stream = guac_rdpdr_new_io_completion(device,
@@ -67,7 +67,7 @@ void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
+ guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
unsigned char* buffer;
int length;
@@ -80,7 +80,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
buffer = Stream_Pointer(input_stream);
/* Write data only if job exists, translating status for RDP */
- if (job != NULL && (length = guac_rdpdr_print_job_write(job,
+ if (job != NULL && (length = guac_rdp_print_job_write(job,
buffer, length)) >= 0) {
status = STATUS_SUCCESS;
}
@@ -105,9 +105,9 @@ void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
wStream* input_stream, int completion_id) {
/* End print job */
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
+ guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
if (job != NULL) {
- guac_rdpdr_print_job_free(job);
+ guac_rdp_print_job_free(job);
device->data = NULL;
}
@@ -180,10 +180,10 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
/* Terminate and free print job if open */
- guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
+ guac_rdp_print_job* job = (guac_rdp_print_job*) device->data;
if (job != NULL) {
- guac_rdpdr_print_job_kill(job);
- guac_rdpdr_print_job_free(job);
+ guac_rdp_print_job_kill(job);
+ guac_rdp_print_job_free(job);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/rdp_print_job.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp_print_job.c b/src/protocols/rdp/rdp_print_job.c
new file mode 100644
index 0000000..a3b51bc
--- /dev/null
+++ b/src/protocols/rdp/rdp_print_job.c
@@ -0,0 +1,651 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+#include "config.h"
+#include "rdp_print_job.h"
+
+#include <guacamole/client.h>
+#include <guacamole/protocol.h>
+#include <guacamole/socket.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * The command to run when filtering postscript to produce PDF. This must be
+ * a NULL-terminated array of arguments, where the first argument is the name
+ * of the file to run.
+ */
+char* const guac_rdp_pdf_filter_command[] = {
+ "gs",
+ "-q",
+ "-dNOPAUSE",
+ "-dBATCH",
+ "-dSAFER",
+ "-dPARANOIDSAFER",
+ "-sDEVICE=pdfwrite",
+ "-sOutputFile=-",
+ "-c",
+ ".setpdfwrite",
+ "-sstdout=/dev/null",
+ "-f",
+ "-",
+ NULL
+};
+
+/**
+ * Updates the state of the given print job. Any threads currently blocked by a
+ * call to guac_rdp_print_job_wait_for_ack() will be unblocked.
+ *
+ * @param job
+ * The print job whose state should be updated.
+ *
+ * @param state
+ * The new state to assign to the given print job.
+ */
+static void guac_rdp_print_job_set_state(guac_rdp_print_job* job,
+ guac_rdp_print_job_state state) {
+
+ pthread_mutex_lock(&(job->state_lock));
+
+ /* Update stream state, signalling modification */
+ job->state = state;
+ pthread_cond_signal(&(job->state_modified));
+
+ pthread_mutex_unlock(&(job->state_lock));
+
+}
+
+/**
+ * Suspends execution of the current thread until the state of the given print
+ * job is not GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK. If the state of the print
+ * job is GUAC_RDP_PRINT_JOB_ACK_RECEIVED, the state is automatically reset
+ * back to GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK prior to returning.
+ *
+ * @param job
+ * The print job to wait for.
+ *
+ * @return
+ * Zero if the state of the print job is GUAC_RDP_PRINT_JOB_CLOSED,
+ * non-zero if the state was GUAC_RDP_PRINT_JOB_ACK_RECEIVED and has been
+ * automatically reset to GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK.
+ */
+static int guac_rdp_print_job_wait_for_ack(guac_rdp_print_job* job) {
+
+ /* Wait for ack if stream open and not yet received */
+ pthread_mutex_lock(&(job->state_lock));
+ if (job->state == GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK)
+ pthread_cond_wait(&job->state_modified, &job->state_lock);
+
+ /* Reset state if ack received */
+ int got_ack = (job->state == GUAC_RDP_PRINT_JOB_ACK_RECEIVED);
+ if (got_ack)
+ job->state = GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK;
+
+ /* Return whether ack was successfully received */
+ pthread_mutex_unlock(&(job->state_lock));
+ return got_ack;
+
+}
+
+/**
+ * Sends a "file" instruction to the given user describing the PDF file that
+ * will be sent using the output of the given print job. If the given user no
+ * longer exists, the print stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "file" instruction.
+ *
+ * @param data
+ * A pointer to the guac_rdp_print_job representing the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdp_print_job_begin_stream(guac_user* user, void* data) {
+
+ guac_rdp_print_job* job = (guac_rdp_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Beginning print stream: %s",
+ job->filename);
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdp_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Send document as a PDF file stream */
+ guac_protocol_send_file(user->socket, job->stream,
+ "application/pdf", job->filename);
+
+ guac_socket_flush(user->socket);
+ return NULL;
+
+}
+
+/**
+ * Sends a "blob" instruction to the given user containing the provided data
+ * along the stream associated with the provided print job. If the given user
+ * no longer exists, the print stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "blob" instruction.
+ *
+ * @param data
+ * A pointer to an guac_rdp_print_blob structure containing the data to
+ * be written, the number of bytes being written, and the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdp_print_job_send_blob(guac_user* user, void* data) {
+
+ guac_rdp_print_blob* blob = (guac_rdp_print_blob*) data;
+ guac_rdp_print_job* job = blob->job;
+
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Sending %i byte(s) "
+ "of filtered output.", blob->length);
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdp_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Send single blob of print data */
+ guac_protocol_send_blob(user->socket, job->stream,
+ blob->buffer, blob->length);
+
+ guac_socket_flush(user->socket);
+ return NULL;
+
+}
+
+/**
+ * Sends an "end" instruction to the given user, closing the stream associated
+ * with the given print job. If the given user no longer exists, the print
+ * stream will be automatically terminated.
+ *
+ * @param user
+ * The user receiving the "end" instruction.
+ *
+ * @param data
+ * A pointer to the guac_rdp_print_job representing the print job being
+ * streamed.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdp_print_job_end_stream(guac_user* user, void* data) {
+
+ guac_rdp_print_job* job = (guac_rdp_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "End of print stream.");
+
+ /* Kill job and do nothing if user no longer exists */
+ if (user == NULL) {
+ guac_rdp_print_job_kill(job);
+ return NULL;
+ }
+
+ /* Explicitly close down stream */
+ guac_protocol_send_end(user->socket, job->stream);
+ guac_socket_flush(user->socket);
+
+ /* Clean up our end of the stream */
+ guac_user_free_stream(job->user, job->stream);
+
+ return NULL;
+
+}
+
+/**
+ * Handler for "ack" messages received in response to printed data. Additional
+ * data will be sent as a result or, if no data remains, the stream will be
+ * terminated. It is required that the data pointer of the provided stream be
+ * set to the file descriptor from which the printed data should be read.
+ *
+ * @param user
+ * The user to whom the printed data is being sent.
+ *
+ * @param stream
+ * The stream along which the printed data is to be sent. The data pointer
+ * of this stream MUST be set to the file descriptor from which the data
+ * being sent is to be read.
+ *
+ * @param message
+ * An arbitrary, human-readable message describing the success/failure of
+ * the operation being acknowledged (either stream creation or receipt of
+ * a blob).
+ *
+ * @param status
+ * The status code describing the success/failure of the operation being
+ * acknowledged (either stream creation or receipt of a blob).
+ *
+ * @return
+ * Always zero.
+ */
+static int guac_rdp_print_filter_ack_handler(guac_user* user,
+ guac_stream* stream, char* message, guac_protocol_status status) {
+
+ guac_rdp_print_job* job = (guac_rdp_print_job*) stream->data;
+
+ /* Update state for successful acks */
+ if (status == GUAC_PROTOCOL_STATUS_SUCCESS)
+ guac_rdp_print_job_set_state(job, GUAC_RDP_PRINT_JOB_ACK_RECEIVED);
+
+ /* Terminate stream if ack signals an error */
+ else {
+
+ /* Note that the stream was aborted by the user */
+ guac_client_log(job->client, GUAC_LOG_INFO, "User explicitly aborted "
+ "print stream.");
+
+ /* Kill job (the results will no longer be received) */
+ guac_rdp_print_job_kill(job);
+
+ }
+
+ return 0;
+
+}
+
+/**
+ * Forks a new print filtering process which accepts PostScript input and
+ * produces PDF output. File descriptors for writing input and reading output
+ * will automatically be allocated and must be manually closed when processing
+ * is complete.
+ *
+ * @param client
+ * The guac_client associated with the print job for which this filter
+ * process is being created.
+ *
+ * @param input_fd
+ * A pointer to an int which should receive the input file descriptor of
+ * the filter process. PostScript input for the filter process should be
+ * written to this file descriptor.
+ *
+ * @param output_fd
+ * A pointer to an int which should receive the output file descriptor of
+ * the filter process. PDF output from the filter process must be
+ * continuously read from this file descriptor or the pipeline may block.
+ *
+ * @return
+ * The PID of the filter process, or -1 if the filter process could not be
+ * created. If the filter process could not be created, the values assigned
+ * through input_fd and output_fd are undefined.
+ */
+static pid_t guac_rdp_create_filter_process(guac_client* client,
+ int* input_fd, int* output_fd) {
+
+ int child_pid;
+ int stdin_pipe[2];
+ int stdout_pipe[2];
+
+ /* Create STDIN pipe */
+ if (pipe(stdin_pipe)) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDIN "
+ "pipe for PDF filter process: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Create STDOUT pipe */
+ if (pipe(stdout_pipe)) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to create STDOUT "
+ "pipe for PDF filter process: %s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+ return -1;
+ }
+
+ /* Store parent side of stdin/stdout */
+ *input_fd = stdin_pipe[1];
+ *output_fd = stdout_pipe[0];
+
+ /* Fork child process */
+ child_pid = fork();
+
+ /* Log fork errors */
+ if (child_pid == -1) {
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to fork PDF filter "
+ "process: %s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ return -1;
+ }
+
+ /* Child process */
+ if (child_pid == 0) {
+
+ /* Close unneeded ends of pipe */
+ close(stdin_pipe[1]);
+ close(stdout_pipe[0]);
+
+ /* Reassign file descriptors as STDIN/STDOUT */
+ dup2(stdin_pipe[0], STDIN_FILENO);
+ dup2(stdout_pipe[1], STDOUT_FILENO);
+
+ /* Run PDF filter */
+ guac_client_log(client, GUAC_LOG_INFO, "Running %s",
+ guac_rdp_pdf_filter_command[0]);
+ if (execvp(guac_rdp_pdf_filter_command[0],
+ guac_rdp_pdf_filter_command) < 0)
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
+ "filter command: %s", strerror(errno));
+ else
+ guac_client_log(client, GUAC_LOG_ERROR, "Unable to execute PDF "
+ "filter command, but no error given");
+
+ /* Terminate child process */
+ exit(1);
+
+ }
+
+ /* Log fork success */
+ guac_client_log(client, GUAC_LOG_INFO, "Created PDF filter process "
+ "PID=%i", child_pid);
+
+ /* Close unneeded ends of pipe */
+ close(stdin_pipe[0]);
+ close(stdout_pipe[1]);
+ return child_pid;
+
+}
+
+/**
+ * Thread which continuously reads from the output file descriptor associated
+ * with the given print job, writing filtered PDF output to the associated
+ * Guacamole stream, and terminating only after the print job has completed
+ * processing or the associated Guacamole stream has closed.
+ *
+ * @param data
+ * A pointer to the guac_rdp_print_job representing the print job that
+ * should be read.
+ *
+ * @return
+ * Always NULL.
+ */
+static void* guac_rdp_print_job_output_thread(void* data) {
+
+ int length;
+ char buffer[6048];
+
+ guac_rdp_print_job* job = (guac_rdp_print_job*) data;
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Reading output from filter "
+ "process...");
+
+ /* Read continuously while data remains */
+ while ((length = read(job->output_fd, buffer, sizeof(buffer))) > 0) {
+
+ /* Wait for client to be ready for blob */
+ if (guac_rdp_print_job_wait_for_ack(job)) {
+
+ guac_rdp_print_blob blob = {
+ .job = job,
+ .buffer = buffer,
+ .length = length
+ };
+
+ /* Write a single blob of output */
+ guac_client_for_user(job->client, job->user,
+ guac_rdp_print_job_send_blob, &blob);
+
+ }
+
+ /* Abort if stream is closed */
+ else {
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Print stream "
+ "explicitly aborted.");
+ break;
+ }
+
+ }
+
+ /* Warn of read errors */
+ if (length < 0)
+ guac_client_log(job->client, GUAC_LOG_ERROR,
+ "Error reading from filter: %s", strerror(errno));
+
+ /* Terminate stream */
+ guac_client_for_user(job->client, job->user,
+ guac_rdp_print_job_end_stream, job);
+
+ /* Ensure all associated file descriptors are closed */
+ close(job->input_fd);
+ close(job->output_fd);
+
+ guac_client_log(job->client, GUAC_LOG_DEBUG, "Print job completed.");
+ return NULL;
+
+}
+
+void* guac_rdp_print_job_alloc(guac_user* user, void* data) {
+
+ /* Allocate nothing if user does not exist */
+ if (user == NULL)
+ return NULL;
+
+ /* Allocate stream for print job output */
+ guac_stream* stream = guac_user_alloc_stream(user);
+ if (stream == NULL)
+ return NULL;
+
+ /* Bail early if allocation fails */
+ guac_rdp_print_job* job = malloc(sizeof(guac_rdp_print_job));
+ if (job == NULL)
+ return NULL;
+
+ /* Associate job with stream and dependent data */
+ job->client = user->client;
+ job->user = user;
+ job->stream = stream;
+ job->bytes_received = 0;
+
+ /* Set default filename for job */
+ strcpy(job->filename, GUAC_RDP_PRINT_JOB_DEFAULT_FILENAME);
+
+ /* Prepare stream for receipt of acks */
+ stream->ack_handler = guac_rdp_print_filter_ack_handler;
+ stream->data = job;
+
+ /* Create print filter process */
+ job->filter_pid = guac_rdp_create_filter_process(job->client,
+ &job->input_fd, &job->output_fd);
+
+ /* Abort if print filter process cannot be created */
+ if (job->filter_pid == -1) {
+ guac_user_free_stream(user, stream);
+ free(job);
+ return NULL;
+ }
+
+ /* Init stream state signal and lock */
+ job->state = GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK;
+ pthread_cond_init(&job->state_modified, NULL);
+ pthread_mutex_init(&job->state_lock, NULL);
+
+ /* Start output thread */
+ pthread_create(&job->output_thread, NULL,
+ guac_rdp_print_job_output_thread, job);
+
+ /* Print job allocated successfully */
+ return job;
+
+}
+
+/**
+ * Attempts to parse the given PostScript "%%Title:" header, storing the
+ * contents within the filename of the given print job. If the given buffer
+ * does not immediately begin with the "%%Title:" header, this function has no
+ * effect.
+ *
+ * @param job
+ * The job whose filename should be set if the "%%Title:" header is
+ * successfully parsed.
+ *
+ * @param buffer
+ * The buffer to parse as the "%%Title:" header.
+ *
+ * @param length
+ * The number of bytes within the buffer.
+ *
+ * @return
+ * Non-zero if the given buffer began with the "%%Title:" header and this
+ * header was successfully parsed, zero otherwise.
+ */
+static int guac_rdp_print_job_parse_title_header(guac_rdp_print_job* job,
+ void* buffer, int length) {
+
+ int i;
+ char* current = buffer;
+ char* filename = job->filename;
+
+ /* Verify that the buffer begins with "%%Title: " */
+ if (strncmp(current, "%%Title: ", 9) != 0)
+ return 0;
+
+ /* Skip past "%%Title: " */
+ current += 9;
+ length -= 9;
+
+ /* Calculate space remaining in filename */
+ int remaining = sizeof(job->filename) - 5 /* ".pdf\0" */;
+
+ /* Do not exceed bounds of provided buffer */
+ if (length < remaining)
+ remaining = length;
+
+ /* Copy as much of title as reasonable */
+ for (i = 0; i < remaining; i++) {
+
+ /* Get character, stop at EOL */
+ char c = *(current++);
+ if (c == '\r' || c == '\n')
+ break;
+
+ /* Copy to filename */
+ *(filename++) = c;
+
+ }
+
+ /* Append extension to filename */
+ strcpy(filename, ".pdf");
+
+ /* Title successfully parsed */
+ return 1;
+
+}
+
+/**
+ * Searches through the given buffer for PostScript headers denoting the title
+ * of the document, assigning the filename of the given print job using the
+ * discovered title. If no title can be found within
+ * GUAC_RDP_PRINT_JOB_TITLE_SEARCH_LENGTH bytes, this function has no effect.
+ *
+ * @param job
+ * The job whose filename should be set if the document title can be found
+ * within the given buffer.
+ *
+ * @param buffer
+ * The buffer to search for the document title.
+ *
+ * @param length
+ * The number of bytes within the buffer.
+ */
+static void guac_rdp_print_job_read_filename(guac_rdp_print_job* job,
+ void* buffer, int length) {
+
+ char* current = buffer;
+ int i;
+
+ /* Restrict search area */
+ if (length > GUAC_RDP_PRINT_JOB_TITLE_SEARCH_LENGTH)
+ length = GUAC_RDP_PRINT_JOB_TITLE_SEARCH_LENGTH;
+
+ /* Search for document title within buffer */
+ for (i = 0; i < length; i++) {
+
+ /* If document title has been found, we're done */
+ if (guac_rdp_print_job_parse_title_header(job, current, length))
+ break;
+
+ /* Advance to next character */
+ length--;
+ current++;
+
+ }
+
+}
+
+int guac_rdp_print_job_write(guac_rdp_print_job* job,
+ void* buffer, int length) {
+
+ /* Create print job, if not yet created */
+ if (job->bytes_received == 0) {
+
+ /* Attempt to read document title from first buffer of data */
+ guac_rdp_print_job_read_filename(job, buffer, length);
+
+ /* Begin print stream */
+ guac_client_for_user(job->client, job->user,
+ guac_rdp_print_job_begin_stream, job);
+
+ }
+
+ /* Update counter of bytes received */
+ job->bytes_received += length;
+
+ /* Write data to filter process */
+ return write(job->input_fd, buffer, length);
+
+}
+
+void guac_rdp_print_job_free(guac_rdp_print_job* job) {
+
+ /* No more input will be provided */
+ close(job->input_fd);
+
+ /* Wait for job to terminate */
+ pthread_join(job->output_thread, NULL);
+
+ /* Free base structure */
+ free(job);
+
+}
+
+void guac_rdp_print_job_kill(guac_rdp_print_job* job) {
+
+ /* Stop all handling of I/O */
+ close(job->input_fd);
+ close(job->output_fd);
+
+ /* Mark stream as closed */
+ guac_rdp_print_job_set_state(job, GUAC_RDP_PRINT_JOB_CLOSED);
+
+}
+
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/1537e475/src/protocols/rdp/rdp_print_job.h
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/rdp_print_job.h b/src/protocols/rdp/rdp_print_job.h
new file mode 100644
index 0000000..aa879d9
--- /dev/null
+++ b/src/protocols/rdp/rdp_print_job.h
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef GUAC_RDP_PRINT_JOB_H
+#define GUAC_RDP_PRINT_JOB_H
+
+#include "config.h"
+
+#include <guacamole/client.h>
+#include <guacamole/stream.h>
+#include <guacamole/user.h>
+
+#include <pthread.h>
+#include <unistd.h>
+
+/**
+ * The maximum number of bytes in the filename of an RDP print job sent as a
+ * file over the Guacamole protocol, including NULL terminator.
+ */
+#define GUAC_RDP_PRINT_JOB_FILENAME_MAX_LENGTH 1024
+
+/**
+ * The default filename to use for the PDF output of an RDP print job if no
+ * document title can be found within the printed data.
+ */
+#define GUAC_RDP_PRINT_JOB_DEFAULT_FILENAME "guacamole-print.pdf"
+
+/**
+ * The maximum number of bytes to search through at the beginning of a
+ * PostScript document when locating the document title.
+ */
+#define GUAC_RDP_PRINT_JOB_TITLE_SEARCH_LENGTH 2048
+
+/**
+ * The current state of an RDP print job.
+ */
+typedef enum guac_rdp_print_job_state {
+
+ /**
+ * The print stream has been opened with the Guacamole client, but the
+ * client has not yet confirmed that it is ready to receive data.
+ */
+ GUAC_RDP_PRINT_JOB_WAITING_FOR_ACK,
+
+ /**
+ * The print stream has been opened with the Guacamole client, and the
+ * client has responded with an "ack", confirming that it is ready to
+ * receive data (or that data has been received and it is ready to receive
+ * more).
+ */
+ GUAC_RDP_PRINT_JOB_ACK_RECEIVED,
+
+ /**
+ * The print stream has been closed or the printer is terminating, and no
+ * further data should be sent to the client.
+ */
+ GUAC_RDP_PRINT_JOB_CLOSED
+
+} guac_rdp_print_job_state;
+
+/**
+ * Data specific to an instance of the printer device.
+ */
+typedef struct guac_rdp_print_job {
+
+ guac_client* client;
+
+ /**
+ * The user receiving the output from the print job.
+ */
+ guac_user* user;
+
+ /**
+ * The stream along which the print job output should be sent.
+ */
+ guac_stream* stream;
+
+ /**
+ * The PID of the print filter process converting PostScript data into PDF.
+ */
+ pid_t filter_pid;
+
+ /**
+ * The filename that should be used when the converted PDF output is
+ * streamed to the Guacamole user. This value will be automatically
+ * determined based on the contents of the printed document.
+ */
+ char filename[GUAC_RDP_PRINT_JOB_FILENAME_MAX_LENGTH];
+
+ /**
+ * File descriptor that should be written to when sending documents to the
+ * printer.
+ */
+ int input_fd;
+
+ /**
+ * File descriptor that should be read from when receiving output from the
+ * printer.
+ */
+ int output_fd;
+
+ /**
+ * The current state of the print stream, dependent on whether the client
+ * has acknowledged creation of the stream, whether the client has
+ * acknowledged receipt of data along the steam, and whether the print
+ * stream itself has closed.
+ */
+ guac_rdp_print_job_state state;
+
+ /**
+ * Lock which is acquired prior to modifying the state property or waiting
+ * on the state_modified conditional.
+ */
+ pthread_mutex_t state_lock;
+
+ /**
+ * Conditional which signals modification to the state property of this
+ * structure.
+ */
+ pthread_cond_t state_modified;
+
+ /**
+ * Thread which transfers data from the printer to the Guacamole client.
+ */
+ pthread_t output_thread;
+
+ /**
+ * The number of bytes received in the current print job.
+ */
+ int bytes_received;
+
+} guac_rdp_print_job;
+
+/**
+ * A blob of print data being sent to the Guacamole user.
+ */
+typedef struct guac_rdp_print_blob {
+
+ /**
+ * The print job which generated the data being sent.
+ */
+ guac_rdp_print_job* job;
+
+ /**
+ * The data being sent.
+ */
+ void* buffer;
+
+ /**
+ * The number of bytes of data being sent.
+ */
+ int length;
+
+} guac_rdp_print_blob;
+
+/**
+ * Allocates a new print job for the given user. It is expected that this
+ * function will be invoked via a call to guac_client_for_user() or
+ * guac_client_for_owner().
+ *
+ * @param user
+ * The user that should receive the output from the print job.
+ *
+ * @param data
+ * An arbitrary data parameter required by guac_client_for_user() and
+ * guac_client_for_owner() but ignored by this function. This should
+ * always be NULL.
+ *
+ * @return
+ * A pointer to a newly-allocated guac_rdp_print_job, or NULL if the
+ * print job could not be created.
+ */
+void* guac_rdp_print_job_alloc(guac_user* user, void* data);
+
+/**
+ * Writes PostScript print data to the given active print job. The print job
+ * will automatically convert this data to PDF, streaming the result to the
+ * Guacamole user associated with the print job. This function may block if
+ * the print job is not yet ready for more data.
+ *
+ * @param buffer
+ * The PostScript print data to write to the given print job.
+ *
+ * @param length
+ * The number of bytes of PostScript print data to write.
+ *
+ * @return
+ * The number of bytes written, or -1 if an error occurs which prevents
+ * further writes.
+ */
+int guac_rdp_print_job_write(guac_rdp_print_job* job,
+ void* buffer, int length);
+
+/**
+ * Frees the memory associated with the given print job, closing all underlying
+ * file descriptors, and ending the file transfer to the associated Guacamole
+ * user. This function may block if the print filter process has not yet
+ * finished processing the received data.
+ *
+ * @param job
+ * The print job to free.
+ */
+void guac_rdp_print_job_free(guac_rdp_print_job* job);
+
+/**
+ * Forcibly kills the given print job, stopping all associated processing and
+ * streaming. The memory associated with the print job will still need to be
+ * reclaimed via guac_rdp_print_job_free().
+ *
+ * @param job
+ * The print job to kill.
+ */
+void guac_rdp_print_job_kill(guac_rdp_print_job* job);
+
+#endif
+
[3/9] incubator-guacamole-server git commit: GUACAMOLE-200: Kill any
remaining print job when connection closes.
Posted by jm...@apache.org.
GUACAMOLE-200: Kill any remaining print job when connection closes.
Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/commit/17093a81
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/tree/17093a81
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/diff/17093a81
Branch: refs/heads/staging/0.9.12-incubating
Commit: 17093a81491d871504d614bf211efe4346fbdeff
Parents: d23a22b
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Feb 13 23:08:54 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Mon Feb 13 23:08:54 2017 -0800
----------------------------------------------------------------------
src/protocols/rdp/guac_rdpdr/rdpdr_printer.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-guacamole-server/blob/17093a81/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
----------------------------------------------------------------------
diff --git a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
index cd7c517..0545f89 100644
--- a/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
+++ b/src/protocols/rdp/guac_rdpdr/rdpdr_printer.c
@@ -179,10 +179,12 @@ static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* devic
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
- /* Close print job if open */
+ /* Terminate and free print job if open */
guac_rdpdr_print_job* job = (guac_rdpdr_print_job*) device->data;
- if (job != NULL)
+ if (job != NULL) {
+ guac_rdpdr_print_job_kill(job);
guac_rdpdr_print_job_free(job);
+ }
}