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