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