From c762908074c45baacde1df04b633e01308030864 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 20 Sep 2005 23:28:22 +0000 Subject: r10371: Adding iPrint printing backend written by Joel J. Smith @ Novell. Jeremy. (This used to be commit 155dc2d52a971bfb8d7565c06f3efc637e130b11) --- source3/Makefile.in | 3 +- source3/configure.in | 11 + source3/include/printing.h | 4 + source3/include/smb.h | 2 +- source3/param/loadparm.c | 6 + source3/printing/pcap.c | 10 + source3/printing/print_iprint.c | 1241 +++++++++++++++++++++++++++++++++++++++ source3/printing/printing.c | 6 + 8 files changed, 1281 insertions(+), 2 deletions(-) create mode 100644 source3/printing/print_iprint.c (limited to 'source3') diff --git a/source3/Makefile.in b/source3/Makefile.in index 3a5f31a1b5..e6e0e66694 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -410,7 +410,8 @@ SMBD_OBJ_BASE = $(PARAM_OBJ) $(SMBD_OBJ_SRV) $(LIBSMB_OBJ) \ PRINTING_OBJ = printing/pcap.o printing/print_svid.o printing/print_aix.o \ printing/print_cups.o printing/print_generic.o \ - printing/lpq_parse.o printing/load.o + printing/lpq_parse.o printing/load.o \ + printing/print_iprint.o PRINTBASE_OBJ = printing/notify.o printing/printing_db.o diff --git a/source3/configure.in b/source3/configure.in index dbb27d1a60..a3fac89514 100644 --- a/source3/configure.in +++ b/source3/configure.in @@ -862,6 +862,17 @@ if test x$enable_cups != xno; then fi fi +AC_ARG_ENABLE(iprint, +[ --enable-iprint Turn on iPrint support (default=yes if cups is yes)]) + +if test x$enable_iprint != xno; then + if test "x$CUPS_CONFIG" != x; then + AC_DEFINE(HAVE_IPRINT,1,[Whether we have iPrint]) + elif test x"$enable_iprint" = x"yes"; then + AC_MSG_ERROR(iPrint support required but cups not enabled. Make sure cups-devel related files are installed and that cups is enabled.) + fi +fi + ############################################ # we need dlopen/dlclose/dlsym/dlerror for PAM, the password database plugins and the plugin loading code AC_SEARCH_LIBS(dlopen, [dl]) diff --git a/source3/include/printing.h b/source3/include/printing.h index 43ff8dd39e..220fd08ef1 100644 --- a/source3/include/printing.h +++ b/source3/include/printing.h @@ -69,6 +69,10 @@ extern struct printif generic_printif; extern struct printif cups_printif; #endif /* HAVE_CUPS */ +#ifdef HAVE_IPRINT +extern struct printif iprint_printif; +#endif /* HAVE_IPRINT */ + /* PRINT_MAX_JOBID is now defined in local.h */ #define UNIX_JOB_START PRINT_MAX_JOBID #define NEXT_JOBID(j) ((j+1) % PRINT_MAX_JOBID > 0 ? (j+1) % PRINT_MAX_JOBID : 1) diff --git a/source3/include/smb.h b/source3/include/smb.h index d3374ccafc..c0778383c6 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -1390,7 +1390,7 @@ enum server_types /* printing types */ enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX, PRINT_QNX,PRINT_PLP,PRINT_LPRNG,PRINT_SOFTQ, - PRINT_CUPS,PRINT_LPRNT,PRINT_LPROS2 + PRINT_CUPS,PRINT_LPRNT,PRINT_LPROS2,PRINT_IPRINT #ifdef DEVELOPER ,PRINT_TEST,PRINT_VLP #endif /* DEVELOPER */ diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index 3c97a3bb37..7c5a17b86f 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -241,6 +241,7 @@ typedef struct char *szLdapAdminDn; char *szAclCompat; char *szCupsServer; + char *szIPrintServer; int ldap_passwd_sync; int ldap_replication_sleep; int ldap_timeout; /* This is initialised in init_globals */ @@ -641,6 +642,7 @@ static const struct enum_list enum_printing[] = { {PRINT_PLP, "plp"}, {PRINT_LPRNG, "lprng"}, {PRINT_CUPS, "cups"}, + {PRINT_IPRINT, "iprint"}, {PRINT_LPRNT, "nt"}, {PRINT_LPROS2, "os2"}, #ifdef DEVELOPER @@ -1007,6 +1009,7 @@ static struct parm_struct parm_table[] = { {"printing", P_ENUM, P_LOCAL, &sDefault.iPrinting, handle_printing, enum_printing, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, {"cups options", P_STRING, P_LOCAL, &sDefault.szCupsOptions, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, {"cups server", P_STRING, P_GLOBAL, &Globals.szCupsServer, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, + {"iprint server", P_STRING, P_GLOBAL, &Globals.szIPrintServer, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, {"disable spoolss", P_BOOL, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_ADVANCED | FLAG_PRINT | FLAG_GLOBAL}, {"enable spoolss", P_BOOLREV, P_GLOBAL, &Globals.bDisableSpoolss, NULL, NULL, FLAG_HIDE}, @@ -1273,6 +1276,7 @@ static void init_printer_values(service *pService) break; case PRINT_CUPS: + case PRINT_IPRINT: #ifdef HAVE_CUPS /* set the lpq command to contain the destination printer name only. This is used by cups_queue_get() */ @@ -1570,6 +1574,7 @@ static void init_globals(void) string_set(&Globals.szWinbindSeparator, "\\"); string_set(&Globals.szAclCompat, ""); string_set(&Globals.szCupsServer, ""); + string_set(&Globals.szIPrintServer, ""); string_set(&Globals.szEventLogOpenCommand, ""); string_set(&Globals.szEventLogReadCommand, ""); @@ -1902,6 +1907,7 @@ FN_LOCAL_LIST(lp_admin_users, szAdminUsers) FN_GLOBAL_LIST(lp_enable_svcctl, &Globals.szServicesList) FN_LOCAL_STRING(lp_cups_options, szCupsOptions) FN_GLOBAL_STRING(lp_cups_server, &Globals.szCupsServer) +FN_GLOBAL_STRING(lp_iprint_server, &Globals.szIPrintServer) FN_LOCAL_STRING(lp_printcommand, szPrintcommand) FN_LOCAL_STRING(lp_lpqcommand, szLpqcommand) FN_LOCAL_STRING(lp_lprmcommand, szLprmcommand) diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c index e9d8195366..9b4b5e4f71 100644 --- a/source3/printing/pcap.c +++ b/source3/printing/pcap.c @@ -56,6 +56,9 @@ * * Modified to call CUPS support if printcap name is set to "cups" * in smb.conf. + * + * Modified to call iPrint support if printcap name is set to "iprint" + * in smb.conf. */ #include "includes.h" @@ -129,6 +132,13 @@ void pcap_cache_reload(void) } #endif +#ifdef HAVE_IPRINT + if (strequal(pcap_name, "iprint")) { + pcap_reloaded = iprint_cache_reload(); + goto done; + } +#endif + #if defined(SYSV) || defined(HPUX) if (strequal(pcap_name, "lpstat")) { pcap_reloaded = sysv_cache_reload(); diff --git a/source3/printing/print_iprint.c b/source3/printing/print_iprint.c new file mode 100644 index 0000000000..33bbcb256a --- /dev/null +++ b/source3/printing/print_iprint.c @@ -0,0 +1,1241 @@ +/* + * Support code for Novell iPrint using the Common UNIX Printing + * System ("CUPS") libraries + * + * Copyright 1999-2003 by Michael R Sweet. + * Portions Copyright 2005 by Joel J. Smith. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "includes.h" +#include "printing.h" + +#ifdef HAVE_IPRINT +#include +#include + +#define OPERATION_NOVELL_LIST_PRINTERS 0x401A +#define OPERATION_NOVELL_MGMT 0x401C +#define NOVELL_SERVER_SYSNAME "sysname=" +#define NOVELL_SERVER_SYSNAME_NETWARE "NetWare IA32" +#define NOVELL_SERVER_VERSION_STRING "iprintserverversion=" +#define NOVELL_SERVER_VERSION_OES_SP1 33554432 + +/* + * 'iprint_passwd_cb()' - The iPrint password callback... + */ + +static const char * /* O - Password or NULL */ +iprint_passwd_cb(const char *prompt) /* I - Prompt */ +{ + /* + * Always return NULL to indicate that no password is available... + */ + + return (NULL); +} + +static const char *iprint_server(void) +{ + if ((lp_iprint_server() != NULL) && (strlen(lp_iprint_server()) > 0)) { + DEBUG(10, ("iprint server explicitly set to %s\n", + lp_iprint_server())); + return lp_iprint_server(); + } + + DEBUG(10, ("iprint server left to default %s\n", cupsServer())); + return cupsServer(); +} + +/* + * Pass in an already connected http_t* + * Returns the server version if one can be found, multiplied by + * -1 for all NetWare versions. Returns 0 if a server version + * cannot be determined + */ + +static int iprint_get_server_version(http_t *http, char* serviceUri) +{ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + char *ver; /* server version pointer */ + char *vertmp; /* server version tmp pointer */ + int serverVersion = 0; /* server version */ + char *os; /* server os */ + int osFlag = 0; /* 0 for NetWare, 1 for anything else */ + char *temp; /* pointer for string manipulation */ + + /* + * Build an OPERATION_NOVELL_MGMT("get-server-version") request, + * which requires the following attributes: + * + * attributes-charset + * attributes-natural-language + * operation-name + * service-uri + */ + + request = ippNew(); + + request->request.op.operation_id = OPERATION_NOVELL_MGMT; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "service-uri", NULL, serviceUri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "operation-name", NULL, "get-server-version"); + + /* + * Do the request and get back a response... + */ + + if (((response = cupsDoRequest(http, request, "/ipp/")) == NULL) || + (response->request.status.status_code >= IPP_OK_CONFLICT)) + goto out; + + if (((attr = ippFindAttribute(response, "server-version", + IPP_TAG_STRING)) != NULL)) { + if ((ver = strstr(attr->values[0].string.text, + NOVELL_SERVER_VERSION_STRING)) != NULL) { + ver += strlen(NOVELL_SERVER_VERSION_STRING); + /* + * Strangely, libcups stores a IPP_TAG_STRING (octet + * string) as a null-terminated string with no length + * even though it could be binary data with nulls in + * it. Luckily, in this case the value is not binary. + */ + serverVersion = strtol(ver, &vertmp, 10); + + /* Check for not found, overflow or negative version */ + if ((ver == vertmp) || (serverVersion < 0)) + serverVersion = 0; + } + + if ((os = strstr(attr->values[0].string.text, + NOVELL_SERVER_SYSNAME)) != NULL) { + os += strlen(NOVELL_SERVER_SYSNAME); + if ((temp = strchr(os,'<')) != NULL) + *temp = '\0'; + if (strcmp(os,NOVELL_SERVER_SYSNAME_NETWARE)) + osFlag = 1; /* 1 for non-NetWare systems */ + } + } + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (osFlag == 0) + serverVersion *= -1; + + return serverVersion; +} + + +static int iprint_cache_add_printer(http_t *http, + int reqId, + char* url) +{ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + char *name, /* printer-name attribute */ + *info, /* printer-info attribute */ + smb_enabled, /* smb-enabled attribute */ + secure; /* security-enabled attrib. */ + + char *httpPath; /* path portion of the printer-uri */ + + static const char *pattrs[] = /* Requested printer attributes */ + { + "printer-name", + "security-enabled", + "printer-info", + "smb-enabled" + }; + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; + request->request.op.request_id = reqId; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, url); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (sizeof(pattrs) / sizeof(pattrs[0])), + NULL, pattrs); + + /* + * Do the request and get back a response... + */ + + if ((httpPath = strstr(url,"://")) == NULL || + (httpPath = strchr(httpPath+3,'/')) == NULL) + { + ippDelete(request); + request = NULL; + goto out; + } + + if ((response = cupsDoRequest(http, request, httpPath)) == NULL) { + ipp_status_t lastErr = cupsLastError(); + + /* + * Ignore printers that cannot be queried without credentials + */ + if (lastErr == IPP_FORBIDDEN || + lastErr == IPP_NOT_AUTHENTICATED || + lastErr == IPP_NOT_AUTHORIZED) + goto out; + + DEBUG(0,("Unable to get printer list - %s\n", + ippErrorString(lastErr))); + goto out; + } + + for (attr = response->attrs; attr != NULL;) { + /* + * Skip leading attributes until we hit a printer... + */ + + while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) + attr = attr->next; + + if (attr == NULL) + break; + + /* + * Pull the needed attributes from this printer... + */ + + name = NULL; + info = NULL; + smb_enabled= 1; + secure = 0; + + while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) { + if (strcmp(attr->name, "printer-name") == 0 && + attr->value_tag == IPP_TAG_NAME) + name = attr->values[0].string.text; + + if (strcmp(attr->name, "printer-info") == 0 && + (attr->value_tag == IPP_TAG_TEXT || + attr->value_tag == IPP_TAG_TEXTLANG)) + info = attr->values[0].string.text; + + /* + * If the smb-enabled attribute is present and the + * value is set to 0, don't show the printer. + * If the attribute is not present, assume that the + * printer should show up + */ + if (!strcmp(attr->name, "smb-enabled") && + ((attr->value_tag == IPP_TAG_INTEGER && + !attr->values[0].integer) || + (attr->value_tag == IPP_TAG_BOOLEAN && + !attr->values[0].boolean))) + smb_enabled = 0; + + /* + * If the security-enabled attribute is present and the + * value is set to 1, don't show the printer. + * If the attribute is not present, assume that the + * printer should show up + */ + if (!strcmp(attr->name, "security-enabled") && + ((attr->value_tag == IPP_TAG_INTEGER && + attr->values[0].integer) || + (attr->value_tag == IPP_TAG_BOOLEAN && + attr->values[0].boolean))) + secure = 1; + + attr = attr->next; + } + + /* + * See if we have everything needed... + * Make sure the printer is not a secure printer + * and make sure smb printing hasn't been explicitly + * disabled for the printer + */ + + if (name != NULL && !secure && smb_enabled) + pcap_cache_add(name, info); + } + + out: + if (response) + ippDelete(response); + return(0); +} + +BOOL iprint_cache_reload(void) +{ + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + int i; + BOOL ret = False; + + DEBUG(5, ("reloading iprint printcap cache\n")); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Build a OPERATION_NOVELL_LIST_PRINTERS request, which requires the following attributes: + * + * attributes-charset + * attributes-natural-language + */ + + request = ippNew(); + + request->request.op.operation_id = OPERATION_NOVELL_LIST_PRINTERS; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "ipp-server", NULL, "ippSrvr"); + + /* + * Do the request and get back a response... + */ + + if ((response = cupsDoRequest(http, request, "/ipp")) == NULL) { + DEBUG(0,("Unable to get printer list - %s\n", + ippErrorString(cupsLastError()))); + goto out; + } + + for (attr = response->attrs; attr != NULL;) { + /* + * Skip leading attributes until we hit a printer... + */ + + while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER) + attr = attr->next; + + if (attr == NULL) + break; + + /* + * Pull the needed attributes from this printer... + */ + + while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) + { + if (strcmp(attr->name, "printer-name") == 0 && + (attr->value_tag == IPP_TAG_URI || + attr->value_tag == IPP_TAG_NAME || + attr->value_tag == IPP_TAG_TEXT || + attr->value_tag == IPP_TAG_NAMELANG || + attr->value_tag == IPP_TAG_TEXTLANG)) + { + for (i = 0; inum_values; i++) + { + char *url = attr->values[i].string.text; + if (!url || !strlen(url)) + continue; + iprint_cache_add_printer(http, i+2, url); + } + } + attr = attr->next; + } + } + + ret = True; + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return ret; +} + + +/* + * 'iprint_job_delete()' - Delete a job. + */ + +static int iprint_job_delete(int snum, struct printjob *pjob) +{ + int ret = 1; /* Return value */ + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */ + + + DEBUG(5,("iprint_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Build an IPP_CANCEL_JOB request, which uses the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * job-id + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_CANCEL_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); + + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum)); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } else { + ret = 0; + } + } else { + DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return ret; +} + + +/* + * 'iprint_job_pause()' - Pause a job. + */ + +static int iprint_job_pause(int snum, struct printjob *pjob) +{ + int ret = 1; /* Return value */ + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */ + + + DEBUG(5,("iprint_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Build an IPP_HOLD_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * job-id + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_HOLD_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); + + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum)); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } else { + ret = 0; + } + } else { + DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return ret; +} + + +/* + * 'iprint_job_resume()' - Resume a paused job. + */ + +static int iprint_job_resume(int snum, struct printjob *pjob) +{ + int ret = 1; /* Return value */ + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */ + + + DEBUG(5,("iprint_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Build an IPP_RELEASE_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * job-id + * requesting-user-name + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_RELEASE_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri); + + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + /* + * Do the request and get back a response... + */ + + slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum)); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } else { + ret = 0; + } + } else { + DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob, + ippErrorString(cupsLastError()))); + } + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return ret; +} + + +/* + * 'iprint_job_submit()' - Submit a job for printing. + */ + +static int iprint_job_submit(int snum, struct printjob *pjob) +{ + int ret = 1; /* Return value */ + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char *clientname = NULL; /* hostname of client for job-originating-host attribute */ + + DEBUG(5,("iprint_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Build an IPP_PRINT_JOB request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * printer-uri + * requesting-user-name + * [document-data] + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_PRINT_JOB; + request->request.op.request_id = 1; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum)); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", + NULL, pjob->user); + + clientname = client_name(); + if (strcmp(clientname, "UNKNOWN") == 0) { + clientname = client_addr(); + } + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "job-originating-host-name", NULL, + clientname); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, + pjob->jobname); + + /* + * Do the request and get back a response... + */ + + slprintf(uri, sizeof(uri) - 1, "/ipp/%s", PRINTERNAME(snum)); + + if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) { + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + } else { + ret = 0; + } + } else { + DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum), + ippErrorString(cupsLastError()))); + } + + if ( ret == 0 ) + unlink(pjob->filename); + /* else print_job_end will do it for us */ + + if ( ret == 0 ) { + + attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER); + if (attr != NULL && attr->group_tag == IPP_TAG_JOB) + { + pjob->sysjob = attr->values[0].integer; + } + } + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return ret; +} + +/* + * 'iprint_queue_get()' - Get all the jobs in the print queue. + */ + +static int iprint_queue_get(const char *sharename, + enum printing_types printing_type, + char *lpq_command, + print_queue_struct **q, + print_status_struct *status) +{ + fstring printername; + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr = NULL; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char serviceUri[HTTP_MAX_URI]; /* service-uri attribute */ + char httpPath[HTTP_MAX_URI]; /* path portion of the uri */ + int jobUseUnixTime = 0; /* Whether job times should + * be assumed to be Unix time */ + int qcount = 0, /* Number of active queue entries */ + qalloc = 0; /* Number of queue entries allocated */ + print_queue_struct *queue = NULL, /* Queue entries */ + *temp; /* Temporary pointer for queue */ + const char *user_name, /* job-originating-user-name attribute */ + *job_name; /* job-name attribute */ + int job_id; /* job-id attribute */ + int job_k_octets; /* job-k-octets attribute */ + time_t job_time; /* time-at-creation attribute */ + time_t printer_current_time = 0; /* printer's current time */ + time_t printer_up_time = 0; /* printer's uptime */ + ipp_jstate_t job_status; /* job-status attribute */ + int job_priority; /* job-priority attribute */ + static const char *jattrs[] = /* Requested job attributes */ + { + "job-id", + "job-k-octets", + "job-name", + "job-originating-user-name", + "job-priority", + "job-state", + "time-at-creation", + }; + static const char *pattrs[] = /* Requested printer attributes */ + { + "printer-state", + "printer-state-message", + "printer-current-time", + "printer-up-time" + }; + + *q = NULL; + + /* HACK ALERT!!! The porblem with support the 'printer name' + option is that we key the tdb off the sharename. So we will + overload the lpq_command string to pass in the printername + (which is basically what we do for non-cups printers ... using + the lpq_command to get the queue listing). */ + + fstrcpy( printername, lpq_command ); + + DEBUG(5,("iprint_queue_get(%s, %p, %p)\n", printername, q, status)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + + if ((http = httpConnect(iprint_server(), ippPort())) == NULL) { + DEBUG(0,("Unable to connect to iPrint server %s - %s\n", + iprint_server(), strerror(errno))); + goto out; + } + + /* + * Generate the printer URI and the service URI that goes with it... + */ + + slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), printername); + slprintf(serviceUri, sizeof(serviceUri) - 1, "ipp://%s/ipp/", iprint_server()); + + /* + * For Linux iPrint servers from OES SP1 on, the iPrint server + * uses Unix time for job start times unless it detects the iPrint + * client in an http User-Agent header. (This was done to accomodate + * CUPS broken behavior. According to RFC 2911, section 4.3.14, job + * start times are supposed to be relative to how long the printer has + * been up.) Since libcups doesn't allow us to set that header before + * the request is sent, this ugly hack allows us to detect the server + * version and decide how to interpret the job time. + */ + if (iprint_get_server_version(http, serviceUri) >= + NOVELL_SERVER_VERSION_OES_SP1) + jobUseUnixTime = 1; + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; + request->request.op.request_id = 2; + + language = cupsLangDefault(); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (sizeof(pattrs) / sizeof(pattrs[0])), + NULL, pattrs); + + /* + * Do the request and get back a response... + */ + + slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername); + + if ((response = cupsDoRequest(http, request, httpPath)) == NULL) { + DEBUG(0,("Unable to get printer status for %s - %s\n", printername, + ippErrorString(cupsLastError()))); + *q = queue; + goto out; + } + + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to get printer status for %s - %s\n", printername, + ippErrorString(response->request.status.status_code))); + *q = queue; + goto out; + } + + /* + * Get the current printer status and convert it to the SAMBA values. + */ + + if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) { + if (attr->values[0].integer == IPP_PRINTER_STOPPED) + status->status = LPSTAT_STOPPED; + else + status->status = LPSTAT_OK; + } + + if ((attr = ippFindAttribute(response, "printer-state-message", + IPP_TAG_TEXT)) != NULL) + fstrcpy(status->message, attr->values[0].string.text); + + if ((attr = ippFindAttribute(response, "printer-current-time", + IPP_TAG_DATE)) != NULL) + printer_current_time = ippDateToTime(attr->values[0].date); + + if ((attr = ippFindAttribute(response, "printer-up-time", + IPP_TAG_INTEGER)) != NULL) + printer_up_time = attr->values[0].integer; + + ippDelete(response); + response = NULL; + + /* + * Build an IPP_GET_JOBS request, which requires the following + * attributes: + * + * attributes-charset + * attributes-natural-language + * requested-attributes + * printer-uri + */ + + request = ippNew(); + + request->request.op.operation_id = IPP_GET_JOBS; + request->request.op.request_id = 3; + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, "utf-8"); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, + "requested-attributes", + (sizeof(jattrs) / sizeof(jattrs[0])), + NULL, jattrs); + + /* + * Do the request and get back a response... + */ + + slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername); + + if ((response = cupsDoRequest(http, request, httpPath)) == NULL) { + DEBUG(0,("Unable to get jobs for %s - %s\n", uri, + ippErrorString(cupsLastError()))); + goto out; + } + + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to get jobs for %s - %s\n", uri, + ippErrorString(response->request.status.status_code))); + goto out; + } + + /* + * Process the jobs... + */ + + qcount = 0; + qalloc = 0; + queue = NULL; + + for (attr = response->attrs; attr != NULL; attr = attr->next) { + /* + * Skip leading attributes until we hit a job... + */ + + while (attr != NULL && attr->group_tag != IPP_TAG_JOB) + attr = attr->next; + + if (attr == NULL) + break; + + /* + * Allocate memory as needed... + */ + if (qcount >= qalloc) { + qalloc += 16; + + temp = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc); + + if (temp == NULL) { + DEBUG(0,("iprint_queue_get: Not enough memory!")); + qcount = 0; + SAFE_FREE(queue); + goto out; + } + + queue = temp; + } + + temp = queue + qcount; + memset(temp, 0, sizeof(print_queue_struct)); + + /* + * Pull the needed attributes from this job... + */ + + job_id = 0; + job_priority = 50; + job_status = IPP_JOB_PENDING; + job_time = 0; + job_k_octets = 0; + user_name = NULL; + job_name = NULL; + + while (attr != NULL && attr->group_tag == IPP_TAG_JOB) { + if (attr->name == NULL) { + attr = attr->next; + break; + } + + if (strcmp(attr->name, "job-id") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_id = attr->values[0].integer; + + if (strcmp(attr->name, "job-k-octets") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_k_octets = attr->values[0].integer; + + if (strcmp(attr->name, "job-priority") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + job_priority = attr->values[0].integer; + + if (strcmp(attr->name, "job-state") == 0 && + attr->value_tag == IPP_TAG_ENUM) + job_status = (ipp_jstate_t)(attr->values[0].integer); + + if (strcmp(attr->name, "time-at-creation") == 0 && + attr->value_tag == IPP_TAG_INTEGER) + { + /* + * If jobs times are in Unix time, the accuracy of the job + * start time depends upon the iPrint server's time being + * set correctly. Otherwise, the accuracy depends upon + * the Samba server's time being set correctly + */ + + if (jobUseUnixTime) + job_time = attr->values[0].integer; + else + job_time = time(NULL) - printer_up_time + attr->values[0].integer; + } + + if (strcmp(attr->name, "job-name") == 0 && + (attr->value_tag == IPP_TAG_NAMELANG || + attr->value_tag == IPP_TAG_NAME)) + job_name = attr->values[0].string.text; + + if (strcmp(attr->name, "job-originating-user-name") == 0 && + (attr->value_tag == IPP_TAG_NAMELANG || + attr->value_tag == IPP_TAG_NAME)) + user_name = attr->values[0].string.text; + + attr = attr->next; + } + + /* + * See if we have everything needed... + */ + + if (user_name == NULL || job_name == NULL || job_id == 0) { + if (attr == NULL) + break; + else + continue; + } + + temp->job = job_id; + temp->size = job_k_octets * 1024; + temp->status = job_status == IPP_JOB_PENDING ? LPQ_QUEUED : + job_status == IPP_JOB_STOPPED ? LPQ_PAUSED : + job_status == IPP_JOB_HELD ? LPQ_PAUSED : + LPQ_PRINTING; + temp->priority = job_priority; + temp->time = job_time; + strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1); + strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1); + + qcount ++; + + if (attr == NULL) + break; + } + + /* + * Return the job queue... + */ + + *q = queue; + + out: + if (response) + ippDelete(response); + + if (language) + cupsLangFree(language); + + if (http) + httpClose(http); + + return qcount; +} + + +/* + * 'iprint_queue_pause()' - Pause a print queue. + */ + +static int iprint_queue_pause(int snum) +{ + return(-1); //Not supported without credentials +} + + +/* + * 'iprint_queue_resume()' - Restart a print queue. + */ + +static int iprint_queue_resume(int snum) +{ + return(-1); //Not supported without credentials +} + +/******************************************************************* + * iPrint printing interface definitions... + ******************************************************************/ + +struct printif iprint_printif = +{ + PRINT_IPRINT, + iprint_queue_get, + iprint_queue_pause, + iprint_queue_resume, + iprint_job_delete, + iprint_job_pause, + iprint_job_resume, + iprint_job_submit, +}; + +#else + /* this keeps fussy compilers happy */ + void print_iprint_dummy(void) {} +#endif /* HAVE_IPRINT */ diff --git a/source3/printing/printing.c b/source3/printing/printing.c index 52a3070466..397ed3b355 100644 --- a/source3/printing/printing.c +++ b/source3/printing/printing.c @@ -244,6 +244,12 @@ static struct printif *get_printer_fns_from_type( int type ) } #endif /* HAVE_CUPS */ +#ifdef HAVE_IPRINT + if ( type == PRINT_IPRINT ) { + printer_fns = &iprint_printif; + } +#endif /* HAVE_IPRINT */ + printer_fns->type = type; return printer_fns; -- cgit