r3067: patch based on volker's initial work in trunk that fixes the queu update probl...
[kai/samba.git] / source / 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  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include "includes.h"
22 #include "printing.h"
23
24 #ifdef HAVE_CUPS
25 #include <cups/cups.h>
26 #include <cups/language.h>
27
28
29 /*
30  * 'cups_passwd_cb()' - The CUPS password callback...
31  */
32
33 static const char *                             /* O - Password or NULL */
34 cups_passwd_cb(const char *prompt)      /* I - Prompt */
35 {
36  /*
37   * Always return NULL to indicate that no password is available...
38   */
39
40   return (NULL);
41 }
42
43 static const char *cups_server(void)
44 {
45         if ((lp_cups_server() != NULL) && (strlen(lp_cups_server()) > 0)) {
46                 DEBUG(10, ("cups server explicitly set to %s\n",
47                            lp_cups_server()));
48                 return lp_cups_server();
49         }
50
51         DEBUG(10, ("cups server left to default %s\n", cupsServer()));
52         return cupsServer();
53 }
54
55 /*
56  * 'cups_printer_fn()' - Call a function for every printer known to the
57  *                       system.
58  */
59
60 void cups_printer_fn(void (*fn)(char *, char *))
61 {
62         /* I - Function to call */
63         http_t          *http;          /* HTTP connection to server */
64         ipp_t           *request,       /* IPP Request */
65                         *response;      /* IPP Response */
66         ipp_attribute_t *attr;          /* Current attribute */
67         cups_lang_t     *language;      /* Default language */
68         char            *name,          /* printer-name attribute */
69                         *make_model,    /* printer-make-and-model attribute */
70                         *info;          /* printer-info attribute */
71         static const char *requested[] =/* Requested attributes */
72                         {
73                           "printer-name",
74                           "printer-make-and-model",
75                           "printer-info"
76                         };       
77
78
79         DEBUG(5,("cups_printer_fn(%p)\n", fn));
80
81        /*
82         * Make sure we don't ask for passwords...
83         */
84
85         cupsSetPasswordCB(cups_passwd_cb);
86
87        /*
88         * Try to connect to the server...
89         */
90
91         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
92         {
93                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
94                          cups_server(), strerror(errno)));
95                 return;
96         }
97
98        /*
99         * Build a CUPS_GET_PRINTERS request, which requires the following
100         * attributes:
101         *
102         *    attributes-charset
103         *    attributes-natural-language
104         *    requested-attributes
105         */
106
107         request = ippNew();
108
109         request->request.op.operation_id = CUPS_GET_PRINTERS;
110         request->request.op.request_id   = 1;
111
112         language = cupsLangDefault();
113
114         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
115                      "attributes-charset", NULL, cupsLangEncoding(language));
116
117         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
118                      "attributes-natural-language", NULL, language->language);
119
120         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
121                       "requested-attributes",
122                       (sizeof(requested) / sizeof(requested[0])),
123                       NULL, requested);
124
125        /*
126         * Do the request and get back a response...
127         */
128
129         if ((response = cupsDoRequest(http, request, "/")) == NULL)
130         {
131                 DEBUG(0,("Unable to get printer list - %s\n",
132                          ippErrorString(cupsLastError())));
133                 httpClose(http);
134                 return;
135         }
136
137         for (attr = response->attrs; attr != NULL;)
138         {
139                /*
140                 * Skip leading attributes until we hit a printer...
141                 */
142
143                 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
144                         attr = attr->next;
145
146                 if (attr == NULL)
147                         break;
148
149                /*
150                 * Pull the needed attributes from this printer...
151                 */
152
153                 name       = NULL;
154                 make_model = NULL;
155                 info       = NULL;
156
157                 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
158                 {
159                         if (strcmp(attr->name, "printer-name") == 0 &&
160                             attr->value_tag == IPP_TAG_NAME)
161                                 name = attr->values[0].string.text;
162
163                         if (strcmp(attr->name, "printer-make-and-model") == 0 &&
164                             attr->value_tag == IPP_TAG_TEXT)
165                                 make_model = attr->values[0].string.text;
166
167                         if (strcmp(attr->name, "printer-info") == 0 &&
168                             attr->value_tag == IPP_TAG_TEXT)
169                                 info = attr->values[0].string.text;
170
171                         attr = attr->next;
172                 }
173
174                /*
175                 * See if we have everything needed...
176                 */
177
178                 if (name == NULL)
179                         break;
180
181                 if (info == NULL || !info[0])
182                         (*fn)(name, make_model);
183                 else
184                         (*fn)(name, info);
185                 
186
187         }
188
189         ippDelete(response);
190
191
192        /*
193         * Build a CUPS_GET_CLASSES request, which requires the following
194         * attributes:
195         *
196         *    attributes-charset
197         *    attributes-natural-language
198         *    requested-attributes
199         */
200
201         request = ippNew();
202
203         request->request.op.operation_id = CUPS_GET_CLASSES;
204         request->request.op.request_id   = 1;
205
206         language = cupsLangDefault();
207
208         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
209                      "attributes-charset", NULL, cupsLangEncoding(language));
210
211         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
212                      "attributes-natural-language", NULL, language->language);
213
214         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
215                       "requested-attributes",
216                       (sizeof(requested) / sizeof(requested[0])),
217                       NULL, requested);
218
219        /*
220         * Do the request and get back a response...
221         */
222
223         if ((response = cupsDoRequest(http, request, "/")) == NULL)
224         {
225                 DEBUG(0,("Unable to get printer list - %s\n",
226                          ippErrorString(cupsLastError())));
227                 httpClose(http);
228                 return;
229         }
230
231         for (attr = response->attrs; attr != NULL;)
232         {
233                /*
234                 * Skip leading attributes until we hit a printer...
235                 */
236
237                 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
238                         attr = attr->next;
239
240                 if (attr == NULL)
241                         break;
242
243                /*
244                 * Pull the needed attributes from this printer...
245                 */
246
247                 name       = NULL;
248                 make_model = NULL;
249                 info       = NULL;
250
251                 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
252                 {
253                         if (strcmp(attr->name, "printer-name") == 0 &&
254                             attr->value_tag == IPP_TAG_NAME)
255                                 name = attr->values[0].string.text;
256
257                         if (strcmp(attr->name, "printer-make-and-model") == 0 &&
258                             attr->value_tag == IPP_TAG_TEXT)
259                                 make_model = attr->values[0].string.text;
260
261                         if (strcmp(attr->name, "printer-info") == 0 &&
262                             attr->value_tag == IPP_TAG_TEXT)
263                                 info = attr->values[0].string.text;
264
265                         attr = attr->next;
266                 }
267
268                /*
269                 * See if we have everything needed...
270                 */
271
272                 if (name == NULL)
273                         break;
274
275                 if (info == NULL || !info[0])
276                         (*fn)(name, make_model);
277                 else
278                         (*fn)(name, info);
279                 
280
281         }
282
283         ippDelete(response);
284
285        /*
286         * Close the connection to the server...
287         */
288
289         httpClose(http);
290 }
291
292
293 /*
294  * 'cups_printername_ok()' - Provide the equivalent of pcap_printername_ok()
295  *                           for CUPS.
296  * O - 1 if printer name OK
297  * I - Name of printer 
298  */
299 int cups_printername_ok(const char *name)
300 {
301         http_t          *http;          /* HTTP connection to server */
302         ipp_t           *request,       /* IPP Request */
303                         *response;      /* IPP Response */
304         cups_lang_t     *language;      /* Default language */
305         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
306
307
308         DEBUG(5,("cups_printername_ok(\"%s\")\n", name));
309
310        /*
311         * Make sure we don't ask for passwords...
312         */
313
314         cupsSetPasswordCB(cups_passwd_cb);
315
316        /*
317         * Try to connect to the server...
318         */
319
320         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
321         {
322                 DEBUG(3,("Unable to connect to CUPS server %s - %s\n", 
323                          cups_server(), strerror(errno)));
324                 return (0);
325         }
326
327        /*
328         * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
329         * attributes:
330         *
331         *    attributes-charset
332         *    attributes-natural-language
333         *    requested-attributes
334         *    printer-uri
335         */
336
337         request = ippNew();
338
339         request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
340         request->request.op.request_id   = 1;
341
342         language = cupsLangDefault();
343
344         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
345                      "attributes-charset", NULL, cupsLangEncoding(language));
346
347         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
348                      "attributes-natural-language", NULL, language->language);
349
350         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
351                      "requested-attributes", NULL, "printer-uri");
352
353         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", name);
354
355         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
356                      "printer-uri", NULL, uri);
357
358        /*
359         * Do the request and get back a response...
360         */
361
362         if ((response = cupsDoRequest(http, request, "/")) == NULL)
363         {
364                 DEBUG(3,("Unable to get printer status for %s - %s\n", name,
365                          ippErrorString(cupsLastError())));
366                 httpClose(http);
367                 return (0);
368         }
369
370         httpClose(http);
371
372         if (response->request.status.status_code >= IPP_OK_CONFLICT)
373         {
374                 DEBUG(3,("Unable to get printer status for %s - %s\n", name,
375                          ippErrorString(response->request.status.status_code)));
376                 ippDelete(response);
377                 return (0);
378         }
379         else
380         {
381                 ippDelete(response);
382                 return (1);
383         }
384 }
385
386
387 /*
388  * 'cups_job_delete()' - Delete a job.
389  */
390
391 static int
392 cups_job_delete(int snum, struct printjob *pjob)
393 {
394         int             ret;            /* Return value */
395         http_t          *http;          /* HTTP connection to server */
396         ipp_t           *request,       /* IPP Request */
397                         *response;      /* IPP Response */
398         cups_lang_t     *language;      /* Default language */
399         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
400
401
402         DEBUG(5,("cups_job_delete(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
403
404        /*
405         * Make sure we don't ask for passwords...
406         */
407
408         cupsSetPasswordCB(cups_passwd_cb);
409
410        /*
411         * Try to connect to the server...
412         */
413
414         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
415         {
416                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
417                          cups_server(), strerror(errno)));
418                 return (1);
419         }
420
421        /*
422         * Build an IPP_CANCEL_JOB request, which requires the following
423         * attributes:
424         *
425         *    attributes-charset
426         *    attributes-natural-language
427         *    job-uri
428         *    requesting-user-name
429         */
430
431         request = ippNew();
432
433         request->request.op.operation_id = IPP_CANCEL_JOB;
434         request->request.op.request_id   = 1;
435
436         language = cupsLangDefault();
437
438         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
439                      "attributes-charset", NULL, cupsLangEncoding(language));
440
441         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
442                      "attributes-natural-language", NULL, language->language);
443
444         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
445
446         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
447
448         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
449                      NULL, pjob->user);
450
451        /*
452         * Do the request and get back a response...
453         */
454
455         ret = 1;
456
457         if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
458         {
459           if (response->request.status.status_code >= IPP_OK_CONFLICT)
460                 DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
461                          ippErrorString(cupsLastError())));
462           else
463                 ret = 0;
464
465           ippDelete(response);
466         }
467         else
468           DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
469                    ippErrorString(cupsLastError())));
470
471         httpClose(http);
472
473         return (ret);
474 }
475
476
477 /*
478  * 'cups_job_pause()' - Pause a job.
479  */
480
481 static int
482 cups_job_pause(int snum, struct printjob *pjob)
483 {
484         int             ret;            /* Return value */
485         http_t          *http;          /* HTTP connection to server */
486         ipp_t           *request,       /* IPP Request */
487                         *response;      /* IPP Response */
488         cups_lang_t     *language;      /* Default language */
489         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
490
491
492         DEBUG(5,("cups_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
493
494        /*
495         * Make sure we don't ask for passwords...
496         */
497
498         cupsSetPasswordCB(cups_passwd_cb);
499
500        /*
501         * Try to connect to the server...
502         */
503
504         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
505         {
506                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
507                          cups_server(), strerror(errno)));
508                 return (1);
509         }
510
511        /*
512         * Build an IPP_HOLD_JOB request, which requires the following
513         * attributes:
514         *
515         *    attributes-charset
516         *    attributes-natural-language
517         *    job-uri
518         *    requesting-user-name
519         */
520
521         request = ippNew();
522
523         request->request.op.operation_id = IPP_HOLD_JOB;
524         request->request.op.request_id   = 1;
525
526         language = cupsLangDefault();
527
528         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
529                      "attributes-charset", NULL, cupsLangEncoding(language));
530
531         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
532                      "attributes-natural-language", NULL, language->language);
533
534         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
535
536         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
537
538         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
539                      NULL, pjob->user);
540
541        /*
542         * Do the request and get back a response...
543         */
544
545         ret = 1;
546
547         if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
548         {
549           if (response->request.status.status_code >= IPP_OK_CONFLICT)
550                 DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
551                          ippErrorString(cupsLastError())));
552           else
553                 ret = 0;
554
555           ippDelete(response);
556         }
557         else
558           DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
559                    ippErrorString(cupsLastError())));
560
561         httpClose(http);
562
563         return (ret);
564 }
565
566
567 /*
568  * 'cups_job_resume()' - Resume a paused job.
569  */
570
571 static int
572 cups_job_resume(int snum, struct printjob *pjob)
573 {
574         int             ret;            /* Return value */
575         http_t          *http;          /* HTTP connection to server */
576         ipp_t           *request,       /* IPP Request */
577                         *response;      /* IPP Response */
578         cups_lang_t     *language;      /* Default language */
579         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
580
581
582         DEBUG(5,("cups_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
583
584        /*
585         * Make sure we don't ask for passwords...
586         */
587
588         cupsSetPasswordCB(cups_passwd_cb);
589
590        /*
591         * Try to connect to the server...
592         */
593
594         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
595         {
596                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
597                          cups_server(), strerror(errno)));
598                 return (1);
599         }
600
601        /*
602         * Build an IPP_RELEASE_JOB request, which requires the following
603         * attributes:
604         *
605         *    attributes-charset
606         *    attributes-natural-language
607         *    job-uri
608         *    requesting-user-name
609         */
610
611         request = ippNew();
612
613         request->request.op.operation_id = IPP_RELEASE_JOB;
614         request->request.op.request_id   = 1;
615
616         language = cupsLangDefault();
617
618         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
619                      "attributes-charset", NULL, cupsLangEncoding(language));
620
621         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
622                      "attributes-natural-language", NULL, language->language);
623
624         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/jobs/%d", pjob->sysjob);
625
626         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
627
628         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
629                      NULL, pjob->user);
630
631        /*
632         * Do the request and get back a response...
633         */
634
635         ret = 1;
636
637         if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
638         {
639           if (response->request.status.status_code >= IPP_OK_CONFLICT)
640                 DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
641                          ippErrorString(cupsLastError())));
642           else
643                 ret = 0;
644
645           ippDelete(response);
646         }
647         else
648           DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
649                    ippErrorString(cupsLastError())));
650
651         httpClose(http);
652
653         return (ret);
654 }
655
656
657 /*
658  * 'cups_job_submit()' - Submit a job for printing.
659  */
660
661 static int
662 cups_job_submit(int snum, struct printjob *pjob)
663 {
664         int             ret;            /* Return value */
665         http_t          *http;          /* HTTP connection to server */
666         ipp_t           *request,       /* IPP Request */
667                         *response;      /* IPP Response */
668         cups_lang_t     *language;      /* Default language */
669         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
670         char            *clientname;    /* hostname of client for job-originating-host attribute */
671         pstring         new_jobname;
672         int             num_options = 0; 
673         cups_option_t   *options;
674
675         DEBUG(5,("cups_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
676
677        /*
678         * Make sure we don't ask for passwords...
679         */
680
681         cupsSetPasswordCB(cups_passwd_cb);
682
683        /*
684         * Try to connect to the server...
685         */
686
687         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
688         {
689                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
690                          cups_server(), strerror(errno)));
691                 return (1);
692         }
693
694        /*
695         * Build an IPP_PRINT_JOB request, which requires the following
696         * attributes:
697         *
698         *    attributes-charset
699         *    attributes-natural-language
700         *    printer-uri
701         *    requesting-user-name
702         *    [document-data]
703         */
704
705         request = ippNew();
706
707         request->request.op.operation_id = IPP_PRINT_JOB;
708         request->request.op.request_id   = 1;
709
710         language = cupsLangDefault();
711
712         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
713                      "attributes-charset", NULL, cupsLangEncoding(language));
714
715         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
716                      "attributes-natural-language", NULL, language->language);
717
718         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
719                  PRINTERNAME(snum));
720
721         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
722                      "printer-uri", NULL, uri);
723
724         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
725                      NULL, pjob->user);
726
727         clientname = client_name();
728         if (strcmp(clientname, "UNKNOWN") == 0) {
729                 clientname = client_addr();
730         }
731
732         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
733                      "job-originating-host-name", NULL,
734                       clientname);
735
736         pstr_sprintf(new_jobname,"%s%.8u %s", PRINT_SPOOL_PREFIX, 
737                 (unsigned int)pjob->smbjob, pjob->jobname);
738
739         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
740                      new_jobname);
741
742         /* 
743          * add any options defined in smb.conf 
744          */
745
746         num_options = 0;
747         options     = NULL;
748         num_options = cupsParseOptions(lp_cups_options(snum), num_options, &options);
749
750         if ( num_options )
751                 cupsEncodeOptions(request, num_options, options); 
752
753        /*
754         * Do the request and get back a response...
755         */
756
757         slprintf(uri, sizeof(uri) - 1, "/printers/%s", PRINTERNAME(snum));
758
759         ret = 1;
760         if ((response = cupsDoFileRequest(http, request, uri,
761                                           pjob->filename)) != NULL)
762         {
763                 if (response->request.status.status_code >= IPP_OK_CONFLICT)
764                         DEBUG(0,("Unable to print file to %s - %s\n", PRINTERNAME(snum),
765                                  ippErrorString(cupsLastError())));
766                 else
767                         ret = 0;
768
769                 ippDelete(response);
770         }
771         else
772                 DEBUG(0,("Unable to print file to `%s' - %s\n", PRINTERNAME(snum),
773                          ippErrorString(cupsLastError())));
774
775         httpClose(http);
776
777         if ( ret == 0 )
778                 unlink(pjob->filename);
779         /* else print_job_end will do it for us */
780
781         return (ret);
782 }
783
784 /*
785  * 'cups_queue_get()' - Get all the jobs in the print queue.
786  */
787
788 static int
789 cups_queue_get(const char *printer_name,
790                enum printing_types printing_type,
791                char *lpq_command,
792                print_queue_struct **q, 
793                print_status_struct *status)
794 {
795         http_t          *http;          /* HTTP connection to server */
796         ipp_t           *request,       /* IPP Request */
797                         *response;      /* IPP Response */
798         ipp_attribute_t *attr;          /* Current attribute */
799         cups_lang_t     *language;      /* Default language */
800         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
801         int             qcount,         /* Number of active queue entries */
802                         qalloc;         /* Number of queue entries allocated */
803         print_queue_struct *queue,      /* Queue entries */
804                         *temp;          /* Temporary pointer for queue */
805         const char      *user_name,     /* job-originating-user-name attribute */
806                         *job_name;      /* job-name attribute */
807         int             job_id;         /* job-id attribute */
808         int             job_k_octets;   /* job-k-octets attribute */
809         time_t          job_time;       /* time-at-creation attribute */
810         ipp_jstate_t    job_status;     /* job-status attribute */
811         int             job_priority;   /* job-priority attribute */
812         static const char *jattrs[] =   /* Requested job attributes */
813                         {
814                           "job-id",
815                           "job-k-octets",
816                           "job-name",
817                           "job-originating-user-name",
818                           "job-priority",
819                           "job-state",
820                           "time-at-creation",
821                         };
822         static const char *pattrs[] =   /* Requested printer attributes */
823                         {
824                           "printer-state",
825                           "printer-state-message"
826                         };
827
828
829         DEBUG(5,("cups_queue_get(%s, %p, %p)\n", printer_name, q, status));
830
831        /*
832         * Make sure we don't ask for passwords...
833         */
834
835         cupsSetPasswordCB(cups_passwd_cb);
836
837        /*
838         * Try to connect to the server...
839         */
840
841         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
842         {
843                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
844                          cups_server(), strerror(errno)));
845                 return (0);
846         }
847
848        /*
849         * Generate the printer URI...
850         */
851
852         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s", printer_name);
853
854        /*
855         * Build an IPP_GET_JOBS request, which requires the following
856         * attributes:
857         *
858         *    attributes-charset
859         *    attributes-natural-language
860         *    requested-attributes
861         *    printer-uri
862         */
863
864         request = ippNew();
865
866         request->request.op.operation_id = IPP_GET_JOBS;
867         request->request.op.request_id   = 1;
868
869         language = cupsLangDefault();
870
871         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
872                      "attributes-charset", NULL, cupsLangEncoding(language));
873
874         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
875                      "attributes-natural-language", NULL, language->language);
876
877         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
878                       "requested-attributes",
879                       (sizeof(jattrs) / sizeof(jattrs[0])),
880                       NULL, jattrs);
881
882         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
883                      "printer-uri", NULL, uri);
884
885        /*
886         * Do the request and get back a response...
887         */
888
889         if ((response = cupsDoRequest(http, request, "/")) == NULL)
890         {
891                 DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
892                          ippErrorString(cupsLastError())));
893                 httpClose(http);
894                 return (0);
895         }
896
897         if (response->request.status.status_code >= IPP_OK_CONFLICT)
898         {
899                 DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
900                          ippErrorString(response->request.status.status_code)));
901                 ippDelete(response);
902                 httpClose(http);
903
904                 return (0);
905         }
906
907        /*
908         * Process the jobs...
909         */
910
911         qcount = 0;
912         qalloc = 0;
913         queue  = NULL;
914
915         for (attr = response->attrs; attr != NULL; attr = attr->next)
916         {
917                /*
918                 * Skip leading attributes until we hit a job...
919                 */
920
921                 while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
922                         attr = attr->next;
923
924                 if (attr == NULL)
925                         break;
926
927                /*
928                 * Allocate memory as needed...
929                 */
930                 if (qcount >= qalloc)
931                 {
932                         qalloc += 16;
933
934                         temp = Realloc(queue, sizeof(print_queue_struct) * qalloc);
935
936                         if (temp == NULL)
937                         {
938                                 DEBUG(0,("cups_queue_get: Not enough memory!"));
939                                 ippDelete(response);
940                                 httpClose(http);
941
942                                 SAFE_FREE(queue);
943                                 return (0);
944                         }
945
946                         queue = temp;
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                 {
966                         if (attr->name == NULL)
967                         {
968                                 attr = attr->next;
969                                 break;
970                         }
971
972                         if (strcmp(attr->name, "job-id") == 0 &&
973                             attr->value_tag == IPP_TAG_INTEGER)
974                                 job_id = attr->values[0].integer;
975
976                         if (strcmp(attr->name, "job-k-octets") == 0 &&
977                             attr->value_tag == IPP_TAG_INTEGER)
978                                 job_k_octets = attr->values[0].integer;
979
980                         if (strcmp(attr->name, "job-priority") == 0 &&
981                             attr->value_tag == IPP_TAG_INTEGER)
982                                 job_priority = attr->values[0].integer;
983
984                         if (strcmp(attr->name, "job-state") == 0 &&
985                             attr->value_tag == IPP_TAG_ENUM)
986                                 job_status = (ipp_jstate_t)(attr->values[0].integer);
987
988                         if (strcmp(attr->name, "time-at-creation") == 0 &&
989                             attr->value_tag == IPP_TAG_INTEGER)
990                                 job_time = attr->values[0].integer;
991
992                         if (strcmp(attr->name, "job-name") == 0 &&
993                             attr->value_tag == IPP_TAG_NAME)
994                                 job_name = attr->values[0].string.text;
995
996                         if (strcmp(attr->name, "job-originating-user-name") == 0 &&
997                             attr->value_tag == IPP_TAG_NAME)
998                                 user_name = attr->values[0].string.text;
999
1000                         attr = attr->next;
1001                 }
1002
1003                /*
1004                 * See if we have everything needed...
1005                 */
1006
1007                 if (user_name == NULL || job_name == NULL || job_id == 0)
1008                 {
1009                   if (attr == NULL)
1010                     break;
1011                   else
1012                     continue;
1013                 }
1014
1015                 temp->job      = job_id;
1016                 temp->size     = job_k_octets * 1024;
1017                 temp->status   = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
1018                                  job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
1019                                  job_status == IPP_JOB_HELD ? LPQ_PAUSED :
1020                                  LPQ_PRINTING;
1021                 temp->priority = job_priority;
1022                 temp->time     = job_time;
1023                 strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
1024                 strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
1025
1026                 qcount ++;
1027
1028                 if (attr == NULL)
1029                   break;
1030         }
1031
1032         ippDelete(response);
1033
1034        /*
1035         * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1036         * following attributes:
1037         *
1038         *    attributes-charset
1039         *    attributes-natural-language
1040         *    requested-attributes
1041         *    printer-uri
1042         */
1043
1044         request = ippNew();
1045
1046         request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
1047         request->request.op.request_id   = 1;
1048
1049         language = cupsLangDefault();
1050
1051         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1052                      "attributes-charset", NULL, cupsLangEncoding(language));
1053
1054         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1055                      "attributes-natural-language", NULL, language->language);
1056
1057         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1058                       "requested-attributes",
1059                       (sizeof(pattrs) / sizeof(pattrs[0])),
1060                       NULL, pattrs);
1061
1062         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1063                      "printer-uri", NULL, uri);
1064
1065        /*
1066         * Do the request and get back a response...
1067         */
1068
1069         if ((response = cupsDoRequest(http, request, "/")) == NULL)
1070         {
1071                 DEBUG(0,("Unable to get printer status for %s - %s\n", printer_name,
1072                          ippErrorString(cupsLastError())));
1073                 httpClose(http);
1074                 *q = queue;
1075                 return (qcount);
1076         }
1077
1078         if (response->request.status.status_code >= IPP_OK_CONFLICT)
1079         {
1080                 DEBUG(0,("Unable to get printer status for %s - %s\n", printer_name,
1081                          ippErrorString(response->request.status.status_code)));
1082                 ippDelete(response);
1083                 httpClose(http);
1084                 *q = queue;
1085                 return (qcount);
1086         }
1087
1088        /*
1089         * Get the current printer status and convert it to the SAMBA values.
1090         */
1091
1092         if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
1093         {
1094                 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
1095                         status->status = LPSTAT_STOPPED;
1096                 else
1097                         status->status = LPSTAT_OK;
1098         }
1099
1100         if ((attr = ippFindAttribute(response, "printer-state-message",
1101                                      IPP_TAG_TEXT)) != NULL)
1102                 fstrcpy(status->message, attr->values[0].string.text);
1103
1104         ippDelete(response);
1105
1106        /*
1107         * Return the job queue...
1108         */
1109
1110         httpClose(http);
1111
1112         *q = queue;
1113         return (qcount);
1114 }
1115
1116
1117 /*
1118  * 'cups_queue_pause()' - Pause a print queue.
1119  */
1120
1121 static int
1122 cups_queue_pause(int snum)
1123 {
1124         extern userdom_struct current_user_info;
1125         int             ret;            /* Return value */
1126         http_t          *http;          /* HTTP connection to server */
1127         ipp_t           *request,       /* IPP Request */
1128                         *response;      /* IPP Response */
1129         cups_lang_t     *language;      /* Default language */
1130         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
1131
1132
1133         DEBUG(5,("cups_queue_pause(%d)\n", snum));
1134
1135         /*
1136          * Make sure we don't ask for passwords...
1137          */
1138
1139         cupsSetPasswordCB(cups_passwd_cb);
1140
1141         /*
1142          * Try to connect to the server...
1143          */
1144
1145         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
1146         {
1147                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
1148                          cups_server(), strerror(errno)));
1149                 return (1);
1150         }
1151
1152         /*
1153          * Build an IPP_PAUSE_PRINTER request, which requires the following
1154          * attributes:
1155          *
1156          *    attributes-charset
1157          *    attributes-natural-language
1158          *    printer-uri
1159          *    requesting-user-name
1160          */
1161
1162         request = ippNew();
1163
1164         request->request.op.operation_id = IPP_PAUSE_PRINTER;
1165         request->request.op.request_id   = 1;
1166
1167         language = cupsLangDefault();
1168
1169         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1170                      "attributes-charset", NULL, cupsLangEncoding(language));
1171
1172         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1173                      "attributes-natural-language", NULL, language->language);
1174
1175         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
1176                  PRINTERNAME(snum));
1177
1178         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
1179
1180         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1181                      NULL, current_user_info.unix_name);
1182
1183        /*
1184         * Do the request and get back a response...
1185         */
1186
1187         ret = 1;
1188
1189         if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
1190         {
1191           if (response->request.status.status_code >= IPP_OK_CONFLICT)
1192                 DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
1193                          ippErrorString(cupsLastError())));
1194           else
1195                 ret = 0;
1196
1197           ippDelete(response);
1198         }
1199         else
1200           DEBUG(0,("Unable to pause printer %s - %s\n", PRINTERNAME(snum),
1201                    ippErrorString(cupsLastError())));
1202
1203         httpClose(http);
1204
1205         return (ret);
1206 }
1207
1208
1209 /*
1210  * 'cups_queue_resume()' - Restart a print queue.
1211  */
1212
1213 static int
1214 cups_queue_resume(int snum)
1215 {
1216         extern userdom_struct current_user_info;
1217         int             ret;            /* Return value */
1218         http_t          *http;          /* HTTP connection to server */
1219         ipp_t           *request,       /* IPP Request */
1220                         *response;      /* IPP Response */
1221         cups_lang_t     *language;      /* Default language */
1222         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
1223
1224
1225         DEBUG(5,("cups_queue_resume(%d)\n", snum));
1226
1227        /*
1228         * Make sure we don't ask for passwords...
1229         */
1230
1231         cupsSetPasswordCB(cups_passwd_cb);
1232
1233        /*
1234         * Try to connect to the server...
1235         */
1236
1237         if ((http = httpConnect(cups_server(), ippPort())) == NULL)
1238         {
1239                 DEBUG(0,("Unable to connect to CUPS server %s - %s\n", 
1240                          cups_server(), strerror(errno)));
1241                 return (1);
1242         }
1243
1244        /*
1245         * Build an IPP_RESUME_PRINTER request, which requires the following
1246         * attributes:
1247         *
1248         *    attributes-charset
1249         *    attributes-natural-language
1250         *    printer-uri
1251         *    requesting-user-name
1252         */
1253
1254         request = ippNew();
1255
1256         request->request.op.operation_id = IPP_RESUME_PRINTER;
1257         request->request.op.request_id   = 1;
1258
1259         language = cupsLangDefault();
1260
1261         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1262                      "attributes-charset", NULL, cupsLangEncoding(language));
1263
1264         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1265                      "attributes-natural-language", NULL, language->language);
1266
1267         slprintf(uri, sizeof(uri) - 1, "ipp://localhost/printers/%s",
1268                  PRINTERNAME(snum));
1269
1270         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
1271
1272         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1273                      NULL, current_user_info.unix_name);
1274
1275        /*
1276         * Do the request and get back a response...
1277         */
1278
1279         ret = 1;
1280
1281         if ((response = cupsDoRequest(http, request, "/admin/")) != NULL)
1282         {
1283           if (response->request.status.status_code >= IPP_OK_CONFLICT)
1284                 DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
1285                          ippErrorString(cupsLastError())));
1286           else
1287                 ret = 0;
1288
1289           ippDelete(response);
1290         }
1291         else
1292           DEBUG(0,("Unable to resume printer %s - %s\n", PRINTERNAME(snum),
1293                    ippErrorString(cupsLastError())));
1294
1295         httpClose(http);
1296
1297         return (ret);
1298 }
1299
1300 /*******************************************************************
1301  * CUPS printing interface definitions...
1302  ******************************************************************/
1303
1304 struct printif  cups_printif =
1305 {
1306         PRINT_CUPS,
1307         cups_queue_get,
1308         cups_queue_pause,
1309         cups_queue_resume,
1310         cups_job_delete,
1311         cups_job_pause,
1312         cups_job_resume,
1313         cups_job_submit,
1314 };
1315
1316 #else
1317  /* this keeps fussy compilers happy */
1318  void print_cups_dummy(void) {}
1319 #endif /* HAVE_CUPS */