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