diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /channels/printer/client/cups/printer_cups.c | |
parent | Initial commit. (diff) | |
download | freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip |
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'channels/printer/client/cups/printer_cups.c')
-rw-r--r-- | channels/printer/client/cups/printer_cups.c | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/channels/printer/client/cups/printer_cups.c b/channels/printer/client/cups/printer_cups.c new file mode 100644 index 0000000..043b44e --- /dev/null +++ b/channels/printer/client/cups/printer_cups.c @@ -0,0 +1,462 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Print Virtual Channel - CUPS driver + * + * Copyright 2010-2011 Vic Lee + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * Copyright 2016 Armin Novak <armin.novak@gmail.com> + * + * Licensed 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 <winpr/assert.h> + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <time.h> +#include <cups/cups.h> + +#include <winpr/crt.h> +#include <winpr/file.h> +#include <winpr/string.h> + +#include <freerdp/channels/rdpdr.h> + +#include <freerdp/client/printer.h> + +#include <freerdp/channels/log.h> +#define TAG CHANNELS_TAG("printer.client.cups") + +#if defined(__APPLE__) +#include <errno.h> +#include <sys/sysctl.h> + +static bool is_mac_os_sonoma_or_later(void) +{ + char str[256] = { 0 }; + size_t size = sizeof(str); + + errno = 0; + int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0); + if (ret != 0) + { + char buffer[256] = { 0 }; + WLog_WARN(TAG, "sysctlbyname('kern.osrelease') failed with %s [%d]", + winpr_strerror(errno, buffer, sizeof(buffer)), errno); + return false; + } + + int major = 0; + int minor = 0; + int patch = 0; + const int rc = sscanf(str, "%d.%d.%d", &major, &minor, &patch); + if (rc != 3) + { + WLog_WARN(TAG, "could not match '%s' to format '%d.%d.%d'"); + return false; + } + + if (major < 23) + return false; + return true; +} +#endif + +typedef struct +{ + rdpPrinterDriver driver; + + size_t id_sequence; + size_t references; +} rdpCupsPrinterDriver; + +typedef struct +{ + rdpPrintJob printjob; + + http_t* printjob_object; + int printjob_id; +} rdpCupsPrintJob; + +typedef struct +{ + rdpPrinter printer; + + rdpCupsPrintJob* printjob; +} rdpCupsPrinter; + +static void printer_cups_get_printjob_name(char* buf, size_t size, size_t id) +{ + struct tm tres; + const time_t tt = time(NULL); + const struct tm* t = localtime_r(&tt, &tres); + + WINPR_ASSERT(buf); + WINPR_ASSERT(size > 0); + + sprintf_s(buf, size - 1, "FreeRDP Print %04d-%02d-%02d %02d-%02d-%02d - Job %" PRIdz, + t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, id); +} + +static bool http_status_ok(http_status_t status) +{ + switch (status) + { + case HTTP_OK: + case HTTP_CONTINUE: + return true; + default: + return false; + } +} + +static UINT write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size) +{ + rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob; + + WINPR_ASSERT(cups_printjob); + + http_status_t rc = + cupsWriteRequestData(cups_printjob->printjob_object, (const char*)data, size); + if (!http_status_ok(rc)) + WLog_WARN(TAG, "cupsWriteRequestData returned %s", httpStatus(rc)); + + return CHANNEL_RC_OK; +} +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT printer_cups_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size) +{ + rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob; + + WINPR_ASSERT(cups_printjob); + + return write_printjob(printjob, data, size); +} + +static void printer_cups_close_printjob(rdpPrintJob* printjob) +{ + rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob; + rdpCupsPrinter* cups_printer = NULL; + + WINPR_ASSERT(cups_printjob); + + ipp_status_t rc = cupsFinishDocument(cups_printjob->printjob_object, printjob->printer->name); + if (rc != IPP_OK) + WLog_WARN(TAG, "cupsFinishDocument returned %s", ippErrorString(rc)); + + cups_printjob->printjob_id = 0; + httpClose(cups_printjob->printjob_object); + + cups_printer = (rdpCupsPrinter*)printjob->printer; + WINPR_ASSERT(cups_printer); + + cups_printer->printjob = NULL; + free(cups_printjob); +} + +static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id) +{ + rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer; + rdpCupsPrintJob* cups_printjob = NULL; + + WINPR_ASSERT(cups_printer); + + if (cups_printer->printjob != NULL) + { + WLog_WARN(TAG, "printjob [printer '%s'] already existing, abort!", printer->name); + return NULL; + } + + cups_printjob = (rdpCupsPrintJob*)calloc(1, sizeof(rdpCupsPrintJob)); + if (!cups_printjob) + return NULL; + + cups_printjob->printjob.id = id; + cups_printjob->printjob.printer = printer; + + cups_printjob->printjob.Write = printer_cups_write_printjob; + cups_printjob->printjob.Close = printer_cups_close_printjob; + + { + char buf[100] = { 0 }; + + cups_printjob->printjob_object = httpConnect2(cupsServer(), ippPort(), NULL, AF_UNSPEC, + HTTP_ENCRYPT_IF_REQUESTED, 1, 10000, NULL); + + if (!cups_printjob->printjob_object) + { + WLog_WARN(TAG, "httpConnect2 failed for '%s:%d", cupsServer(), ippPort()); + free(cups_printjob); + return NULL; + } + + printer_cups_get_printjob_name(buf, sizeof(buf), cups_printjob->printjob.id); + + cups_printjob->printjob_id = + cupsCreateJob(cups_printjob->printjob_object, printer->name, buf, 0, NULL); + + if (!cups_printjob->printjob_id) + { + WLog_WARN(TAG, "cupsCreateJob failed for printer '%s', driver '%s'", printer->name, + printer->driver); + httpClose(cups_printjob->printjob_object); + free(cups_printjob); + return NULL; + } + + http_status_t rc = cupsStartDocument(cups_printjob->printjob_object, printer->name, + cups_printjob->printjob_id, buf, CUPS_FORMAT_AUTO, 1); + if (!http_status_ok(rc)) + WLog_WARN(TAG, "cupsStartDocument [printer '%s', driver '%s'] returned %s", + printer->name, printer->driver, httpStatus(rc)); + } + + cups_printer->printjob = cups_printjob; + + return &cups_printjob->printjob; +} + +static rdpPrintJob* printer_cups_find_printjob(rdpPrinter* printer, UINT32 id) +{ + rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer; + + WINPR_ASSERT(cups_printer); + + if (cups_printer->printjob == NULL) + return NULL; + if (cups_printer->printjob->printjob.id != id) + return NULL; + + return &cups_printer->printjob->printjob; +} + +static void printer_cups_free_printer(rdpPrinter* printer) +{ + rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer; + + WINPR_ASSERT(cups_printer); + + if (cups_printer->printjob) + { + WINPR_ASSERT(cups_printer->printjob->printjob.Close); + cups_printer->printjob->printjob.Close(&cups_printer->printjob->printjob); + } + + if (printer->backend) + { + WINPR_ASSERT(printer->backend->ReleaseRef); + printer->backend->ReleaseRef(printer->backend); + } + free(printer->name); + free(printer->driver); + free(printer); +} + +static void printer_cups_add_ref_printer(rdpPrinter* printer) +{ + if (printer) + printer->references++; +} + +static void printer_cups_release_ref_printer(rdpPrinter* printer) +{ + if (!printer) + return; + if (printer->references <= 1) + printer_cups_free_printer(printer); + else + printer->references--; +} + +static rdpPrinter* printer_cups_new_printer(rdpCupsPrinterDriver* cups_driver, const char* name, + const char* driverName, BOOL is_default) +{ + rdpCupsPrinter* cups_printer = NULL; + + cups_printer = (rdpCupsPrinter*)calloc(1, sizeof(rdpCupsPrinter)); + if (!cups_printer) + return NULL; + + cups_printer->printer.backend = &cups_driver->driver; + + cups_printer->printer.id = cups_driver->id_sequence++; + cups_printer->printer.name = _strdup(name); + if (!cups_printer->printer.name) + goto fail; + + if (driverName) + cups_printer->printer.driver = _strdup(driverName); + else + { + const char* dname = "MS Publisher Imagesetter"; +#if defined(__APPLE__) + if (is_mac_os_sonoma_or_later()) + dname = "Microsoft Print to PDF"; +#endif + cups_printer->printer.driver = _strdup(dname); + } + if (!cups_printer->printer.driver) + goto fail; + + cups_printer->printer.is_default = is_default; + + cups_printer->printer.CreatePrintJob = printer_cups_create_printjob; + cups_printer->printer.FindPrintJob = printer_cups_find_printjob; + cups_printer->printer.AddRef = printer_cups_add_ref_printer; + cups_printer->printer.ReleaseRef = printer_cups_release_ref_printer; + + WINPR_ASSERT(cups_printer->printer.AddRef); + cups_printer->printer.AddRef(&cups_printer->printer); + + WINPR_ASSERT(cups_printer->printer.backend->AddRef); + cups_printer->printer.backend->AddRef(cups_printer->printer.backend); + + return &cups_printer->printer; + +fail: + printer_cups_free_printer(&cups_printer->printer); + return NULL; +} + +static void printer_cups_release_enum_printers(rdpPrinter** printers) +{ + rdpPrinter** cur = printers; + + while ((cur != NULL) && ((*cur) != NULL)) + { + if ((*cur)->ReleaseRef) + (*cur)->ReleaseRef(*cur); + cur++; + } + free(printers); +} + +static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver) +{ + rdpPrinter** printers = NULL; + int num_printers = 0; + cups_dest_t* dests = NULL; + BOOL haveDefault = FALSE; + const int num_dests = cupsGetDests(&dests); + + WINPR_ASSERT(driver); + if (num_dests >= 0) + printers = (rdpPrinter**)calloc((size_t)num_dests + 1, sizeof(rdpPrinter*)); + if (!printers) + return NULL; + + for (size_t i = 0; i < num_dests; i++) + { + const cups_dest_t* dest = &dests[i]; + if (dest->instance == NULL) + { + rdpPrinter* current = printer_cups_new_printer((rdpCupsPrinterDriver*)driver, + dest->name, NULL, dest->is_default); + if (!current) + { + printer_cups_release_enum_printers(printers); + printers = NULL; + break; + } + + if (current->is_default) + haveDefault = TRUE; + + printers[num_printers++] = current; + } + } + cupsFreeDests(num_dests, dests); + + if (!haveDefault && (num_dests > 0) && printers) + { + if (printers[0]) + printers[0]->is_default = TRUE; + } + + return printers; +} + +static rdpPrinter* printer_cups_get_printer(rdpPrinterDriver* driver, const char* name, + const char* driverName, BOOL isDefault) +{ + rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver; + + WINPR_ASSERT(cups_driver); + return printer_cups_new_printer(cups_driver, name, driverName, isDefault); +} + +static void printer_cups_add_ref_driver(rdpPrinterDriver* driver) +{ + rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver; + if (cups_driver) + cups_driver->references++; +} + +/* Singleton */ +static rdpCupsPrinterDriver* uniq_cups_driver = NULL; + +static void printer_cups_release_ref_driver(rdpPrinterDriver* driver) +{ + rdpCupsPrinterDriver* cups_driver = (rdpCupsPrinterDriver*)driver; + + WINPR_ASSERT(cups_driver); + + if (cups_driver->references <= 1) + { + if (uniq_cups_driver == cups_driver) + uniq_cups_driver = NULL; + free(cups_driver); + } + else + cups_driver->references--; +} + +FREERDP_ENTRY_POINT(UINT cups_freerdp_printer_client_subsystem_entry(void* arg)) +{ + rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg; + if (!ppPrinter) + return ERROR_INVALID_PARAMETER; + + if (!uniq_cups_driver) + { + uniq_cups_driver = (rdpCupsPrinterDriver*)calloc(1, sizeof(rdpCupsPrinterDriver)); + + if (!uniq_cups_driver) + return ERROR_OUTOFMEMORY; + + uniq_cups_driver->driver.EnumPrinters = printer_cups_enum_printers; + uniq_cups_driver->driver.ReleaseEnumPrinters = printer_cups_release_enum_printers; + uniq_cups_driver->driver.GetPrinter = printer_cups_get_printer; + + uniq_cups_driver->driver.AddRef = printer_cups_add_ref_driver; + uniq_cups_driver->driver.ReleaseRef = printer_cups_release_ref_driver; + + uniq_cups_driver->id_sequence = 1; + } + + WINPR_ASSERT(uniq_cups_driver->driver.AddRef); + uniq_cups_driver->driver.AddRef(&uniq_cups_driver->driver); + + *ppPrinter = &uniq_cups_driver->driver; + return CHANNEL_RC_OK; +} |