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