s3: smbtorture: Add new SMB2-DIR-FSYNC test to show behavior of FSYNC on directories.
[nivanova/samba-autobuild/.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                                    const char *url,
210                                    struct pcap_cache **pcache)
211 {
212         ipp_t           *request = NULL,        /* IPP Request */
213                         *response = NULL;       /* IPP Response */
214         ipp_attribute_t *attr;                  /* Current attribute */
215         cups_lang_t     *language = NULL;       /* Default language */
216         const char      *name,                  /* printer-name attribute */
217                         *info;                  /* printer-info attribute */
218         char            smb_enabled,            /* smb-enabled attribute */
219                         secure;                 /* security-enabled attrib. */
220
221         const char      *httpPath;      /* path portion of the printer-uri */
222
223         static const char *pattrs[] =   /* Requested printer attributes */
224                         {
225                           "printer-name",
226                           "security-enabled",
227                           "printer-info",
228                           "smb-enabled"
229                         };       
230
231         request = ippNew();
232
233         ippSetOperation(request, IPP_GET_PRINTER_ATTRIBUTES);
234         ippSetRequestId(request, reqId);
235
236         language = cupsLangDefault();
237
238         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
239                      "attributes-charset", NULL, "utf-8");
240
241         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
242                      "attributes-natural-language", NULL, language->language);
243
244         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, url);
245
246         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
247                       "requested-attributes",
248                       (sizeof(pattrs) / sizeof(pattrs[0])),
249                       NULL, pattrs);
250
251         /*
252          * Do the request and get back a response...
253          */
254
255         if ((httpPath = strstr(url,"://")) == NULL ||
256                         (httpPath = strchr(httpPath+3,'/')) == NULL)
257         {
258                 ippDelete(request);
259                 request = NULL;
260                 goto out;
261         }
262
263         if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
264                 ipp_status_t lastErr = cupsLastError();
265
266                /*
267                 * Ignore printers that cannot be queried without credentials
268                 */
269                 if (lastErr == IPP_FORBIDDEN || 
270                     lastErr == IPP_NOT_AUTHENTICATED ||
271                     lastErr == IPP_NOT_AUTHORIZED)
272                         goto out;
273
274                 DEBUG(0,("Unable to get printer list - %s\n",
275                       ippErrorString(lastErr)));
276                 goto out;
277         }
278
279         for (attr = ippFirstAttribute(response); attr != NULL;) {
280                /*
281                 * Skip leading attributes until we hit a printer...
282                 */
283
284                 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
285                         attr = ippNextAttribute(response);
286
287                 if (attr == NULL)
288                         break;
289
290                /*
291                 * Pull the needed attributes from this printer...
292                 */
293
294                 name       = NULL;
295                 info       = NULL;
296                 smb_enabled= 1;
297                 secure     = 0;
298
299                 while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER) {
300                         if (strcmp(ippGetName(attr), "printer-name") == 0 &&
301                             ippGetValueTag(attr) == IPP_TAG_NAME)
302                                 name = ippGetString(attr, 0, NULL);
303
304                         if (strcmp(ippGetName(attr), "printer-info") == 0 &&
305                             (ippGetValueTag(attr) == IPP_TAG_TEXT ||
306                             ippGetValueTag(attr) == IPP_TAG_TEXTLANG))
307                                 info = ippGetString(attr, 0, NULL);
308
309                        /*
310                         * If the smb-enabled attribute is present and the
311                         * value is set to 0, don't show the printer.
312                         * If the attribute is not present, assume that the
313                         * printer should show up
314                         */
315                         if (!strcmp(ippGetName(attr), "smb-enabled") &&
316                             ((ippGetValueTag(attr) == IPP_TAG_INTEGER &&
317                             !ippGetInteger(attr, 0)) ||
318                             (ippGetValueTag(attr) == IPP_TAG_BOOLEAN &&
319                             !ippGetBoolean(attr, 0))))
320                                 smb_enabled = 0;
321
322                        /*
323                         * If the security-enabled attribute is present and the
324                         * value is set to 1, don't show the printer.
325                         * If the attribute is not present, assume that the
326                         * printer should show up
327                         */
328                         if (!strcmp(ippGetName(attr), "security-enabled") &&
329                             ((ippGetValueTag(attr) == IPP_TAG_INTEGER &&
330                             ippGetInteger(attr, 0)) ||
331                             (ippGetValueTag(attr) == IPP_TAG_BOOLEAN &&
332                             ippGetBoolean(attr, 0))))
333                                 secure = 1;
334
335                         attr = ippNextAttribute(response);
336                 }
337
338                /*
339                 * See if we have everything needed...
340                 * Make sure the printer is not a secure printer
341                 * and make sure smb printing hasn't been explicitly
342                 * disabled for the printer
343                 */
344
345                 if (name != NULL && !secure && smb_enabled) 
346                         pcap_cache_add_specific(pcache, name, info, NULL);
347         }
348
349  out:
350         if (response)
351                 ippDelete(response);
352         return(0);
353 }
354
355 bool iprint_cache_reload(struct pcap_cache **_pcache)
356 {
357         http_t          *http = NULL;           /* HTTP connection to server */
358         ipp_t           *request = NULL,        /* IPP Request */
359                         *response = NULL;       /* IPP Response */
360         ipp_attribute_t *attr;                  /* Current attribute */
361         cups_lang_t     *language = NULL;       /* Default language */
362         int             i;
363         bool ret = false;
364         struct pcap_cache *pcache = NULL;
365
366         DEBUG(5, ("reloading iprint printcap cache\n"));
367
368        /*
369         * Make sure we don't ask for passwords...
370         */
371
372         cupsSetPasswordCB(iprint_passwd_cb);
373
374        /*
375         * Try to connect to the server...
376         */
377
378         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
379                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
380                          iprint_server(), strerror(errno)));
381                 goto out;
382         }
383
384        /*
385         * Build a OPERATION_NOVELL_LIST_PRINTERS request, which requires the following attributes:
386         *
387         *    attributes-charset
388         *    attributes-natural-language
389         */
390
391         request = ippNew();
392
393         ippSetOperation(request, (ipp_op_t)OPERATION_NOVELL_LIST_PRINTERS);
394         ippSetRequestId(request, 1);
395
396         language = cupsLangDefault();
397
398         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
399                      "attributes-charset", NULL, "utf-8");
400
401         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
402                      "attributes-natural-language", NULL, language->language);
403
404         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
405                      "ipp-server", NULL, "ippSrvr");
406
407        /*
408         * Do the request and get back a response...
409         */
410
411         if ((response = cupsDoRequest(http, request, "/ipp")) == NULL) {
412                 DEBUG(0,("Unable to get printer list - %s\n",
413                          ippErrorString(cupsLastError())));
414                 goto out;
415         }
416
417         for (attr = ippFirstAttribute(response); attr != NULL;) {
418                /*
419                 * Skip leading attributes until we hit a printer...
420                 */
421
422                 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_PRINTER)
423                         attr = ippNextAttribute(response);
424
425                 if (attr == NULL)
426                         break;
427
428                /*
429                 * Pull the needed attributes from this printer...
430                 */
431
432                 while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_PRINTER)
433                 {
434                         if (strcmp(ippGetName(attr), "printer-name") == 0 &&
435                             (ippGetValueTag(attr) == IPP_TAG_URI ||
436                              ippGetValueTag(attr) == IPP_TAG_NAME ||
437                              ippGetValueTag(attr) == IPP_TAG_TEXT ||
438                              ippGetValueTag(attr) == IPP_TAG_NAMELANG ||
439                              ippGetValueTag(attr) == IPP_TAG_TEXTLANG))
440                         {
441                                 for (i = 0; i<ippGetCount(attr); i++)
442                                 {
443                                         const char *url = ippGetString(attr, i, NULL);
444                                         if (!url || !strlen(url))
445                                                 continue;
446                                         iprint_cache_add_printer(http, i+2, url,
447                                                                  &pcache);
448                                 }
449                         }
450                         attr = ippNextAttribute(response);
451                 }
452         }
453
454         ret = true;
455         *_pcache = pcache;
456
457  out:
458         if (response)
459                 ippDelete(response);
460
461         if (language)
462                 cupsLangFree(language);
463
464         if (http)
465                 httpClose(http);
466
467         return ret;
468 }
469
470
471 /*
472  * 'iprint_job_delete()' - Delete a job.
473  */
474
475 static int iprint_job_delete(const char *sharename, const char *lprm_command, struct printjob *pjob)
476 {
477         int             ret = 1;                /* Return value */
478         http_t          *http = NULL;           /* HTTP connection to server */
479         ipp_t           *request = NULL,        /* IPP Request */
480                         *response = NULL;       /* IPP Response */
481         cups_lang_t     *language = NULL;       /* Default language */
482         char            uri[HTTP_MAX_URI];      /* printer-uri attribute */
483         char            httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
484
485
486         DEBUG(5,("iprint_job_delete(%s, %p (%d))\n", sharename, pjob, pjob->sysjob));
487
488        /*
489         * Make sure we don't ask for passwords...
490         */
491
492         cupsSetPasswordCB(iprint_passwd_cb);
493
494        /*
495         * Try to connect to the server...
496         */
497
498         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
499                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
500                          iprint_server(), strerror(errno)));
501                 goto out;
502         }
503
504        /*
505         * Build an IPP_CANCEL_JOB request, which uses the following
506         * attributes:
507         *
508         *    attributes-charset
509         *    attributes-natural-language
510         *    printer-uri
511         *    job-id
512         *    requesting-user-name
513         */
514
515         request = ippNew();
516
517         ippSetOperation(request, IPP_CANCEL_JOB);
518         ippSetRequestId(request, 1);
519
520         language = cupsLangDefault();
521
522         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
523                      "attributes-charset", NULL, "utf-8");
524
525         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
526                      "attributes-natural-language", NULL, language->language);
527
528         slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), sharename);
529
530         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
531
532         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
533
534         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
535                      NULL, pjob->user);
536
537        /*
538         * Do the request and get back a response...
539         */
540
541         slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", sharename);
542
543         if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
544                 if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
545                         DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
546                                 ippErrorString(cupsLastError())));
547                 } else {
548                         ret = 0;
549                 }
550         } else {
551                 DEBUG(0,("Unable to cancel job %d - %s\n", pjob->sysjob,
552                         ippErrorString(cupsLastError())));
553         }
554
555  out:
556         if (response)
557                 ippDelete(response);
558
559         if (language)
560                 cupsLangFree(language);
561
562         if (http)
563                 httpClose(http);
564
565         return ret;
566 }
567
568
569 /*
570  * 'iprint_job_pause()' - Pause a job.
571  */
572
573 static int iprint_job_pause(int snum, struct printjob *pjob)
574 {
575         int             ret = 1;                /* Return value */
576         http_t          *http = NULL;           /* HTTP connection to server */
577         ipp_t           *request = NULL,        /* IPP Request */
578                         *response = NULL;       /* IPP Response */
579         cups_lang_t     *language = NULL;       /* Default language */
580         char            uri[HTTP_MAX_URI];      /* printer-uri attribute */
581         char            httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
582
583
584         DEBUG(5,("iprint_job_pause(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
585
586        /*
587         * Make sure we don't ask for passwords...
588         */
589
590         cupsSetPasswordCB(iprint_passwd_cb);
591
592        /*
593         * Try to connect to the server...
594         */
595
596         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
597                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
598                          iprint_server(), strerror(errno)));
599                 goto out;
600         }
601
602        /*
603         * Build an IPP_HOLD_JOB request, which requires the following
604         * attributes:
605         *
606         *    attributes-charset
607         *    attributes-natural-language
608         *    printer-uri
609         *    job-id
610         *    requesting-user-name
611         */
612
613         request = ippNew();
614
615         ippSetOperation(request, IPP_HOLD_JOB);
616         ippSetRequestId(request, 1);
617
618         language = cupsLangDefault();
619
620         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
621                      "attributes-charset", NULL, "utf-8");
622
623         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
624                      "attributes-natural-language", NULL, language->language);
625
626         slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(),
627                  lp_printername(talloc_tos(), snum));
628
629         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
630
631         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
632
633         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
634                      NULL, pjob->user);
635
636        /*
637         * Do the request and get back a response...
638         */
639
640         slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s",
641                  lp_printername(talloc_tos(), snum));
642
643         if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
644                 if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
645                         DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
646                                 ippErrorString(cupsLastError())));
647                 } else {
648                         ret = 0;
649                 }
650         } else {
651                 DEBUG(0,("Unable to hold job %d - %s\n", pjob->sysjob,
652                         ippErrorString(cupsLastError())));
653         }
654
655  out:
656         if (response)
657                 ippDelete(response);
658
659         if (language)
660                 cupsLangFree(language);
661
662         if (http)
663                 httpClose(http);
664
665         return ret;
666 }
667
668
669 /*
670  * 'iprint_job_resume()' - Resume a paused job.
671  */
672
673 static int iprint_job_resume(int snum, struct printjob *pjob)
674 {
675         int             ret = 1;                /* Return value */
676         http_t          *http = NULL;           /* HTTP connection to server */
677         ipp_t           *request = NULL,        /* IPP Request */
678                         *response = NULL;       /* IPP Response */
679         cups_lang_t     *language = NULL;       /* Default language */
680         char            uri[HTTP_MAX_URI];      /* printer-uri attribute */
681         char            httpPath[HTTP_MAX_URI]; /* path portion of the printer-uri */
682
683
684         DEBUG(5,("iprint_job_resume(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
685
686        /*
687         * Make sure we don't ask for passwords...
688         */
689
690         cupsSetPasswordCB(iprint_passwd_cb);
691
692        /*
693         * Try to connect to the server...
694         */
695
696         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
697                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
698                          iprint_server(), strerror(errno)));
699                 goto out;
700         }
701
702        /*
703         * Build an IPP_RELEASE_JOB request, which requires the following
704         * attributes:
705         *
706         *    attributes-charset
707         *    attributes-natural-language
708         *    printer-uri
709         *    job-id
710         *    requesting-user-name
711         */
712
713         request = ippNew();
714
715         ippSetOperation(request, IPP_RELEASE_JOB);
716         ippSetRequestId(request, 1);
717
718         language = cupsLangDefault();
719
720         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
721                      "attributes-charset", NULL, "utf-8");
722
723         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
724                      "attributes-natural-language", NULL, language->language);
725
726         slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(),
727                  lp_printername(talloc_tos(), snum));
728
729         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, uri);
730
731         ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", pjob->sysjob);
732
733         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
734                      NULL, pjob->user);
735
736        /*
737         * Do the request and get back a response...
738         */
739
740         slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s",
741                  lp_printername(talloc_tos(), snum));
742
743         if ((response = cupsDoRequest(http, request, httpPath)) != NULL) {
744                 if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
745                         DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
746                                 ippErrorString(cupsLastError())));
747                 } else {
748                         ret = 0;
749                 }
750         } else {
751                 DEBUG(0,("Unable to release job %d - %s\n", pjob->sysjob,
752                         ippErrorString(cupsLastError())));
753         }
754
755  out:
756         if (response)
757                 ippDelete(response);
758
759         if (language)
760                 cupsLangFree(language);
761
762         if (http)
763                 httpClose(http);
764
765         return ret;
766 }
767
768
769 /*
770  * 'iprint_job_submit()' - Submit a job for printing.
771  */
772
773 static int iprint_job_submit(int snum, struct printjob *pjob,
774                              enum printing_types printing_type,
775                              char *lpq_cmd)
776 {
777         int             ret = 1;                /* Return value */
778         http_t          *http = NULL;           /* HTTP connection to server */
779         ipp_t           *request = NULL,        /* IPP Request */
780                         *response = NULL;       /* IPP Response */
781         ipp_attribute_t *attr;          /* Current attribute */
782         cups_lang_t     *language = NULL;       /* Default language */
783         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
784
785         DEBUG(5,("iprint_job_submit(%d, %p (%d))\n", snum, pjob, pjob->sysjob));
786
787        /*
788         * Make sure we don't ask for passwords...
789         */
790
791         cupsSetPasswordCB(iprint_passwd_cb);
792
793        /*
794         * Try to connect to the server...
795         */
796
797         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
798                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
799                          iprint_server(), strerror(errno)));
800                 goto out;
801         }
802
803        /*
804         * Build an IPP_PRINT_JOB request, which requires the following
805         * attributes:
806         *
807         *    attributes-charset
808         *    attributes-natural-language
809         *    printer-uri
810         *    requesting-user-name
811         *    [document-data]
812         */
813
814         request = ippNew();
815
816         ippSetOperation(request, IPP_PRINT_JOB);
817         ippSetRequestId(request, 1);
818
819         language = cupsLangDefault();
820
821         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
822                      "attributes-charset", NULL, "utf-8");
823
824         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
825                      "attributes-natural-language", NULL, language->language);
826
827         slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(),
828                  lp_printername(talloc_tos(), snum));
829
830         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
831                      "printer-uri", NULL, uri);
832
833         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
834                      NULL, pjob->user);
835
836         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
837                      "job-originating-host-name", NULL,
838                      pjob->clientmachine);
839
840         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
841                      pjob->jobname);
842
843        /*
844         * Do the request and get back a response...
845         */
846
847         slprintf(uri, sizeof(uri) - 1, "/ipp/%s", lp_printername(talloc_tos(), snum));
848
849         if ((response = cupsDoFileRequest(http, request, uri, pjob->filename)) != NULL) {
850                 if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
851                         DEBUG(0,("Unable to print file to %s - %s\n",
852                                  lp_printername(talloc_tos(), snum),
853                                  ippErrorString(cupsLastError())));
854                 } else {
855                         ret = 0;
856                 }
857         } else {
858                 DEBUG(0,("Unable to print file to `%s' - %s\n",
859                          lp_printername(talloc_tos(), snum),
860                          ippErrorString(cupsLastError())));
861         }
862
863         if ( ret == 0 )
864                 unlink(pjob->filename);
865         /* else print_job_end will do it for us */
866
867         if ( ret == 0 ) {
868
869                 attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER);
870                 if (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_JOB)
871                 {
872                         pjob->sysjob = ippGetInteger(attr, 0);
873                 }
874         }
875
876  out:
877         if (response)
878                 ippDelete(response);
879
880         if (language)
881                 cupsLangFree(language);
882
883         if (http)
884                 httpClose(http);
885
886         return ret;
887 }
888
889 /*
890  * 'iprint_queue_get()' - Get all the jobs in the print queue.
891  */
892
893 static int iprint_queue_get(const char *sharename,
894                             enum printing_types printing_type,
895                             char *lpq_command,
896                             print_queue_struct **q, 
897                             print_status_struct *status)
898 {
899         fstring         printername;
900         http_t          *http = NULL;           /* HTTP connection to server */
901         ipp_t           *request = NULL,        /* IPP Request */
902                         *response = NULL;       /* IPP Response */
903         ipp_attribute_t *attr = NULL;           /* Current attribute */
904         cups_lang_t     *language = NULL;       /* Default language */
905         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
906         char            serviceUri[HTTP_MAX_URI]; /* service-uri attribute */
907         char            httpPath[HTTP_MAX_URI]; /* path portion of the uri */
908         int             jobUseUnixTime = 0;     /* Whether job times should
909                                                  * be assumed to be Unix time */
910         int             qcount = 0,             /* Number of active queue entries */
911                         qalloc = 0;             /* Number of queue entries allocated */
912         print_queue_struct *queue = NULL,       /* Queue entries */
913                         *temp;          /* Temporary pointer for queue */
914         const char      *user_name,     /* job-originating-user-name attribute */
915                         *job_name;      /* job-name attribute */
916         int             job_id;         /* job-id attribute */
917         int             job_k_octets;   /* job-k-octets attribute */
918         time_t          job_time;       /* time-at-creation attribute */
919         time_t          printer_up_time = 0;    /* printer's uptime */
920         ipp_jstate_t    job_status;     /* job-status attribute */
921         int             job_priority;   /* job-priority attribute */
922         static const char *jattrs[] =   /* Requested job attributes */
923                         {
924                           "job-id",
925                           "job-k-octets",
926                           "job-name",
927                           "job-originating-user-name",
928                           "job-priority",
929                           "job-state",
930                           "time-at-creation",
931                         };
932         static const char *pattrs[] =   /* Requested printer attributes */
933                         {
934                           "printer-state",
935                           "printer-state-message",
936                           "printer-current-time",
937                           "printer-up-time"
938                         };
939
940         *q = NULL;
941
942         /* HACK ALERT!!!  The porblem with support the 'printer name' 
943            option is that we key the tdb off the sharename.  So we will 
944            overload the lpq_command string to pass in the printername 
945            (which is basically what we do for non-cups printers ... using 
946            the lpq_command to get the queue listing). */
947
948         fstrcpy( printername, lpq_command );
949
950         DEBUG(5,("iprint_queue_get(%s, %p, %p)\n", printername, q, status));
951
952        /*
953         * Make sure we don't ask for passwords...
954         */
955
956         cupsSetPasswordCB(iprint_passwd_cb);
957
958        /*
959         * Try to connect to the server...
960         */
961
962         if ((http = httpConnect(iprint_server(), ippPort())) == NULL) {
963                 DEBUG(0,("Unable to connect to iPrint server %s - %s\n", 
964                          iprint_server(), strerror(errno)));
965                 goto out;
966         }
967
968        /*
969         * Generate the printer URI and the service URI that goes with it...
970         */
971
972         slprintf(uri, sizeof(uri) - 1, "ipp://%s/ipp/%s", iprint_server(), printername);
973         slprintf(serviceUri, sizeof(serviceUri) - 1, "ipp://%s/ipp/", iprint_server());
974
975        /*
976         * For Linux iPrint servers from OES SP1 on, the iPrint server
977         * uses Unix time for job start times unless it detects the iPrint
978         * client in an http User-Agent header.  (This was done to accomodate
979         * CUPS broken behavior.  According to RFC 2911, section 4.3.14, job
980         * start times are supposed to be relative to how long the printer has
981         * been up.)  Since libcups doesn't allow us to set that header before
982         * the request is sent, this ugly hack allows us to detect the server
983         * version and decide how to interpret the job time.
984         */
985         if (iprint_get_server_version(http, serviceUri) >=
986             NOVELL_SERVER_VERSION_OES_SP1)
987                 jobUseUnixTime = 1;
988
989         request = ippNew();
990
991         ippSetOperation(request, IPP_GET_PRINTER_ATTRIBUTES);
992         ippSetRequestId(request, 2);
993
994         language = cupsLangDefault();
995
996         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
997                      "attributes-charset", NULL, "utf-8");
998
999         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1000                      "attributes-natural-language", NULL, language->language);
1001
1002         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1003                      "printer-uri", NULL, uri);
1004
1005         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1006                       "requested-attributes",
1007                       (sizeof(pattrs) / sizeof(pattrs[0])),
1008                       NULL, pattrs);
1009
1010        /*
1011         * Do the request and get back a response...
1012         */
1013
1014         slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
1015
1016         if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
1017                 DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
1018                          ippErrorString(cupsLastError())));
1019                 *q = queue;
1020                 goto out;
1021         }
1022
1023         if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1024                 DEBUG(0,("Unable to get printer status for %s - %s\n", printername,
1025                          ippErrorString(ippGetStatusCode(response))));
1026                 *q = queue;
1027                 goto out;
1028         }
1029
1030        /*
1031         * Get the current printer status and convert it to the SAMBA values.
1032         */
1033
1034         if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) {
1035                 if (ippGetInteger(attr, 0) == IPP_PRINTER_STOPPED)
1036                         status->status = LPSTAT_STOPPED;
1037                 else
1038                         status->status = LPSTAT_OK;
1039         }
1040
1041         if ((attr = ippFindAttribute(response, "printer-state-message",
1042                                      IPP_TAG_TEXT)) != NULL)
1043                 fstrcpy(status->message, ippGetString(attr, 0, NULL));
1044
1045         if ((attr = ippFindAttribute(response, "printer-up-time",
1046                                      IPP_TAG_INTEGER)) != NULL)
1047                 printer_up_time = ippGetInteger(attr, 0);
1048
1049         ippDelete(response);
1050         response = NULL;
1051
1052        /*
1053         * Build an IPP_GET_JOBS request, which requires the following
1054         * attributes:
1055         *
1056         *    attributes-charset
1057         *    attributes-natural-language
1058         *    requested-attributes
1059         *    printer-uri
1060         */
1061
1062         request = ippNew();
1063
1064         ippSetOperation(request, IPP_GET_JOBS);
1065         ippSetRequestId(request, 3);
1066
1067         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
1068                      "attributes-charset", NULL, "utf-8");
1069
1070         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
1071                      "attributes-natural-language", NULL, language->language);
1072
1073         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1074                      "printer-uri", NULL, uri);
1075
1076         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1077                       "requested-attributes",
1078                       (sizeof(jattrs) / sizeof(jattrs[0])),
1079                       NULL, jattrs);
1080
1081        /*
1082         * Do the request and get back a response...
1083         */
1084
1085         slprintf(httpPath, sizeof(httpPath) - 1, "/ipp/%s", printername);
1086
1087         if ((response = cupsDoRequest(http, request, httpPath)) == NULL) {
1088                 DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1089                          ippErrorString(cupsLastError())));
1090                 goto out;
1091         }
1092
1093         if (ippGetStatusCode(response) >= IPP_OK_CONFLICT) {
1094                 DEBUG(0,("Unable to get jobs for %s - %s\n", uri,
1095                          ippErrorString(ippGetStatusCode(response))));
1096                 goto out;
1097         }
1098
1099        /*
1100         * Process the jobs...
1101         */
1102
1103         qcount = 0;
1104         qalloc = 0;
1105         queue  = NULL;
1106
1107         for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)) {
1108                /*
1109                 * Skip leading attributes until we hit a job...
1110                 */
1111
1112                 while (attr != NULL && ippGetGroupTag(attr) != IPP_TAG_JOB)
1113                         attr = ippNextAttribute(response);
1114
1115                 if (attr == NULL)
1116                         break;
1117
1118                /*
1119                 * Allocate memory as needed...
1120                 */
1121                 if (qcount >= qalloc) {
1122                         qalloc += 16;
1123
1124                         queue = SMB_REALLOC_ARRAY(queue, print_queue_struct, qalloc);
1125
1126                         if (queue == NULL) {
1127                                 DEBUG(0,("iprint_queue_get: Not enough memory!"));
1128                                 qcount = 0;
1129                                 goto out;
1130                         }
1131                 }
1132
1133                 temp = queue + qcount;
1134                 memset(temp, 0, sizeof(print_queue_struct));
1135
1136                /*
1137                 * Pull the needed attributes from this job...
1138                 */
1139
1140                 job_id       = 0;
1141                 job_priority = 50;
1142                 job_status   = IPP_JOB_PENDING;
1143                 job_time     = 0;
1144                 job_k_octets = 0;
1145                 user_name    = NULL;
1146                 job_name     = NULL;
1147
1148                 while (attr != NULL && ippGetGroupTag(attr) == IPP_TAG_JOB) {
1149                         if (ippGetName(attr) == NULL) {
1150                                 attr = ippNextAttribute(response);
1151                                 break;
1152                         }
1153
1154                         if (strcmp(ippGetName(attr), "job-id") == 0 &&
1155                             ippGetValueTag(attr) == IPP_TAG_INTEGER)
1156                                 job_id = ippGetInteger(attr, 0);
1157
1158                         if (strcmp(ippGetName(attr), "job-k-octets") == 0 &&
1159                             ippGetValueTag(attr) == IPP_TAG_INTEGER)
1160                                 job_k_octets = ippGetInteger(attr, 0);
1161
1162                         if (strcmp(ippGetName(attr), "job-priority") == 0 &&
1163                             ippGetValueTag(attr) == IPP_TAG_INTEGER)
1164                                 job_priority = ippGetInteger(attr, 0);
1165
1166                         if (strcmp(ippGetName(attr), "job-state") == 0 &&
1167                             ippGetValueTag(attr) == IPP_TAG_ENUM)
1168                                 job_status = (ipp_jstate_t)ippGetInteger(attr, 0);
1169
1170                         if (strcmp(ippGetName(attr), "time-at-creation") == 0 &&
1171                             ippGetValueTag(attr) == IPP_TAG_INTEGER)
1172                         {
1173                                /*
1174                                 * If jobs times are in Unix time, the accuracy of the job
1175                                 * start time depends upon the iPrint server's time being
1176                                 * set correctly.  Otherwise, the accuracy depends upon
1177                                 * the Samba server's time being set correctly
1178                                 */
1179
1180                                 if (jobUseUnixTime)
1181                                         job_time = ippGetInteger(attr, 0);
1182                                 else
1183                                         job_time = time(NULL) - printer_up_time + ippGetInteger(attr, 0);
1184                         }
1185
1186                         if (strcmp(ippGetName(attr), "job-name") == 0 &&
1187                             (ippGetValueTag(attr) == IPP_TAG_NAMELANG ||
1188                              ippGetValueTag(attr) == IPP_TAG_NAME))
1189                                 job_name = ippGetString(attr, 0, NULL);
1190
1191                         if (strcmp(ippGetName(attr), "job-originating-user-name") == 0 &&
1192                             (ippGetValueTag(attr) == IPP_TAG_NAMELANG ||
1193                              ippGetValueTag(attr) == IPP_TAG_NAME))
1194                                 user_name = ippGetString(attr, 0, NULL);
1195
1196                         attr = ippNextAttribute(response);
1197                 }
1198
1199                /*
1200                 * See if we have everything needed...
1201                 */
1202
1203                 if (user_name == NULL || job_name == NULL || job_id == 0) {
1204                         if (attr == NULL)
1205                                 break;
1206                         else
1207                                 continue;
1208                 }
1209
1210                 temp->sysjob   = job_id;
1211                 temp->size     = job_k_octets * 1024;
1212                 temp->status   = job_status == IPP_JOB_PENDING ? LPQ_QUEUED :
1213                                  job_status == IPP_JOB_STOPPED ? LPQ_PAUSED :
1214                                  job_status == IPP_JOB_HELD ? LPQ_PAUSED :
1215                                  LPQ_PRINTING;
1216                 temp->priority = job_priority;
1217                 temp->time     = job_time;
1218                 strncpy(temp->fs_user, user_name, sizeof(temp->fs_user) - 1);
1219                 strncpy(temp->fs_file, job_name, sizeof(temp->fs_file) - 1);
1220
1221                 qcount ++;
1222
1223                 if (attr == NULL)
1224                         break;
1225         }
1226
1227        /*
1228         * Return the job queue...
1229         */
1230
1231         *q = queue;
1232
1233  out:
1234         if (response)
1235                 ippDelete(response);
1236
1237         if (language)
1238                 cupsLangFree(language);
1239
1240         if (http)
1241                 httpClose(http);
1242
1243         return qcount;
1244 }
1245
1246
1247 /*
1248  * 'iprint_queue_pause()' - Pause a print queue.
1249  */
1250
1251 static int iprint_queue_pause(int snum)
1252 {
1253         return(-1); /* Not supported without credentials */
1254 }
1255
1256
1257 /*
1258  * 'iprint_queue_resume()' - Restart a print queue.
1259  */
1260
1261 static int iprint_queue_resume(int snum)
1262 {
1263         return(-1); /* Not supported without credentials */
1264 }
1265
1266 /*******************************************************************
1267  * iPrint printing interface definitions...
1268  ******************************************************************/
1269
1270 struct printif  iprint_printif =
1271 {
1272         PRINT_IPRINT,
1273         iprint_queue_get,
1274         iprint_queue_pause,
1275         iprint_queue_resume,
1276         iprint_job_delete,
1277         iprint_job_pause,
1278         iprint_job_resume,
1279         iprint_job_submit,
1280 };
1281
1282 #else
1283  /* this keeps fussy compilers happy */
1284  void print_iprint_dummy(void);
1285  void print_iprint_dummy(void) {}
1286 #endif /* HAVE_IPRINT */