2013-12-29 04:53:12 +00:00
|
|
|
/*
|
2016-03-25 19:59:40 +00:00
|
|
|
* 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
|
2013-12-29 04:53:12 +00:00
|
|
|
*
|
2016-03-25 19:59:40 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2013-12-29 04:53:12 +00:00
|
|
|
*
|
2016-03-25 19:59:40 +00:00
|
|
|
* 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.
|
2013-12-29 04:53:12 +00:00
|
|
|
*/
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2014-01-01 22:44:28 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include "rdpdr_messages.h"
|
|
|
|
#include "rdpdr_printer.h"
|
|
|
|
#include "rdpdr_service.h"
|
|
|
|
#include "rdp_status.h"
|
|
|
|
|
|
|
|
#include <freerdp/utils/svc_plugin.h>
|
2014-06-11 01:45:14 +00:00
|
|
|
#include <guacamole/client.h>
|
2014-01-01 22:44:28 +00:00
|
|
|
#include <guacamole/protocol.h>
|
2016-06-09 20:57:22 +00:00
|
|
|
#include <guacamole/socket.h>
|
|
|
|
#include <guacamole/stream.h>
|
|
|
|
#include <guacamole/user.h>
|
2014-01-01 22:44:28 +00:00
|
|
|
|
2013-07-17 18:45:53 +00:00
|
|
|
#ifdef ENABLE_WINPR
|
|
|
|
#include <winpr/stream.h>
|
|
|
|
#else
|
|
|
|
#include "compat/winpr-stream.h"
|
|
|
|
#endif
|
|
|
|
|
2014-06-11 01:45:14 +00:00
|
|
|
#include <errno.h>
|
2016-06-09 20:57:22 +00:00
|
|
|
#include <stdint.h>
|
2014-06-11 01:45:14 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2013-06-28 18:26:28 +00:00
|
|
|
/* 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",
|
2015-02-02 15:45:04 +00:00
|
|
|
"-sstdout=/dev/null",
|
2013-06-28 18:26:28 +00:00
|
|
|
"-f",
|
|
|
|
"-",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/**
|
|
|
|
* 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) {
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
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));
|
|
|
|
|
|
|
|
}
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/* Note if stream aborted by user, fall through to termination */
|
|
|
|
else
|
|
|
|
guac_user_log(user, GUAC_LOG_INFO, "Print stream aborted.");
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/* Explicitly close down stream */
|
|
|
|
guac_protocol_send_end(user->socket, stream);
|
|
|
|
guac_socket_flush(user->socket);
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/* Clean up our end of the stream */
|
|
|
|
guac_user_free_stream(user, stream);
|
|
|
|
close(fd);
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
return 0;
|
2013-06-28 18:26:28 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
static int guac_rdpdr_create_print_process(guac_rdpdr_device* device) {
|
|
|
|
|
|
|
|
guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
|
2013-06-28 18:26:28 +00:00
|
|
|
|
|
|
|
int child_pid;
|
|
|
|
int stdin_pipe[2];
|
|
|
|
int stdout_pipe[2];
|
|
|
|
|
|
|
|
/* Create STDIN pipe */
|
|
|
|
if (pipe(stdin_pipe)) {
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
2013-07-24 22:52:46 +00:00
|
|
|
"Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
|
2013-06-28 18:26:28 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create STDOUT pipe */
|
|
|
|
if (pipe(stdout_pipe)) {
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
2013-07-24 22:52:46 +00:00
|
|
|
"Unable to create STDIN pipe for PDF filter process: %s", strerror(errno));
|
2013-06-28 18:26:28 +00:00
|
|
|
close(stdin_pipe[0]);
|
|
|
|
close(stdin_pipe[1]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store our side of stdin/stdout */
|
2013-07-24 22:52:46 +00:00
|
|
|
printer_data->printer_input = stdin_pipe[1];
|
|
|
|
printer_data->printer_output = stdout_pipe[0];
|
2013-06-28 18:26:28 +00:00
|
|
|
|
|
|
|
/* Fork child process */
|
|
|
|
child_pid = fork();
|
|
|
|
|
|
|
|
/* Log fork errors */
|
|
|
|
if (child_pid == -1) {
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
2013-07-24 22:52:46 +00:00
|
|
|
"Unable to fork PDF filter process: %s", strerror(errno));
|
2013-06-28 18:26:28 +00:00
|
|
|
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 */
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Running %s", guac_rdpdr_pdf_filter_command[0]);
|
2013-06-28 18:26:28 +00:00
|
|
|
if (execvp(guac_rdpdr_pdf_filter_command[0], guac_rdpdr_pdf_filter_command) < 0)
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command: %s", strerror(errno));
|
2013-06-28 18:26:28 +00:00
|
|
|
else
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Unable to execute PDF filter command, but no error given");
|
2013-06-28 18:26:28 +00:00
|
|
|
|
|
|
|
/* Terminate child process */
|
|
|
|
exit(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Log fork success */
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Created PDF filter process PID=%i", child_pid);
|
2013-06-28 18:26:28 +00:00
|
|
|
|
|
|
|
/* Close unneeded ends of pipe */
|
|
|
|
close(stdin_pipe[0]);
|
|
|
|
close(stdout_pipe[1]);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
void guac_rdpdr_process_print_job_create(guac_rdpdr_device* device,
|
|
|
|
wStream* input_stream, int completion_id) {
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-10-22 21:08:03 +00:00
|
|
|
guac_rdpdr_printer_data* printer_data =
|
|
|
|
(guac_rdpdr_printer_data*) device->data;
|
|
|
|
|
|
|
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
|
|
|
completion_id, STATUS_SUCCESS, 4);
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-06-25 00:49:23 +00:00
|
|
|
/* No bytes received yet */
|
2013-07-24 22:52:46 +00:00
|
|
|
printer_data->bytes_received = 0;
|
2013-07-17 18:45:53 +00:00
|
|
|
Stream_Write_UINT32(output_stream, 0); /* fileId */
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
2013-06-23 00:43:05 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
void guac_rdpdr_process_print_job_write(guac_rdpdr_device* device,
|
|
|
|
wStream* input_stream, int completion_id) {
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
guac_rdpdr_printer_data* printer_data = (guac_rdpdr_printer_data*) device->data;
|
2013-06-28 18:26:28 +00:00
|
|
|
int status=0, length;
|
2013-06-24 19:34:30 +00:00
|
|
|
unsigned char* buffer;
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-10-22 21:08:03 +00:00
|
|
|
wStream* output_stream;
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-07-17 18:45:53 +00:00
|
|
|
Stream_Read_UINT32(input_stream, length);
|
|
|
|
Stream_Seek(input_stream, 8); /* Offset */
|
|
|
|
Stream_Seek(input_stream, 20); /* Padding */
|
|
|
|
buffer = Stream_Pointer(input_stream);
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-06-25 00:49:23 +00:00
|
|
|
/* Create print job, if not yet created */
|
2013-07-24 22:52:46 +00:00
|
|
|
if (printer_data->bytes_received == 0) {
|
2013-06-25 00:49:23 +00:00
|
|
|
|
2013-06-28 18:26:28 +00:00
|
|
|
char filename[1024] = "guacamole-print.pdf";
|
2013-06-25 00:49:23 +00:00
|
|
|
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;
|
2013-06-28 18:26:28 +00:00
|
|
|
for (j=0; j<sizeof(filename) - 5 /* extension + 1 */ && i<length; i++, j++) {
|
2013-06-25 00:49:23 +00:00
|
|
|
|
|
|
|
/* Get character, stop at EOL */
|
|
|
|
char c = *(search++);
|
|
|
|
if (c == '\r' || c == '\n')
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Copy to filename */
|
|
|
|
filename[j] = c;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Append filename with extension */
|
2013-06-28 18:26:28 +00:00
|
|
|
strcpy(&(filename[j]), ".pdf");
|
2013-06-25 00:49:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Next character */
|
|
|
|
search++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-06-28 18:26:28 +00:00
|
|
|
/* Start print process */
|
2013-07-24 22:52:46 +00:00
|
|
|
if (guac_rdpdr_create_print_process(device) != 0) {
|
2013-07-26 21:11:44 +00:00
|
|
|
status = STATUS_DEVICE_OFF_LINE;
|
2013-06-28 18:26:28 +00:00
|
|
|
length = 0;
|
|
|
|
}
|
2013-06-25 00:49:23 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/* 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 */
|
2013-06-25 00:49:23 +00:00
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
printer_data->bytes_received += length;
|
2013-06-25 00:49:23 +00:00
|
|
|
|
2013-06-28 18:26:28 +00:00
|
|
|
/* If not yet failed, write received data */
|
|
|
|
if (status == 0) {
|
|
|
|
|
|
|
|
/* Write data to printer, translate output for RDP */
|
2013-07-24 22:52:46 +00:00
|
|
|
length = write(printer_data->printer_input, buffer, length);
|
2013-06-28 18:26:28 +00:00
|
|
|
if (length == -1) {
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR, "Error writing to printer: %s", strerror(errno));
|
2013-07-26 21:11:44 +00:00
|
|
|
status = STATUS_DEVICE_OFF_LINE;
|
2013-06-28 18:26:28 +00:00
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-10-22 21:08:03 +00:00
|
|
|
output_stream = guac_rdpdr_new_io_completion(device, completion_id,
|
|
|
|
status, 5);
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-07-17 18:45:53 +00:00
|
|
|
Stream_Write_UINT32(output_stream, length);
|
2013-10-22 21:08:03 +00:00
|
|
|
Stream_Write_UINT8(output_stream, 0); /* Padding */
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
2013-06-23 00:43:05 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
void guac_rdpdr_process_print_job_close(guac_rdpdr_device* device,
|
|
|
|
wStream* input_stream, int completion_id) {
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2013-10-22 21:08:03 +00:00
|
|
|
guac_rdpdr_printer_data* printer_data =
|
|
|
|
(guac_rdpdr_printer_data*) device->data;
|
|
|
|
|
|
|
|
wStream* output_stream = guac_rdpdr_new_io_completion(device,
|
2016-03-04 23:56:30 +00:00
|
|
|
completion_id, STATUS_SUCCESS, 4);
|
2013-10-22 21:08:03 +00:00
|
|
|
|
2016-03-04 23:56:30 +00:00
|
|
|
Stream_Write_UINT32(output_stream, 0); /* Padding */
|
2013-06-23 00:43:05 +00:00
|
|
|
|
2016-06-09 20:57:22 +00:00
|
|
|
/* Close input - the Guacamole stream will continue while output remains */
|
2013-07-24 22:52:46 +00:00
|
|
|
close(printer_data->printer_input);
|
2016-06-09 20:57:22 +00:00
|
|
|
printer_data->printer_input = -1;
|
2013-06-28 18:26:28 +00:00
|
|
|
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Print job closed");
|
2013-07-24 22:52:46 +00:00
|
|
|
svc_plugin_send((rdpSvcPlugin*) device->rdpdr, output_stream);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-25 01:28:20 +00:00
|
|
|
static void guac_rdpdr_device_printer_announce_handler(guac_rdpdr_device* device,
|
|
|
|
wStream* output_stream, int device_id) {
|
|
|
|
|
|
|
|
/* Printer header */
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_INFO, "Sending printer");
|
2013-07-25 01:28:20 +00:00
|
|
|
Stream_Write_UINT32(output_stream, RDPDR_DTYP_PRINT);
|
|
|
|
Stream_Write_UINT32(output_stream, device_id);
|
|
|
|
Stream_Write(output_stream, "PRN1\0\0\0\0", 8); /* DOS name */
|
|
|
|
|
|
|
|
/* Printer data */
|
|
|
|
Stream_Write_UINT32(output_stream, 24 + GUAC_PRINTER_DRIVER_LENGTH + GUAC_PRINTER_NAME_LENGTH);
|
|
|
|
Stream_Write_UINT32(output_stream,
|
|
|
|
RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER
|
|
|
|
| RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER);
|
|
|
|
Stream_Write_UINT32(output_stream, 0); /* reserved - must be 0 */
|
|
|
|
Stream_Write_UINT32(output_stream, 0); /* PnPName length (PnPName is ultimately ignored) */
|
|
|
|
Stream_Write_UINT32(output_stream, GUAC_PRINTER_DRIVER_LENGTH); /* DriverName length */
|
|
|
|
Stream_Write_UINT32(output_stream, GUAC_PRINTER_NAME_LENGTH); /* PrinterName length */
|
|
|
|
Stream_Write_UINT32(output_stream, 0); /* CachedFields length */
|
|
|
|
|
|
|
|
Stream_Write(output_stream, GUAC_PRINTER_DRIVER, GUAC_PRINTER_DRIVER_LENGTH);
|
|
|
|
Stream_Write(output_stream, GUAC_PRINTER_NAME, GUAC_PRINTER_NAME_LENGTH);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
static void guac_rdpdr_device_printer_iorequest_handler(guac_rdpdr_device* device,
|
|
|
|
wStream* input_stream, int file_id, int completion_id, int major_func, int minor_func) {
|
|
|
|
|
|
|
|
switch (major_func) {
|
|
|
|
|
|
|
|
/* Print job create */
|
|
|
|
case IRP_MJ_CREATE:
|
|
|
|
guac_rdpdr_process_print_job_create(device, input_stream, completion_id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Printer job write */
|
|
|
|
case IRP_MJ_WRITE:
|
|
|
|
guac_rdpdr_process_print_job_write(device, input_stream, completion_id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Printer job close */
|
|
|
|
case IRP_MJ_CLOSE:
|
|
|
|
guac_rdpdr_process_print_job_close(device, input_stream, completion_id);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Log unknown */
|
|
|
|
default:
|
2014-11-08 00:32:19 +00:00
|
|
|
guac_client_log(device->rdpdr->client, GUAC_LOG_ERROR,
|
2013-07-24 22:52:46 +00:00
|
|
|
"Unknown printer I/O request function: 0x%x/0x%x",
|
|
|
|
major_func, minor_func);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void guac_rdpdr_device_printer_free_handler(guac_rdpdr_device* device) {
|
2016-06-09 20:57:22 +00:00
|
|
|
|
|
|
|
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 */
|
2013-07-24 22:52:46 +00:00
|
|
|
free(device->data);
|
2016-06-09 20:57:22 +00:00
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void guac_rdpdr_register_printer(guac_rdpdrPlugin* rdpdr) {
|
|
|
|
|
2013-07-25 05:20:29 +00:00
|
|
|
int id = rdpdr->devices_registered++;
|
|
|
|
|
2013-07-24 22:52:46 +00:00
|
|
|
/* Get new device */
|
2013-07-25 05:20:29 +00:00
|
|
|
guac_rdpdr_device* device = &(rdpdr->devices[id]);
|
2013-09-24 19:58:58 +00:00
|
|
|
guac_rdpdr_printer_data* printer_data;
|
2013-07-24 22:52:46 +00:00
|
|
|
|
|
|
|
/* Init device */
|
2013-07-25 05:20:29 +00:00
|
|
|
device->rdpdr = rdpdr;
|
|
|
|
device->device_id = id;
|
|
|
|
device->device_name = "Guacamole Printer";
|
|
|
|
|
|
|
|
/* Set handlers */
|
2013-07-25 01:28:20 +00:00
|
|
|
device->announce_handler = guac_rdpdr_device_printer_announce_handler;
|
2013-07-24 22:52:46 +00:00
|
|
|
device->iorequest_handler = guac_rdpdr_device_printer_iorequest_handler;
|
|
|
|
device->free_handler = guac_rdpdr_device_printer_free_handler;
|
|
|
|
|
|
|
|
/* Init data */
|
2013-09-24 19:58:58 +00:00
|
|
|
printer_data = malloc(sizeof(guac_rdpdr_printer_data));
|
2016-06-09 20:57:22 +00:00
|
|
|
printer_data->printer_input = -1;
|
|
|
|
printer_data->printer_output = -1;
|
2013-09-24 19:58:58 +00:00
|
|
|
device->data = printer_data;
|
2013-06-23 00:43:05 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|