Commit b09f8714 authored by Piotr Caban's avatar Piotr Caban Committed by Alexandre Julliard

localspl: Add cups port extension.

parent ee147d92
MODULE = localspl.dll
UNIXLIB = localspl.so
IMPORTS = spoolss user32 advapi32
UNIX_CFLAGS = $(CUPS_CFLAGS)
UNIX_LIBS = $(APPLICATIONSERVICES_LIBS)
EXTRADLLFLAGS = -Wb,--prefer-native
......
......@@ -27,10 +27,14 @@
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#ifdef HAVE_CUPS_CUPS_H
#include <cups/cups.h>
#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
......@@ -40,6 +44,32 @@
WINE_DEFAULT_DEBUG_CHANNEL(localspl);
#ifdef SONAME_LIBCUPS
static void *libcups_handle;
#define CUPS_FUNCS \
DO_FUNC(cupsAddOption); \
DO_FUNC(cupsCreateJob); \
DO_FUNC(cupsFinishDocument); \
DO_FUNC(cupsFreeDests); \
DO_FUNC(cupsFreeOptions); \
DO_FUNC(cupsGetOption); \
DO_FUNC(cupsParseOptions); \
DO_FUNC(cupsStartDocument); \
DO_FUNC(cupsWriteRequestData)
#define CUPS_OPT_FUNCS \
DO_FUNC(cupsGetNamedDest); \
DO_FUNC(cupsLastErrorString)
#define DO_FUNC(f) static typeof(f) *p##f
CUPS_FUNCS;
#undef DO_FUNC
static cups_dest_t * (*pcupsGetNamedDest)(http_t *, const char *, const char *);
static const char * (*pcupsLastErrorString)(void);
#endif /* SONAME_LIBCUPS */
typedef struct _doc_t
{
BOOL (*write_doc)(struct _doc_t *, const BYTE *buf, unsigned int size);
......@@ -56,6 +86,25 @@ typedef struct _doc_t
{
int fd;
} unixname;
#ifdef SONAME_LIBCUPS
struct
{
char *queue;
char *doc_title;
enum
{
doc_parse_header = 0,
doc_parse_options,
doc_create_job,
doc_initialized,
} state;
BOOL restore_ps_header;
int num_options;
cups_option_t *options;
int buf_len;
char buf[257]; /* DSC max of 256 + '\0' */
} cups;
#endif
};
} doc_t;
......@@ -178,10 +227,242 @@ static BOOL lpr_start_doc(doc_t *doc, const WCHAR *printer_name)
return pipe_start_doc(doc, cmd);
}
#ifdef SONAME_LIBCUPS
static int get_cups_default_options(const char *printer, int num_options, cups_option_t **options)
{
cups_dest_t *dest;
int i;
if (!pcupsGetNamedDest) return num_options;
dest = pcupsGetNamedDest(NULL, printer, NULL);
if (!dest) return num_options;
for (i = 0; i < dest->num_options; i++)
{
if (!pcupsGetOption(dest->options[i].name, num_options, *options))
num_options = pcupsAddOption(dest->options[i].name,
dest->options[i].value, num_options, options);
}
pcupsFreeDests(1, dest);
return num_options;
}
static BOOL cups_gets(doc_t *doc, const BYTE **buf, unsigned int *size)
{
BYTE b;
while(doc->cups.buf_len < sizeof(doc->cups.buf) && *size)
{
b = (*buf)[0];
doc->cups.buf[doc->cups.buf_len++] = b;
(*buf)++;
(*size)--;
if (b == '\n')
return TRUE;
}
return FALSE;
}
static BOOL cups_write(const char *buf, unsigned int size)
{
if (!size)
return TRUE;
if (pcupsWriteRequestData(CUPS_HTTP_DEFAULT, buf, size) != HTTP_STATUS_CONTINUE)
{
if (pcupsLastErrorString)
WARN("cupsWriteRequestData failed: %s\n", debugstr_a(pcupsLastErrorString()));
return FALSE;
}
return TRUE;
}
static BOOL cups_write_doc(doc_t *doc, const BYTE *buf, unsigned int size)
{
const char ps_adobe[] = "%!PS-Adobe-3.0\n";
const char cups_job[] = "%cupsJobTicket:";
if (doc->cups.state == doc_parse_header)
{
if (!cups_gets(doc, &buf, &size))
{
if (doc->cups.buf_len != sizeof(doc->cups.buf))
return TRUE;
doc->cups.state = doc_create_job;
}
else if (!strncmp(doc->cups.buf, ps_adobe, sizeof(ps_adobe) - 1))
{
doc->cups.restore_ps_header = TRUE;
doc->cups.state = doc_parse_options;
doc->cups.buf_len = 0;
}
else
{
doc->cups.state = doc_create_job;
}
}
/* Explicitly set CUPS options based on any %cupsJobTicket lines.
* The CUPS scheduler only looks for these in Print-File requests, and since
* we use Create-Job / Send-Document, the ticket lines don't get parsed.
*/
if (doc->cups.state == doc_parse_options)
{
while (1)
{
if (!cups_gets(doc, &buf, &size))
{
if (doc->cups.buf_len != sizeof(doc->cups.buf))
return TRUE;
doc->cups.state = doc_create_job;
break;
}
else if (!strncmp(doc->cups.buf, cups_job, sizeof(cups_job) - 1))
{
doc->cups.buf[doc->cups.buf_len - 1] = 0;
doc->cups.num_options = pcupsParseOptions(doc->cups.buf + sizeof(cups_job) - 1,
doc->cups.num_options, &doc->cups.options);
doc->cups.buf_len = 0;
}
else
{
doc->cups.state = doc_create_job;
break;
}
}
}
if (doc->cups.state == doc_create_job)
{
const char *format;
int i, job_id;
doc->cups.num_options = get_cups_default_options(doc->cups.queue,
doc->cups.num_options, &doc->cups.options);
TRACE("printing via cups with options:\n");
for (i = 0; i < doc->cups.num_options; i++)
TRACE("\t%d: %s = %s\n", i, doc->cups.options[i].name, doc->cups.options[i].value);
if (pcupsGetOption("raw", doc->cups.num_options, doc->cups.options))
format = CUPS_FORMAT_RAW;
else if (!(format = pcupsGetOption("document-format", doc->cups.num_options, doc->cups.options)))
format = CUPS_FORMAT_AUTO;
job_id = pcupsCreateJob(CUPS_HTTP_DEFAULT, doc->cups.queue,
doc->cups.doc_title, doc->cups.num_options, doc->cups.options);
if (!job_id)
{
if (pcupsLastErrorString)
WARN("cupsCreateJob failed: %s\n", debugstr_a(pcupsLastErrorString()));
return FALSE;
}
if (pcupsStartDocument(CUPS_HTTP_DEFAULT, doc->cups.queue, job_id,
doc->cups.doc_title, format, TRUE) != HTTP_STATUS_CONTINUE)
{
if (pcupsLastErrorString)
WARN("cupsStartDocument failed: %s\n", debugstr_a(pcupsLastErrorString()));
return FALSE;
}
doc->cups.state = doc_initialized;
}
if (doc->cups.restore_ps_header)
{
if (!cups_write(ps_adobe, sizeof(ps_adobe) - 1))
return FALSE;
doc->cups.restore_ps_header = FALSE;
}
if (doc->cups.buf_len)
{
if (!cups_write(doc->cups.buf, doc->cups.buf_len))
return FALSE;
doc->cups.buf_len = 0;
}
return cups_write((const char *)buf, size);
}
static BOOL cups_end_doc(doc_t *doc)
{
if (doc->cups.buf_len)
{
if (doc->cups.state != doc_initialized)
doc->cups.state = doc_create_job;
cups_write_doc(doc, NULL, 0);
}
if (doc->cups.state == doc_initialized)
pcupsFinishDocument(CUPS_HTTP_DEFAULT, doc->cups.queue);
free(doc->cups.queue);
free(doc->cups.doc_title);
pcupsFreeOptions(doc->cups.num_options, doc->cups.options);
return TRUE;
}
#endif
static BOOL cups_start_doc(doc_t *doc, const WCHAR *printer_name, const WCHAR *document_title)
{
#ifdef SONAME_LIBCUPS
if (pcupsWriteRequestData)
{
int len;
doc->write_doc = cups_write_doc;
doc->end_doc = cups_end_doc;
len = wcslen(printer_name);
doc->cups.queue = malloc(len * 3 + 1);
ntdll_wcstoumbs(printer_name, len + 1, doc->cups.queue, len * 3 + 1, FALSE);
len = wcslen(document_title);
doc->cups.doc_title = malloc(len * 3 + 1);
ntdll_wcstoumbs(document_title, len + 1, doc->cups.doc_title, len + 3 + 1, FALSE);
return TRUE;
}
#endif
return lpr_start_doc(doc, printer_name);
}
static NTSTATUS process_attach(void *args)
{
#ifdef SONAME_LIBCUPS
libcups_handle = dlopen(SONAME_LIBCUPS, RTLD_NOW);
TRACE("%p: %s loaded\n", libcups_handle, SONAME_LIBCUPS);
if (!libcups_handle) return STATUS_DLL_NOT_FOUND;
#define DO_FUNC(x) \
p##x = dlsym(libcups_handle, #x); \
if (!p##x) \
{ \
ERR("failed to load symbol %s\n", #x); \
libcups_handle = NULL; \
return STATUS_ENTRYPOINT_NOT_FOUND; \
}
CUPS_FUNCS;
#undef DO_FUNC
#define DO_FUNC(x) p##x = dlsym(libcups_handle, #x)
CUPS_OPT_FUNCS;
#undef DO_FUNC
return STATUS_SUCCESS;
#else /* SONAME_LIBCUPS */
return STATUS_NOT_SUPPORTED;
#endif /* SONAME_LIBCUPS */
}
static NTSTATUS start_doc(void *args)
{
const struct start_doc_params *params = args;
doc_t *doc = malloc(sizeof(*doc));
doc_t *doc = calloc(1, sizeof(*doc));
BOOL ret = FALSE;
if (!doc) return STATUS_NO_MEMORY;
......@@ -192,6 +473,9 @@ static NTSTATUS start_doc(void *args)
ret = unixname_start_doc(doc, params->port);
else if (params->type == PORT_IS_LPR)
ret = lpr_start_doc(doc, params->port + 4 /* strlen("lpr:") */);
else if (params->type == PORT_IS_CUPS)
ret = cups_start_doc(doc, params->port + 5 /*strlen("cups:") */,
params->document_title);
if (ret)
*params->doc = (size_t)doc;
......@@ -221,6 +505,7 @@ static NTSTATUS end_doc(void *args)
const unixlib_entry_t __wine_unix_call_funcs[] =
{
process_attach,
start_doc,
write_doc,
end_doc,
......@@ -236,6 +521,7 @@ static NTSTATUS wow64_start_doc(void *args)
{
unsigned int type;
const PTR32 port;
const PTR32 document_title;
INT64 *doc;
} const *params32 = args;
......@@ -243,6 +529,7 @@ static NTSTATUS wow64_start_doc(void *args)
{
params32->type,
ULongToPtr(params32->port),
ULongToPtr(params32->document_title),
params32->doc,
};
......@@ -270,6 +557,7 @@ static NTSTATUS wow64_write_doc(void *args)
const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
{
process_attach,
wow64_start_doc,
wow64_write_doc,
end_doc,
......
......@@ -96,7 +96,10 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls( hinstDLL );
localspl_instance = hinstDLL;
return !__wine_init_unix_call();
if (__wine_init_unix_call())
return FALSE;
UNIX_CALL(process_attach, NULL);
break;
}
return TRUE;
}
......@@ -506,8 +509,7 @@ static BOOL WINAPI localmon_StartDocPort(HANDLE hport, WCHAR *printer_name,
TRACE("(%p %s %ld %ld %p)\n", hport, debugstr_w(printer_name),
job_id, level, doc_info);
if (port->type == PORT_IS_PIPE || port->type == PORT_IS_UNIXNAME ||
port->type == PORT_IS_LPR)
if (port->type >= PORT_IS_WINE)
{
struct start_doc_params params;
......@@ -516,6 +518,7 @@ static BOOL WINAPI localmon_StartDocPort(HANDLE hport, WCHAR *printer_name,
params.type = port->type;
params.port = port->nameW;
params.document_title = doc_info ? doc_info->pDocName : NULL;
params.doc = &port->doc_handle;
return UNIX_CALL(start_doc, &params);
}
......@@ -547,12 +550,17 @@ static BOOL WINAPI localmon_WritePort(HANDLE hport, BYTE *buf, DWORD size,
TRACE("(%p %p %lu %p)\n", hport, buf, size, written);
if (port->type == PORT_IS_PIPE || port->type == PORT_IS_UNIXNAME ||
port->type == PORT_IS_LPR)
if (port->type >= PORT_IS_WINE)
{
struct write_doc_params params;
BOOL ret;
if (!port->doc_handle)
{
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
params.doc = port->doc_handle;
params.buf = buf;
params.size = size;
......@@ -570,11 +578,13 @@ static BOOL WINAPI localmon_EndDocPort(HANDLE hport)
TRACE("(%p)\n", hport);
if (port->type == PORT_IS_PIPE || port->type == PORT_IS_UNIXNAME ||
port->type == PORT_IS_LPR)
if (port->type >= PORT_IS_WINE)
{
struct end_doc_params params;
if (!port->doc_handle)
return TRUE;
params.doc = port->doc_handle;
port->doc_handle = 0;
return UNIX_CALL(end_doc, &params);
......
......@@ -171,6 +171,7 @@ struct start_doc_params
{
unsigned int type;
const WCHAR *port;
const WCHAR *document_title;
INT64 *doc;
};
......@@ -190,6 +191,7 @@ struct end_doc_params
enum cups_funcs
{
unix_process_attach,
unix_start_doc,
unix_write_doc,
unix_end_doc,
......
......@@ -988,7 +988,8 @@ static monitor_t * monitor_load_by_port(LPCWSTR portname)
TRACE("(%s)\n", debugstr_w(portname));
/* wine specific ports */
if (portname[0] == '|' || portname[0] == '/')
if (portname[0] == '|' || portname[0] == '/' ||
!wcsncmp(portname, L"LPR:", 4) || !wcsncmp(portname, L"CUPS:", 5))
return monitor_load(L"Local Port", NULL);
/* Try the Local Monitor first */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment