Convert PS to PDF using gs as filter.
This commit is contained in:
parent
62258c9278
commit
400920b3bb
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <freerdp/utils/stream.h>
|
#include <freerdp/utils/stream.h>
|
||||||
#include <freerdp/utils/svc_plugin.h>
|
#include <freerdp/utils/svc_plugin.h>
|
||||||
|
|
||||||
@ -9,6 +11,127 @@
|
|||||||
#include "rdpdr_service.h"
|
#include "rdpdr_service.h"
|
||||||
#include "client.h"
|
#include "client.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",
|
||||||
|
"-f",
|
||||||
|
"-",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* guac_rdpdr_print_filter_output_thread(void* data) {
|
||||||
|
|
||||||
|
guac_rdpdrPlugin* rdpdr = (guac_rdpdrPlugin*) data;
|
||||||
|
rdp_guac_client_data* client_data = (rdp_guac_client_data*) rdpdr->client->data;
|
||||||
|
|
||||||
|
int length;
|
||||||
|
char buffer[8192];
|
||||||
|
|
||||||
|
/* Write all output as blobs */
|
||||||
|
while ((length = read(rdpdr->printer_output, buffer, sizeof(buffer))) > 0) {
|
||||||
|
pthread_mutex_lock(&(client_data->update_lock));
|
||||||
|
guac_protocol_send_blob(rdpdr->client->socket,
|
||||||
|
GUAC_RDPDR_PRINTER_BLOB, buffer, length);
|
||||||
|
pthread_mutex_unlock(&(client_data->update_lock));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log any error */
|
||||||
|
if (length < 0)
|
||||||
|
guac_client_log_error(rdpdr->client, "Error reading from filter: %s", strerror(errno));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int guac_rdpdr_create_print_process(guac_rdpdrPlugin* rdpdr) {
|
||||||
|
|
||||||
|
int child_pid;
|
||||||
|
int stdin_pipe[2];
|
||||||
|
int stdout_pipe[2];
|
||||||
|
|
||||||
|
/* Create STDIN pipe */
|
||||||
|
if (pipe(stdin_pipe)) {
|
||||||
|
guac_client_log_error(rdpdr->client, "Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create STDOUT pipe */
|
||||||
|
if (pipe(stdout_pipe)) {
|
||||||
|
guac_client_log_error(rdpdr->client, "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 */
|
||||||
|
rdpdr->printer_input = stdin_pipe[1];
|
||||||
|
rdpdr->printer_output = stdout_pipe[0];
|
||||||
|
|
||||||
|
/* Start output thread */
|
||||||
|
if (pthread_create(&(rdpdr->printer_output_thread), NULL, guac_rdpdr_print_filter_output_thread, rdpdr)) {
|
||||||
|
guac_client_log_error(rdpdr->client, "Unable to fork PDF filter process");
|
||||||
|
close(stdin_pipe[0]);
|
||||||
|
close(stdin_pipe[1]);
|
||||||
|
close(stdout_pipe[0]);
|
||||||
|
close(stdout_pipe[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fork child process */
|
||||||
|
child_pid = fork();
|
||||||
|
|
||||||
|
/* Log fork errors */
|
||||||
|
if (child_pid == -1) {
|
||||||
|
guac_client_log_error(rdpdr->client, "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_info(rdpdr->client, "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_error(rdpdr->client, "Unable to execute PDF filter command: %s", strerror(errno));
|
||||||
|
else
|
||||||
|
guac_client_log_error(rdpdr->client, "Unable to execute PDF filter command, but no error given");
|
||||||
|
|
||||||
|
/* Terminate child process */
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log fork success */
|
||||||
|
guac_client_log_info(rdpdr->client, "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_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id) {
|
void guac_rdpdr_process_print_job_create(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id) {
|
||||||
|
|
||||||
STREAM* output_stream = stream_new(24);
|
STREAM* output_stream = stream_new(24);
|
||||||
@ -23,7 +146,7 @@ void guac_rdpdr_process_print_job_create(guac_rdpdrPlugin* rdpdr, STREAM* input_
|
|||||||
/* Write content */
|
/* Write content */
|
||||||
stream_write_uint32(output_stream, GUAC_PRINTER_DEVICE_ID);
|
stream_write_uint32(output_stream, GUAC_PRINTER_DEVICE_ID);
|
||||||
stream_write_uint32(output_stream, completion_id);
|
stream_write_uint32(output_stream, completion_id);
|
||||||
stream_write_uint32(output_stream, 0); /* NTSTATUS - success */
|
stream_write_uint32(output_stream, 0); /* Success */
|
||||||
stream_write_uint32(output_stream, 0); /* fileId */
|
stream_write_uint32(output_stream, 0); /* fileId */
|
||||||
|
|
||||||
svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
|
svc_plugin_send((rdpSvcPlugin*) rdpdr, output_stream);
|
||||||
@ -32,7 +155,7 @@ void guac_rdpdr_process_print_job_create(guac_rdpdrPlugin* rdpdr, STREAM* input_
|
|||||||
|
|
||||||
void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id) {
|
void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id) {
|
||||||
|
|
||||||
int length;
|
int status=0, length;
|
||||||
unsigned char* buffer;
|
unsigned char* buffer;
|
||||||
|
|
||||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) rdpdr->client->data;
|
rdp_guac_client_data* client_data = (rdp_guac_client_data*) rdpdr->client->data;
|
||||||
@ -49,7 +172,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
/* Create print job, if not yet created */
|
/* Create print job, if not yet created */
|
||||||
if (rdpdr->bytes_received == 0) {
|
if (rdpdr->bytes_received == 0) {
|
||||||
|
|
||||||
char filename[1024] = "guacamole-print.ps";
|
char filename[1024] = "guacamole-print.pdf";
|
||||||
unsigned char* search = buffer;
|
unsigned char* search = buffer;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -64,7 +187,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
|
|
||||||
/* Copy as much of title as reasonable */
|
/* Copy as much of title as reasonable */
|
||||||
int j;
|
int j;
|
||||||
for (j=0; j<sizeof(filename) - 4 /* sizeof(".ps") + 1 */ && i<length; i++, j++) {
|
for (j=0; j<sizeof(filename) - 5 /* extension + 1 */ && i<length; i++, j++) {
|
||||||
|
|
||||||
/* Get character, stop at EOL */
|
/* Get character, stop at EOL */
|
||||||
char c = *(search++);
|
char c = *(search++);
|
||||||
@ -77,7 +200,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Append filename with extension */
|
/* Append filename with extension */
|
||||||
strcpy(&(filename[j]), ".ps");
|
strcpy(&(filename[j]), ".pdf");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,18 +209,36 @@ void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Begin file */
|
||||||
guac_client_log_info(rdpdr->client, "Print job created");
|
guac_client_log_info(rdpdr->client, "Print job created");
|
||||||
guac_protocol_send_file(rdpdr->client->socket,
|
guac_protocol_send_file(rdpdr->client->socket,
|
||||||
GUAC_RDPDR_PRINTER_BLOB, "application/postscript", filename);
|
GUAC_RDPDR_PRINTER_BLOB, "application/pdf", filename);
|
||||||
|
|
||||||
|
/* Start print process */
|
||||||
|
if (guac_rdpdr_create_print_process(rdpdr) != 0) {
|
||||||
|
status = 0x80000010;
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rdpdr->bytes_received += length;
|
rdpdr->bytes_received += length;
|
||||||
|
|
||||||
guac_protocol_send_blob(rdpdr->client->socket,
|
|
||||||
GUAC_RDPDR_PRINTER_BLOB, buffer, length);
|
|
||||||
pthread_mutex_unlock(&(client_data->update_lock));
|
pthread_mutex_unlock(&(client_data->update_lock));
|
||||||
|
|
||||||
|
/* If not yet failed, write received data */
|
||||||
|
if (status == 0) {
|
||||||
|
|
||||||
|
/* Write data to printer, translate output for RDP */
|
||||||
|
length = write(rdpdr->printer_input, buffer, length);
|
||||||
|
if (length == -1) {
|
||||||
|
guac_client_log_error(rdpdr->client, "Error writing to printer: %s", strerror(errno));
|
||||||
|
status = 0x80000010;
|
||||||
|
length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/* Write header */
|
/* Write header */
|
||||||
stream_write_uint16(output_stream, RDPDR_CTYP_CORE);
|
stream_write_uint16(output_stream, RDPDR_CTYP_CORE);
|
||||||
stream_write_uint16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
|
stream_write_uint16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION);
|
||||||
@ -105,7 +246,7 @@ void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
/* Write content */
|
/* Write content */
|
||||||
stream_write_uint32(output_stream, GUAC_PRINTER_DEVICE_ID);
|
stream_write_uint32(output_stream, GUAC_PRINTER_DEVICE_ID);
|
||||||
stream_write_uint32(output_stream, completion_id);
|
stream_write_uint32(output_stream, completion_id);
|
||||||
stream_write_uint32(output_stream, 0); /* NTSTATUS - success */
|
stream_write_uint32(output_stream, status);
|
||||||
stream_write_uint32(output_stream, length);
|
stream_write_uint32(output_stream, length);
|
||||||
stream_write_uint8(output_stream, 0); /* padding (stated as optional in spec, but requests fail without) */
|
stream_write_uint8(output_stream, 0); /* padding (stated as optional in spec, but requests fail without) */
|
||||||
|
|
||||||
@ -118,6 +259,13 @@ void guac_rdpdr_process_print_job_close(guac_rdpdrPlugin* rdpdr, STREAM* input_s
|
|||||||
rdp_guac_client_data* client_data = (rdp_guac_client_data*) rdpdr->client->data;
|
rdp_guac_client_data* client_data = (rdp_guac_client_data*) rdpdr->client->data;
|
||||||
STREAM* output_stream = stream_new(24);
|
STREAM* output_stream = stream_new(24);
|
||||||
|
|
||||||
|
/* Close input and wait for output thread to finish */
|
||||||
|
close(rdpdr->printer_input);
|
||||||
|
pthread_join(rdpdr->printer_output_thread, NULL);
|
||||||
|
|
||||||
|
/* Close file descriptors */
|
||||||
|
close(rdpdr->printer_output);
|
||||||
|
|
||||||
/* Close file */
|
/* Close file */
|
||||||
guac_client_log_info(rdpdr->client, "Print job closed");
|
guac_client_log_info(rdpdr->client, "Print job closed");
|
||||||
pthread_mutex_lock(&(client_data->update_lock));
|
pthread_mutex_lock(&(client_data->update_lock));
|
||||||
|
@ -54,5 +54,12 @@ void guac_rdpdr_process_print_job_create(guac_rdpdrPlugin* rdpdr, STREAM* input_
|
|||||||
void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id);
|
void guac_rdpdr_process_print_job_write(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id);
|
||||||
void guac_rdpdr_process_print_job_close(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id);
|
void guac_rdpdr_process_print_job_close(guac_rdpdrPlugin* rdpdr, STREAM* input_stream, int completion_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
#endif
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@
|
|||||||
#ifndef __GUAC_RDPDR_SERVICE_H
|
#ifndef __GUAC_RDPDR_SERVICE_H
|
||||||
#define __GUAC_RDPDR_SERVICE_H
|
#define __GUAC_RDPDR_SERVICE_H
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <guacamole/client.h>
|
#include <guacamole/client.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,6 +60,23 @@ typedef struct guac_rdpdrPlugin {
|
|||||||
*/
|
*/
|
||||||
guac_client* client;
|
guac_client* client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File descriptor that should be written to when sending documents to the
|
||||||
|
* printer.
|
||||||
|
*/
|
||||||
|
int printer_input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File descriptor that should be read from when receiving output from the
|
||||||
|
* printer.
|
||||||
|
*/
|
||||||
|
int printer_output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread which transfers data from the printer to the Guacamole client.
|
||||||
|
*/
|
||||||
|
pthread_t printer_output_thread;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of bytes received in the current print job.
|
* The number of bytes received in the current print job.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user