]> arthur.barton.de Git - netatalk.git/blob - etc/papd/print_cups.c
remove a 'discard const ...' warning in papd cups
[netatalk.git] / etc / papd / print_cups.c
1 /*
2  * $Id: print_cups.c,v 1.6 2010-01-26 20:43:11 didg Exp $
3  *
4  * Copyright 2004 Bjoern Fernhomberg.
5  *
6  * Some code copied or adapted from print_cups.c for samba
7  * Copyright 1999-2003 by Michael R Sweet.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif /* HAVE_CONFIG_H */
27
28 #include <ctype.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif /* HAVE_UNISTD_H */
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <errno.h>
38
39
40 #ifdef HAVE_CUPS
41
42 #include <cups/cups.h>
43 #include <cups/language.h>
44 #include <atalk/unicode.h>
45 #include <atalk/logger.h>
46 #include <atalk/atp.h>
47 #include <atalk/pap.h>
48 #include <atalk/util.h>
49
50 #include "printer.h"
51 #include "print_cups.h"
52
53 #define MAXCHOOSERLEN 31
54 #define HTTP_MAX_URI 1024
55
56 static const char* cups_status_msg[] = {
57         "status: busy; info: \"%s\" is rejecting jobs; ",
58         "status: idle; info: \"%s\" is stopped, accepting jobs ;",
59         "status: idle; info: \"%s\" is ready ; ",
60 };
61
62 /* Local functions */
63 static int      convert_to_mac_name ( const char *encoding, char * inptr, char * outptr, size_t outlen);
64 static size_t   to_ascii ( char *inbuf, char **outbuf);
65 static int      cups_mangle_printer_name ( struct printer *pr, struct printer *printers);
66 static void     cups_free_printer ( struct printer *pr);
67
68
69 const char * cups_get_language (void)
70 {
71         cups_lang_t *language;
72
73         language = cupsLangDefault();           /* needed for conversion */
74         return cupsLangEncoding(language);
75 }
76
77 /*
78  * 'cups_passwd_cb()' - The CUPS password callback...
79  */
80
81 static const char *                            /* O - Password or NULL */
82 cups_passwd_cb(const char *prompt _U_)      /* I - Prompt */
83 {
84  /*
85   * Always return NULL to indicate that no password is available...
86   */
87   return (NULL);
88 }
89
90
91 /*
92  * 'cups_printername_ok()' - Verify supplied printer name is a valid cups printer
93  */
94
95 int                                     /* O - 1 if printer name OK */
96 cups_printername_ok(char *name)         /* I - Name of printer */
97 {
98         http_t          *http;          /* HTTP connection to server */
99         ipp_t           *request,       /* IPP Request */
100                         *response;      /* IPP Response */
101         cups_lang_t     *language;      /* Default language */
102         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
103
104        /*
105         * Make sure we don't ask for passwords...
106         */
107
108         cupsSetPasswordCB(cups_passwd_cb);
109
110        /*
111         * Try to connect to the server...
112         */
113
114         if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
115         {
116                 LOG(log_error, logtype_papd, "Unable to connect to CUPS server %s - %s",
117                          cupsServer(), strerror(errno));
118                 return (0);
119         }
120
121
122        /*
123         * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
124         * attributes:
125         *
126         *    attributes-charset
127         *    attributes-natural-language
128         *    requested-attributes
129         *    printer-uri
130         */
131
132         request = ippNew();
133
134         request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
135         request->request.op.request_id   = 1;
136
137         language = cupsLangDefault();
138
139         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
140                      "attributes-charset", NULL, cupsLangEncoding(language));
141
142         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
143                      "attributes-natural-language", NULL, language->language);
144
145         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
146                      "requested-attributes", NULL, "printer-uri");
147
148         sprintf(uri, "ipp://localhost/printers/%s", name);
149
150         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
151                      "printer-uri", NULL, uri);
152
153        /*
154         * Do the request and get back a response...
155         */
156
157         if ((response = cupsDoRequest(http, request, "/")) == NULL)
158         {
159                 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", name,
160                          ippErrorString(cupsLastError()));
161                 httpClose(http);
162                 return (0);
163         }
164
165         httpClose(http);
166
167         if (response->request.status.status_code >= IPP_OK_CONFLICT)
168         {
169                 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", name,
170                          ippErrorString(response->request.status.status_code));
171                 ippDelete(response);
172                 return (0);
173         }
174         else
175         {
176                 ippDelete(response);
177                 return (1);
178         }
179
180         return (0);
181 }
182
183 const char * 
184 cups_get_printer_ppd ( char * name)
185 {
186         cupsSetPasswordCB(cups_passwd_cb);
187         return cupsGetPPD( name );
188 }
189
190 int
191 cups_get_printer_status (struct printer *pr)
192 {
193
194         http_t          *http;          /* HTTP connection to server */
195         ipp_t           *request,       /* IPP Request */
196                         *response;      /* IPP Response */
197         ipp_attribute_t *attr;          /* Current attribute */
198         cups_lang_t     *language;      /* Default language */
199         char            uri[HTTP_MAX_URI]; /* printer-uri attribute */
200         int             status = -1;
201
202         static const char *pattrs[] =   /* Requested printer attributes */
203                         {
204                           "printer-state",
205                           "printer-state-message",
206                           "printer-is-accepting-jobs"
207                         };
208
209        /*
210         * Make sure we don't ask for passwords...
211         */
212
213         cupsSetPasswordCB(cups_passwd_cb);
214
215        /*
216         * Try to connect to the server...
217         */
218
219         if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
220         {
221                 LOG(log_error, logtype_papd, "Unable to connect to CUPS server %s - %s",
222                          cupsServer(), strerror(errno));
223                 return (0);
224         }
225
226        /*
227         * Generate the printer URI...
228         */
229
230         sprintf(uri, "ipp://localhost/printers/%s", pr->p_printer);
231
232
233
234        /*
235         * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
236         * following attributes:
237         *
238         *    attributes-charset
239         *    attributes-natural-language
240         *    requested-attributes
241         *    printer-uri
242         */
243
244         request = ippNew();
245
246         request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
247         request->request.op.request_id   = 1;
248
249         language = cupsLangDefault();
250
251         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
252                      "attributes-charset", NULL, cupsLangEncoding(language));
253
254         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
255                      "attributes-natural-language", NULL, language->language);
256
257         ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
258                       "requested-attributes",
259                       (sizeof(pattrs) / sizeof(pattrs[0])),
260                       NULL, pattrs);
261
262         ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
263                      "printer-uri", NULL, uri);
264
265        /*
266         * Do the request and get back a response...
267         */
268
269         if ((response = cupsDoRequest(http, request, "/")) == NULL)
270         {
271                 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", pr->p_printer,
272                          ippErrorString(cupsLastError()));
273                 httpClose(http);
274                 return (0);
275         }
276
277         if (response->request.status.status_code >= IPP_OK_CONFLICT)
278         {
279                 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", pr->p_printer,
280                          ippErrorString(response->request.status.status_code));
281                 ippDelete(response);
282                 httpClose(http);
283                 return (0);
284         }
285
286        /*
287         * Get the current printer status and convert it to the status values.
288         */
289
290         memset ( pr->p_status, 0 ,sizeof(pr->p_status));
291
292         if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
293         {
294                 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
295                         status = 1;
296                 else if (attr->values[0].integer == IPP_NOT_ACCEPTING)
297                         status = 0;
298                 else
299                         status = 2;
300         }
301
302         if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
303         {
304                 if ( attr->values[0].integer == 0 ) 
305                         status = 0;
306         }
307                 
308         snprintf ( pr->p_status, 255, cups_status_msg[status], pr->p_printer );
309
310         if ((attr = ippFindAttribute(response, "printer-state-message", IPP_TAG_TEXT)) != NULL)
311                 strncat ( pr->p_status, attr->values[0].string.text, 255-strlen(pr->p_status));
312
313         ippDelete(response);
314
315        /*
316         * Return the print status ...
317         */
318
319         httpClose(http);
320
321         return (status);
322 }
323
324
325 /*------------------------------------------------------------------------*/
326
327 /* pass the job to cups */
328
329 int cups_print_job ( char * name, char *filename, char *job, char *username, char * cupsoptions )
330 {
331         int             jobid;
332         char            filepath[MAXPATHLEN];
333         int             num_options;
334         cups_option_t   *options;
335
336         /* Initialize the options array */
337         num_options = 0;
338         options     = (cups_option_t *)0;
339
340         cupsSetPasswordCB(cups_passwd_cb);
341
342         if ( username != NULL )
343         {
344                 /* Add options using cupsAddOption() */
345                 num_options = cupsAddOption("job-originating-user-name", username, num_options, &options);
346                 num_options = cupsAddOption("originating-user-name", username, num_options, &options);
347                 cupsSetUser ( username );
348         }
349
350         if (cupsoptions != NULL)
351         {
352               num_options = cupsParseOptions(cupsoptions, num_options, &options);
353         }
354         
355         strlcpy ( filepath, SPOOLDIR, sizeof(filepath));
356         strlcat ( filepath , "/", sizeof(filepath));
357         strlcat ( filepath , filename, sizeof(filepath));
358         
359         if ((jobid = cupsPrintFile( name, filepath, job, 0, options)) == 0)
360                 LOG(log_error, logtype_papd, "Unable to print job '%s' (%s) to printer '%s' for user '%s' - CUPS error : '%s'", job, filepath, name, username, ippErrorString(cupsLastError()));
361         else 
362                 LOG(log_info, logtype_papd, "Job '%s' queued to printer '%s' with id '%d'", job, name, jobid);
363
364         cupsFreeOptions(num_options, options);
365         return (jobid);
366 }
367
368
369 /*------------------------------------------------------------------------*/
370
371 struct printer  *
372 cups_autoadd_printers ( struct printer  *defprinter, struct printer *printers)
373 {
374         struct printer  *pr;
375         int             num_dests,i;
376         int             ret;
377         cups_dest_t     *dests;
378         cups_lang_t     *language;
379         char            name[MAXCHOOSERLEN+1], *p;
380
381         language  = cupsLangDefault();          /* needed for conversion */
382         num_dests = cupsGetDests(&dests);       /* get the available destination from CUPS */
383
384         for  (i=0; i< num_dests; i++)
385         {
386                 if (( pr = (struct printer *)malloc( sizeof( struct printer ))) == NULL ) {
387                         LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
388                         exit( 1 );
389                 }
390         
391                 memcpy( pr, defprinter, sizeof( struct printer ) );
392
393                 /* convert from CUPS to local encoding */
394                 convert_string_allocate( add_charset(cupsLangEncoding(language)), CH_UNIX, 
395                                          dests[i].name, -1, &pr->p_u_name);
396
397                 /* convert CUPS name to Mac charset */
398                 if ( convert_to_mac_name ( cupsLangEncoding(language), dests[i].name, name, sizeof(name)) <= 0)
399                 {
400                         LOG (log_error, logtype_papd, "Conversion from CUPS to MAC name failed for %s", dests[i].name);
401                         free (pr);
402                         continue;
403                 }
404
405                 if (( pr->p_name = (char *)malloc( strlen( name ) + 1 )) == NULL ) {
406                         LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
407                         exit( 1 );
408                 }
409                 strcpy( pr->p_name, name );
410
411                 /* set printer flags */
412                 pr->p_flags &= ~P_PIPED;
413                 pr->p_flags |= P_SPOOLED;
414                 pr->p_flags |= P_CUPS;
415                 pr->p_flags |= P_CUPS_AUTOADDED;
416                         
417                 if (( pr->p_printer = (char *)malloc( strlen( dests[i].name ) + 1 )) == NULL ) {
418                         LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
419                         exit( 1 );
420                 }
421                 strcpy( pr->p_printer, dests[i].name );                 
422
423                 if ( (p = (char *) cups_get_printer_ppd ( pr->p_printer )) != NULL ) {
424                         if (( pr->p_ppdfile = (char *)malloc( strlen( p ) + 1 )) == NULL ) {
425                                 LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
426                                 exit( 1 );
427                         }
428                         strcpy( pr->p_ppdfile, p );
429                         pr->p_flags |= P_CUPS_PPD;
430                 }
431
432                 if ( (ret = cups_check_printer ( pr, printers, 0)) == -1) 
433                         ret = cups_mangle_printer_name ( pr, printers );
434
435                 if (ret) {
436                         cups_free_printer (pr);
437                         LOG(log_info, logtype_papd, "Printer %s not added: reason %d", name, ret);
438                 }
439                 else {
440                         pr->p_next = printers;
441                         printers = pr;
442                 }
443         }
444  
445         cupsFreeDests(num_dests, dests);
446         cupsLangFree(language);
447
448         return printers;
449 }
450
451
452 /*------------------------------------------------------------------------*/
453
454 /* cups_mangle_printer_name
455  *    Mangles the printer name if two CUPS printer provide the same Chooser Name
456  *    Append '#nn' to the chooser name, if it is longer than 28 char we overwrite the last three chars
457  * Returns: 0 on Success, 2 on Error
458  */
459
460 static int cups_mangle_printer_name ( struct printer *pr, struct printer *printers)
461 {
462         size_t  count, name_len;
463         char    name[MAXCHOOSERLEN];
464
465         count = 1;
466         name_len = strlen (pr->p_name);
467         strncpy ( name, pr->p_name, MAXCHOOSERLEN-3);
468         
469         /* Reallocate necessary space */
470         (name_len >= MAXCHOOSERLEN-3) ? (name_len = MAXCHOOSERLEN+1) : (name_len = name_len + 4);
471         pr->p_name = (char *) realloc (pr->p_name, name_len );
472                 
473         while ( ( cups_check_printer ( pr, printers, 0 )) && count < 100)
474         {
475                 memset ( pr->p_name, 0, name_len);
476                 strncpy ( pr->p_name, name, MAXCHOOSERLEN-3);
477                 sprintf ( pr->p_name, "%s#%2.2u", pr->p_name, count++);
478         }
479
480         if ( count > 99) 
481                 return (2);
482         
483         return (0);
484 }       
485
486 /*------------------------------------------------------------------------*/
487
488 /* fallback ASCII conversion */
489
490 static size_t
491 to_ascii ( char  *inptr, char **outptr)
492 {
493         char *out, *osav;
494
495         if ( NULL == (out = (char*) malloc ( strlen ( inptr) + 1 )) ) {
496                 LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
497                 exit (1);
498         }
499
500         osav = out;
501
502         while ( *inptr != '\0' ) {
503                 if ( *inptr & 0x80 ) {
504                         *out = '_';
505                         out++;
506                         inptr++;
507                 }
508                 else
509                         *out++ = *inptr++;
510         }
511         *out = '\0';
512         *outptr = osav;
513         return ( strlen (osav) );
514 }
515
516
517 /*------------------------------------------------------------------------*/
518
519 /* convert_to_mac_name
520  *      1) Convert from encoding to MacRoman
521  *      2) Shorten to MAXCHOOSERLEN (31)
522  *      3) Replace @ and _ as they are illegal
523  * Returns: -1 on failure, length of name on success; outpr contains name in MacRoman
524  */
525
526 static int convert_to_mac_name ( const char * encoding, char * inptr, char * outptr, size_t outlen)
527 {
528         char    *outbuf;
529         char    *soptr;
530         size_t  name_len = 0;
531         size_t  i;
532         charset_t chCups;
533
534         /* Change the encoding */
535         if ((charset_t)-1 != (chCups = add_charset(encoding))) {
536                 name_len = convert_string_allocate( chCups, CH_MAC, inptr, -1, &outbuf);
537         }
538
539         if (name_len == 0 || name_len == (size_t)-1) {
540                 /* charset conversion failed, use ascii fallback */
541                 name_len = to_ascii ( inptr, &outbuf );
542         }
543         
544         soptr = outptr;
545
546         for ( i=0; i< name_len && i < outlen-1 ; i++)
547         {
548                 if ( outbuf[i] == '_' ) 
549                         *soptr = ' '; /* Replace '_' with a space (just for the looks) */
550                 else if ( outbuf[i] == '@' || outbuf[i] == ':' ) 
551                         *soptr = '_'; /* Replace @ and : with '_' as they are illegal chars */
552                 else
553                         *soptr = outbuf[i];
554                 soptr++;
555         }
556         *soptr = '\0';
557         free (outbuf);
558         return (i);
559 }
560
561
562 /*------------------------------------------------------------------------*/
563
564 /*
565  * cups_check_printer:
566  * check if a printer with this name already exists.
567  * if yes, and replace = 1 the existing printer is replaced with
568  * the new one. This allows to overwrite printer settings
569  * created by cupsautoadd. It also used by cups_mangle_printer.
570  */
571
572 int cups_check_printer ( struct printer *pr, struct printer *printers, int replace)
573 {
574         struct printer *listptr, *listprev;
575
576         listptr = printers;
577         listprev = NULL;
578
579         while ( listptr != NULL) {
580                 if ( strcasecmp (pr->p_name, listptr->p_name) == 0) {
581                         if ( pr->p_flags & P_CUPS_AUTOADDED ) {  /* Check if printer has been autoadded */
582                                 if ( listptr->p_flags & P_CUPS_AUTOADDED )
583                                         return (-1);             /* Conflicting Cups Auto Printer (mangling issue?) */
584                                 else
585                                         return (1);              /* Never replace a hand edited printer with auto one */
586                         }
587
588                         if ( replace ) {
589                                 /* Replace printer */
590                                 if ( listprev != NULL) {
591                                         pr->p_next = listptr->p_next;
592                                         listprev->p_next = pr;
593                                         cups_free_printer (listptr);
594                                 }
595                                 else {
596                                         printers = pr;
597                                         printers->p_next = listptr->p_next;
598                                         cups_free_printer (listptr);
599                                 }
600                         }
601                         return (1);  /* Conflicting Printers */
602                 }
603                 listprev = listptr;
604                 listptr = listptr->p_next;
605         }       
606         return (0);     /* No conflict */
607 }
608
609
610 /*------------------------------------------------------------------------*/
611
612
613 void
614 cups_free_printer ( struct printer *pr)
615 {
616         if ( pr->p_name != NULL)
617                 free (pr->p_name);
618         if ( pr->p_printer != NULL)
619                 free (pr->p_printer);
620         if ( pr->p_ppdfile != NULL)
621                 free (pr->p_ppdfile);
622         
623         /* CUPS autoadded printers share the other informations
624          * so if the printer is autoadded we won't free them.
625          * We might leak some bytes here though.
626          */
627         if ( pr->p_flags & P_CUPS_AUTOADDED ) {
628                 free (pr);
629                 return;
630         }
631
632         if ( pr->p_operator != NULL )
633                 free (pr->p_operator);
634         if ( pr->p_zone != NULL )
635                 free (pr->p_zone);
636         if ( pr->p_type != NULL )
637                 free (pr->p_type);
638         if ( pr->p_authprintdir != NULL )
639                 free (pr->p_authprintdir);
640         
641         free ( pr );
642         return;
643         
644 }
645         
646 #endif /* HAVE_CUPS*/