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