Index: Makefile =================================================================== --- Makefile (revision 10713) +++ Makefile (working copy) @@ -57,7 +57,8 @@ test1284.o \ testbackend.o \ testsupplies.o \ - usb.o + usb.o \ + wps.o # @@ -288,6 +289,17 @@ # +# wps +# + +wps: wps.o ../cups/$(LIBCUPS) libbackend.a + echo Linking $@... + $(CC) $(ARCHFLAGS) $(LDFLAGS) -o wps wps.o libbackend.a /usr/lib/w32api/libwinspool.a \ + $(BACKLIBS) $(LIBS) +wps.o: wps.c + + +# # Dependencies... # Index: wps.c =================================================================== --- wps.c (revision 0) +++ wps.c (working copy) @@ -0,0 +1,446 @@ +/* + * Windows Print Spooler backend for CUPS. + * + * Copyright 2012 by HIRAOKA HYPERS TOOLS, Inc. + * + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * "LICENSE" which should have been included with this file. If this + * file is missing or damaged, see the license at "http://www.cups.org/". + * + * This file is subject to the Apple OS-Developed Software exception. + * + * Contents: + * + * list_devices() - List all available USB devices to stdout. + * print_device() - Print a file to a USB device. + * main() - Send a file to the specified USB port. + * side_cb() - Handle side-channel requests... + */ + +/* + * Include necessary headers. + */ + +#include "backend-private.h" + +#ifdef __CYGWIN__ +# define WIN32 +#endif + +#ifdef WIN32 +# include +#endif /* WIN32 */ + + +/* + * Local functions... + */ + +void list_devices(void); +int print_device(const char *uri, const char *hostname, + const char *resource, char *options, + int print_fd, int copies, int argc, char *argv[]); + +static void urlenc(char *dst, const char *src, size_t dstsize) + __attribute__((nonnull(1,2))); +static int side_cb(int print_fd, int device_fd, int snmp_fd, + http_addr_t *addr, int use_bc); + +/* + * Use dummy functions that do nothing on unsupported platforms... + * These can be used as templates for implementing USB printing on new + * platforms... + */ + +/* + * 'list_devices()' - List all available USB devices to stdout. + */ + +void +list_devices(void) +{ + /* + * Don't have any devices to list... Use output of the form: + * + * direct usb:/make/model?serial=foo "Make Model" "USB Printer" + * + * Note that "Hewlett Packard" or any other variation MUST be mapped to + * "HP" for compatibility with the PPD and ICC specs. + */ + + BYTE buff[60000]; // at least 25904 on my PC. + DWORD cbNeeded = 0; + DWORD cRet = 0; + DWORD i; + PRINTER_INFO_2 *printers = (PRINTER_INFO_2 *)buff; + + EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 2, buff, sizeof(buff), &cbNeeded, &cRet); + + //printf("direct wps \"Unknown\" \"Windows Print Spooler\"\n"); + + for (i = 0; i < cRet; i++) + { + char device_uri[300]; + char device_info_urle[300]; + const char *make_model = printers[i].pDriverName; + const char *device_info = printers[i].pPrinterName; + const char *device_id = printers[i].pPortName; + + urlenc(device_info_urle, device_info, 300); + + snprintf(device_uri, 300, "wps://./%s", device_info_urle); + + cupsBackendReport("direct", device_uri, make_model, device_info, + device_id, NULL); + } +} + + +/* + * 'print_device()' - Print a file to a USB device. + */ + +int /* O - Exit status */ +print_device(const char *uri, /* I - Device URI */ + const char *hostname, /* I - Hostname/manufacturer */ + const char *resource, /* I - Resource/modelname */ + char *options, /* I - Device options/serial number */ + int print_fd, /* I - File descriptor to print */ + int copies, /* I - Copies to print */ + int argc, /* I - Number of command-line arguments (6 or 7) */ + char *argv[]) /* I - Command-line arguments */ +{ + /* + * Can't print, so just reference the arguments to eliminate compiler + * warnings and return and exit status of 1. Normally you would use the + * arguments to send a file to the printer and return 0 if everything + * worked OK and non-zero if there was an error. + */ + DOC_INFO_1 docInfo; + HANDLE hPrinter = NULL; + int copy; + ssize_t tbytes = 0; + size_t nbytes; + char buffer[32768]; /* Output buffer */ + + (void)uri; + (void)hostname; + (void)resource; + (void)options; + (void)print_fd; + (void)copies; + (void)argc; + (void)argv; + + if (resource == NULL) + return (CUPS_BACKEND_FAILED); + + fputs("STATE: +connecting-to-device\n", stderr); + + if (!OpenPrinter((char *)(resource +1), &hPrinter, NULL)) { + _cupsLangPrintError("ERROR", _("Unable to open printer")); + return (CUPS_BACKEND_FAILED); + } + + fputs("STATE: -connecting-to-device\n", stderr); + + docInfo.pDocName = argv[3]; + docInfo.pOutputFile = NULL; + docInfo.pDatatype = NULL; + + if (!StartDocPrinter(hPrinter, 1, (LPBYTE)&docInfo)) { + _cupsLangPrintError("ERROR", _("Unable to start document printing")); + return (CUPS_BACKEND_FAILED); + } + + for (copy = 0; copy < copies; copy ++) { + lseek(print_fd, 0, SEEK_SET); + fprintf(stderr, "PAGE: %d %d\n", 1 + copy, atoi(argv[4])); + + if (!StartPagePrinter(hPrinter)) { + _cupsLangPrintError("ERROR", _("Unable to start page printing")); + return (CUPS_BACKEND_FAILED); + } + + while (1) { + cups_sc_command_t command; /* Request command */ + cups_sc_status_t status; /* Request/response status */ + char data[2048]; /* Request/response data */ + int datalen; /* Request/response data size */ + + if (!cupsSideChannelRead(&command, &status, data, &datalen, 0.0)) { + switch (command) + { + case CUPS_SC_CMD_DRAIN_OUTPUT : + status = CUPS_SC_STATUS_OK; + + datalen = 0; + break; + + case CUPS_SC_CMD_GET_BIDI : + status = CUPS_SC_STATUS_OK; + datalen = 0; + break; + + default : + status = CUPS_SC_STATUS_NOT_IMPLEMENTED; + datalen = 0; + break; + } + + cupsSideChannelWrite(command, status, data, datalen, 1.0); + } + + if ((nbytes = read(print_fd, buffer, sizeof(buffer))) <= 0) + break; + DWORD cbWritten = 0; + if (!WritePrinter(hPrinter, buffer, nbytes, &cbWritten)) { + perror("DEBUG: Unable to send print file to printer"); + break; + } + else { + tbytes += nbytes; + } + } + + EndPagePrinter(hPrinter); + } + + EndDocPrinter(hPrinter); + + ClosePrinter(hPrinter); + + return (CUPS_BACKEND_OK); +} + + +/* + * 'main()' - Send a file to the specified USB port. + * + * Usage: + * + * printer-uri job-id user title copies options [file] + */ + +int /* O - Exit status */ +main(int argc, /* I - Number of command-line arguments (6 or 7) */ + char *argv[]) /* I - Command-line arguments */ +{ + int print_fd; /* Print file */ + int copies; /* Number of copies to print */ + int status; /* Exit status */ + int port; /* Port number (not used) */ + const char *uri; /* Device URI */ + char method[255], /* Method in URI */ + hostname[1024], /* Hostname */ + username[255], /* Username info (not used) */ + resource[1024], /* Resource info (device and options) */ + *options; /* Pointer to options */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) + struct sigaction action; /* Actions for POSIX signals */ +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ + + + /* + * Make sure status messages are not buffered... + */ + + setbuf(stderr, NULL); + + /* + * Ignore SIGPIPE signals... + */ + +#ifdef HAVE_SIGSET + sigset(SIGPIPE, SIG_IGN); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &action, NULL); +#else + signal(SIGPIPE, SIG_IGN); +#endif /* HAVE_SIGSET */ + + /* + * Check command-line... + */ + + if (argc == 1) + { + list_devices(); + return (CUPS_BACKEND_OK); + } + else if (argc < 6 || argc > 7) + { + _cupsLangPrintf(stderr, + _("Usage: %s job-id user title copies options [file]"), + argv[0]); + return (CUPS_BACKEND_FAILED); + } + + /* + * Extract the device name and options from the URI... + */ + + uri = cupsBackendDeviceURI(argv); + + if (httpSeparateURI(HTTP_URI_CODING_ALL, uri, + method, sizeof(method), username, sizeof(username), + hostname, sizeof(hostname), &port, + resource, sizeof(resource)) < HTTP_URI_OK) + { + _cupsLangPrintFilter(stderr, "ERROR", + _("No device URI found in argv[0] or in DEVICE_URI " + "environment variable.")); + return (1); + } + + /* + * See if there are any options... + */ + + if ((options = strchr(resource, '?')) != NULL) + { + /* + * Yup, terminate the device name string and move to the first + * character of the options... + */ + + *options++ = '\0'; + } + + /* + * If we have 7 arguments, print the file named on the command-line. + * Otherwise, send stdin instead... + */ + + if (argc == 6) + { + print_fd = 0; + copies = 1; + } + else + { + /* + * Try to open the print file... + */ + + if ((print_fd = open(argv[6], O_RDONLY)) < 0) + { + _cupsLangPrintError("ERROR", _("Unable to open print file")); + return (CUPS_BACKEND_FAILED); + } + + copies = atoi(argv[4]); + } + + /* + * Finally, send the print file... + */ + + status = print_device(uri, hostname, resource, options, print_fd, copies, + argc, argv); + + /* + * Close the input file and return... + */ + + if (print_fd != 0) + close(print_fd); + + return (status); +} + +/* + * urlencode + */ + +static void +urlenc(char *dst, /* I - Destination buffer */ + const char *src, /* I - Source string */ + size_t dstsize) /* I - Size of destination buffer */ +{ + char *dstend = dst + dstsize - 1; /* End of destination buffer */ + const char hextbl[] = "0123456789abcdef"; + + while (*src && dst < dstend) + { + if (isalnum((int)*src) + || *src == '-' + || *src == '_' + || *src == '.' + || *src == '!' + || *src == '~' + || *src == '*' + || *src == '\'' + || *src == '(' + || *src == ')' + ) { + *dst++ = *src++; + } + else { + *dst++ = '%'; + if (dst < dstend) { + *dst++ = hextbl[(*src >> 4) & 15]; + if (dst < dstend) { + *dst++ = hextbl[(*src) & 15]; + } + } + src ++; + } + } + + *dst = '\0'; +} + +/* + * 'side_cb()' - Handle side-channel requests... + */ + +static int /* O - 0 on success, -1 on error */ +side_cb(int print_fd, /* I - Print file */ + int device_fd, /* I - Device file */ + int snmp_fd, /* I - SNMP socket (unused) */ + http_addr_t *addr, /* I - Device address (unused) */ + int use_bc) /* I - Using back-channel? */ +{ + cups_sc_command_t command; /* Request command */ + cups_sc_status_t status; /* Request/response status */ + char data[2048]; /* Request/response data */ + int datalen; /* Request/response data size */ + + + (void)snmp_fd; + (void)addr; + + datalen = sizeof(data); + + if (cupsSideChannelRead(&command, &status, data, &datalen, 0.0)) + return (-1); + + switch (command) + { + case CUPS_SC_CMD_DRAIN_OUTPUT : + if (backendDrainOutput(print_fd, device_fd)) + status = CUPS_SC_STATUS_IO_ERROR; + else + status = CUPS_SC_STATUS_OK; + + datalen = 0; + break; + + case CUPS_SC_CMD_GET_BIDI : + status = CUPS_SC_STATUS_OK; + datalen = 0; + break; + + default : + status = CUPS_SC_STATUS_NOT_IMPLEMENTED; + datalen = 0; + break; + } + + return (cupsSideChannelWrite(command, status, data, datalen, 1.0)); +}