diff options
Diffstat (limited to 'source3/printing/print_iprint.c')
-rw-r--r-- | source3/printing/print_iprint.c | 1367 |
1 files changed, 1367 insertions, 0 deletions
diff --git a/source3/printing/print_iprint.c b/source3/printing/print_iprint.c new file mode 100644 index 0000000..f10dee0 --- /dev/null +++ b/source3/printing/print_iprint.c @@ -0,0 +1,1367 @@ +/* + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "printing.h" +#include "printing/pcap.h" +#include "lib/util/string_wrappers.h" + +#ifdef HAVE_IPRINT +#include <cups/cups.h> +#include <cups/language.h> + +#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 + +#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5) +#define HAVE_CUPS_1_6 1 +#endif + +#ifndef HAVE_CUPS_1_6 +#define ippGetCount(attr) attr->num_values +#define ippGetGroupTag(attr) attr->group_tag +#define ippGetName(attr) attr->name +#define ippGetValueTag(attr) attr->value_tag +#define ippGetStatusCode(ipp) ipp->request.status.status_code +#define ippGetBoolean(attr, element) attr->values[element].boolean +#define ippGetInteger(attr, element) attr->values[element].integer +#define ippGetString(attr, element, language) attr->values[element].string.text + +static ipp_attribute_t * +ippFirstAttribute(ipp_t *ipp) +{ + if (!ipp) + return (NULL); + return (ipp->current = ipp->attrs); +} + +static ipp_attribute_t * +ippNextAttribute(ipp_t *ipp) +{ + if (!ipp || !ipp->current) + return (NULL); + return (ipp->current = ipp->current->next); +} + +static int ippSetOperation(ipp_t *ipp, ipp_op_t op) +{ + ipp->request.op.operation_id = op; + return (1); +} + +static int ippSetRequestId(ipp_t *ipp, int request_id) +{ + ipp->request.any.request_id = request_id; + return (1); +} +#endif + +/* + * '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) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *server = lp_iprint_server(talloc_tos(), lp_sub); + + if ((server != NULL) && (strlen(server) > 0)) { + DEBUG(10, ("iprint server explicitly set to %s\n", + server)); + return 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(); + + ippSetOperation(request, (ipp_op_t)OPERATION_NOVELL_MGMT); + ippSetRequestId(request, 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) || + (ippGetStatusCode(response) >= IPP_OK_CONFLICT)) + goto out; + + if (((attr = ippFindAttribute(response, "server-version", + IPP_TAG_STRING)) != NULL)) { + if ((ver = strstr(ippGetString(attr, 0, NULL), + 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(ippGetString(attr, 0, NULL), + 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, + const char *url, + struct pcap_cache **pcache) +{ + ipp_t *request = NULL, /* IPP Request */ + *response = NULL; /* IPP Response */ + ipp_attribute_t *attr; /* Current attribute */ + cups_lang_t *language = NULL; /* Default language */ + const char *name, /* printer-name attribute */ + *info; /* printer-info attribute */ + char smb_enabled, /* smb-enabled attribute */ + secure; /* security-enabled attrib. */ + + const 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(); + + ippSetOperation(request, IPP_GET_PRINTER_ATTRIBUTES); + ippSetRequestId(request, 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 = ippFirstAttribute(response); attr != NULL;) { + /* + * Skip leading attributes until we hit a printer... + */ + + while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) + attr = ippNextAttribute(response); + + if (attr == NULL) + break; + + /* + * Pull the needed attributes from this printer... + */ + + name = NULL; + info = NULL; + smb_enabled= 1; + secure = 0; + + while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) { + if (strcmp(ippGetName(attr), "printer-name") == 0 && + ippGetValueTag(attr) == IPP_TAG_NAME) + name = ippGetString(attr, 0, NULL); + + if (strcmp(ippGetName(attr), "printer-info") == 0 && + (ippGetValueTag(attr) == IPP_TAG_TEXT || + ippGetValueTag(attr) == IPP_TAG_TEXTLANG)) + info = ippGetString(attr, 0, NULL); + + /* + * 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(ippGetName(attr), "smb-enabled") && + ((ippGetValueTag(attr) == IPP_TAG_INTEGER && + !ippGetInteger(attr, 0)) || + (ippGetValueTag(attr) == IPP_TAG_BOOLEAN && + !ippGetBoolean(attr, 0)))) + 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(ippGetName(attr), "security-enabled") && + ((ippGetValueTag(attr) == IPP_TAG_INTEGER && + ippGetInteger(attr, 0)) || + (ippGetValueTag(attr) == IPP_TAG_BOOLEAN && + ippGetBoolean(attr, 0)))) + secure = 1; + + attr = ippNextAttribute(response); + } + + /* + * 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_specific(pcache, name, info, NULL); + } + + out: + if (response) + ippDelete(response); + return(0); +} + +bool iprint_cache_reload(struct pcap_cache **_pcache) +{ + 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; + struct pcap_cache *pcache = NULL; + + 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... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, (ipp_op_t)OPERATION_NOVELL_LIST_PRINTERS); + ippSetRequestId(request, 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 = ippFirstAttribute(response); attr != NULL;) { + /* + * Skip leading attributes until we hit a printer... + */ + + while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER) + attr = ippNextAttribute(response); + + if (attr == NULL) + break; + + /* + * Pull the needed attributes from this printer... + */ + + while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) + { + if (strcmp(ippGetName(attr), "printer-name") == 0 && + (ippGetValueTag(attr) == IPP_TAG_URI || + ippGetValueTag(attr) == IPP_TAG_NAME || + ippGetValueTag(attr) == IPP_TAG_TEXT || + ippGetValueTag(attr) == IPP_TAG_NAMELANG || + ippGetValueTag(attr) == IPP_TAG_TEXTLANG)) + { + for (i = 0; i<ippGetCount(attr); i++) + { + const char *url = ippGetString(attr, i, NULL); + if (!url || !strlen(url)) + continue; + iprint_cache_add_printer(http, i+2, url, + &pcache); + } + } + attr = ippNextAttribute(response); + } + } + + ret = true; + *_pcache = pcache; + + 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(const char *sharename, const char *lprm_command, 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(%s, %p (%d))\n", sharename, pjob, pjob->sysjob)); + + /* + * Make sure we don't ask for passwords... + */ + + cupsSetPasswordCB(iprint_passwd_cb); + + /* + * Try to connect to the server... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, IPP_CANCEL_JOB); + ippSetRequestId(request, 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(), sharename); + + 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", sharename); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (ippGetStatusCode(response) >= 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 */ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + + 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... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, IPP_HOLD_JOB); + ippSetRequestId(request, 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(), + lp_printername(talloc_tos(), lp_sub, 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", + lp_printername(talloc_tos(), lp_sub, snum)); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (ippGetStatusCode(response) >= 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 */ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + + 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... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, IPP_RELEASE_JOB); + ippSetRequestId(request, 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(), + lp_printername(talloc_tos(), lp_sub, 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", + lp_printername(talloc_tos(), lp_sub, snum)); + + if ((response = cupsDoRequest(http, request, httpPath)) != NULL) { + if (ippGetStatusCode(response) >= 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, + enum printing_types printing_type, + char *lpq_cmd) +{ + 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 */ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + 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... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, IPP_PRINT_JOB); + ippSetRequestId(request, 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(), + lp_printername(talloc_tos(), lp_sub, 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); + + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "job-originating-host-name", NULL, + pjob->clientmachine); + + 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", lp_printername(talloc_tos(), lp_sub, snum)); + + if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) { + if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to print file to %s - %s\n", + lp_printername(talloc_tos(), lp_sub, snum), + ippErrorString(cupsLastError()))); + } else { + ret = 0; + } + } else { + DEBUG(0,("Unable to print file to `%s' - %s\n", + lp_printername(talloc_tos(), lp_sub, 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 && ippGetGroupTag(attr) == IPP_TAG_JOB) + { + pjob->sysjob = ippGetInteger(attr, 0); + } + } + + 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_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... + */ + +#ifdef HAVE_HTTPCONNECT2 + http = httpConnect2(iprint_server(), + ippPort(), + NULL, + AF_UNSPEC, + HTTP_ENCRYPTION_NEVER, + 1, /* blocking */ + 30 * 1000, /* timeout */ + NULL); +#else + http = httpConnect(iprint_server(), ippPort()); +#endif + if (http == 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(); + + ippSetOperation(request, IPP_GET_PRINTER_ATTRIBUTES); + ippSetRequestId(request, 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 (ippGetStatusCode(response) >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to get printer status for %s - %s\n", printername, + ippErrorString(ippGetStatusCode(response)))); + *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 (ippGetInteger(attr, 0) == 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, ippGetString(attr, 0, NULL)); + + if ((attr = ippFindAttribute(response, "printer-up-time", + IPP_TAG_INTEGER)) != NULL) + printer_up_time = ippGetInteger(attr, 0); + + 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(); + + ippSetOperation(request, IPP_GET_JOBS); + ippSetRequestId(request, 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 (ippGetStatusCode(response) >= IPP_OK_CONFLICT) { + DEBUG(0,("Unable to get jobs for %s - %s\n", uri, + ippErrorString(ippGetStatusCode(response)))); + goto out; + } + + /* + * Process the jobs... + */ + + qcount = 0; + qalloc = 0; + queue = NULL; + + for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) { + /* + * Skip leading attributes until we hit a job... + */ + + while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_JOB) + attr = ippNextAttribute(response); + + if (attr == NULL) + break; + + /* + * Allocate memory as needed... + */ + if (qcount >= qalloc) { + qalloc += 16; + + queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc); + + if (queue == NULL) { + DEBUG(0,("iprint_queue_get: Not enough memory!")); + qcount = 0; + goto out; + } + } + + 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 && ippGetGroupTag(attr) == IPP_TAG_JOB) { + if (ippGetName(attr) == NULL) { + attr = ippNextAttribute(response); + break; + } + + if (strcmp(ippGetName(attr), "job-id") == 0 && + ippGetValueTag(attr) == IPP_TAG_INTEGER) + job_id = ippGetInteger(attr, 0); + + if (strcmp(ippGetName(attr), "job-k-octets") == 0 && + ippGetValueTag(attr) == IPP_TAG_INTEGER) + job_k_octets = ippGetInteger(attr, 0); + + if (strcmp(ippGetName(attr), "job-priority") == 0 && + ippGetValueTag(attr) == IPP_TAG_INTEGER) + job_priority = ippGetInteger(attr, 0); + + if (strcmp(ippGetName(attr), "job-state") == 0 && + ippGetValueTag(attr) == IPP_TAG_ENUM) + job_status = (ipp_jstate_t)ippGetInteger(attr, 0); + + if (strcmp(ippGetName(attr), "time-at-creation") == 0 && + ippGetValueTag(attr) == 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 = ippGetInteger(attr, 0); + else + job_time = time(NULL) - printer_up_time + ippGetInteger(attr, 0); + } + + if (strcmp(ippGetName(attr), "job-name") == 0 && + (ippGetValueTag(attr) == IPP_TAG_NAMELANG || + ippGetValueTag(attr) == IPP_TAG_NAME)) + job_name = ippGetString(attr, 0, NULL); + + if (strcmp(ippGetName(attr), "job-originating-user-name") == 0 && + (ippGetValueTag(attr) == IPP_TAG_NAMELANG || + ippGetValueTag(attr) == IPP_TAG_NAME)) + user_name = ippGetString(attr, 0, NULL); + + attr = ippNextAttribute(response); + } + + /* + * See if we have everything needed... + */ + + if (user_name == NULL || job_name == NULL || job_id == 0) { + if (attr == NULL) + break; + else + continue; + } + + temp->sysjob = 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); + void print_iprint_dummy(void) {} +#endif /* HAVE_IPRINT */ |