1/*
2 * Support code for Novell iPrint using the Common UNIX Printing
3 * System ("CUPS") libraries
4 *
5 * Copyright 1999-2003 by Michael R Sweet.
6 * Portions Copyright 2005 by Joel J. Smith.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "includes.h"
23#include "printing.h"
24
25#ifdef HAVE_IPRINT
26#include <cups/cups.h>
27#include <cups/language.h>
28
29#define OPERATION_NOVELL_LIST_PRINTERS          0x401A
30#define OPERATION_NOVELL_MGMT                   0x401C
31#define NOVELL_SERVER_SYSNAME			"sysname="
32#define NOVELL_SERVER_SYSNAME_NETWARE		"NetWare IA32"
33#define NOVELL_SERVER_VERSION_STRING		"iprintserverversion="
34#define NOVELL_SERVER_VERSION_OES_SP1		33554432
35
36/*
37 * 'iprint_passwd_cb()' - The iPrint password callback...
38 */
39
40static const char *				/* O - Password or NULL */
41iprint_passwd_cb(const char *prompt)	/* I - Prompt */
42{
43       /*
44	* Always return NULL to indicate that no password is available...
45	*/
46
47	return (NULL);
48}
49
50static const char *iprint_server(void)
51{
52	if ((lp_iprint_server() != NULL) && (strlen(lp_iprint_server()) > 0)) {
53		DEBUG(10, ("iprint server explicitly set to %s\n",
54			   lp_iprint_server()));
55		return lp_iprint_server();
56	}
57
58	DEBUG(10, ("iprint server left to default %s\n", cupsServer()));
59	return cupsServer();
60}
61
62/*
63 * Pass in an already connected http_t*
64 * Returns the server version if one can be found, multiplied by
65 * -1 for all NetWare versions.  Returns 0 if a server version
66 * cannot be determined
67 */
68
69static int iprint_get_server_version(http_t *http, char* serviceUri)
70{
71	ipp_t		*request = NULL,	/* IPP Request */
72			*response = NULL;	/* IPP Response */
73	ipp_attribute_t	*attr;			/* Current attribute */
74	cups_lang_t	*language = NULL;	/* Default language */
75	char		*ver;			/* server version pointer */
76	char		*vertmp;		/* server version tmp pointer */
77	int		serverVersion = 0;	/* server version */
78	char		*os;			/* server os */
79	int		osFlag = 0;		/* 0 for NetWare, 1 for anything else */
80	char		*temp;			/* pointer for string manipulation */
81
82       /*
83	* Build an OPERATION_NOVELL_MGMT("get-server-version") request,
84	* which requires the following attributes:
85	*
86	*    attributes-charset
87	*    attributes-natural-language
88	*    operation-name
89	*    service-uri
90	*/
91
92	request = ippNew();
93
94	request->request.op.operation_id = (ipp_op_t)OPERATION_NOVELL_MGMT;
95	request->request.op.request_id   = 1;
96
97	language = cupsLangDefault();
98
99	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
100	             "attributes-charset", NULL, "utf-8");
101
102	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
103	             "attributes-natural-language", NULL, language->language);
104
105	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
106	             "service-uri", NULL, serviceUri);
107
108	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
109	              "operation-name", NULL, "get-server-version");
110
111       /*
112	* Do the request and get back a response...
113	*/
114
115	if (((response = cupsDoRequest(http, request, "/ipp/")) == NULL) ||
116	    (response->request.status.status_code >= IPP_OK_CONFLICT))
117		goto out;
118
119	if (((attr = ippFindAttribute(response, "server-version",
120	                              IPP_TAG_STRING)) != NULL)) {
121		if ((ver = strstr(attr->values[0].string.text,
122                                  NOVELL_SERVER_VERSION_STRING)) != NULL) {
123			ver += strlen(NOVELL_SERVER_VERSION_STRING);
124		       /*
125			* Strangely, libcups stores a IPP_TAG_STRING (octet
126			* string) as a null-terminated string with no length
127			* even though it could be binary data with nulls in
128			* it.  Luckily, in this case the value is not binary.
129			*/
130			serverVersion = strtol(ver, &vertmp, 10);
131
132			/* Check for not found, overflow or negative version */
133			if ((ver == vertmp) || (serverVersion < 0))
134				serverVersion = 0;
135		}
136
137		if ((os = strstr(attr->values[0].string.text,
138                                  NOVELL_SERVER_SYSNAME)) != NULL) {
139			os += strlen(NOVELL_SERVER_SYSNAME);
140			if ((temp = strchr(os,'<')) != NULL)
141				*temp = '\0';
142			if (strcmp(os,NOVELL_SERVER_SYSNAME_NETWARE))
143				osFlag = 1; /* 1 for non-NetWare systems */
144		}
145	}
146
147 out:
148	if (response)
149		ippDelete(response);
150
151	if (language)
152		cupsLangFree(language);
153
154	if (osFlag == 0)
155		serverVersion *= -1;
156
157	return serverVersion;
158}
159
160
161static int iprint_cache_add_printer(http_t *http,
162				   int reqId,
163				   char* url)
164{
165	ipp_t		*request = NULL,	/* IPP Request */
166			*response = NULL;	/* IPP Response */
167	ipp_attribute_t	*attr;			/* Current attribute */
168	cups_lang_t	*language = NULL;	/* Default language */
169	char		*name,			/* printer-name attribute */
170			*info,			/* printer-info attribute */
171			smb_enabled,		/* smb-enabled attribute */
172			secure;			/* security-enabled attrib. */
173
174	char		*httpPath;	/* path portion of the printer-uri */
175
176	static const char *pattrs[] =	/* Requested printer attributes */
177			{
178			  "printer-name",
179			  "security-enabled",
180			  "printer-info",
181			  "smb-enabled"
182			};
183
184	request = ippNew();
185
186	request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
187	request->request.op.request_id   = reqId;
188
189	language = cupsLangDefault();
190
191	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
192	             "attributes-charset", NULL, "utf-8");
193
194	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
195	             "attributes-natural-language", NULL, language->language);
196
197	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, url);
198
199	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
200	              "requested-attributes",
201	              (sizeof(pattrs) / sizeof(pattrs[0])),
202	              NULL, pattrs);
203
204	/*
205	 * Do the request and get back a response...
206	 */
207
208	if ((httpPath = strstr(url,"://")) == NULL ||
209			(httpPath = strchr(httpPath+3,'/')) == NULL)
210	{
211		ippDelete(request);
212		request = NULL;
213		goto out;
214	}
215
216	if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
217		ipp_status_t lastErr = cupsLastError();
218
219	       /*
220		* Ignore printers that cannot be queried without credentials
221		*/
222		if (lastErr == IPP_FORBIDDEN ||
223		    lastErr == IPP_NOT_AUTHENTICATED ||
224		    lastErr == IPP_NOT_AUTHORIZED)
225			goto out;
226
227		DEBUG(0,("Unable to get printer list - %s\n",
228		      ippErrorString(lastErr)));
229		goto out;
230	}
231
232	for (attr = response->attrs; attr != NULL;) {
233	       /*
234		* Skip leading attributes until we hit a printer...
235		*/
236
237		while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
238			attr = attr->next;
239
240		if (attr == NULL)
241			break;
242
243	       /*
244		* Pull the needed attributes from this printer...
245		*/
246
247		name       = NULL;
248		info       = NULL;
249		smb_enabled= 1;
250		secure     = 0;
251
252		while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER) {
253			if (strcmp(attr->name, "printer-name") == 0 &&
254			    attr->value_tag == IPP_TAG_NAME)
255				name = attr->values[0].string.text;
256
257			if (strcmp(attr->name, "printer-info") == 0 &&
258			    (attr->value_tag == IPP_TAG_TEXT ||
259			    attr->value_tag == IPP_TAG_TEXTLANG))
260				info = attr->values[0].string.text;
261
262		       /*
263			* If the smb-enabled attribute is present and the
264			* value is set to 0, don't show the printer.
265			* If the attribute is not present, assume that the
266			* printer should show up
267			*/
268			if (!strcmp(attr->name, "smb-enabled") &&
269			    ((attr->value_tag == IPP_TAG_INTEGER &&
270			    !attr->values[0].integer) ||
271			    (attr->value_tag == IPP_TAG_BOOLEAN &&
272			    !attr->values[0].boolean)))
273				smb_enabled = 0;
274
275		       /*
276			* If the security-enabled attribute is present and the
277			* value is set to 1, don't show the printer.
278			* If the attribute is not present, assume that the
279			* printer should show up
280			*/
281			if (!strcmp(attr->name, "security-enabled") &&
282			    ((attr->value_tag == IPP_TAG_INTEGER &&
283			    attr->values[0].integer) ||
284			    (attr->value_tag == IPP_TAG_BOOLEAN &&
285			    attr->values[0].boolean)))
286				secure = 1;
287
288			attr = attr->next;
289		}
290
291	       /*
292		* See if we have everything needed...
293		* Make sure the printer is not a secure printer
294		* and make sure smb printing hasn't been explicitly
295		* disabled for the printer
296		*/
297
298		if (name != NULL && !secure && smb_enabled)
299			pcap_cache_add(name, info);
300	}
301
302 out:
303	if (response)
304		ippDelete(response);
305	return(0);
306}
307
308bool iprint_cache_reload(void)
309{
310	http_t		*http = NULL;		/* HTTP connection to server */
311	ipp_t		*request = NULL,	/* IPP Request */
312			*response = NULL;	/* IPP Response */
313	ipp_attribute_t	*attr;			/* Current attribute */
314	cups_lang_t	*language = NULL;	/* Default language */
315	int		i;
316	bool ret = False;
317
318	DEBUG(5, ("reloading iprint printcap cache\n"));
319
320       /*
321	* Make sure we don't ask for passwords...
322	*/
323
324	cupsSetPasswordCB(iprint_passwd_cb);
325
326       /*
327	* Try to connect to the server...
328	*/
329
330	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
331		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
332			 iprint_server(), strerror(errno)));
333		goto out;
334	}
335
336       /*
337	* Build a OPERATION_NOVELL_LIST_PRINTERS request, which requires the following attributes:
338	*
339	*    attributes-charset
340	*    attributes-natural-language
341	*/
342
343	request = ippNew();
344
345	request->request.op.operation_id =
346		(ipp_op_t)OPERATION_NOVELL_LIST_PRINTERS;
347	request->request.op.request_id   = 1;
348
349	language = cupsLangDefault();
350
351	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
352	             "attributes-charset", NULL, "utf-8");
353
354	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
355	             "attributes-natural-language", NULL, language->language);
356
357	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
358	             "ipp-server", NULL, "ippSrvr");
359
360       /*
361	* Do the request and get back a response...
362	*/
363
364	if ((response = cupsDoRequest(http, request, "/ipp")) == NULL) {
365		DEBUG(0,("Unable to get printer list - %s\n",
366			 ippErrorString(cupsLastError())));
367		goto out;
368	}
369
370	for (attr = response->attrs; attr != NULL;) {
371	       /*
372		* Skip leading attributes until we hit a printer...
373		*/
374
375		while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
376			attr = attr->next;
377
378		if (attr == NULL)
379			break;
380
381	       /*
382		* Pull the needed attributes from this printer...
383		*/
384
385		while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
386		{
387			if (strcmp(attr->name, "printer-name") == 0 &&
388			    (attr->value_tag == IPP_TAG_URI ||
389			     attr->value_tag == IPP_TAG_NAME ||
390			     attr->value_tag == IPP_TAG_TEXT ||
391			     attr->value_tag == IPP_TAG_NAMELANG ||
392			     attr->value_tag == IPP_TAG_TEXTLANG))
393			{
394				for (i = 0; i<attr->num_values; i++)
395				{
396					char *url = attr->values[i].string.text;
397					if (!url || !strlen(url))
398						continue;
399					iprint_cache_add_printer(http, i+2, url);
400				}
401			}
402			attr = attr->next;
403		}
404	}
405
406	ret = True;
407
408 out:
409	if (response)
410		ippDelete(response);
411
412	if (language)
413		cupsLangFree(language);
414
415	if (http)
416		httpClose(http);
417
418	return ret;
419}
420
421
422/*
423 * 'iprint_job_delete()' - Delete a job.
424 */
425
426static int iprint_job_delete(const char *sharename, const char *lprm_command, struct printjob *pjob)
427{
428	int		ret = 1;		/* Return value */
429	http_t		*http = NULL;		/* HTTP connection to server */
430	ipp_t		*request = NULL,	/* IPP Request */
431			*response = NULL;	/* IPP Response */
432	cups_lang_t	*language = NULL;	/* Default language */
433	char		uri[HTTP_MAX_URI];	/* printer-uri attribute */
434	char		httpPath[HTTP_MAX_URI];	/* path portion of the printer-uri */
435
436
437	DEBUG(5,("iprint_job_delete(%s, %p (%d))\n", sharename, pjob, pjob->sysjob));
438
439       /*
440	* Make sure we don't ask for passwords...
441	*/
442
443	cupsSetPasswordCB(iprint_passwd_cb);
444
445       /*
446	* Try to connect to the server...
447	*/
448
449	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
450		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
451			 iprint_server(), strerror(errno)));
452		goto out;
453	}
454
455       /*
456	* Build an IPP_CANCEL_JOB request, which uses the following
457	* attributes:
458	*
459	*    attributes-charset
460	*    attributes-natural-language
461	*    printer-uri
462	*    job-id
463	*    requesting-user-name
464	*/
465
466	request = ippNew();
467
468	request->request.op.operation_id = IPP_CANCEL_JOB;
469	request->request.op.request_id   = 1;
470
471	language = cupsLangDefault();
472
473	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
474	             "attributes-charset", NULL, "utf-8");
475
476	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
477	             "attributes-natural-language", NULL, language->language);
478
479	slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), sharename);
480
481	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
482
483	ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
484
485	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
486	             NULL, pjob->user);
487
488       /*
489	* Do the request and get back a response...
490	*/
491
492	slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", sharename);
493
494	if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
495		if (response->request.status.status_code >= IPP_OK_CONFLICT) {
496			DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
497				ippErrorString(cupsLastError())));
498		} else {
499			ret = 0;
500		}
501	} else {
502		DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
503			ippErrorString(cupsLastError())));
504	}
505
506 out:
507	if (response)
508		ippDelete(response);
509
510	if (language)
511		cupsLangFree(language);
512
513	if (http)
514		httpClose(http);
515
516	return ret;
517}
518
519
520/*
521 * 'iprint_job_pause()' - Pause a job.
522 */
523
524static int iprint_job_pause(int snum, struct printjob *pjob)
525{
526	int		ret = 1;		/* Return value */
527	http_t		*http = NULL;		/* HTTP connection to server */
528	ipp_t		*request = NULL,	/* IPP Request */
529			*response = NULL;	/* IPP Response */
530	cups_lang_t	*language = NULL;	/* Default language */
531	char		uri[HTTP_MAX_URI];	/* printer-uri attribute */
532	char		httpPath[HTTP_MAX_URI];	/* path portion of the printer-uri */
533
534
535	DEBUG(5,("iprint_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
536
537       /*
538	* Make sure we don't ask for passwords...
539	*/
540
541	cupsSetPasswordCB(iprint_passwd_cb);
542
543       /*
544	* Try to connect to the server...
545	*/
546
547	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
548		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
549			 iprint_server(), strerror(errno)));
550		goto out;
551	}
552
553       /*
554	* Build an IPP_HOLD_JOB request, which requires the following
555	* attributes:
556	*
557	*    attributes-charset
558	*    attributes-natural-language
559	*    printer-uri
560	*    job-id
561	*    requesting-user-name
562	*/
563
564	request = ippNew();
565
566	request->request.op.operation_id = IPP_HOLD_JOB;
567	request->request.op.request_id   = 1;
568
569	language = cupsLangDefault();
570
571	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
572	             "attributes-charset", NULL, "utf-8");
573
574	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
575	             "attributes-natural-language", NULL, language->language);
576
577	slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum));
578
579	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
580
581	ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
582
583	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
584	             NULL, pjob->user);
585
586       /*
587	* Do the request and get back a response...
588	*/
589
590	slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum));
591
592	if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
593		if (response->request.status.status_code >= IPP_OK_CONFLICT) {
594			DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
595				ippErrorString(cupsLastError())));
596		} else {
597			ret = 0;
598		}
599	} else {
600		DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
601			ippErrorString(cupsLastError())));
602	}
603
604 out:
605	if (response)
606		ippDelete(response);
607
608	if (language)
609		cupsLangFree(language);
610
611	if (http)
612		httpClose(http);
613
614	return ret;
615}
616
617
618/*
619 * 'iprint_job_resume()' - Resume a paused job.
620 */
621
622static int iprint_job_resume(int snum, struct printjob *pjob)
623{
624	int		ret = 1;		/* Return value */
625	http_t		*http = NULL;		/* HTTP connection to server */
626	ipp_t		*request = NULL,	/* IPP Request */
627			*response = NULL;	/* IPP Response */
628	cups_lang_t	*language = NULL;	/* Default language */
629	char		uri[HTTP_MAX_URI];	/* printer-uri attribute */
630	char		httpPath[HTTP_MAX_URI];	/* path portion of the printer-uri */
631
632
633	DEBUG(5,("iprint_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
634
635       /*
636	* Make sure we don't ask for passwords...
637	*/
638
639	cupsSetPasswordCB(iprint_passwd_cb);
640
641       /*
642	* Try to connect to the server...
643	*/
644
645	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
646		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
647			 iprint_server(), strerror(errno)));
648		goto out;
649	}
650
651       /*
652	* Build an IPP_RELEASE_JOB request, which requires the following
653	* attributes:
654	*
655	*    attributes-charset
656	*    attributes-natural-language
657	*    printer-uri
658	*    job-id
659	*    requesting-user-name
660	*/
661
662	request = ippNew();
663
664	request->request.op.operation_id = IPP_RELEASE_JOB;
665	request->request.op.request_id   = 1;
666
667	language = cupsLangDefault();
668
669	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
670	             "attributes-charset", NULL, "utf-8");
671
672	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
673	             "attributes-natural-language", NULL, language->language);
674
675	slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum));
676
677	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
678
679	ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
680
681	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
682	             NULL, pjob->user);
683
684       /*
685	* Do the request and get back a response...
686	*/
687
688	slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", PRINTERNAME(snum));
689
690	if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
691		if (response->request.status.status_code >= IPP_OK_CONFLICT) {
692			DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
693				ippErrorString(cupsLastError())));
694		} else {
695			ret = 0;
696		}
697	} else {
698		DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
699			ippErrorString(cupsLastError())));
700	}
701
702 out:
703	if (response)
704		ippDelete(response);
705
706	if (language)
707		cupsLangFree(language);
708
709	if (http)
710		httpClose(http);
711
712	return ret;
713}
714
715
716/*
717 * 'iprint_job_submit()' - Submit a job for printing.
718 */
719
720static int iprint_job_submit(int snum, struct printjob *pjob)
721{
722	int		ret = 1;		/* Return value */
723	http_t		*http = NULL;		/* HTTP connection to server */
724	ipp_t		*request = NULL,	/* IPP Request */
725			*response = NULL;	/* IPP Response */
726	ipp_attribute_t	*attr;		/* Current attribute */
727	cups_lang_t	*language = NULL;	/* Default language */
728	char		uri[HTTP_MAX_URI]; /* printer-uri attribute */
729	const char	*clientname = NULL; 	/* hostname of client for job-originating-host attribute */
730	char addr[INET6_ADDRSTRLEN];
731
732	DEBUG(5,("iprint_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
733
734       /*
735	* Make sure we don't ask for passwords...
736	*/
737
738	cupsSetPasswordCB(iprint_passwd_cb);
739
740       /*
741	* Try to connect to the server...
742	*/
743
744	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
745		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
746			 iprint_server(), strerror(errno)));
747		goto out;
748	}
749
750       /*
751	* Build an IPP_PRINT_JOB request, which requires the following
752	* attributes:
753	*
754	*    attributes-charset
755	*    attributes-natural-language
756	*    printer-uri
757	*    requesting-user-name
758	*    [document-data]
759	*/
760
761	request = ippNew();
762
763	request->request.op.operation_id = IPP_PRINT_JOB;
764	request->request.op.request_id   = 1;
765
766	language = cupsLangDefault();
767
768	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
769	             "attributes-charset", NULL, "utf-8");
770
771	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
772	             "attributes-natural-language", NULL, language->language);
773
774	slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), PRINTERNAME(snum));
775
776	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
777	             "printer-uri", NULL, uri);
778
779	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
780	             NULL, pjob->user);
781
782	clientname = client_name(get_client_fd());
783	if (strcmp(clientname, "UNKNOWN") == 0) {
784		clientname = client_addr(get_client_fd(),addr,sizeof(addr));
785	}
786
787	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
788	             "job-originating-host-name", NULL,
789	             clientname);
790
791	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
792	             pjob->jobname);
793
794       /*
795	* Do the request and get back a response...
796	*/
797
798	slprintf(uri, sizeof(uri) - 1, "/ipp/%s", PRINTERNAME(snum));
799
800	if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) {
801		if (response->request.status.status_code >= IPP_OK_CONFLICT) {
802			DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum),
803			         ippErrorString(cupsLastError())));
804		} else {
805			ret = 0;
806		}
807	} else {
808		DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
809			 ippErrorString(cupsLastError())));
810	}
811
812	if ( ret == 0 )
813		unlink(pjob->filename);
814	/* else print_job_end will do it for us */
815
816	if ( ret == 0 ) {
817
818		attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
819		if (attr != NULL && attr->group_tag == IPP_TAG_JOB)
820		{
821			pjob->sysjob = attr->values[0].integer;
822		}
823	}
824
825 out:
826	if (response)
827		ippDelete(response);
828
829	if (language)
830		cupsLangFree(language);
831
832	if (http)
833		httpClose(http);
834
835	return ret;
836}
837
838/*
839 * 'iprint_queue_get()' - Get all the jobs in the print queue.
840 */
841
842static int iprint_queue_get(const char *sharename,
843			    enum printing_types printing_type,
844			    char *lpq_command,
845			    print_queue_struct **q,
846			    print_status_struct *status)
847{
848	fstring		printername;
849	http_t		*http = NULL;		/* HTTP connection to server */
850	ipp_t		*request = NULL,	/* IPP Request */
851			*response = NULL;	/* IPP Response */
852	ipp_attribute_t	*attr = NULL;		/* Current attribute */
853	cups_lang_t	*language = NULL;	/* Default language */
854	char		uri[HTTP_MAX_URI]; /* printer-uri attribute */
855	char		serviceUri[HTTP_MAX_URI]; /* service-uri attribute */
856	char		httpPath[HTTP_MAX_URI];	/* path portion of the uri */
857	int		jobUseUnixTime = 0;	/* Whether job times should
858                                                 * be assumed to be Unix time */
859	int		qcount = 0,		/* Number of active queue entries */
860			qalloc = 0;		/* Number of queue entries allocated */
861	print_queue_struct *queue = NULL,	/* Queue entries */
862			*temp;		/* Temporary pointer for queue */
863	const char	*user_name,	/* job-originating-user-name attribute */
864			*job_name;	/* job-name attribute */
865	int		job_id;		/* job-id attribute */
866	int		job_k_octets;	/* job-k-octets attribute */
867	time_t		job_time;	/* time-at-creation attribute */
868	time_t		printer_current_time = 0;	/* printer's current time */
869	time_t		printer_up_time = 0;	/* printer's uptime */
870	ipp_jstate_t	job_status;	/* job-status attribute */
871	int		job_priority;	/* job-priority attribute */
872	static const char *jattrs[] =	/* Requested job attributes */
873			{
874			  "job-id",
875			  "job-k-octets",
876			  "job-name",
877			  "job-originating-user-name",
878			  "job-priority",
879			  "job-state",
880			  "time-at-creation",
881			};
882	static const char *pattrs[] =	/* Requested printer attributes */
883			{
884			  "printer-state",
885			  "printer-state-message",
886			  "printer-current-time",
887			  "printer-up-time"
888			};
889
890	*q = NULL;
891
892	/* HACK ALERT!!!  The porblem with support the 'printer name'
893	   option is that we key the tdb off the sharename.  So we will
894	   overload the lpq_command string to pass in the printername
895	   (which is basically what we do for non-cups printers ... using
896	   the lpq_command to get the queue listing). */
897
898	fstrcpy( printername, lpq_command );
899
900	DEBUG(5,("iprint_queue_get(%s, %p, %p)\n", printername, q, status));
901
902       /*
903	* Make sure we don't ask for passwords...
904	*/
905
906	cupsSetPasswordCB(iprint_passwd_cb);
907
908       /*
909	* Try to connect to the server...
910	*/
911
912	if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
913		DEBUG(0,("Unable to connect to iPrint server %s - %s\n",
914			 iprint_server(), strerror(errno)));
915		goto out;
916	}
917
918       /*
919	* Generate the printer URI and the service URI that goes with it...
920	*/
921
922	slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), printername);
923	slprintf(serviceUri, sizeof(serviceUri) - 1, "ipp://%s/ipp/", iprint_server());
924
925       /*
926	* For Linux iPrint servers from OES SP1 on, the iPrint server
927	* uses Unix time for job start times unless it detects the iPrint
928	* client in an http User-Agent header.  (This was done to accomodate
929	* CUPS broken behavior.  According to RFC 2911, section 4.3.14, job
930	* start times are supposed to be relative to how long the printer has
931	* been up.)  Since libcups doesn't allow us to set that header before
932	* the request is sent, this ugly hack allows us to detect the server
933	* version and decide how to interpret the job time.
934	*/
935	if (iprint_get_server_version(http, serviceUri) >=
936	    NOVELL_SERVER_VERSION_OES_SP1)
937		jobUseUnixTime = 1;
938
939	request = ippNew();
940
941	request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
942	request->request.op.request_id   = 2;
943
944	language = cupsLangDefault();
945
946	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
947	             "attributes-charset", NULL, "utf-8");
948
949	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
950	             "attributes-natural-language", NULL, language->language);
951
952	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
953	             "printer-uri", NULL, uri);
954
955	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
956	              "requested-attributes",
957	              (sizeof(pattrs) / sizeof(pattrs[0])),
958	              NULL, pattrs);
959
960       /*
961	* Do the request and get back a response...
962	*/
963
964	slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
965
966	if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
967		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
968			 ippErrorString(cupsLastError())));
969		*q = queue;
970		goto out;
971	}
972
973	if (response->request.status.status_code >= IPP_OK_CONFLICT) {
974		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
975			 ippErrorString(response->request.status.status_code)));
976		*q = queue;
977		goto out;
978	}
979
980       /*
981	* Get the current printer status and convert it to the SAMBA values.
982	*/
983
984	if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) {
985		if (attr->values[0].integer == IPP_PRINTER_STOPPED)
986			status->status = LPSTAT_STOPPED;
987		else
988			status->status = LPSTAT_OK;
989	}
990
991	if ((attr = ippFindAttribute(response, "printer-state-message",
992	                             IPP_TAG_TEXT)) != NULL)
993		fstrcpy(status->message, attr->values[0].string.text);
994
995	if ((attr = ippFindAttribute(response, "printer-current-time",
996	                             IPP_TAG_DATE)) != NULL)
997		printer_current_time = ippDateToTime(attr->values[0].date);
998
999	if ((attr = ippFindAttribute(response, "printer-up-time",
1000	                             IPP_TAG_INTEGER)) != NULL)
1001		printer_up_time = attr->values[0].integer;
1002
1003	ippDelete(response);
1004	response = NULL;
1005
1006       /*
1007	* Build an IPP_GET_JOBS request, which requires the following
1008	* attributes:
1009	*
1010	*    attributes-charset
1011	*    attributes-natural-language
1012	*    requested-attributes
1013	*    printer-uri
1014	*/
1015
1016	request = ippNew();
1017
1018	request->request.op.operation_id = IPP_GET_JOBS;
1019	request->request.op.request_id   = 3;
1020
1021	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1022	             "attributes-charset", NULL, "utf-8");
1023
1024	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1025	             "attributes-natural-language", NULL, language->language);
1026
1027	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1028	             "printer-uri", NULL, uri);
1029
1030	ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1031	              "requested-attributes",
1032	              (sizeof(jattrs) / sizeof(jattrs[0])),
1033	              NULL, jattrs);
1034
1035       /*
1036	* Do the request and get back a response...
1037	*/
1038
1039	slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
1040
1041	if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
1042		DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1043			 ippErrorString(cupsLastError())));
1044		goto out;
1045	}
1046
1047	if (response->request.status.status_code >= IPP_OK_CONFLICT) {
1048		DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1049			 ippErrorString(response->request.status.status_code)));
1050		goto out;
1051	}
1052
1053       /*
1054	* Process the jobs...
1055	*/
1056
1057	qcount = 0;
1058	qalloc = 0;
1059	queue  = NULL;
1060
1061	for (attr = response->attrs; attr != NULL; attr = attr->next) {
1062	       /*
1063		* Skip leading attributes until we hit a job...
1064		*/
1065
1066		while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
1067			attr = attr->next;
1068
1069		if (attr == NULL)
1070			break;
1071
1072	       /*
1073		* Allocate memory as needed...
1074		*/
1075		if (qcount >= qalloc) {
1076			qalloc += 16;
1077
1078			queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc);
1079
1080			if (queue == NULL) {
1081				DEBUG(0,("iprint_queue_get: Not enough memory!"));
1082				qcount = 0;
1083				goto out;
1084			}
1085		}
1086
1087		temp = queue + qcount;
1088		memset(temp, 0, sizeof(print_queue_struct));
1089
1090	       /*
1091		* Pull the needed attributes from this job...
1092		*/
1093
1094		job_id       = 0;
1095		job_priority = 50;
1096		job_status   = IPP_JOB_PENDING;
1097		job_time     = 0;
1098		job_k_octets = 0;
1099		user_name    = NULL;
1100		job_name     = NULL;
1101
1102		while (attr != NULL && attr->group_tag == IPP_TAG_JOB) {
1103			if (attr->name == NULL) {
1104				attr = attr->next;
1105				break;
1106			}
1107
1108			if (strcmp(attr->name, "job-id") == 0 &&
1109			    attr->value_tag == IPP_TAG_INTEGER)
1110				job_id = attr->values[0].integer;
1111
1112			if (strcmp(attr->name, "job-k-octets") == 0 &&
1113			    attr->value_tag == IPP_TAG_INTEGER)
1114				job_k_octets = attr->values[0].integer;
1115
1116			if (strcmp(attr->name, "job-priority") == 0 &&
1117			    attr->value_tag == IPP_TAG_INTEGER)
1118				job_priority = attr->values[0].integer;
1119
1120			if (strcmp(attr->name, "job-state") == 0 &&
1121			    attr->value_tag == IPP_TAG_ENUM)
1122				job_status = (ipp_jstate_t)(attr->values[0].integer);
1123
1124			if (strcmp(attr->name, "time-at-creation") == 0 &&
1125			    attr->value_tag == IPP_TAG_INTEGER)
1126			{
1127			       /*
1128				* If jobs times are in Unix time, the accuracy of the job
1129				* start time depends upon the iPrint server's time being
1130				* set correctly.  Otherwise, the accuracy depends upon
1131				* the Samba server's time being set correctly
1132				*/
1133
1134				if (jobUseUnixTime)
1135					job_time = attr->values[0].integer;
1136				else
1137					job_time = time(NULL) - printer_up_time + attr->values[0].integer;
1138			}
1139
1140			if (strcmp(attr->name, "job-name") == 0 &&
1141			    (attr->value_tag == IPP_TAG_NAMELANG ||
1142			     attr->value_tag == IPP_TAG_NAME))
1143				job_name = attr->values[0].string.text;
1144
1145			if (strcmp(attr->name, "job-originating-user-name") == 0 &&
1146			    (attr->value_tag == IPP_TAG_NAMELANG ||
1147			     attr->value_tag == IPP_TAG_NAME))
1148				user_name = attr->values[0].string.text;
1149
1150			attr = attr->next;
1151		}
1152
1153	       /*
1154		* See if we have everything needed...
1155		*/
1156
1157		if (user_name == NULL || job_name == NULL || job_id == 0) {
1158			if (attr == NULL)
1159				break;
1160			else
1161				continue;
1162		}
1163
1164		temp->job      = job_id;
1165		temp->size     = job_k_octets * 1024;
1166		temp->status   = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
1167		                 job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
1168                                 job_status == IPP_JOB_HELD ? LPQ_PAUSED :
1169                                 LPQ_PRINTING;
1170		temp->priority = job_priority;
1171		temp->time     = job_time;
1172		strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
1173		strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
1174
1175		qcount ++;
1176
1177		if (attr == NULL)
1178			break;
1179	}
1180
1181       /*
1182	* Return the job queue...
1183	*/
1184
1185	*q = queue;
1186
1187 out:
1188	if (response)
1189		ippDelete(response);
1190
1191	if (language)
1192		cupsLangFree(language);
1193
1194	if (http)
1195		httpClose(http);
1196
1197	return qcount;
1198}
1199
1200
1201/*
1202 * 'iprint_queue_pause()' - Pause a print queue.
1203 */
1204
1205static int iprint_queue_pause(int snum)
1206{
1207	return(-1); /* Not supported without credentials */
1208}
1209
1210
1211/*
1212 * 'iprint_queue_resume()' - Restart a print queue.
1213 */
1214
1215static int iprint_queue_resume(int snum)
1216{
1217	return(-1); /* Not supported without credentials */
1218}
1219
1220/*******************************************************************
1221 * iPrint printing interface definitions...
1222 ******************************************************************/
1223
1224struct printif	iprint_printif =
1225{
1226	PRINT_IPRINT,
1227	iprint_queue_get,
1228	iprint_queue_pause,
1229	iprint_queue_resume,
1230	iprint_job_delete,
1231	iprint_job_pause,
1232	iprint_job_resume,
1233	iprint_job_submit,
1234};
1235
1236#else
1237 /* this keeps fussy compilers happy */
1238 void print_iprint_dummy(void);
1239 void print_iprint_dummy(void) {}
1240#endif /* HAVE_IPRINT */
1241