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