1/* 2 * $Id: lp.c,v 1.33 2009-10-29 13:38:15 didg Exp $ 3 * 4 * Copyright (c) 1990,1994 Regents of The University of Michigan. 5 * All Rights Reserved. See COPYRIGHT. 6 * 7 * Portions: 8 * Copyright (c) 1983 Regents of the University of California. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the University of 22 * California, Berkeley and its contributors. 23 * 4. Neither the name of the University nor the names of its contributors 24 * may be used to endorse or promote products derived from this software 25 * without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 * SUCH DAMAGE. 38 */ 39 40/* 41 * Interface to lpr system. 42 */ 43 44#ifdef HAVE_CONFIG_H 45#include "config.h" 46#endif /* HAVE_CONFIG_H */ 47 48#include <sys/param.h> 49#include <sys/time.h> 50#include <sys/socket.h> 51#include <sys/stat.h> 52#include <ctype.h> 53#ifdef HAVE_UNISTD_H 54#include <unistd.h> 55#endif /* HAVE_UNISTD_H */ 56 57#include <sys/file.h> 58#include <sys/un.h> 59#include <netinet/in.h> 60#undef s_net 61 62#ifdef ABS_PRINT 63#include <math.h> 64#endif /* ABS_PRINT */ 65#include <stdio.h> 66#include <stdlib.h> 67#include <string.h> 68#include <netdb.h> 69#ifdef HAVE_FCNTL_H 70#include <fcntl.h> 71#endif /* HAVE_FCNTL_H */ 72#include <pwd.h> 73 74#include <atalk/logger.h> 75#include <netatalk/at.h> 76#include <atalk/atp.h> 77#include <atalk/paths.h> 78#include <atalk/unicode.h> 79 80#include "printer.h" 81#include "file.h" 82#include "lp.h" 83 84#ifdef HAVE_CUPS 85#include "print_cups.h" 86#endif 87 88 89/* These functions aren't used outside of lp.c */ 90int lp_conn_inet(); 91int lp_disconn_inet( int ); 92int lp_conn_unix(); 93int lp_disconn_unix( int ); 94 95static char hostname[ MAXHOSTNAMELEN ]; 96 97extern struct sockaddr_at *sat; 98 99static struct lp { 100 int lp_flags; 101 FILE *lp_stream; 102 int lp_seq; 103 int lp_origin; 104 char lp_letter; 105 char *lp_person; 106 char *lp_created_for; /* Holds the content of the Postscript %%For Comment if available */ 107 char *lp_host; 108 char *lp_job; 109 char *lp_spoolfile; 110} lp; 111#define LP_INIT (1<<0) 112#define LP_OPEN (1<<1) 113#define LP_PIPE (1<<2) 114#define LP_CONNECT (1<<3) 115#define LP_QUEUE (1<<4) 116#define LP_JOBPENDING (1<<5) 117 118void lp_origin (int origin) 119{ 120 lp.lp_origin = origin; 121} 122 123/* the converted string should always be shorter, but ... FIXME! */ 124static void convert_octal (char *string, charset_t dest) 125{ 126 unsigned char *p, *q; 127 char temp[4]; 128 long int ch; 129 130 q=p=(unsigned char *)string; 131 while ( *p != '\0' ) { 132 ch = 0; 133 if ( *p == '\\' ) { 134 p++; 135 if (dest && isdigit(*p) && isdigit(*(p+1)) && isdigit(*(p+2)) ) { 136 temp[0] = *p; 137 temp[1] = *(p+1); 138 temp[2] = *(p+2); 139 temp[3] = 0; 140 ch = strtol( temp, NULL, 8); 141 if ( ch && ch < 0xff) 142 *q = ch; 143 else 144 *q = '.'; 145 p += 2; 146 } 147 else 148 *q = '.'; 149 } 150 else { 151 *q = *p; 152 } 153 p++; 154 q++; 155 } 156 *q = 0; 157} 158 159 160static void translate(charset_t from, charset_t dest, char **option) 161{ 162 char *translated; 163 164 if (*option != NULL) { 165 convert_octal(*option, from); 166 if (from) { 167 if ((size_t) -1 != (convert_string_allocate(from, dest, *option, -1, &translated)) ) { 168 free (*option); 169 *option = translated; 170 } 171 } 172 } 173} 174 175 176static void lp_setup_comments (charset_t dest) 177{ 178 charset_t from=0; 179 180 switch (lp.lp_origin) { 181 case 1: 182 from=CH_MAC; 183 break; 184 case 2: 185 from=CH_UTF8_MAC; 186 break; 187 } 188 189 if (lp.lp_job) { 190#ifdef DEBUG1 191 LOG(log_debug9, logtype_papd, "job: %s", lp.lp_job ); 192#endif 193 translate(from, dest, &lp.lp_job); 194 } 195 if (lp.lp_created_for) { 196#ifdef DEBUG1 197 LOG(log_debug9, logtype_papd, "for: %s", lp.lp_created_for ); 198#endif 199 translate(from, dest, &lp.lp_created_for); 200 } 201 if (lp.lp_person) { 202#ifdef DEBUG1 203 LOG(log_debug9, logtype_papd, "person: %s", lp.lp_person ); 204#endif 205 translate(from, dest, &lp.lp_person); 206 } 207} 208 209#define is_var(a, b) (strncmp((a), (b), 2) == 0) 210 211#if 0 212/* removed, it's not used and a pain to get it right from a security POV */ 213static size_t quote(char *dest, char *src, const size_t bsize, size_t len) 214{ 215size_t used = 0; 216 217 while (len && used < bsize ) { 218 switch (*src) { 219 case '$': 220 case '\\': 221 case '"': 222 case '`': 223 if (used + 2 > bsize ) 224 return used; 225 *dest = '\\'; 226 dest++; 227 used++; 228 break; 229 } 230 *dest = *src; 231 src++; 232 dest++; 233 len--; 234 used++; 235 } 236 return used; 237} 238 239static char* pipexlate(char *src) 240{ 241 char *p, *q, *dest; 242 static char destbuf[MAXPATHLEN +1]; 243 size_t destlen = MAXPATHLEN; 244 int len = 0; 245 246 dest = destbuf; 247 248 if (!src) 249 return NULL; 250 251 memset(dest, 0, MAXPATHLEN +1); 252 if ((p = strchr(src, '%')) == NULL) { /* nothing to do */ 253 strncpy(dest, src, MAXPATHLEN); 254 return destbuf; 255 } 256 /* first part of the path. copy and forward to the next variable. */ 257 len = MIN((size_t)(p - src), destlen); 258 if (len > 0) { 259 strncpy(dest, src, len); 260 destlen -= len; 261 dest += len; 262 } 263 264 while (p && destlen > 0) { 265 /* now figure out what the variable is */ 266 q = NULL; 267 if (is_var(p, "%U")) { 268 q = lp.lp_person; 269 } else if (is_var(p, "%C") || is_var(p, "%J") ) { 270 q = lp.lp_job; 271 } else if (is_var(p, "%F")) { 272 q = lp.lp_created_for; 273 } else if (is_var(p, "%%")) { 274 q = "%"; 275 } 276 277 /* copy the stuff over. if we don't understand something that we 278 * should, just skip it over. */ 279 if (q) { 280 len = MIN(strlen(q), destlen); 281 len = quote(dest, q, destlen, len); 282 } 283 else { 284 len = MIN(2, destlen); 285 strncpy(dest, q, len); 286 } 287 dest += len; 288 destlen -= len; 289 290 /* stuff up to next % */ 291 src = p + 2; 292 p = strchr(src, '%'); 293 len = p ? MIN((size_t)(p - src), destlen) : destlen; 294 if (len > 0) { 295 strncpy(dest, src, len); 296 dest += len; 297 destlen -= len; 298 } 299 } 300 if (!destlen) { 301 /* reach end of buffer, maybe prematurely, give up */ 302 return NULL; 303 } 304 return destbuf; 305} 306#endif 307 308void lp_person(char *person) 309{ 310 if ( lp.lp_person != NULL ) { 311 free( lp.lp_person ); 312 } 313 if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) { 314 LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) ); 315 exit( 1 ); 316 } 317 strcpy( lp.lp_person, person ); 318} 319 320#ifdef ABS_PRINT 321int lp_pagecost(void) 322{ 323 char cost[ 22 ]; 324 char balance[ 22 ]; 325 int err; 326 327 if ( lp.lp_person == NULL ) { 328 return( -1 ); 329 } 330 err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid, 331 cost, balance ); 332 printer->p_pagecost = floor( atof( cost ) * 10000.0 ); 333 printer->p_balance = atof( balance ) + atof( cost ); 334 return( err < 0 ? -1 : 0 ); 335} 336#endif /* ABS_PRINT */ 337 338void lp_host( char *host) 339{ 340 if ( lp.lp_host != NULL ) { 341 free( lp.lp_host ); 342 } 343 if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) { 344 LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) ); 345 exit( 1 ); 346 } 347 strcpy( lp.lp_host, host ); 348 LOG(log_debug, logtype_papd, "host: %s", lp.lp_host ); 349} 350 351/* Currently lp_job and lp_for will not handle the 352 * conversion of macroman chars > 0x7f correctly 353 * This should be added. 354 */ 355 356void lp_job(char *job) 357{ 358 if ( lp.lp_job != NULL ) { 359 free( lp.lp_job ); 360 } 361 362 lp.lp_job = strdup(job); 363#ifdef DEBUG 364 LOG(log_debug9, logtype_papd, "job: %s", lp.lp_job ); 365#endif 366 367} 368 369void lp_for (char *lpfor) 370{ 371 if ( lp.lp_created_for != NULL ) { 372 free( lp.lp_created_for ); 373 } 374 375 lp.lp_created_for = strdup(lpfor); 376} 377 378 379static int lp_init(struct papfile *out, struct sockaddr_at *sat) 380{ 381 int authenticated = 0; 382#ifndef HAVE_CUPS 383 int fd, n, len; 384 char *cp, buf[ BUFSIZ ]; 385 struct stat st; 386#endif /* HAVE_CUPS */ 387#ifdef ABS_PRINT 388 char cost[ 22 ]; 389 char balance[ 22 ]; 390#endif /* ABS_PRINT */ 391 392 if ( printer->p_flags & P_AUTH ) { 393 authenticated = 0; 394 395 /* cap style "log on to afp server before printing" authentication */ 396 397 if ( printer->p_authprintdir && (printer->p_flags & P_AUTH_CAP) ) { 398 int addr_net = ntohs( sat->sat_addr.s_net ); 399 int addr_node = sat->sat_addr.s_node; 400 char addr_filename[256]; 401 char auth_string[256]; 402 char *username, *afpdpid; 403 struct stat cap_st; 404 FILE *cap_file; 405 406 memset( auth_string, 0, 256 ); 407 sprintf(addr_filename, "%s/net%d.%dnode%d", 408 printer->p_authprintdir, addr_net/256, addr_net%256, 409 addr_node); 410 if (stat(addr_filename, &cap_st) == 0) { 411 if ((cap_file = fopen(addr_filename, "r")) != NULL) { 412 if (fgets(auth_string, 256, cap_file) != NULL) { 413 username = auth_string; 414 if ((afpdpid = strrchr( auth_string, ':' )) != NULL) { 415 *afpdpid = '\0'; 416 afpdpid++; 417 } 418 if (getpwnam(username) != NULL ) { 419 LOG(log_info, logtype_papd, "CAP authenticated %s", username); 420 lp_person(username); 421 authenticated = 1; 422 } else { 423 LOG(log_info, logtype_papd, "CAP error: invalid username: '%s'", username); 424 } 425 } else { 426 LOG(log_info, logtype_papd, "CAP error: could not read username"); 427 } 428 } else { 429 LOG(log_info, logtype_papd, "CAP error: %s", strerror(errno)); 430 } 431 } else { 432 LOG(log_info, logtype_papd, "CAP error: %s", strerror(errno)); 433 } 434 } 435 436 if ( printer->p_flags & P_AUTH_PSSP ) { 437 if ( lp.lp_person != NULL ) { 438 authenticated = 1; 439 } 440 } 441 442 if ( authenticated == 0 ) { 443 LOG(log_error, logtype_papd, "lp_init: must authenticate" ); 444 spoolerror( out, "Authentication required." ); 445 return( -1 ); 446 } 447 448#ifdef ABS_PRINT 449 if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 && 450 ! ABS_canprint( lp.lp_person, printer->p_role, 451 printer->p_srvid, cost, balance )) { 452 LOG(log_error, logtype_papd, "lp_init: no ABS funds" ); 453 spoolerror( out, "No ABS funds available." ); 454 return( -1 ); 455 } 456#endif /* ABS_PRINT */ 457 } 458 459 if ( gethostname( hostname, sizeof( hostname )) < 0 ) { 460 LOG(log_error, logtype_papd, "gethostname: %s", strerror(errno) ); 461 exit( 1 ); 462 } 463 464 if ( lp.lp_flags & LP_INIT ) { 465 LOG(log_error, logtype_papd, "lp_init: already inited, die!" ); 466 abort(); 467 } 468 469 lp.lp_flags = 0; 470 lp.lp_stream = NULL; 471 lp.lp_letter = 'A'; 472 473 if ( printer->p_flags & P_SPOOLED ) { 474 475#ifndef HAVE_CUPS 476 /* check if queuing is enabled: mode & 010 on lock file */ 477 if ( stat( printer->p_lock, &st ) < 0 ) { 478 LOG(log_error, logtype_papd, "lp_init: %s: %s", printer->p_lock, strerror(errno) ); 479 spoolerror( out, NULL ); 480 return( -1 ); 481 } 482 if ( st.st_mode & 010 ) { 483 LOG(log_info, logtype_papd, "lp_init: queuing is disabled" ); 484 spoolerror( out, "Queuing is disabled." ); 485 return( -1 ); 486 } 487 488 if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) { 489 LOG(log_error, logtype_papd, "lp_init: can't create .seq" ); 490 spoolerror( out, NULL ); 491 return( -1 ); 492 } 493 494#ifndef SOLARIS /* flock is unsupported, I doubt this stuff works anyway with newer solaris so ignore for now */ 495 if ( flock( fd, LOCK_EX ) < 0 ) { 496 LOG(log_error, logtype_papd, "lp_init: can't lock .seq" ); 497 spoolerror( out, NULL ); 498 return( -1 ); 499 } 500#endif 501 502 n = 0; 503 if (( len = read( fd, buf, sizeof( buf ))) < 0 ) { 504 LOG(log_error, logtype_papd, "lp_init read: %s", strerror(errno) ); 505 spoolerror( out, NULL ); 506 return( -1 ); 507 } 508 if ( len > 0 ) { 509 for ( cp = buf; len; len--, cp++ ) { 510 if ( *cp < '0' || *cp > '9' ) { 511 break; 512 } 513 n = n * 10 + ( *cp - '0' ); 514 } 515 } 516 lp.lp_seq = n; 517 518 n = ( n + 1 ) % 1000; 519 sprintf( buf, "%03d\n", n ); 520 lseek( fd, 0L, 0 ); 521 write( fd, buf, strlen( buf )); 522 close( fd ); 523#else 524 525 if (cups_get_printer_status ( printer ) == 0) 526 { 527 spoolerror( out, "Queuing is disabled." ); 528 return( -1 ); 529 } 530 531 lp.lp_seq = getpid(); 532#endif /* HAVE CUPS */ 533 } else { 534 lp.lp_flags |= LP_PIPE; 535 lp.lp_seq = getpid(); 536 } 537 538 lp.lp_flags |= LP_INIT; 539 return( 0 ); 540} 541 542int lp_open(struct papfile *out, struct sockaddr_at *sat) 543{ 544 char name[ MAXPATHLEN ]; 545 int fd; 546 struct passwd *pwent; 547 548#ifdef DEBUG 549 LOG (log_debug9, logtype_papd, "lp_open"); 550#endif 551 552 if ( lp.lp_flags & LP_JOBPENDING ) { 553 lp_print(); 554 } 555 556 if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) { 557 return( -1 ); 558 } 559 if ( lp.lp_flags & LP_OPEN ) { 560 /* LOG(log_error, logtype_papd, "lp_open already open" ); */ 561 /* abort(); */ 562 return (-1); 563 } 564 565 if ( lp.lp_flags & LP_PIPE ) { 566 char *pipe_cmd; 567 568 /* go right to program */ 569 if (lp.lp_person != NULL) { 570 if((pwent = getpwnam(lp.lp_person)) != NULL) { 571 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) { 572 LOG(log_error, logtype_papd, "setreuid error: %s", strerror(errno)); 573 exit(1); 574 } 575 } else { 576 LOG(log_error, logtype_papd, "Error getting username (%s)", lp.lp_person); 577 exit(1); 578 } 579 } 580 581 lp_setup_comments(CH_UNIX); 582 pipe_cmd = printer->p_printer; 583 if (!pipe_cmd) { 584 LOG(log_error, logtype_papd, "lp_open: no pipe cmd" ); 585 spoolerror( out, NULL ); 586 return( -1 ); 587 } 588 if (( lp.lp_stream = popen(pipe_cmd, "w" )) == NULL ) { 589 LOG(log_error, logtype_papd, "lp_open popen %s: %s", printer->p_printer, strerror(errno) ); 590 spoolerror( out, NULL ); 591 return( -1 ); 592 } 593 LOG(log_debug, logtype_papd, "lp_open: opened %s", pipe_cmd ); 594 } else { 595 sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname ); 596 597 if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) { 598 LOG(log_error, logtype_papd, "lp_open %s: %s", name, strerror(errno) ); 599 spoolerror( out, NULL ); 600 return( -1 ); 601 } 602 603 if ( NULL == (lp.lp_spoolfile = (char *) malloc (strlen (name) +1)) ) { 604 LOG(log_error, logtype_papd, "malloc: %s", strerror(errno)); 605 exit(1); 606 } 607 strcpy ( lp.lp_spoolfile, name); 608 609 if (lp.lp_person != NULL) { 610 if ((pwent = getpwnam(lp.lp_person)) == NULL) { 611 LOG(log_error, logtype_papd, "getpwnam %s: no such user", lp.lp_person); 612 spoolerror( out, NULL ); 613 return( -1 ); 614 } 615 } else { 616 if ((pwent = getpwnam(printer->p_operator)) == NULL) { 617 LOG(log_error, logtype_papd, "getpwnam %s: no such user", printer->p_operator); 618 spoolerror( out, NULL ); 619 return( -1 ); 620 } 621 } 622 623 if (fchown(fd, pwent->pw_uid, -1) < 0) { 624 LOG(log_error, logtype_papd, "chown %s %s: %s", pwent->pw_name, name, strerror(errno)); 625 spoolerror( out, NULL ); 626 return( -1 ); 627 } 628 629 if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) { 630 LOG(log_error, logtype_papd, "lp_open fdopen: %s", strerror(errno) ); 631 spoolerror( out, NULL ); 632 return( -1 ); 633 } 634#ifdef DEBUG 635 LOG(log_debug9, logtype_papd, "lp_open: opened %s", name ); 636#endif 637 } 638 lp.lp_flags |= LP_OPEN; 639 return( 0 ); 640} 641 642int lp_close(void) 643{ 644 if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) { 645 return 0; 646 } 647 fclose( lp.lp_stream ); 648 lp.lp_stream = NULL; 649 lp.lp_flags &= ~LP_OPEN; 650 lp.lp_flags |= LP_JOBPENDING; 651 return 0; 652} 653 654 655 656int lp_write(struct papfile *in, char *buf, size_t len) 657{ 658#define BUFSIZE 32768 659 static char tempbuf[BUFSIZE]; 660 static char tempbuf2[BUFSIZE]; 661 static size_t bufpos = 0; 662 static int last_line_translated = 1; /* if 0, append a \n a the start */ 663 char *tbuf = buf; 664 665 /* Before we write out anything check for a pending job, e.g. cover page */ 666 if (lp.lp_flags & LP_JOBPENDING) 667 lp_print(); 668 669 /* foomatic doesn't handle mac line endings, so we convert them for 670 * the Postscript headers 671 * REALLY ugly hack, remove ASAP again */ 672 if ((printer->p_flags & P_FOOMATIC_HACK) && (in->pf_state & PF_TRANSLATE) && 673 (buf[len-1] != '\n') ) { 674 if (len <= BUFSIZE) { 675 if (!last_line_translated) { 676 tempbuf2[0] = '\n'; 677 memcpy(tempbuf2+1, buf, len++); 678 } 679 else 680 memcpy(tempbuf2, buf, len); 681 682 if (tempbuf2[len-1] == '\r') 683 tempbuf2[len-1] = '\n'; 684 tempbuf2[len] = 0; 685 tbuf = tempbuf2; 686 last_line_translated = 1; 687#ifdef DEBUG 688 LOG(log_debug9, logtype_papd, "lp_write: %s", tbuf ); 689#endif 690 } 691 else { 692 LOG(log_error, logtype_papd, "lp_write: conversion buffer too small" ); 693 abort(); 694 } 695 } 696 else { 697 if (printer->p_flags & P_FOOMATIC_HACK && buf[len-1] == '\n') { 698 last_line_translated = 1; 699 } 700 else 701 last_line_translated = 0; 702 } 703 704 /* To be able to do commandline substitutions on piped printers 705 * we store the start of the print job in a buffer. 706 * %%EndComment triggers writing to file */ 707 if (( lp.lp_flags & LP_OPEN ) == 0 ) { 708#ifdef DEBUG 709 LOG(log_debug9, logtype_papd, "lp_write: writing to temporary buffer" ); 710#endif 711 if ((bufpos+len) > BUFSIZE) { 712 LOG(log_error, logtype_papd, "lp_write: temporary buffer too small" ); 713 /* FIXME: call lp_open here? abort isn't nice... */ 714 abort(); 715 } 716 else { 717 memcpy(tempbuf + bufpos, tbuf, len); 718 bufpos += len; 719 if (bufpos > BUFSIZE/2) 720 in->pf_state |= PF_STW; /* we used half of the buffer, start writing */ 721 return(0); 722 } 723 } 724 else if ( bufpos) { 725 if ( fwrite( tempbuf, 1, bufpos, lp.lp_stream ) != bufpos ) { 726 LOG(log_error, logtype_papd, "lp_write: %s", strerror(errno) ); 727 abort(); 728 } 729 bufpos=0; 730 } 731 732 if ( fwrite( tbuf, 1, len, lp.lp_stream ) != len ) { 733 LOG(log_error, logtype_papd, "lp_write: %s", strerror(errno) ); 734 abort(); 735 } 736 return( 0 ); 737} 738 739int lp_cancel(void) 740{ 741 char name[ MAXPATHLEN ]; 742 char letter; 743 744 if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) { 745 return 0; 746 } 747 748 if ( lp.lp_flags & LP_OPEN ) { 749 lp_close(); 750 } 751 752 for ( letter = 'A'; letter < lp.lp_letter; letter++ ) { 753 sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname ); 754 if ( unlink( name ) < 0 ) { 755 LOG(log_error, logtype_papd, "lp_cancel unlink %s: %s", name, strerror(errno) ); 756 } 757 } 758 759 return 0; 760} 761 762/* 763 * Create printcap control file, signal printer. Errors here should 764 * remove queue files. 765 * 766 * XXX piped? 767 */ 768int lp_print(void) 769{ 770#ifndef HAVE_CUPS 771 char buf[ MAXPATHLEN ]; 772 char tfname[ MAXPATHLEN ]; 773 char cfname[ MAXPATHLEN ]; 774 char letter; 775 int fd, n, s; 776 FILE *cfile; 777#endif /* HAVE_CUPS */ 778 779 if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) { 780 return 0; 781 } 782 lp_close(); 783 lp.lp_flags &= ~LP_JOBPENDING; 784 785 if ( printer->p_flags & P_SPOOLED ) { 786#ifndef HAVE_CUPS 787 sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname ); 788 if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) { 789 LOG(log_error, logtype_papd, "lp_print %s: %s", tfname, strerror(errno) ); 790 return 0; 791 } 792 if (( cfile = fdopen( fd, "w" )) == NULL ) { 793 LOG(log_error, logtype_papd, "lp_print %s: %s", tfname, strerror(errno) ); 794 return 0; 795 } 796 fprintf( cfile, "H%s\n", hostname ); /* XXX lp_host? */ 797 798 if ( lp.lp_person ) { 799 fprintf( cfile, "P%s\n", lp.lp_person ); 800 } else { 801 fprintf( cfile, "P%s\n", printer->p_operator ); 802 } 803 804 if ( lp.lp_job && *lp.lp_job ) { 805 fprintf( cfile, "J%s\n", lp.lp_job ); 806 fprintf( cfile, "T%s\n", lp.lp_job ); 807 } else { 808 fprintf( cfile, "JMac Job\n" ); 809 fprintf( cfile, "TMac Job\n" ); 810 } 811 812 fprintf( cfile, "C%s\n", hostname ); /* XXX lp_host? */ 813 814 if ( lp.lp_person ) { 815 fprintf( cfile, "L%s\n", lp.lp_person ); 816 } else { 817 fprintf( cfile, "L%s\n", printer->p_operator ); 818 } 819 820 for ( letter = 'A'; letter < lp.lp_letter; letter++ ) { 821 fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname ); 822 fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname ); 823 } 824 825 if ( lp.lp_job && *lp.lp_job ) { 826 fprintf( cfile, "N%s\n", lp.lp_job ); 827 } else { 828 fprintf( cfile, "NMac Job\n" ); 829 } 830 fclose( cfile ); 831 832 sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname ); 833 if ( link( tfname, cfname ) < 0 ) { 834 LOG(log_error, logtype_papd, "lp_print can't link %s to %s: %s", cfname, 835 tfname, strerror(errno) ); 836 return 0; 837 } 838 unlink( tfname ); 839 840 if (( s = lp_conn_unix()) < 0 ) { 841 LOG(log_error, logtype_papd, "lp_print: lp_conn_unix: %s", strerror(errno) ); 842 return 0; 843 } 844 845 sprintf( buf, "\1%s\n", printer->p_printer ); 846 n = strlen( buf ); 847 if ( write( s, buf, n ) != n ) { 848 LOG(log_error, logtype_papd, "lp_print write: %s" , strerror(errno)); 849 return 0; 850 } 851 if ( read( s, buf, 1 ) != 1 ) { 852 LOG(log_error, logtype_papd, "lp_print read: %s" , strerror(errno)); 853 return 0; 854 } 855 856 lp_disconn_unix( s ); 857 858 if ( buf[ 0 ] != '\0' ) { 859 LOG(log_error, logtype_papd, "lp_print lpd said %c: %s", buf[ 0 ], strerror(errno) ); 860 return 0; 861 } 862#else 863 if ( ! (lp.lp_job && *lp.lp_job) ) { 864 lp.lp_job = strdup("Mac Job"); 865 } 866 867 lp_setup_comments(add_charset(cups_get_language ())); 868 869 if (lp.lp_person != NULL) { 870 cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, lp.lp_person, printer->p_cupsoptions); 871 } else if (lp.lp_created_for != NULL) { 872 cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, lp.lp_created_for, printer->p_cupsoptions); 873 } else { 874 cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, printer->p_operator, printer->p_cupsoptions); 875 } 876 877 /*LOG(log_info, logtype_papd, "lp_print unlink %s", lp.lp_spoolfile );*/ 878 unlink ( lp.lp_spoolfile ); 879 return 0; 880#endif /* HAVE_CUPS*/ 881 } 882 LOG(log_info, logtype_papd, "lp_print queued" ); 883 return 0; 884} 885 886#ifndef HAVE_CUPS 887int lp_disconn_unix( int fd ) 888{ 889 return( close( fd )); 890} 891 892int lp_conn_unix(void) 893{ 894 int s; 895 struct sockaddr_un saun; 896 897 if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) { 898 LOG(log_error, logtype_papd, "lp_conn_unix socket: %s", strerror(errno) ); 899 return( -1 ); 900 } 901 memset( &saun, 0, sizeof( struct sockaddr_un )); 902 saun.sun_family = AF_UNIX; 903 strcpy( saun.sun_path, _PATH_DEVPRINTER ); 904 if ( connect( s, (struct sockaddr *)&saun, 905 strlen( saun.sun_path ) + 2 ) < 0 ) { 906 LOG(log_error, logtype_papd, "lp_conn_unix connect %s: %s", saun.sun_path, strerror(errno) ); 907 close( s ); 908 return( -1 ); 909 } 910 911 return( s ); 912} 913 914int lp_disconn_inet( int fd ) 915{ 916 return( close( fd )); 917} 918 919int lp_conn_inet(void) 920{ 921 int privfd, port = IPPORT_RESERVED - 1; 922 struct sockaddr_in sin; 923 struct servent *sp; 924 struct hostent *hp; 925 926 if (( sp = getservbyname( "printer", "tcp" )) == NULL ) { 927 LOG(log_error, logtype_papd, "printer/tcp: unknown service" ); 928 return( -1 ); 929 } 930 931 if ( gethostname( hostname, sizeof( hostname )) < 0 ) { 932 LOG(log_error, logtype_papd, "gethostname: %s", strerror(errno) ); 933 exit( 1 ); 934 } 935 936 if (( hp = gethostbyname( hostname )) == NULL ) { 937 LOG(log_error, logtype_papd, "%s: unknown host", hostname ); 938 return( -1 ); 939 } 940 941 if (( privfd = rresvport( &port )) < 0 ) { 942 LOG(log_error, logtype_papd, "lp_connect: socket: %s", strerror(errno) ); 943 close( privfd ); 944 return( -1 ); 945 } 946 947 memset( &sin, 0, sizeof( struct sockaddr_in )); 948 sin.sin_family = AF_INET; 949/* sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */ 950 memcpy( &sin.sin_addr, hp->h_addr, hp->h_length ); 951 sin.sin_port = sp->s_port; 952 953 if ( connect( privfd, (struct sockaddr *)&sin, 954 sizeof( struct sockaddr_in )) < 0 ) { 955 LOG(log_error, logtype_papd, "lp_connect: %s", strerror(errno) ); 956 close( privfd ); 957 return( -1 ); 958 } 959 960 return( privfd ); 961} 962 963int lp_rmjob( int job) 964{ 965 char buf[ 1024 ]; 966 int n, s; 967 968 if (( s = lp_conn_inet()) < 0 ) { 969 LOG(log_error, logtype_papd, "lp_rmjob: %s", strerror(errno) ); 970 return( -1 ); 971 } 972 973 if ( lp.lp_person == NULL ) { 974 return( -1 ); 975 } 976 977 sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job ); 978 n = strlen( buf ); 979 if ( write( s, buf, n ) != n ) { 980 LOG(log_error, logtype_papd, "lp_rmjob write: %s", strerror(errno) ); 981 lp_disconn_inet( s ); 982 return( -1 ); 983 } 984 while (( n = read( s, buf, sizeof( buf ))) > 0 ) { 985 LOG(log_debug, logtype_papd, "read %.*s", n, buf ); 986 } 987 988 lp_disconn_inet( s ); 989 return( 0 ); 990} 991 992char *kw_rank = "Rank"; 993char *kw_active = "active"; 994 995char *tag_rank = "rank: "; 996char *tag_owner = "owner: "; 997char *tag_job = "job: "; 998char *tag_files = "files: "; 999char *tag_size = "size: "; 1000char *tag_status = "status: "; 1001 1002int lp_queue( struct papfile *out) 1003{ 1004 char buf[ 1024 ], *start, *stop, *p, *q; 1005 int linelength, crlflength; 1006 static struct papfile pf; 1007 int s; 1008 size_t len; 1009 ssize_t n; 1010 1011 if (( s = lp_conn_unix()) < 0 ) { 1012 LOG(log_error, logtype_papd, "lp_queue: %s", strerror(errno) ); 1013 return( -1 ); 1014 } 1015 1016 sprintf( buf, "\3%s\n", printer->p_printer ); 1017 n = strlen( buf ); 1018 if ( write( s, buf, n ) != n ) { 1019 LOG(log_error, logtype_papd, "lp_queue write: %s", strerror(errno) ); 1020 lp_disconn_unix( s ); 1021 return( -1 ); 1022 } 1023 pf.pf_state = PF_BOT; 1024 1025 while (( n = read( s, buf, sizeof( buf ))) > 0 ) { 1026 append( &pf, buf, n ); 1027 } 1028 1029 for (;;) { 1030 if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) { 1031 /* parse */ 1032 stop = start + linelength; 1033 for ( p = start; p < stop; p++ ) { 1034 if ( *p == ' ' || *p == '\t' ) { 1035 break; 1036 } 1037 } 1038 if ( p >= stop ) { 1039 CONSUME( &pf , linelength + crlflength); 1040 continue; 1041 } 1042 1043 /* 1044 * Keys: "Rank", a number, "active" 1045 * Anything else is status. 1046 */ 1047 len = p - start; 1048 if ( len == strlen( kw_rank ) && 1049 strncmp( kw_rank, start, len ) == 0 ) { 1050 CONSUME( &pf, linelength + crlflength ); 1051 continue; 1052 } 1053 if (( len == strlen( kw_active ) && 1054 strncmp( kw_active, start, len ) == 0 ) || 1055 isdigit( *start )) { /* a job line */ 1056 append( out, tag_rank, strlen( tag_rank )); 1057 append( out, start, p - start ); 1058 append( out, "\n", 1 ); 1059 1060 for ( ; p < stop; p++ ) { 1061 if ( *p != ' ' && *p != '\t' ) { 1062 break; 1063 } 1064 } 1065 for ( q = p; p < stop; p++ ) { 1066 if ( *p == ' ' || *p == '\t' ) { 1067 break; 1068 } 1069 } 1070 if ( p >= stop ) { 1071 append( out, ".\n", 2 ); 1072 CONSUME( &pf, linelength + crlflength ); 1073 continue; 1074 } 1075 append( out, tag_owner, strlen( tag_owner )); 1076 append( out, q, p - q ); 1077 append( out, "\n", 1 ); 1078 1079 for ( ; p < stop; p++ ) { 1080 if ( *p != ' ' && *p != '\t' ) { 1081 break; 1082 } 1083 } 1084 for ( q = p; p < stop; p++ ) { 1085 if ( *p == ' ' || *p == '\t' ) { 1086 break; 1087 } 1088 } 1089 if ( p >= stop ) { 1090 append( out, ".\n", 2 ); 1091 CONSUME( &pf , linelength + crlflength ); 1092 continue; 1093 } 1094 append( out, tag_job, strlen( tag_job )); 1095 append( out, q, p - q ); 1096 append( out, "\n", 1 ); 1097 1098 for ( ; p < stop; p++ ) { 1099 if ( *p != ' ' && *p != '\t' ) { 1100 break; 1101 } 1102 } 1103 for ( q = p, p = stop; p > q; p-- ) { 1104 if ( *p == ' ' || *p == '\t' ) { 1105 break; 1106 } 1107 } 1108 for ( ; p > q; p-- ) { 1109 if ( *p != ' ' && *p != '\t' ) { 1110 break; 1111 } 1112 } 1113 for ( ; p > q; p-- ) { 1114 if ( *p == ' ' || *p == '\t' ) { 1115 break; 1116 } 1117 } 1118 if ( p <= q ) { 1119 append( out, ".\n", 2 ); 1120 CONSUME( &pf, linelength + crlflength ); 1121 continue; 1122 } 1123 append( out, tag_files, strlen( tag_files )); 1124 append( out, q, p - q ); 1125 append( out, "\n", 1 ); 1126 1127 for ( ; p < stop; p++ ) { 1128 if ( *p != ' ' && *p != '\t' ) { 1129 break; 1130 } 1131 } 1132 append( out, tag_size, strlen( tag_size )); 1133 append( out, p, stop - p ); 1134 append( out, "\n.\n", 3 ); 1135 1136 CONSUME( &pf, linelength + crlflength ); 1137 continue; 1138 } 1139 1140 /* status */ 1141 append( out, tag_status, strlen( tag_status )); 1142 append( out, start, linelength ); 1143 append( out, "\n.\n", 3 ); 1144 1145 CONSUME( &pf, linelength + crlflength ); 1146 } else { 1147 append( out, "*\n", 2 ); 1148 lp_disconn_unix( s ); 1149 return( 0 ); 1150 } 1151 } 1152} 1153#endif /* HAVE_CUPS */ 1154