1 /*
2  * Support code for the Common UNIX Printing System ("CUPS")
3  *
4  * Copyright 1999-2003 by Michael R Sweet.
5  * Copyright 2008 Jeremy Allison.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * JRA. Converted to utf8 pull/push.
23  */
24 
25 #include "includes.h"
26 #include "printing.h"
27 #include "printing/pcap.h"
28 #include "librpc/gen_ndr/ndr_printcap.h"
29 #include "lib/util/sys_rw.h"
30 
31 #ifdef HAVE_CUPS
aix_cache_reload(struct pcap_cache ** _pcache)32 #include <cups/cups.h>
33 #include <cups/language.h>
34 #include <cups/http.h>
35 
36 /* CUPS prior to version 1.7 doesn't have HTTP_URI_STATUS_OK */
37 #if (CUPS_VERSION_MAJOR == 1) && (CUPS_VERSION_MINOR < 7)
38 #define HTTP_URI_STATUS_OK HTTP_URI_OK
39 #endif
40 
41 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
42 #define HAVE_CUPS_1_6 1
43 #endif
44 
45 #ifndef HAVE_CUPS_1_6
46 #define ippGetGroupTag(attr)  attr->group_tag
47 #define ippGetName(attr)      attr->name
48 #define ippGetValueTag(attr)  attr->value_tag
49 #define ippGetStatusCode(ipp) ipp->request.status.status_code
50 #define ippGetInteger(attr, element) attr->values[element].integer
51 #define ippGetString(attr, element, language) attr->values[element].string.text
52 
53 static ipp_attribute_t *
54 ippFirstAttribute(ipp_t *ipp)
55 {
56   if (!ipp)
57     return (NULL);
58   return (ipp->current = ipp->attrs);
59 }
60 
61 static ipp_attribute_t *
62 ippNextAttribute(ipp_t *ipp)
63 {
64   if (!ipp || !ipp->current)
65     return (NULL);
66   return (ipp->current = ipp->current->next);
67 }
68 
69 static int ippSetOperation(ipp_t *ipp, ipp_op_t op)
70 {
71     ipp->request.op.operation_id = op;
72     return (1);
73 }
74 
75 static int ippSetRequestId(ipp_t *ipp, int request_id)
76 {
77     ipp->request.any.request_id = request_id;
78     return (1);
79 }
80 #endif
81 
82 static SIG_ATOMIC_T gotalarm;
83 
84 /***************************************************************
85  Signal function to tell us we timed out.
86 ****************************************************************/
87 
88 static void gotalarm_sig(int signum)
89 {
90         gotalarm = 1;
91 }
92 
93 extern userdom_struct current_user_info;
94 
95 /*
96  * 'cups_passwd_cb()' - The CUPS password callback...
97  */
98 
99 static const char *				/* O - Password or NULL */
100 cups_passwd_cb(const char *prompt)	/* I - Prompt */
101 {
102 	/*
103 	 * Always return NULL to indicate that no password is available...
104 	 */
105 
106 	return (NULL);
107 }
108 
109 static http_t *cups_connect(TALLOC_CTX *frame)
110 {
111 	const struct loadparm_substitution *lp_sub =
112 		loadparm_s3_global_substitution();
113 	http_t *http = NULL;
114 	char *server = NULL, *p = NULL;
115 	int port;
116 	int timeout = lp_cups_connection_timeout();
117 	size_t size;
118 
119 	if (lp_cups_server(talloc_tos(), lp_sub) != NULL && strlen(lp_cups_server(talloc_tos(), lp_sub)) > 0) {
120 		if (!push_utf8_talloc(frame, &server, lp_cups_server(talloc_tos(), lp_sub), &size)) {
121 			return NULL;
122 		}
123 	} else {
124 		server = talloc_strdup(frame,cupsServer());
125 	}
126 	if (!server) {
127 		return NULL;
128 	}
129 
130 	p = strchr(server, ':');
131 	if (p) {
132 		port = atoi(p+1);
133 		*p = '\0';
134 	} else {
135 		port = ippPort();
136 	}
137 
138 	DEBUG(10, ("connecting to cups server %s:%d\n",
print_aix_dummy(void)139 		   server, port));
140 
141 	gotalarm = 0;
142 
143 	if (timeout) {
144                 CatchSignal(SIGALRM, gotalarm_sig);
145                 alarm(timeout);
146         }
147 
148 #if defined(HAVE_HTTPCONNECT2)
149 	http = httpConnect2(server,
150 			    port,
151 			    NULL,
152 			    AF_UNSPEC,
153 			    lp_cups_encrypt() ?
154 				HTTP_ENCRYPTION_ALWAYS :
155 				HTTP_ENCRYPTION_IF_REQUESTED,
156 			    1, /* blocking */
157 			    30 * 1000, /* timeout */
158 			    NULL);
159 #elif defined(HAVE_HTTPCONNECTENCRYPT)
160 	http = httpConnectEncrypt(server, port, lp_cups_encrypt());
161 #else
162 	http = httpConnect(server, port);
163 #endif
164 
165 
166 	CatchSignal(SIGALRM, SIG_IGN);
167         alarm(0);
168 
169 	if (http == NULL) {
170 		DEBUG(3,("Unable to connect to CUPS server %s:%d - %s\n",
171 			 server, port, strerror(errno)));
172 	}
173 
174 	return http;
175 }
176 
177 static bool send_pcap_blob(DATA_BLOB *pcap_blob, int fd)
178 {
179 	size_t ret;
180 
181 	ret = sys_write(fd, &pcap_blob->length, sizeof(pcap_blob->length));
182 	if (ret != sizeof(pcap_blob->length)) {
183 		return false;
184 	}
185 
186 	ret = sys_write(fd, pcap_blob->data, pcap_blob->length);
187 	if (ret != pcap_blob->length) {
188 		return false;
189 	}
190 
191 	DEBUG(10, ("successfully sent blob of len %d\n", (int)ret));
192 	return true;
193 }
194 
195 static bool recv_pcap_blob(TALLOC_CTX *mem_ctx, int fd, DATA_BLOB *pcap_blob)
196 {
197 	size_t blob_len;
198 	size_t ret;
199 
200 	ret = sys_read(fd, &blob_len, sizeof(blob_len));
201 	if (ret != sizeof(blob_len)) {
202 		return false;
203 	}
204 
205 	*pcap_blob = data_blob_talloc_named(mem_ctx, NULL, blob_len,
206 					   "cups pcap");
207 	if (pcap_blob->length != blob_len) {
208 		return false;
209 	}
210 	ret = sys_read(fd, pcap_blob->data, blob_len);
211 	if (ret != blob_len) {
212 		talloc_free(pcap_blob->data);
213 		return false;
214 	}
215 
216 	DEBUG(10, ("successfully recvd blob of len %d\n", (int)ret));
217 	return true;
218 }
219 
220 static bool process_cups_printers_response(TALLOC_CTX *mem_ctx,
221 					   ipp_t *response,
222 					   struct pcap_data *pcap_data)
223 {
224 	ipp_attribute_t	*attr;
225 	char *name;
226 	char *info;
227 	char *location = NULL;
228 	struct pcap_printer *printer;
229 	bool ret_ok = false;
230 
231 	for (attr = ippFirstAttribute(response); attr != NULL;) {
232 	       /*
233 		* Skip leading attributes until we hit a printer...
234 		*/
235 
236 		while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
237 			attr = ippNextAttribute(response);
238 
239 		if (attr == NULL)
240 			break;
241 
242 	       /*
243 		* Pull the needed attributes from this printer...
244 		*/
245 
246 		name       = NULL;
247 		info       = NULL;
248 
249 		while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
250 			size_t size;
251 			if (strcmp(ippGetName(attr), "printer-name") == 0 &&
252 			    ippGetValueTag(attr) == IPP_TAG_NAME) {
253 				if (!pull_utf8_talloc(mem_ctx,
254 						&name,
255 						ippGetString(attr, 0, NULL),
256 						&size)) {
257 					goto err_out;
258 				}
259 			}
260 
261 			if (strcmp(ippGetName(attr), "printer-info") == 0 &&
262 			    ippGetValueTag(attr) == IPP_TAG_TEXT) {
263 				if (!pull_utf8_talloc(mem_ctx,
264 						&info,
265 						ippGetString(attr, 0, NULL),
266 						&size)) {
267 					goto err_out;
268 				}
269 			}
270 
271 			if (strcmp(ippGetName(attr), "printer-location") == 0 &&
272 			    ippGetValueTag(attr) == IPP_TAG_TEXT) {
273 				if (!pull_utf8_talloc(mem_ctx,
274 						&location,
275 						ippGetString(attr, 0, NULL),
276 						&size)) {
277 					goto err_out;
278 				}
279 			}
280 
281 			attr = ippNextAttribute(response);
282 		}
283 
284 	       /*
285 		* See if we have everything needed...
286 		*/
287 
288 		if (name == NULL)
289 			break;
290 
291 		if (pcap_data->count == 0) {
292 			printer = talloc_array(mem_ctx, struct pcap_printer, 1);
293 		} else {
294 			printer = talloc_realloc(mem_ctx, pcap_data->printers,
295 						 struct pcap_printer,
296 						 pcap_data->count + 1);
297 		}
298 		if (printer == NULL) {
299 			goto err_out;
300 		}
301 		pcap_data->printers = printer;
302 		pcap_data->printers[pcap_data->count].name = name;
303 		pcap_data->printers[pcap_data->count].info = info;
304 		pcap_data->printers[pcap_data->count].location = location;
305 		pcap_data->count++;
306 	}
307 
308 	ret_ok = true;
309 err_out:
310 	return ret_ok;
311 }
312 
313 /*
314  * request printer list from cups, send result back to up parent via fd.
315  * returns true if the (possibly failed) result was successfully sent to parent.
316  */
317 static bool cups_cache_reload_async(int fd)
318 {
319 	TALLOC_CTX *frame = talloc_stackframe();
320 	struct pcap_data pcap_data;
321 	http_t		*http = NULL;		/* HTTP connection to server */
322 	ipp_t		*request = NULL,	/* IPP Request */
323 			*response = NULL;	/* IPP Response */
324 	cups_lang_t	*language = NULL;	/* Default language */
325 	static const char *requested[] =/* Requested attributes */
326 			{
327 			  "printer-name",
328 			  "printer-info",
329 			  "printer-location"
330 			};
331 	bool ret = False;
332 	enum ndr_err_code ndr_ret;
333 	DATA_BLOB pcap_blob;
334 
335 	ZERO_STRUCT(pcap_data);
336 	pcap_data.status = NT_STATUS_UNSUCCESSFUL;
337 
338 	DEBUG(5, ("reloading cups printcap cache\n"));
339 
340        /*
341         * Make sure we don't ask for passwords...
342 	*/
343 
344         cupsSetPasswordCB(cups_passwd_cb);
345 
346 	if ((http = cups_connect(frame)) == NULL) {
347 		goto out;
348 	}
349 
350        /*
351 	* Build a CUPS_GET_PRINTERS request, which requires the following
352 	* attributes:
353 	*
354 	*    attributes-charset
355 	*    attributes-natural-language
356 	*    requested-attributes
357 	*/
358 
359 	request = ippNew();
360 
361 	ippSetOperation(request, CUPS_GET_PRINTERS);
362 	ippSetRequestId(request, 1);
363 
364 	language = cupsLangDefault();
365 
366 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
367                      "attributes-charset", NULL, "utf-8");
368 
369 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
370                      "attributes-natural-language", NULL, language->language);
371 
372         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
373 	              "requested-attributes",
374 		      (sizeof(requested) / sizeof(requested[0])),
375 		      NULL, requested);
376 
377 	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
378 		DEBUG(0,("Unable to get printer list - %s\n",
379 			 ippErrorString(cupsLastError())));
380 		goto out;
381 	}
382 
383 	ret = process_cups_printers_response(frame, response, &pcap_data);
384 	if (!ret) {
385 		DEBUG(0,("failed to process cups response\n"));
386 		goto out;
387 	}
388 
389 	ippDelete(response);
390 	response = NULL;
391 
392        /*
393 	* Build a CUPS_GET_CLASSES request, which requires the following
394 	* attributes:
395 	*
396 	*    attributes-charset
397 	*    attributes-natural-language
398 	*    requested-attributes
399 	*/
400 
401 	request = ippNew();
402 
403 	ippSetOperation(request, CUPS_GET_CLASSES);
404 	ippSetRequestId(request, 1);
405 
406 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
407                      "attributes-charset", NULL, "utf-8");
408 
409 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
410                      "attributes-natural-language", NULL, language->language);
411 
412         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
413 	              "requested-attributes",
414 		      (sizeof(requested) / sizeof(requested[0])),
415 		      NULL, requested);
416 
417 	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
418 		DEBUG(0,("Unable to get printer list - %s\n",
419 			 ippErrorString(cupsLastError())));
420 		goto out;
421 	}
422 
423 	ret = process_cups_printers_response(frame, response, &pcap_data);
424 	if (!ret) {
425 		DEBUG(0,("failed to process cups response\n"));
426 		goto out;
427 	}
428 
429 	pcap_data.status = NT_STATUS_OK;
430  out:
431 	if (response)
432 		ippDelete(response);
433 
434 	if (language)
435 		cupsLangFree(language);
436 
437 	if (http)
438 		httpClose(http);
439 
440 	ret = false;
441 	ndr_ret = ndr_push_struct_blob(&pcap_blob, frame, &pcap_data,
442 				       (ndr_push_flags_fn_t)ndr_push_pcap_data);
443 	if (ndr_ret == NDR_ERR_SUCCESS) {
444 		ret = send_pcap_blob(&pcap_blob, fd);
445 	}
446 
447 	TALLOC_FREE(frame);
448 	return ret;
449 }
450 
451 static struct tevent_fd *cache_fd_event;
452 
453 static bool cups_pcap_load_async(struct tevent_context *ev,
454 				 struct messaging_context *msg_ctx,
455 				 int *pfd)
456 {
457 	int fds[2];
458 	pid_t pid;
459 	NTSTATUS status;
460 
461 	*pfd = -1;
462 
463 	if (cache_fd_event) {
464 		DEBUG(3,("cups_pcap_load_async: already waiting for "
465 			"a refresh event\n" ));
466 		return false;
467 	}
468 
469 	DEBUG(5,("cups_pcap_load_async: asynchronously loading cups printers\n"));
470 
471 	if (pipe(fds) == -1) {
472 		return false;
473 	}
474 
475 	pid = fork();
476 	if (pid == (pid_t)-1) {
477 		DEBUG(10,("cups_pcap_load_async: fork failed %s\n",
478 			strerror(errno) ));
479 		close(fds[0]);
480 		close(fds[1]);
481 		return false;
482 	}
483 
484 	if (pid) {
485 		DEBUG(10,("cups_pcap_load_async: child pid = %u\n",
486 			(unsigned int)pid ));
487 		/* Parent. */
488 		close(fds[1]);
489 		*pfd = fds[0];
490 		return true;
491 	}
492 
493 	/* Child. */
494 
495 	close_all_print_db();
496 
497 	status = reinit_after_fork(msg_ctx, ev, true, NULL);
498 	if (!NT_STATUS_IS_OK(status)) {
499 		DEBUG(0,("cups_pcap_load_async: reinit_after_fork() failed\n"));
500 		smb_panic("cups_pcap_load_async: reinit_after_fork() failed");
501 	}
502 
503 	close(fds[0]);
504 	cups_cache_reload_async(fds[1]);
505 	close(fds[1]);
506 	TALLOC_FREE(msg_ctx);
507 	_exit(0);
508 }
509 
510 struct cups_async_cb_args {
511 	int pipe_fd;
512 	struct tevent_context *event_ctx;
513 	struct messaging_context *msg_ctx;
514 	void (*post_cache_fill_fn)(struct tevent_context *,
515 				   struct messaging_context *);
516 };
517 
518 static void cups_async_callback(struct tevent_context *event_ctx,
519 				struct tevent_fd *event,
520 				uint16_t flags,
521 				void *p)
522 {
523 	TALLOC_CTX *frame = talloc_stackframe();
524 	struct cups_async_cb_args *cb_args = (struct cups_async_cb_args *)p;
525 	struct pcap_cache *tmp_pcap_cache = NULL;
526 	bool ret_ok;
527 	struct pcap_data pcap_data;
528 	DATA_BLOB pcap_blob;
529 	enum ndr_err_code ndr_ret;
530 	uint32_t i;
531 
532 	DEBUG(5,("cups_async_callback: callback received for printer data. "
533 		"fd = %d\n", cb_args->pipe_fd));
534 
535 	ret_ok = recv_pcap_blob(frame, cb_args->pipe_fd, &pcap_blob);
536 	if (!ret_ok) {
537 		DEBUG(0,("failed to recv pcap blob\n"));
538 		goto err_out;
539 	}
540 
541 	ndr_ret = ndr_pull_struct_blob(&pcap_blob, frame, &pcap_data,
542 				       (ndr_pull_flags_fn_t)ndr_pull_pcap_data);
543 	if (ndr_ret != NDR_ERR_SUCCESS) {
544 		goto err_out;
545 	}
546 
547 	if (!NT_STATUS_IS_OK(pcap_data.status)) {
548 		DEBUG(3,("failed to retrieve printer list: %s\n",
549 			 nt_errstr(pcap_data.status)));
550 		goto err_out;
551 	}
552 
553 	for (i = 0; i < pcap_data.count; i++) {
554 		ret_ok = pcap_cache_add_specific(&tmp_pcap_cache,
555 						 pcap_data.printers[i].name,
556 						 pcap_data.printers[i].info,
557 						 pcap_data.printers[i].location);
558 		if (!ret_ok) {
559 			DEBUG(0, ("failed to add to tmp pcap cache\n"));
560 			goto err_out;
561 		}
562 	}
563 
564 	/* replace the system-wide pcap cache with a (possibly empty) new one */
565 	ret_ok = pcap_cache_replace(tmp_pcap_cache);
566 	if (!ret_ok) {
567 		DEBUG(0, ("failed to replace pcap cache\n"));
568 	} else if (cb_args->post_cache_fill_fn != NULL) {
569 		/* Caller requested post cache fill callback */
570 		cb_args->post_cache_fill_fn(cb_args->event_ctx,
571 					    cb_args->msg_ctx);
572 	}
573 err_out:
574 	pcap_cache_destroy_specific(&tmp_pcap_cache);
575 	TALLOC_FREE(frame);
576 	TALLOC_FREE(cache_fd_event);
577 	close(cb_args->pipe_fd);
578 	TALLOC_FREE(cb_args);
579 }
580 
581 bool cups_cache_reload(struct tevent_context *ev,
582 		       struct messaging_context *msg_ctx,
583 		       void (*post_cache_fill_fn)(struct tevent_context *,
584 						  struct messaging_context *))
585 {
586 	struct cups_async_cb_args *cb_args;
587 	int *p_pipe_fd;
588 
589 	cb_args = talloc(NULL, struct cups_async_cb_args);
590 	if (cb_args == NULL) {
591 		return false;
592 	}
593 
594 	cb_args->post_cache_fill_fn = post_cache_fill_fn;
595 	cb_args->event_ctx = ev;
596 	cb_args->msg_ctx = msg_ctx;
597 	p_pipe_fd = &cb_args->pipe_fd;
598 	*p_pipe_fd = -1;
599 
600 	/* Set up an async refresh. */
601 	if (!cups_pcap_load_async(ev, msg_ctx, p_pipe_fd)) {
602 		talloc_free(cb_args);
603 		return false;
604 	}
605 
606 	DEBUG(10,("cups_cache_reload: async read on fd %d\n",
607 		*p_pipe_fd ));
608 
609 	/* Trigger an event when the pipe can be read. */
610 	cache_fd_event = tevent_add_fd(ev,
611 				NULL, *p_pipe_fd,
612 				TEVENT_FD_READ,
613 				cups_async_callback,
614 				(void *)cb_args);
615 	if (!cache_fd_event) {
616 		close(*p_pipe_fd);
617 		TALLOC_FREE(cb_args);
618 		return false;
619 	}
620 
621 	return true;
622 }
623 
624 /*
625  * 'cups_job_delete()' - Delete a job.
626  */
627 
628 static int cups_job_delete(const char *sharename, const char *lprm_command, struct printjob *pjob)
629 {
630 	TALLOC_CTX *frame = talloc_stackframe();
631 	int		ret = 1;		/* Return value */
632 	http_t		*http = NULL;		/* HTTP connection to server */
633 	ipp_t		*request = NULL,	/* IPP Request */
634 			*response = NULL;	/* IPP Response */
635 	cups_lang_t	*language = NULL;	/* Default language */
636 	char *user = NULL;
637 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
638 	http_uri_status_t ustatus;
639 	size_t size;
640 
641 	DEBUG(5,("cups_job_delete(%s, %p (%d))\n", sharename, pjob, pjob->sysjob));
642 
643        /*
644         * Make sure we don't ask for passwords...
645 	*/
646 
647         cupsSetPasswordCB(cups_passwd_cb);
648 
649        /*
650 	* Try to connect to the server...
651 	*/
652 
653 	if ((http = cups_connect(frame)) == NULL) {
654 		goto out;
655 	}
656 
657        /*
658 	* Build an IPP_CANCEL_JOB request, which requires the following
659 	* attributes:
660 	*
661 	*    attributes-charset
662 	*    attributes-natural-language
663 	*    job-uri
664 	*    requesting-user-name
665 	*/
666 
667 	request = ippNew();
668 
669 	ippSetOperation(request, IPP_CANCEL_JOB);
670 	ippSetRequestId(request, 1);
671 
672 	language = cupsLangDefault();
673 
674 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
675                      "attributes-charset", NULL, "utf-8");
676 
677 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
678         	     "attributes-natural-language", NULL, language->language);
679 
680 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
681 				   uri,
682 				   sizeof(uri),
683 				   "ipp",
684 				   NULL, /* username */
685 				   "localhost",
686 				   ippPort(),
687 				   "/jobs/%d",
688 				   pjob->sysjob);
689 	if (ustatus != HTTP_URI_STATUS_OK) {
690 		goto out;
691 	}
692 
693 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
694 
695 	if (!push_utf8_talloc(frame, &user, pjob->user, &size)) {
696 		goto out;
697 	}
698 
699 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
700         	     NULL, user);
701 
702        /*
703 	* Do the request and get back a response...
704 	*/
705 
706 	if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) {
707 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
708 			DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
709 				ippErrorString(cupsLastError())));
710 		} else {
711 			ret = 0;
712 		}
713 	} else {
714 		DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
715 			ippErrorString(cupsLastError())));
716 	}
717 
718  out:
719 	if (response)
720 		ippDelete(response);
721 
722 	if (language)
723 		cupsLangFree(language);
724 
725 	if (http)
726 		httpClose(http);
727 
728 	TALLOC_FREE(frame);
729 	return ret;
730 }
731 
732 
733 /*
734  * 'cups_job_pause()' - Pause a job.
735  */
736 
737 static int cups_job_pause(int snum, struct printjob *pjob)
738 {
739 	TALLOC_CTX *frame = talloc_stackframe();
740 	int		ret = 1;		/* Return value */
741 	http_t		*http = NULL;		/* HTTP connection to server */
742 	ipp_t		*request = NULL,	/* IPP Request */
743 			*response = NULL;	/* IPP Response */
744 	cups_lang_t	*language = NULL;	/* Default language */
745 	char *user = NULL;
746 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
747 	http_uri_status_t ustatus;
748 	size_t size;
749 
750 	DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
751 
752        /*
753         * Make sure we don't ask for passwords...
754 	*/
755 
756         cupsSetPasswordCB(cups_passwd_cb);
757 
758        /*
759 	* Try to connect to the server...
760 	*/
761 
762 	if ((http = cups_connect(frame)) == NULL) {
763 		goto out;
764 	}
765 
766        /*
767 	* Build an IPP_HOLD_JOB request, which requires the following
768 	* attributes:
769 	*
770 	*    attributes-charset
771 	*    attributes-natural-language
772 	*    job-uri
773 	*    requesting-user-name
774 	*/
775 
776 	request = ippNew();
777 
778 	ippSetOperation(request, IPP_HOLD_JOB);
779 	ippSetRequestId(request, 1);
780 
781 	language = cupsLangDefault();
782 
783 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
784                      "attributes-charset", NULL, "utf-8");
785 
786 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
787         	     "attributes-natural-language", NULL, language->language);
788 
789 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
790 				   uri,
791 				   sizeof(uri),
792 				   "ipp",
793 				   NULL, /* username */
794 				   "localhost",
795 				   ippPort(),
796 				   "/jobs/%d",
797 				   pjob->sysjob);
798 	if (ustatus != HTTP_URI_STATUS_OK) {
799 		goto out;
800 	}
801 
802 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
803 
804 	if (!push_utf8_talloc(frame, &user, pjob->user, &size)) {
805 		goto out;
806 	}
807 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
808         	     NULL, user);
809 
810        /*
811 	* Do the request and get back a response...
812 	*/
813 
814 	if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) {
815 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
816 			DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
817 				ippErrorString(cupsLastError())));
818 		} else {
819 			ret = 0;
820 		}
821 	} else {
822 		DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
823 			ippErrorString(cupsLastError())));
824 	}
825 
826  out:
827 	if (response)
828 		ippDelete(response);
829 
830 	if (language)
831 		cupsLangFree(language);
832 
833 	if (http)
834 		httpClose(http);
835 
836 	TALLOC_FREE(frame);
837 	return ret;
838 }
839 
840 
841 /*
842  * 'cups_job_resume()' - Resume a paused job.
843  */
844 
845 static int cups_job_resume(int snum, struct printjob *pjob)
846 {
847 	TALLOC_CTX *frame = talloc_stackframe();
848 	int		ret = 1;		/* Return value */
849 	http_t		*http = NULL;		/* HTTP connection to server */
850 	ipp_t		*request = NULL,	/* IPP Request */
851 			*response = NULL;	/* IPP Response */
852 	cups_lang_t	*language = NULL;	/* Default language */
853 	char *user = NULL;
854 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
855 	http_uri_status_t ustatus;
856 	size_t size;
857 
858 	DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
859 
860        /*
861         * Make sure we don't ask for passwords...
862 	*/
863 
864         cupsSetPasswordCB(cups_passwd_cb);
865 
866        /*
867 	* Try to connect to the server...
868 	*/
869 
870 	if ((http = cups_connect(frame)) == NULL) {
871 		goto out;
872 	}
873 
874        /*
875 	* Build an IPP_RELEASE_JOB request, which requires the following
876 	* attributes:
877 	*
878 	*    attributes-charset
879 	*    attributes-natural-language
880 	*    job-uri
881 	*    requesting-user-name
882 	*/
883 
884 	request = ippNew();
885 
886 	ippSetOperation(request, IPP_RELEASE_JOB);
887 	ippSetRequestId(request, 1);
888 
889 	language = cupsLangDefault();
890 
891 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
892                      "attributes-charset", NULL, "utf-8");
893 
894 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
895         	     "attributes-natural-language", NULL, language->language);
896 
897 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
898 				   uri,
899 				   sizeof(uri),
900 				   "ipp",
901 				   NULL, /* username */
902 				   "localhost",
903 				   ippPort(),
904 				   "/jobs/%d",
905 				   pjob->sysjob);
906 	if (ustatus != HTTP_URI_STATUS_OK) {
907 		goto out;
908 	}
909 
910 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
911 
912 	if (!push_utf8_talloc(frame, &user, pjob->user, &size)) {
913 		goto out;
914 	}
915 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
916         	     NULL, user);
917 
918        /*
919 	* Do the request and get back a response...
920 	*/
921 
922 	if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) {
923 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
924 			DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
925 				ippErrorString(cupsLastError())));
926 		} else {
927 			ret = 0;
928 		}
929 	} else {
930 		DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
931 			ippErrorString(cupsLastError())));
932 	}
933 
934  out:
935 	if (response)
936 		ippDelete(response);
937 
938 	if (language)
939 		cupsLangFree(language);
940 
941 	if (http)
942 		httpClose(http);
943 
944 	TALLOC_FREE(frame);
945 	return ret;
946 }
947 
948 
949 /*
950  * 'cups_job_submit()' - Submit a job for printing.
951  */
952 
953 static int cups_job_submit(int snum, struct printjob *pjob,
954 			   enum printing_types printing_type,
955 			   char *lpq_cmd)
956 {
957 	TALLOC_CTX *frame = talloc_stackframe();
958 	const struct loadparm_substitution *lp_sub =
959 		loadparm_s3_global_substitution();
960 	int		ret = 1;		/* Return value */
961 	http_t		*http = NULL;		/* HTTP connection to server */
962 	ipp_t		*request = NULL,	/* IPP Request */
963 			*response = NULL;	/* IPP Response */
964 	ipp_attribute_t *attr_job_id = NULL;	/* IPP Attribute "job-id" */
965 	cups_lang_t	*language = NULL;	/* Default language */
966 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
967 	http_uri_status_t ustatus;
968 	char *new_jobname = NULL;
969 	int		num_options = 0;
970 	cups_option_t 	*options = NULL;
971 	char *printername = NULL;
972 	char *user = NULL;
973 	char *jobname = NULL;
974 	char *cupsoptions = NULL;
975 	char *filename = NULL;
976 	size_t size;
977 
978 	DEBUG(5,("cups_job_submit(%d, %p)\n", snum, pjob));
979 
980        /*
981         * Make sure we don't ask for passwords...
982 	*/
983 
984         cupsSetPasswordCB(cups_passwd_cb);
985 
986        /*
987 	* Try to connect to the server...
988 	*/
989 
990 	if ((http = cups_connect(frame)) == NULL) {
991 		goto out;
992 	}
993 
994        /*
995 	* Build an IPP_PRINT_JOB request, which requires the following
996 	* attributes:
997 	*
998 	*    attributes-charset
999 	*    attributes-natural-language
1000 	*    printer-uri
1001 	*    requesting-user-name
1002 	*    [document-data]
1003 	*/
1004 
1005 	request = ippNew();
1006 
1007 	ippSetOperation(request, IPP_PRINT_JOB);
1008 	ippSetRequestId(request, 1);
1009 
1010 	language = cupsLangDefault();
1011 
1012 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1013                      "attributes-charset", NULL, "utf-8");
1014 
1015 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1016         	     "attributes-natural-language", NULL, language->language);
1017 
1018 	if (!push_utf8_talloc(frame, &printername,
1019 			      lp_printername(talloc_tos(), lp_sub, snum),
1020 			      &size)) {
1021 		goto out;
1022 	}
1023 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
1024 				   uri,
1025 				   sizeof(uri),
1026 				   "ipp",
1027 				   NULL, /* username */
1028 				   "localhost",
1029 				   ippPort(),
1030 				   "/printers/%s",
1031 				   printername);
1032 	if (ustatus != HTTP_URI_STATUS_OK) {
1033 		goto out;
1034 	}
1035 
1036 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1037         	     "printer-uri", NULL, uri);
1038 
1039 	if (!push_utf8_talloc(frame, &user, pjob->user, &size)) {
1040 		goto out;
1041 	}
1042 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1043         	     NULL, user);
1044 
1045 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1046 	             "job-originating-host-name", NULL,
1047 		     pjob->clientmachine);
1048 
1049 	if (!push_utf8_talloc(frame, &jobname, pjob->jobname, &size)) {
1050 		goto out;
1051 	}
1052 	new_jobname = talloc_asprintf(frame,
1053 			"%s%.8u %s", PRINT_SPOOL_PREFIX,
1054 			pjob->jobid, jobname);
1055 	if (new_jobname == NULL) {
1056 		goto out;
1057 	}
1058 
1059 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
1060         	     new_jobname);
1061 
1062 	/*
1063 	 * add any options defined in smb.conf
1064 	 */
1065 
1066 	if (!push_utf8_talloc(frame, &cupsoptions,
1067 			      lp_cups_options(talloc_tos(), lp_sub, snum), &size)) {
1068 		goto out;
1069 	}
1070 	num_options = 0;
1071 	options     = NULL;
1072 	num_options = cupsParseOptions(cupsoptions, num_options, &options);
1073 
1074 	if ( num_options )
1075 		cupsEncodeOptions(request, num_options, options);
1076 
1077        /*
1078 	* Do the request and get back a response...
1079 	*/
1080 
1081 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
1082 				   uri,
1083 				   sizeof(uri),
1084 				   "ipp",
1085 				   NULL, /* username */
1086 				   "localhost",
1087 				   ippPort(),
1088 				   "/printers/%s",
1089 				   printername);
1090 	if (ustatus != HTTP_URI_STATUS_OK) {
1091 		goto out;
1092 	}
1093 
1094 	if (!push_utf8_talloc(frame, &filename, pjob->filename, &size)) {
1095 		goto out;
1096 	}
1097 	if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) {
1098 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1099 			DEBUG(0,("Unable to print file to %s - %s\n",
1100 				 lp_printername(talloc_tos(), lp_sub, snum),
1101 			         ippErrorString(cupsLastError())));
1102 		} else {
1103 			ret = 0;
1104 			attr_job_id = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
1105 			if(attr_job_id) {
1106 				pjob->sysjob = ippGetInteger(attr_job_id, 0);
1107 				DEBUG(5,("cups_job_submit: job-id %d\n", pjob->sysjob));
1108 			} else {
1109 				DEBUG(0,("Missing job-id attribute in IPP response"));
1110 			}
1111 		}
1112 	} else {
1113 		DEBUG(0,("Unable to print file to `%s' - %s\n",
1114 			 lp_printername(talloc_tos(), lp_sub, snum),
1115 			 ippErrorString(cupsLastError())));
1116 	}
1117 
1118 	if ( ret == 0 )
1119 		unlink(pjob->filename);
1120 	/* else print_job_end will do it for us */
1121 
1122  out:
1123 	if (response)
1124 		ippDelete(response);
1125 
1126 	if (language)
1127 		cupsLangFree(language);
1128 
1129 	if (http)
1130 		httpClose(http);
1131 
1132 	TALLOC_FREE(frame);
1133 
1134 	return ret;
1135 }
1136 
1137 /*
1138  * 'cups_queue_get()' - Get all the jobs in the print queue.
1139  */
1140 
1141 static int cups_queue_get(const char *sharename,
1142                enum printing_types printing_type,
1143                char *lpq_command,
1144                print_queue_struct **q,
1145                print_status_struct *status)
1146 {
1147 	TALLOC_CTX *frame = talloc_stackframe();
1148 	char *printername = NULL;
1149 	http_t		*http = NULL;		/* HTTP connection to server */
1150 	ipp_t		*request = NULL,	/* IPP Request */
1151 			*response = NULL;	/* IPP Response */
1152 	ipp_attribute_t	*attr = NULL;		/* Current attribute */
1153 	cups_lang_t	*language = NULL;	/* Default language */
1154 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
1155 	http_uri_status_t ustatus;
1156 	int		qcount = 0,		/* Number of active queue entries */
1157 			qalloc = 0;		/* Number of queue entries allocated */
1158 	print_queue_struct *queue = NULL,	/* Queue entries */
1159 			*temp;		/* Temporary pointer for queue */
1160 	char		*user_name = NULL,	/* job-originating-user-name attribute */
1161 			*job_name = NULL;	/* job-name attribute */
1162 	int		job_id;		/* job-id attribute */
1163 	int		job_k_octets;	/* job-k-octets attribute */
1164 	time_t		job_time;	/* time-at-creation attribute */
1165 	ipp_jstate_t	job_status;	/* job-status attribute */
1166 	int		job_priority;	/* job-priority attribute */
1167 	size_t size;
1168 	static const char *jattrs[] =	/* Requested job attributes */
1169 			{
1170 			  "job-id",
1171 			  "job-k-octets",
1172 			  "job-name",
1173 			  "job-originating-user-name",
1174 			  "job-priority",
1175 			  "job-state",
1176 			  "time-at-creation",
1177 			};
1178 	static const char *pattrs[] =	/* Requested printer attributes */
1179 			{
1180 			  "printer-state",
1181 			  "printer-state-message"
1182 			};
1183 
1184 	*q = NULL;
1185 
1186 	/* HACK ALERT!!!  The problem with support the 'printer name'
1187 	   option is that we key the tdb off the sharename.  So we will
1188 	   overload the lpq_command string to pass in the printername
1189 	   (which is basically what we do for non-cups printers ... using
1190 	   the lpq_command to get the queue listing). */
1191 
1192 	if (!push_utf8_talloc(frame, &printername, lpq_command, &size)) {
1193 		goto out;
1194 	}
1195 	DEBUG(5,("cups_queue_get(%s, %p, %p)\n", lpq_command, q, status));
1196 
1197        /*
1198         * Make sure we don't ask for passwords...
1199 	*/
1200 
1201         cupsSetPasswordCB(cups_passwd_cb);
1202 
1203        /*
1204 	* Try to connect to the server...
1205 	*/
1206 
1207 	if ((http = cups_connect(frame)) == NULL) {
1208 		goto out;
1209 	}
1210 
1211        /*
1212         * Generate the printer URI...
1213 	*/
1214 
1215 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
1216 				   uri,
1217 				   sizeof(uri),
1218 				   "ipp",
1219 				   NULL, /* username */
1220 				   "localhost",
1221 				   ippPort(),
1222 				   "/printers/%s",
1223 				   printername);
1224 	if (ustatus != HTTP_URI_STATUS_OK) {
1225 		goto out;
1226 	}
1227 
1228        /*
1229 	* Build an IPP_GET_JOBS request, which requires the following
1230 	* attributes:
1231 	*
1232 	*    attributes-charset
1233 	*    attributes-natural-language
1234 	*    requested-attributes
1235 	*    printer-uri
1236 	*/
1237 
1238 	request = ippNew();
1239 
1240 	ippSetOperation(request, IPP_GET_JOBS);
1241 	ippSetRequestId(request, 1);
1242 
1243 	language = cupsLangDefault();
1244 
1245 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1246                      "attributes-charset", NULL, "utf-8");
1247 
1248 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1249                      "attributes-natural-language", NULL, language->language);
1250 
1251         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1252 	              "requested-attributes",
1253 		      (sizeof(jattrs) / sizeof(jattrs[0])),
1254 		      NULL, jattrs);
1255 
1256 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1257                      "printer-uri", NULL, uri);
1258 
1259        /*
1260 	* Do the request and get back a response...
1261 	*/
1262 
1263 	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
1264 		DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1265 			 ippErrorString(cupsLastError())));
1266 		goto out;
1267 	}
1268 
1269 	if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1270 		DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1271 			 ippErrorString(ippGetStatusCode(response))));
1272 		goto out;
1273 	}
1274 
1275        /*
1276         * Process the jobs...
1277 	*/
1278 
1279         qcount = 0;
1280 	qalloc = 0;
1281 	queue  = NULL;
1282 
1283         for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) {
1284 	       /*
1285 		* Skip leading attributes until we hit a job...
1286 		*/
1287 
1288 		while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_JOB)
1289 			attr = ippNextAttribute(response);
1290 
1291 		if (attr == NULL)
1292 			break;
1293 
1294 	       /*
1295 	        * Allocate memory as needed...
1296 		*/
1297 		if (qcount >= qalloc) {
1298 			qalloc += 16;
1299 
1300 			queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc);
1301 
1302 			if (queue == NULL) {
1303 				DEBUG(0,("cups_queue_get: Not enough memory!"));
1304 				qcount = 0;
1305 				goto out;
1306 			}
1307 		}
1308 
1309 		temp = queue + qcount;
1310 		memset(temp, 0, sizeof(print_queue_struct));
1311 
1312 	       /*
1313 		* Pull the needed attributes from this job...
1314 		*/
1315 
1316 		job_id       = 0;
1317 		job_priority = 50;
1318 		job_status   = IPP_JOB_PENDING;
1319 		job_time     = 0;
1320 		job_k_octets = 0;
1321 		user_name    = NULL;
1322 		job_name     = NULL;
1323 
1324 		while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_JOB) {
1325 			if (ippGetName(attr) == NULL) {
1326 				attr = ippNextAttribute(response);
1327 				break;
1328 			}
1329 
1330 			if (strcmp(ippGetName(attr), "job-id") == 0 &&
1331 			    ippGetValueTag(attr) == IPP_TAG_INTEGER)
1332 				job_id = ippGetInteger(attr, 0);
1333 
1334 			if (strcmp(ippGetName(attr), "job-k-octets") == 0 &&
1335 			    ippGetValueTag(attr) == IPP_TAG_INTEGER)
1336 				job_k_octets = ippGetInteger(attr, 0);
1337 
1338 			if (strcmp(ippGetName(attr), "job-priority") == 0 &&
1339 			    ippGetValueTag(attr) == IPP_TAG_INTEGER)
1340 				job_priority = ippGetInteger(attr, 0);
1341 
1342 			if (strcmp(ippGetName(attr), "job-state") == 0 &&
1343 			    ippGetValueTag(attr) == IPP_TAG_ENUM)
1344 				job_status = (ipp_jstate_t)ippGetInteger(attr, 0);
1345 
1346 			if (strcmp(ippGetName(attr), "time-at-creation") == 0 &&
1347 			    ippGetValueTag(attr) == IPP_TAG_INTEGER)
1348 				job_time = ippGetInteger(attr, 0);
1349 
1350 			if (strcmp(ippGetName(attr), "job-name") == 0 &&
1351 			    ippGetValueTag(attr) == IPP_TAG_NAME) {
1352 				if (!pull_utf8_talloc(frame,
1353 						&job_name,
1354 						ippGetString(attr, 0, NULL),
1355 						&size)) {
1356 					goto out;
1357 				}
1358 			}
1359 
1360 			if (strcmp(ippGetName(attr), "job-originating-user-name") == 0 &&
1361 			    ippGetValueTag(attr) == IPP_TAG_NAME) {
1362 				if (!pull_utf8_talloc(frame,
1363 						&user_name,
1364 						ippGetString(attr, 0, NULL),
1365 						&size)) {
1366 					goto out;
1367 				}
1368 			}
1369 
1370 			attr = ippNextAttribute(response);
1371 		}
1372 
1373 	       /*
1374 		* See if we have everything needed...
1375 		*/
1376 
1377 		if (user_name == NULL || job_name == NULL || job_id == 0) {
1378 			if (attr == NULL)
1379 				break;
1380 			else
1381 				continue;
1382 		}
1383 
1384 		temp->sysjob   = job_id;
1385 		temp->size     = job_k_octets * 1024;
1386 		temp->status   = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
1387 				 job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
1388                                  job_status == IPP_JOB_HELD ? LPQ_PAUSED :
1389 			         LPQ_PRINTING;
1390 		temp->priority = job_priority;
1391 		temp->time     = job_time;
1392 		strlcpy(temp->fs_user, user_name, sizeof(temp->fs_user));
1393 		strlcpy(temp->fs_file, job_name, sizeof(temp->fs_file));
1394 
1395 		qcount ++;
1396 
1397 		if (attr == NULL)
1398 			break;
1399 	}
1400 
1401 	ippDelete(response);
1402 	response = NULL;
1403 
1404        /*
1405 	* Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1406 	* following attributes:
1407 	*
1408 	*    attributes-charset
1409 	*    attributes-natural-language
1410 	*    requested-attributes
1411 	*    printer-uri
1412 	*/
1413 
1414 	request = ippNew();
1415 
1416 	ippSetOperation(request, IPP_GET_PRINTER_ATTRIBUTES);
1417 	ippSetRequestId(request, 1);
1418 
1419 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1420                      "attributes-charset", NULL, "utf-8");
1421 
1422 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1423                      "attributes-natural-language", NULL, language->language);
1424 
1425         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1426 	              "requested-attributes",
1427 		      (sizeof(pattrs) / sizeof(pattrs[0])),
1428 		      NULL, pattrs);
1429 
1430 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1431                      "printer-uri", NULL, uri);
1432 
1433        /*
1434 	* Do the request and get back a response...
1435 	*/
1436 
1437 	if ((response = cupsDoRequest(http, request, "/")) == NULL) {
1438 		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
1439 			 ippErrorString(cupsLastError())));
1440 		goto out;
1441 	}
1442 
1443 	if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1444 		DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
1445 			 ippErrorString(ippGetStatusCode(response))));
1446 		goto out;
1447 	}
1448 
1449        /*
1450         * Get the current printer status and convert it to the SAMBA values.
1451 	*/
1452 
1453         if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) {
1454 		if (ippGetInteger(attr, 0) == IPP_PRINTER_STOPPED)
1455 			status->status = LPSTAT_STOPPED;
1456 		else
1457 			status->status = LPSTAT_OK;
1458 	}
1459 
1460         if ((attr = ippFindAttribute(response, "printer-state-message",
1461 	                             IPP_TAG_TEXT)) != NULL) {
1462 		char *msg = NULL;
1463 		if (!pull_utf8_talloc(frame, &msg,
1464 				ippGetString(attr, 0, NULL),
1465 				&size)) {
1466 			SAFE_FREE(queue);
1467 			qcount = 0;
1468 			goto out;
1469 		}
1470 	        fstrcpy(status->message, msg);
1471 	}
1472 
1473  out:
1474 
1475        /*
1476         * Return the job queue...
1477 	*/
1478 
1479 	*q = queue;
1480 
1481 	if (response)
1482 		ippDelete(response);
1483 
1484 	if (language)
1485 		cupsLangFree(language);
1486 
1487 	if (http)
1488 		httpClose(http);
1489 
1490 	TALLOC_FREE(frame);
1491 	return qcount;
1492 }
1493 
1494 
1495 /*
1496  * 'cups_queue_pause()' - Pause a print queue.
1497  */
1498 
1499 static int cups_queue_pause(int snum)
1500 {
1501 	TALLOC_CTX *frame = talloc_stackframe();
1502 	const struct loadparm_substitution *lp_sub =
1503 		loadparm_s3_global_substitution();
1504 	int		ret = 1;		/* Return value */
1505 	http_t		*http = NULL;		/* HTTP connection to server */
1506 	ipp_t		*request = NULL,	/* IPP Request */
1507 			*response = NULL;	/* IPP Response */
1508 	cups_lang_t	*language = NULL;	/* Default language */
1509 	char *printername = NULL;
1510 	char *username = NULL;
1511 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
1512 	http_uri_status_t ustatus;
1513 	size_t size;
1514 
1515 	DEBUG(5,("cups_queue_pause(%d)\n", snum));
1516 
1517 	/*
1518 	 * Make sure we don't ask for passwords...
1519 	 */
1520 
1521         cupsSetPasswordCB(cups_passwd_cb);
1522 
1523 	/*
1524 	 * Try to connect to the server...
1525 	 */
1526 
1527 	if ((http = cups_connect(frame)) == NULL) {
1528 		goto out;
1529 	}
1530 
1531 	/*
1532 	 * Build an IPP_PAUSE_PRINTER request, which requires the following
1533 	 * attributes:
1534 	 *
1535 	 *    attributes-charset
1536 	 *    attributes-natural-language
1537 	 *    printer-uri
1538 	 *    requesting-user-name
1539 	 */
1540 
1541 	request = ippNew();
1542 
1543 	ippSetOperation(request, IPP_PAUSE_PRINTER);
1544 	ippSetRequestId(request, 1);
1545 
1546 	language = cupsLangDefault();
1547 
1548 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1549                      "attributes-charset", NULL, "utf-8");
1550 
1551 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1552         	     "attributes-natural-language", NULL, language->language);
1553 
1554 	if (!push_utf8_talloc(frame, &printername,
1555 			      lp_printername(talloc_tos(), lp_sub, snum), &size)) {
1556 		goto out;
1557 	}
1558 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
1559 				   uri,
1560 				   sizeof(uri),
1561 				   "ipp",
1562 				   NULL, /* username */
1563 				   "localhost",
1564 				   ippPort(),
1565 				   "/printers/%s",
1566 				   printername);
1567 	if (ustatus != HTTP_URI_STATUS_OK) {
1568 		goto out;
1569 	}
1570 
1571 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
1572 
1573 	if (!push_utf8_talloc(frame, &username, current_user_info.unix_name, &size)) {
1574 		goto out;
1575 	}
1576 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1577         	     NULL, username);
1578 
1579        /*
1580 	* Do the request and get back a response...
1581 	*/
1582 
1583 	if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) {
1584 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1585 			DEBUG(0,("Unable to pause printer %s - %s\n",
1586 				 lp_printername(talloc_tos(), lp_sub, snum),
1587 				ippErrorString(cupsLastError())));
1588 		} else {
1589 			ret = 0;
1590 		}
1591 	} else {
1592 		DEBUG(0,("Unable to pause printer %s - %s\n",
1593 			 lp_printername(talloc_tos(), lp_sub, snum),
1594 			ippErrorString(cupsLastError())));
1595 	}
1596 
1597  out:
1598 	if (response)
1599 		ippDelete(response);
1600 
1601 	if (language)
1602 		cupsLangFree(language);
1603 
1604 	if (http)
1605 		httpClose(http);
1606 
1607 	TALLOC_FREE(frame);
1608 	return ret;
1609 }
1610 
1611 
1612 /*
1613  * 'cups_queue_resume()' - Restart a print queue.
1614  */
1615 
1616 static int cups_queue_resume(int snum)
1617 {
1618 	TALLOC_CTX *frame = talloc_stackframe();
1619 	const struct loadparm_substitution *lp_sub =
1620 		loadparm_s3_global_substitution();
1621 	int		ret = 1;		/* Return value */
1622 	http_t		*http = NULL;		/* HTTP connection to server */
1623 	ipp_t		*request = NULL,	/* IPP Request */
1624 			*response = NULL;	/* IPP Response */
1625 	cups_lang_t	*language = NULL;	/* Default language */
1626 	char *printername = NULL;
1627 	char *username = NULL;
1628 	char		uri[HTTP_MAX_URI] = {0}; /* printer-uri attribute */
1629 	http_uri_status_t ustatus;
1630 	size_t size;
1631 
1632 	DEBUG(5,("cups_queue_resume(%d)\n", snum));
1633 
1634        /*
1635         * Make sure we don't ask for passwords...
1636 	*/
1637 
1638         cupsSetPasswordCB(cups_passwd_cb);
1639 
1640        /*
1641 	* Try to connect to the server...
1642 	*/
1643 
1644 	if ((http = cups_connect(frame)) == NULL) {
1645 		goto out;
1646 	}
1647 
1648        /*
1649 	* Build an IPP_RESUME_PRINTER request, which requires the following
1650 	* attributes:
1651 	*
1652 	*    attributes-charset
1653 	*    attributes-natural-language
1654 	*    printer-uri
1655 	*    requesting-user-name
1656 	*/
1657 
1658 	request = ippNew();
1659 
1660 	ippSetOperation(request, IPP_RESUME_PRINTER);
1661 	ippSetRequestId(request, 1);
1662 
1663 	language = cupsLangDefault();
1664 
1665 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1666                      "attributes-charset", NULL, "utf-8");
1667 
1668 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1669         	     "attributes-natural-language", NULL, language->language);
1670 
1671 	if (!push_utf8_talloc(frame, &printername, lp_printername(talloc_tos(), lp_sub, snum),
1672 			      &size)) {
1673 		goto out;
1674 	}
1675 	ustatus = httpAssembleURIf(HTTP_URI_CODING_ALL,
1676 				   uri,
1677 				   sizeof(uri),
1678 				   "ipp",
1679 				   NULL, /* username */
1680 				   "localhost",
1681 				   ippPort(),
1682 				   "/printers/%s",
1683 				   printername);
1684 	if (ustatus != HTTP_URI_STATUS_OK) {
1685 		goto out;
1686 	}
1687 
1688 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
1689 
1690 	if (!push_utf8_talloc(frame, &username, current_user_info.unix_name, &size)) {
1691 		goto out;
1692 	}
1693 	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1694         	     NULL, username);
1695 
1696        /*
1697 	* Do the request and get back a response...
1698 	*/
1699 
1700 	if ((response = cupsDoRequest(http, request, "/admin/")) != NULL) {
1701 		if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1702 			DEBUG(0,("Unable to resume printer %s - %s\n",
1703 				 lp_printername(talloc_tos(), lp_sub, snum),
1704 				ippErrorString(cupsLastError())));
1705 		} else {
1706 			ret = 0;
1707 		}
1708 	} else {
1709 		DEBUG(0,("Unable to resume printer %s - %s\n",
1710 			 lp_printername(talloc_tos(), lp_sub, snum),
1711 			ippErrorString(cupsLastError())));
1712 	}
1713 
1714  out:
1715 	if (response)
1716 		ippDelete(response);
1717 
1718 	if (language)
1719 		cupsLangFree(language);
1720 
1721 	if (http)
1722 		httpClose(http);
1723 
1724 	TALLOC_FREE(frame);
1725 	return ret;
1726 }
1727 
1728 /*******************************************************************
1729  * CUPS printing interface definitions...
1730  ******************************************************************/
1731 
1732 struct printif	cups_printif =
1733 {
1734 	PRINT_CUPS,
1735 	cups_queue_get,
1736 	cups_queue_pause,
1737 	cups_queue_resume,
1738 	cups_job_delete,
1739 	cups_job_pause,
1740 	cups_job_resume,
1741 	cups_job_submit,
1742 };
1743 
1744 #else
1745  /* this keeps fussy compilers happy */
1746  void print_cups_dummy(void);
1747  void print_cups_dummy(void) {}
1748 #endif /* HAVE_CUPS */
1749