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