Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
31
third_party/FreeRDP/channels/printer/client/CMakeLists.txt
vendored
Normal file
31
third_party/FreeRDP/channels/printer/client/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@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.
|
||||
|
||||
define_channel_client("printer")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS printer_main.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp)
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry")
|
||||
|
||||
if(WITH_CUPS)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "cups" "")
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT UWP)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "win" "")
|
||||
endif()
|
||||
29
third_party/FreeRDP/channels/printer/client/cups/CMakeLists.txt
vendored
Normal file
29
third_party/FreeRDP/channels/printer/client/cups/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2019 Thincast Technologies GmbH
|
||||
#
|
||||
# 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.
|
||||
define_channel_client_subsystem("printer" "cups" "")
|
||||
|
||||
find_package(Cups 2.0 REQUIRED)
|
||||
freerdp_client_pc_add_requires_private("cups")
|
||||
set(${MODULE_PREFIX}_SRCS printer_cups.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp ${CUPS_LIBRARIES})
|
||||
|
||||
include_directories(..)
|
||||
include_directories(SYSTEM ${CUPS_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
477
third_party/FreeRDP/channels/printer/client/cups/printer_cups.c
vendored
Normal file
477
third_party/FreeRDP/channels/printer/client/cups/printer_cups.c
vendored
Normal file
@@ -0,0 +1,477 @@
|
||||
/**
|
||||
* 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 <freerdp/utils/helpers.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] = WINPR_C_ARRAY_INIT;
|
||||
size_t size = sizeof(str);
|
||||
|
||||
errno = 0;
|
||||
int ret = sysctlbyname("kern.osrelease", str, &size, nullptr, 0);
|
||||
if (ret != 0)
|
||||
{
|
||||
char buffer[256] = WINPR_C_ARRAY_INIT;
|
||||
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;
|
||||
|
||||
// NOLINTNEXTLINE(cert-err34-c)
|
||||
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'", str, major, minor, patch);
|
||||
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;
|
||||
|
||||
WINPR_ATTR_MALLOC(free, 1)
|
||||
static char* printer_cups_get_printjob_name(size_t id)
|
||||
{
|
||||
struct tm tres = WINPR_C_ARRAY_INIT;
|
||||
const time_t tt = time(nullptr);
|
||||
const struct tm* t = localtime_r(&tt, &tres);
|
||||
|
||||
char* str = nullptr;
|
||||
size_t len = 0;
|
||||
const int rc =
|
||||
winpr_asprintf(&str, &len, "%s Print %04d-%02d-%02d %02d-%02d-%02d - Job %" PRIuz,
|
||||
freerdp_getApplicationDetailsString(), t->tm_year + 1900, t->tm_mon + 1,
|
||||
t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, id);
|
||||
if (rc <= 0)
|
||||
{
|
||||
free(str);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return write_printjob(printjob, data, size);
|
||||
}
|
||||
|
||||
static void printer_cups_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpCupsPrintJob* cups_printjob = (rdpCupsPrintJob*)printjob;
|
||||
rdpCupsPrinter* cups_printer = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
free(cups_printjob);
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_cups_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpCupsPrinter* cups_printer = (rdpCupsPrinter*)printer;
|
||||
rdpCupsPrintJob* cups_printjob = nullptr;
|
||||
|
||||
WINPR_ASSERT(cups_printer);
|
||||
|
||||
if (cups_printer->printjob != nullptr)
|
||||
{
|
||||
WLog_WARN(TAG, "printjob [printer '%s'] already existing, abort!", printer->name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cups_printjob = (rdpCupsPrintJob*)calloc(1, sizeof(rdpCupsPrintJob));
|
||||
if (!cups_printjob)
|
||||
return nullptr;
|
||||
|
||||
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;
|
||||
|
||||
{
|
||||
cups_printjob->printjob_object = httpConnect2(cupsServer(), ippPort(), nullptr, AF_UNSPEC,
|
||||
HTTP_ENCRYPT_IF_REQUESTED, 1, 10000, nullptr);
|
||||
|
||||
if (!cups_printjob->printjob_object)
|
||||
{
|
||||
WLog_WARN(TAG, "httpConnect2 failed for '%s:%d", cupsServer(), ippPort());
|
||||
free(cups_printjob);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* jobTitle = printer_cups_get_printjob_name(cups_printjob->printjob.id);
|
||||
if (!jobTitle)
|
||||
{
|
||||
httpClose(cups_printjob->printjob_object);
|
||||
free(cups_printjob);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cups_printjob->printjob_id =
|
||||
cupsCreateJob(cups_printjob->printjob_object, printer->name, jobTitle, 0, nullptr);
|
||||
|
||||
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);
|
||||
free(jobTitle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
http_status_t rc =
|
||||
cupsStartDocument(cups_printjob->printjob_object, printer->name,
|
||||
cups_printjob->printjob_id, jobTitle, CUPS_FORMAT_AUTO, 1);
|
||||
free(jobTitle);
|
||||
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 == nullptr)
|
||||
return nullptr;
|
||||
if (cups_printer->printjob->printjob.id != id)
|
||||
return nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
cups_printer = (rdpCupsPrinter*)calloc(1, sizeof(rdpCupsPrinter));
|
||||
if (!cups_printer)
|
||||
return nullptr;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
static void printer_cups_release_enum_printers(rdpPrinter** printers)
|
||||
{
|
||||
rdpPrinter** cur = printers;
|
||||
|
||||
while ((cur != nullptr) && ((*cur) != nullptr))
|
||||
{
|
||||
if ((*cur)->ReleaseRef)
|
||||
(*cur)->ReleaseRef(*cur);
|
||||
cur++;
|
||||
}
|
||||
free((void*)printers);
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_cups_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers = nullptr;
|
||||
int num_printers = 0;
|
||||
cups_dest_t* dests = nullptr;
|
||||
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 nullptr;
|
||||
|
||||
for (size_t i = 0; i < (size_t)num_dests; i++)
|
||||
{
|
||||
const cups_dest_t* dest = &dests[i];
|
||||
if (dest->instance == nullptr)
|
||||
{
|
||||
rdpPrinter* current = printer_cups_new_printer((rdpCupsPrinterDriver*)driver,
|
||||
dest->name, nullptr, dest->is_default);
|
||||
if (!current)
|
||||
{
|
||||
printer_cups_release_enum_printers(printers);
|
||||
printers = nullptr;
|
||||
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 = nullptr;
|
||||
|
||||
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 = nullptr;
|
||||
free(cups_driver);
|
||||
}
|
||||
else
|
||||
cups_driver->references--;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE 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;
|
||||
}
|
||||
1220
third_party/FreeRDP/channels/printer/client/printer_main.c
vendored
Normal file
1220
third_party/FreeRDP/channels/printer/client/printer_main.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
26
third_party/FreeRDP/channels/printer/client/win/CMakeLists.txt
vendored
Normal file
26
third_party/FreeRDP/channels/printer/client/win/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2019 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright 2019 Thincast Technologies GmbH
|
||||
#
|
||||
# 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.
|
||||
define_channel_client_subsystem("printer" "win" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS printer_win.c)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS winpr freerdp)
|
||||
|
||||
include_directories(..)
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
480
third_party/FreeRDP/channels/printer/client/win/printer_win.c
vendored
Normal file
480
third_party/FreeRDP/channels/printer/client/win/printer_win.c
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Print Virtual Channel - WIN driver
|
||||
*
|
||||
* Copyright 2012 Gerald Richter
|
||||
* 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 <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/wtsapi.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <winspool.h>
|
||||
|
||||
#include <freerdp/client/printer.h>
|
||||
#include <freerdp/utils/helpers.h>
|
||||
|
||||
#define WIDEN_INT(x) L##x
|
||||
#define WIDEN(x) WIDEN_INT(x)
|
||||
#define PRINTER_TAG CHANNELS_TAG("printer.client")
|
||||
#ifdef WITH_DEBUG_WINPR
|
||||
#define DEBUG_WINPR(...) WLog_DBG(PRINTER_TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_WINPR(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rdpPrinterDriver driver;
|
||||
|
||||
size_t id_sequence;
|
||||
size_t references;
|
||||
} rdpWinPrinterDriver;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rdpPrintJob printjob;
|
||||
DOC_INFO_1 di;
|
||||
DWORD handle;
|
||||
|
||||
void* printjob_object;
|
||||
int printjob_id;
|
||||
} rdpWinPrintJob;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
rdpPrinter printer;
|
||||
HANDLE hPrinter;
|
||||
rdpWinPrintJob* printjob;
|
||||
} rdpWinPrinter;
|
||||
|
||||
WINPR_ATTR_MALLOC(free, 1)
|
||||
static WCHAR* printer_win_get_printjob_name(size_t id)
|
||||
{
|
||||
struct tm tres = WINPR_C_ARRAY_INIT;
|
||||
WCHAR* str = nullptr;
|
||||
size_t len = 0;
|
||||
|
||||
const time_t tt = time(nullptr);
|
||||
const errno_t err = localtime_s(&tres, &tt);
|
||||
|
||||
do
|
||||
{
|
||||
if (len > 0)
|
||||
{
|
||||
str = calloc(len + 1, sizeof(WCHAR));
|
||||
if (!str)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int rc = swprintf_s(
|
||||
str, len,
|
||||
WIDEN("%s Print %04d-%02d-%02d %02d-%02d-%02d - Job %") WIDEN(PRIuz) WIDEN("\0"),
|
||||
freerdp_getApplicationDetailsStringW(), tres.tm_year + 1900, tres.tm_mon + 1,
|
||||
tres.tm_mday, tres.tm_hour, tres.tm_min, tres.tm_sec, id);
|
||||
if (rc <= 0)
|
||||
{
|
||||
free(str);
|
||||
return nullptr;
|
||||
}
|
||||
len = WINPR_ASSERTING_INT_CAST(size_t, rc) + 1ull;
|
||||
} while (!str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT printer_win_write_printjob(rdpPrintJob* printjob, const BYTE* data, size_t size)
|
||||
{
|
||||
LPCVOID pBuf = data;
|
||||
DWORD pcWritten = 0;
|
||||
|
||||
if (size > UINT32_MAX)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
if (!printjob || !data)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
rdpWinPrinter* printer = (rdpWinPrinter*)printjob->printer;
|
||||
if (!printer)
|
||||
return ERROR_BAD_ARGUMENTS;
|
||||
|
||||
DWORD cbBuf = WINPR_ASSERTING_INT_CAST(uint32_t, size);
|
||||
if (!WritePrinter(printer->hPrinter, WINPR_CAST_CONST_PTR_AWAY(pBuf, void*), cbBuf, &pcWritten))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void printer_win_close_printjob(rdpPrintJob* printjob)
|
||||
{
|
||||
rdpWinPrintJob* win_printjob = (rdpWinPrintJob*)printjob;
|
||||
rdpWinPrinter* win_printer;
|
||||
|
||||
if (!printjob)
|
||||
return;
|
||||
|
||||
win_printer = (rdpWinPrinter*)printjob->printer;
|
||||
if (!win_printer)
|
||||
return;
|
||||
|
||||
if (!EndPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
}
|
||||
|
||||
if (!EndDocPrinter(win_printer->hPrinter))
|
||||
{
|
||||
}
|
||||
|
||||
win_printer->printjob = nullptr;
|
||||
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_create_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
rdpWinPrintJob* win_printjob;
|
||||
|
||||
if (win_printer->printjob != nullptr)
|
||||
return nullptr;
|
||||
|
||||
win_printjob = (rdpWinPrintJob*)calloc(1, sizeof(rdpWinPrintJob));
|
||||
if (!win_printjob)
|
||||
return nullptr;
|
||||
|
||||
win_printjob->printjob.id = id;
|
||||
win_printjob->printjob.printer = printer;
|
||||
win_printjob->di.pDocName = printer_win_get_printjob_name(id);
|
||||
win_printjob->di.pDatatype = nullptr;
|
||||
win_printjob->di.pOutputFile = nullptr;
|
||||
|
||||
win_printjob->handle = StartDocPrinter(win_printer->hPrinter, 1, (LPBYTE) & (win_printjob->di));
|
||||
|
||||
if (!win_printjob->handle)
|
||||
{
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!StartPagePrinter(win_printer->hPrinter))
|
||||
{
|
||||
free(win_printjob->di.pDocName);
|
||||
free(win_printjob);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
win_printjob->printjob.Write = printer_win_write_printjob;
|
||||
win_printjob->printjob.Close = printer_win_close_printjob;
|
||||
|
||||
win_printer->printjob = win_printjob;
|
||||
|
||||
return &win_printjob->printjob;
|
||||
}
|
||||
|
||||
static rdpPrintJob* printer_win_find_printjob(rdpPrinter* printer, UINT32 id)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
|
||||
if (!win_printer->printjob)
|
||||
return nullptr;
|
||||
|
||||
if (win_printer->printjob->printjob.id != id)
|
||||
return nullptr;
|
||||
|
||||
return (rdpPrintJob*)win_printer->printjob;
|
||||
}
|
||||
|
||||
static void printer_win_free_printer(rdpPrinter* printer)
|
||||
{
|
||||
rdpWinPrinter* win_printer = (rdpWinPrinter*)printer;
|
||||
|
||||
if (win_printer->printjob)
|
||||
win_printer->printjob->printjob.Close((rdpPrintJob*)win_printer->printjob);
|
||||
|
||||
if (win_printer->hPrinter)
|
||||
ClosePrinter(win_printer->hPrinter);
|
||||
|
||||
if (printer->backend)
|
||||
printer->backend->ReleaseRef(printer->backend);
|
||||
|
||||
free(printer->name);
|
||||
free(printer->driver);
|
||||
free(printer);
|
||||
}
|
||||
|
||||
static void printer_win_add_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (printer)
|
||||
printer->references++;
|
||||
}
|
||||
|
||||
static void printer_win_release_ref_printer(rdpPrinter* printer)
|
||||
{
|
||||
if (!printer)
|
||||
return;
|
||||
if (printer->references <= 1)
|
||||
printer_win_free_printer(printer);
|
||||
else
|
||||
printer->references--;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_new_printer(rdpWinPrinterDriver* win_driver, const WCHAR* name,
|
||||
const WCHAR* drivername, BOOL is_default)
|
||||
{
|
||||
rdpWinPrinter* win_printer;
|
||||
DWORD needed = 0;
|
||||
PRINTER_INFO_2* prninfo = nullptr;
|
||||
|
||||
if (!name)
|
||||
return nullptr;
|
||||
|
||||
win_printer = (rdpWinPrinter*)calloc(1, sizeof(rdpWinPrinter));
|
||||
if (!win_printer)
|
||||
return nullptr;
|
||||
|
||||
win_printer->printer.backend = &win_driver->driver;
|
||||
win_printer->printer.id = win_driver->id_sequence++;
|
||||
win_printer->printer.name = ConvertWCharToUtf8Alloc(name, nullptr);
|
||||
if (!win_printer->printer.name)
|
||||
goto fail;
|
||||
|
||||
if (!win_printer->printer.name)
|
||||
goto fail;
|
||||
win_printer->printer.is_default = is_default;
|
||||
|
||||
win_printer->printer.CreatePrintJob = printer_win_create_printjob;
|
||||
win_printer->printer.FindPrintJob = printer_win_find_printjob;
|
||||
win_printer->printer.AddRef = printer_win_add_ref_printer;
|
||||
win_printer->printer.ReleaseRef = printer_win_release_ref_printer;
|
||||
|
||||
if (!OpenPrinter(WINPR_CAST_CONST_PTR_AWAY(name, WCHAR*), &(win_printer->hPrinter), nullptr))
|
||||
goto fail;
|
||||
|
||||
/* How many memory should be allocated for printer data */
|
||||
GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, 0, &needed);
|
||||
if (needed == 0)
|
||||
goto fail;
|
||||
|
||||
prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
|
||||
if (!prninfo)
|
||||
goto fail;
|
||||
|
||||
if (!GetPrinter(win_printer->hPrinter, 2, (LPBYTE)prninfo, needed, &needed))
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (drivername)
|
||||
win_printer->printer.driver = ConvertWCharToUtf8Alloc(drivername, nullptr);
|
||||
else
|
||||
win_printer->printer.driver = ConvertWCharToUtf8Alloc(prninfo->pDriverName, nullptr);
|
||||
GlobalFree(prninfo);
|
||||
if (!win_printer->printer.driver)
|
||||
goto fail;
|
||||
|
||||
win_printer->printer.AddRef(&win_printer->printer);
|
||||
win_printer->printer.backend->AddRef(win_printer->printer.backend);
|
||||
return &win_printer->printer;
|
||||
|
||||
fail:
|
||||
printer_win_free_printer(&win_printer->printer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void printer_win_release_enum_printers(rdpPrinter** printers)
|
||||
{
|
||||
rdpPrinter** cur = printers;
|
||||
|
||||
while ((cur != nullptr) && ((*cur) != nullptr))
|
||||
{
|
||||
if ((*cur)->ReleaseRef)
|
||||
(*cur)->ReleaseRef(*cur);
|
||||
cur++;
|
||||
}
|
||||
free(printers);
|
||||
}
|
||||
|
||||
static rdpPrinter** printer_win_enum_printers(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpPrinter** printers;
|
||||
int num_printers;
|
||||
PRINTER_INFO_2* prninfo = nullptr;
|
||||
DWORD needed, returned;
|
||||
BOOL haveDefault = FALSE;
|
||||
LPWSTR defaultPrinter = nullptr;
|
||||
|
||||
GetDefaultPrinter(nullptr, &needed);
|
||||
if (needed)
|
||||
{
|
||||
defaultPrinter = (LPWSTR)calloc(needed, sizeof(WCHAR));
|
||||
|
||||
if (!defaultPrinter)
|
||||
return nullptr;
|
||||
|
||||
if (!GetDefaultPrinter(defaultPrinter, &needed))
|
||||
defaultPrinter[0] = '\0';
|
||||
}
|
||||
|
||||
/* find required size for the buffer */
|
||||
EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, nullptr, 0, &needed,
|
||||
&returned);
|
||||
|
||||
/* allocate array of PRINTER_INFO structures */
|
||||
prninfo = (PRINTER_INFO_2*)GlobalAlloc(GPTR, needed);
|
||||
if (!prninfo)
|
||||
{
|
||||
free(defaultPrinter);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* call again */
|
||||
if (!EnumPrinters(PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 2, (LPBYTE)prninfo,
|
||||
needed, &needed, &returned))
|
||||
{
|
||||
}
|
||||
|
||||
printers = (rdpPrinter**)calloc((returned + 1), sizeof(rdpPrinter*));
|
||||
if (!printers)
|
||||
{
|
||||
GlobalFree(prninfo);
|
||||
free(defaultPrinter);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
num_printers = 0;
|
||||
|
||||
for (int i = 0; i < (int)returned; i++)
|
||||
{
|
||||
rdpPrinter* current = printers[num_printers];
|
||||
current = printer_win_new_printer((rdpWinPrinterDriver*)driver, prninfo[i].pPrinterName,
|
||||
prninfo[i].pDriverName,
|
||||
_wcscmp(prninfo[i].pPrinterName, defaultPrinter) == 0);
|
||||
if (!current)
|
||||
{
|
||||
printer_win_release_enum_printers(printers);
|
||||
printers = nullptr;
|
||||
break;
|
||||
}
|
||||
if (current->is_default)
|
||||
haveDefault = TRUE;
|
||||
printers[num_printers++] = current;
|
||||
}
|
||||
|
||||
if (!haveDefault && (returned > 0))
|
||||
printers[0]->is_default = TRUE;
|
||||
|
||||
GlobalFree(prninfo);
|
||||
free(defaultPrinter);
|
||||
return printers;
|
||||
}
|
||||
|
||||
static rdpPrinter* printer_win_get_printer(rdpPrinterDriver* driver, const char* name,
|
||||
const char* driverName, BOOL isDefault)
|
||||
{
|
||||
WCHAR* driverNameW = nullptr;
|
||||
WCHAR* nameW = nullptr;
|
||||
rdpWinPrinterDriver* win_driver = (rdpWinPrinterDriver*)driver;
|
||||
rdpPrinter* myPrinter = nullptr;
|
||||
|
||||
if (name)
|
||||
{
|
||||
nameW = ConvertUtf8ToWCharAlloc(name, nullptr);
|
||||
if (!nameW)
|
||||
return nullptr;
|
||||
}
|
||||
if (driverName)
|
||||
{
|
||||
driverNameW = ConvertUtf8ToWCharAlloc(driverName, nullptr);
|
||||
if (!driverNameW)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
myPrinter = printer_win_new_printer(win_driver, nameW, driverNameW, isDefault);
|
||||
free(driverNameW);
|
||||
free(nameW);
|
||||
|
||||
return myPrinter;
|
||||
}
|
||||
|
||||
static void printer_win_add_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
|
||||
if (win)
|
||||
win->references++;
|
||||
}
|
||||
|
||||
/* Singleton */
|
||||
static rdpWinPrinterDriver* win_driver = nullptr;
|
||||
|
||||
static void printer_win_release_ref_driver(rdpPrinterDriver* driver)
|
||||
{
|
||||
rdpWinPrinterDriver* win = (rdpWinPrinterDriver*)driver;
|
||||
if (win->references <= 1)
|
||||
{
|
||||
free(win);
|
||||
win_driver = nullptr;
|
||||
}
|
||||
else
|
||||
win->references--;
|
||||
}
|
||||
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE win_freerdp_printer_client_subsystem_entry(void* arg))
|
||||
{
|
||||
rdpPrinterDriver** ppPrinter = (rdpPrinterDriver**)arg;
|
||||
if (!ppPrinter)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!win_driver)
|
||||
{
|
||||
win_driver = (rdpWinPrinterDriver*)calloc(1, sizeof(rdpWinPrinterDriver));
|
||||
|
||||
if (!win_driver)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
win_driver->driver.EnumPrinters = printer_win_enum_printers;
|
||||
win_driver->driver.ReleaseEnumPrinters = printer_win_release_enum_printers;
|
||||
win_driver->driver.GetPrinter = printer_win_get_printer;
|
||||
|
||||
win_driver->driver.AddRef = printer_win_add_ref_driver;
|
||||
win_driver->driver.ReleaseRef = printer_win_release_ref_driver;
|
||||
|
||||
win_driver->id_sequence = 1;
|
||||
}
|
||||
|
||||
win_driver->driver.AddRef(&win_driver->driver);
|
||||
|
||||
*ppPrinter = &win_driver->driver;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user