2 * $Id: print_cups.c,v 1.2 2005-04-28 20:49:49 bfernhomberg Exp $
4 * Copyright 2004 Bjoern Fernhomberg.
6 * Some code copied or adapted from print_cups.c for samba
7 * Copyright 1999-2003 by Michael R Sweet.
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.
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.
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.
26 #endif /* HAVE_CONFIG_H */
34 #endif /* HAVE_UNISTD_H */
35 #include <sys/types.h>
36 #include <sys/param.h>
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>
51 #include "print_cups.h"
53 #define MAXCHOOSERLEN 31
54 #define HTTP_MAX_URI 1024
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 ; ",
63 static int convert_to_mac_name ( 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);
69 char * cups_get_language ()
71 cups_lang_t *language;
73 language = cupsLangDefault(); /* needed for conversion */
74 return cupsLangEncoding(language);
78 * 'cups_passwd_cb()' - The CUPS password callback...
81 static const char * /* O - Password or NULL */
82 cups_passwd_cb(const char *prompt _U_) /* I - Prompt */
85 * Always return NULL to indicate that no password is available...
92 * 'cups_printername_ok()' - Verify supplied printer name is a valid cups printer
95 int /* O - 1 if printer name OK */
96 cups_printername_ok(char *name) /* I - Name of printer */
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 */
105 * Make sure we don't ask for passwords...
108 cupsSetPasswordCB(cups_passwd_cb);
111 * Try to connect to the server...
114 if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
116 LOG(log_error, logtype_papd, "Unable to connect to CUPS server %s - %s",
117 cupsServer(), strerror(errno));
123 * Build an IPP_GET_PRINTER_ATTRS request, which requires the following
127 * attributes-natural-language
128 * requested-attributes
134 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
135 request->request.op.request_id = 1;
137 language = cupsLangDefault();
139 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
140 "attributes-charset", NULL, cupsLangEncoding(language));
142 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
143 "attributes-natural-language", NULL, language->language);
145 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
146 "requested-attributes", NULL, "printer-uri");
148 sprintf(uri, "ipp://localhost/printers/%s", name);
150 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
151 "printer-uri", NULL, uri);
154 * Do the request and get back a response...
157 if ((response = cupsDoRequest(http, request, "/")) == NULL)
159 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", name,
160 ippErrorString(cupsLastError()));
167 if (response->request.status.status_code >= IPP_OK_CONFLICT)
169 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", name,
170 ippErrorString(response->request.status.status_code));
184 cups_get_printer_ppd ( char * name)
186 cupsSetPasswordCB(cups_passwd_cb);
187 return cupsGetPPD( name );
191 cups_get_printer_status (struct printer *pr)
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 */
202 static const char *pattrs[] = /* Requested printer attributes */
205 "printer-state-message",
206 "printer-is-accepting-jobs"
210 * Make sure we don't ask for passwords...
213 cupsSetPasswordCB(cups_passwd_cb);
216 * Try to connect to the server...
219 if ((http = httpConnect(cupsServer(), ippPort())) == NULL)
221 LOG(log_error, logtype_papd, "Unable to connect to CUPS server %s - %s",
222 cupsServer(), strerror(errno));
227 * Generate the printer URI...
230 sprintf(uri, "ipp://localhost/printers/%s", pr->p_printer);
235 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
236 * following attributes:
239 * attributes-natural-language
240 * requested-attributes
246 request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES;
247 request->request.op.request_id = 1;
249 language = cupsLangDefault();
251 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
252 "attributes-charset", NULL, cupsLangEncoding(language));
254 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
255 "attributes-natural-language", NULL, language->language);
257 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
258 "requested-attributes",
259 (sizeof(pattrs) / sizeof(pattrs[0])),
262 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
263 "printer-uri", NULL, uri);
266 * Do the request and get back a response...
269 if ((response = cupsDoRequest(http, request, "/")) == NULL)
271 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", pr->p_printer,
272 ippErrorString(cupsLastError()));
277 if (response->request.status.status_code >= IPP_OK_CONFLICT)
279 LOG(log_error, logtype_papd, "Unable to get printer status for %s - %s", pr->p_printer,
280 ippErrorString(response->request.status.status_code));
287 * Get the current printer status and convert it to the status values.
290 memset ( pr->p_status, 0 ,sizeof(pr->p_status));
292 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
294 if (attr->values[0].integer == IPP_PRINTER_STOPPED)
296 else if (attr->values[0].integer == IPP_NOT_ACCEPTING)
302 if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs", IPP_TAG_BOOLEAN)) != NULL)
304 if ( attr->values[0].integer == 0 )
308 snprintf ( pr->p_status, 255, cups_status_msg[status], pr->p_printer );
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));
316 * Return the print status ...
325 /*------------------------------------------------------------------------*/
327 /* pass the job to cups */
329 int cups_print_job ( char * name, char *filename, char *job, char *username, char * cupsoptions )
332 char filepath[MAXPATHLEN];
334 cups_option_t *options;
336 /* Initialize the options array */
338 options = (cups_option_t *)0;
340 cupsSetPasswordCB(cups_passwd_cb);
342 if ( username != NULL )
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 );
350 if (cupsoptions != NULL)
352 num_options = cupsParseOptions(cupsoptions, num_options, &options);
355 strlcpy ( filepath, SPOOLDIR, sizeof(filepath));
356 strlcat ( filepath , "/", sizeof(filepath));
357 strlcat ( filepath , filename, sizeof(filepath));
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()));
362 LOG(log_info, logtype_papd, "Job '%s' queued to printer '%s' with id '%d'", job, name, jobid);
364 cupsFreeOptions(num_options, options);
369 /*------------------------------------------------------------------------*/
372 cups_autoadd_printers ( struct printer *defprinter, struct printer *printers)
378 cups_lang_t *language;
379 char name[MAXCHOOSERLEN+1], *p;
381 language = cupsLangDefault(); /* needed for conversion */
382 num_dests = cupsGetDests(&dests); /* get the available destination from CUPS */
384 for (i=0; i< num_dests; i++)
386 if (( pr = (struct printer *)malloc( sizeof( struct printer ))) == NULL ) {
387 LOG(log_error, logtype_papd, "malloc: %m" );
391 memcpy( pr, defprinter, sizeof( struct printer ) );
393 /* convert from CUPS to local encoding */
394 convert_string_allocate( add_charset(cupsLangEncoding(language)), CH_UNIX,
395 dests[i].name, strlen(dests[i].name), &pr->p_u_name);
397 /* convert CUPS name to Mac charset */
398 if ( convert_to_mac_name ( cupsLangEncoding(language), dests[i].name, name, sizeof(name)) <= 0)
400 LOG (log_error, logtype_papd, "Conversion from CUPS to MAC name failed for %s", dests[i].name);
405 if (( pr->p_name = (char *)malloc( strlen( name ) + 1 )) == NULL ) {
406 LOG(log_error, logtype_papd, "malloc: %m" );
409 strcpy( pr->p_name, name );
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;
417 if (( pr->p_printer = (char *)malloc( strlen( dests[i].name ) + 1 )) == NULL ) {
418 LOG(log_error, logtype_papd, "malloc: %m" );
421 strcpy( pr->p_printer, dests[i].name );
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: %m" );
428 strcpy( pr->p_ppdfile, p );
429 pr->p_flags |= P_CUPS_PPD;
432 if ( (ret = cups_check_printer ( pr, printers, 0)) == -1)
433 ret = cups_mangle_printer_name ( pr, printers );
436 cups_free_printer (pr);
437 LOG(log_info, logtype_papd, "Printer %s not added: reason %d", name, ret);
440 pr->p_next = printers;
445 cupsFreeDests(num_dests, dests);
446 cupsLangFree(language);
452 /*------------------------------------------------------------------------*/
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
460 static int cups_mangle_printer_name ( struct printer *pr, struct printer *printers)
462 size_t count, name_len;
463 char name[MAXCHOOSERLEN];
466 name_len = strlen (pr->p_name);
467 strncpy ( name, pr->p_name, MAXCHOOSERLEN-3);
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 );
473 while ( ( cups_check_printer ( pr, printers, 0 )) && count < 100)
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++);
486 /*------------------------------------------------------------------------*/
488 /* fallback ASCII conversion */
491 to_ascii ( char *inptr, char **outptr)
495 if ( NULL == (out = (char*) malloc ( strlen ( inptr) + 1 )) ) {
496 LOG(log_error, logtype_papd, "malloc: %m" );
502 while ( *inptr != '\0' ) {
503 if ( *inptr & 0x80 ) {
513 return ( strlen (osav) );
517 /*------------------------------------------------------------------------*/
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
526 static int convert_to_mac_name ( char * encoding, char * inptr, char * outptr, size_t outlen)
534 /* Change the encoding */
535 if ((charset_t)-1 != (chCups = add_charset(encoding))) {
536 name_len = convert_string_allocate( chCups, CH_MAC, inptr, strlen(inptr), &outbuf);
539 if (name_len == 0 || name_len == (size_t)-1) {
540 /* charset conversion failed, use ascii fallback */
541 name_len = to_ascii ( inptr, &outbuf );
546 for ( i=0; i< name_len && i < outlen-1 ; i++)
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 */
562 /*------------------------------------------------------------------------*/
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.
572 int cups_check_printer ( struct printer *pr, struct printer *printers, int replace)
574 struct printer *listptr, *listprev;
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?) */
585 return (1); /* Never replace a hand edited printer with auto one */
589 /* Replace printer */
590 if ( listprev != NULL) {
591 pr->p_next = listptr->p_next;
592 listprev->p_next = pr;
593 cups_free_printer (listptr);
597 printers->p_next = listptr->p_next;
598 cups_free_printer (listptr);
601 return (1); /* Conflicting Printers */
604 listptr = listptr->p_next;
606 return (0); /* No conflict */
610 /*------------------------------------------------------------------------*/
614 cups_free_printer ( struct printer *pr)
616 if ( pr->p_name != NULL)
618 if ( pr->p_printer != NULL)
619 free (pr->p_printer);
620 if ( pr->p_ppdfile != NULL)
621 free (pr->p_ppdfile);
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.
627 if ( pr->p_flags & P_CUPS_AUTOADDED ) {
632 if ( pr->p_operator != NULL )
633 free (pr->p_operator);
634 if ( pr->p_zone != NULL )
636 if ( pr->p_type != NULL )
638 if ( pr->p_authprintdir != NULL )
639 free (pr->p_authprintdir);
646 #endif /* HAVE_CUPS*/