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 56static 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 */ 63static int convert_to_mac_name ( const char *encoding, char * inptr, char * outptr, size_t outlen); 64static size_t to_ascii ( char *inbuf, char **outbuf); 65static int cups_mangle_printer_name ( struct printer *pr, struct printer *printers); 66static void cups_free_printer ( struct printer *pr); 67 68 69const 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 81static const char * /* O - Password or NULL */ 82cups_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 95int /* O - 1 if printer name OK */ 96cups_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 183const char * 184cups_get_printer_ppd ( char * name) 185{ 186 cupsSetPasswordCB(cups_passwd_cb); 187 return cupsGetPPD( name ); 188} 189 190int 191cups_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 329int 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 371struct printer * 372cups_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 460static 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 490static size_t 491to_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 526static 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 572int 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 613void 614cups_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*/ 647