]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
Expand list of shell metacharacters to escape in variable substitution. Courtesty...
[netatalk.git] / etc / papd / lp.c
1 /*
2  * $Id: lp.c,v 1.22 2009-01-28 18:03:15 morgana 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 */
90 int lp_conn_inet();
91 int lp_disconn_inet( int );
92 int lp_conn_unix();
93 int lp_disconn_unix( int );
94
95 char    hostname[ MAXHOSTNAMELEN ];
96
97 extern struct sockaddr_at *sat;
98
99 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
118 void lp_origin (int origin)
119 {
120     lp.lp_origin = origin;
121 }
122
123 /* the converted string should always be shorter, but ... FIXME! */
124 static 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=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
160 static 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, strlen(*option), &translated)) ) {
168                  free (*option);
169                  *option = translated;
170              }
171         }
172     }
173 }
174
175
176 static 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_debug, 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_debug, 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_debug, 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 static size_t quote(char *dest, char *src, const size_t bsize, size_t len)
212 {
213 size_t used = 0;
214
215     while (len && used < bsize ) {
216         switch (*src) {
217           case '$':
218           case '\\':
219           case '"':
220           case ';':
221           case '&':
222           case '(':
223           case ')':
224           case ' ':
225           case '*':
226           case '#':
227           case '|':
228           case '>':
229           case '<':
230           case '[':
231           case ']':
232           case '{':
233           case '}':
234           case '^':
235           case '?':
236           case '~':
237           case '`':
238           case '\x0A':
239           case '\xFF':
240             if (used + 2 > bsize )
241               return used;
242             *dest = '\\';
243             dest++;
244             used++;
245             break;
246         }
247         *dest = *src;
248         src++;
249         dest++;
250         len--;
251         used++;
252     }
253     return used;
254 }
255
256
257 static char* pipexlate(char *src)
258 {
259     char *p, *q, *dest; 
260     static char destbuf[MAXPATHLEN +1];
261     size_t destlen = MAXPATHLEN;
262     int len = 0;
263    
264     dest = destbuf; 
265
266     if (!src)
267         return NULL;
268
269     memset(dest, 0, sizeof(destbuf));
270     if ((p = strchr(src, '%')) == NULL) { /* nothing to do */
271         strncpy(dest, src, sizeof(dest) - 1);
272         return destbuf;
273     }
274     /* first part of the path. copy and forward to the next variable. */
275     len = MIN((size_t)(p - src), destlen);
276     if (len > 0) {
277         strncpy(dest, src, len);
278         destlen -= len;
279         dest += len;
280     }
281
282     while (p && destlen > 0) {
283         /* now figure out what the variable is */
284         q = NULL;
285         if (is_var(p, "%U")) {
286             q = lp.lp_person;
287         } else if (is_var(p, "%C") || is_var(p, "%J") ) {
288             q = lp.lp_job;
289         } else if (is_var(p, "%F")) {
290             q =  lp.lp_created_for;
291         } else if (is_var(p, "%%")) {
292             q = "%";
293         } 
294
295         /* copy the stuff over. if we don't understand something that we
296          * should, just skip it over. */
297         if (q) {
298             len = MIN(strlen(q), destlen);
299             len = quote(dest, q, destlen, len);
300         }
301         else {
302             len = MIN(2, destlen);
303             strncpy(dest, q, len);
304         }
305         dest += len;
306         destlen -= len;
307
308         /* stuff up to next % */
309         src = p + 2;
310         p = strchr(src, '%');
311         len = p ? MIN((size_t)(p - src), destlen) : destlen;
312         if (len > 0) {
313             strncpy(dest, src, len);
314             dest += len;
315             destlen -= len;
316         }
317     }
318     return destbuf;
319 }
320
321
322 void lp_person( person )
323     char        *person;
324 {
325     if ( lp.lp_person != NULL ) {
326         free( lp.lp_person );
327     }
328     if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
329         LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
330         exit( 1 );
331     }
332     strcpy( lp.lp_person, person );
333 }
334
335 #ifdef ABS_PRINT
336 int lp_pagecost()
337 {
338     char        cost[ 22 ];
339     char        balance[ 22 ];
340     int         err;
341
342     if ( lp.lp_person == NULL ) {
343         return( -1 );
344     }
345     err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
346             cost, balance );
347     printer->p_pagecost = floor( atof( cost ) * 10000.0 );
348     printer->p_balance = atof( balance ) + atof( cost );
349     return( err < 0 ? -1 : 0 );
350 }
351 #endif /* ABS_PRINT */
352
353 void lp_host( host )
354     char        *host;
355 {
356     if ( lp.lp_host != NULL ) {
357         free( lp.lp_host );
358     }
359     if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
360         LOG(log_error, logtype_papd, "malloc: %s", strerror(errno) );
361         exit( 1 );
362     }
363     strcpy( lp.lp_host, host );
364     LOG(log_debug, logtype_papd, "host: %s", lp.lp_host );
365 }
366
367 /* Currently lp_job and lp_for will not handle the
368  * conversion of macroman chars > 0x7f correctly
369  * This should be added.
370  */
371
372 void lp_job( job )
373     char        *job;
374 {
375     if ( lp.lp_job != NULL ) {
376         free( lp.lp_job );
377     }
378
379     lp.lp_job = strdup(job);
380 #ifdef DEBUG
381     LOG(log_debug, logtype_papd, "job: %s", lp.lp_job );
382 #endif
383     
384 }
385
386 void lp_for ( lpfor )
387         char    *lpfor;
388 {
389     if ( lp.lp_created_for != NULL ) {
390         free( lp.lp_created_for );
391     }
392
393     lp.lp_created_for = strdup(lpfor);
394 }
395
396
397 int lp_init( out, sat )
398     struct papfile      *out;
399     struct sockaddr_at  *sat;
400 {
401     int         authenticated = 0;
402 #ifndef HAVE_CUPS
403     int         fd, n, len;
404     char        *cp, buf[ BUFSIZ ];
405     struct stat st;
406 #endif /* HAVE_CUPS */
407 #ifdef ABS_PRINT
408     char        cost[ 22 ];
409     char        balance[ 22 ];
410 #endif /* ABS_PRINT */
411
412     if ( printer->p_flags & P_AUTH ) {
413         authenticated = 0;
414
415         /* cap style "log on to afp server before printing" authentication */
416
417         if ( printer->p_authprintdir && (printer->p_flags & P_AUTH_CAP) ) {
418             int addr_net = ntohs( sat->sat_addr.s_net );
419             int addr_node  = sat->sat_addr.s_node;
420             char addr_filename[256];
421             char auth_string[256];
422             char *username, *afpdpid;
423             struct stat cap_st;
424             FILE *cap_file;
425
426             memset( auth_string, 0, 256 );
427             sprintf(addr_filename, "%s/net%d.%dnode%d", 
428                 printer->p_authprintdir, addr_net/256, addr_net%256, 
429                 addr_node);
430             if (stat(addr_filename, &cap_st) == 0) {
431                 if ((cap_file = fopen(addr_filename, "r")) != NULL) {
432                     if (fgets(auth_string, 256, cap_file) != NULL) {
433                         username = auth_string;
434                         if ((afpdpid = strrchr( auth_string, ':' )) != NULL) {
435                             *afpdpid = '\0';
436                             afpdpid++;
437                         }
438                         if (getpwnam(username) != NULL ) {
439                             LOG(log_info, logtype_papd, "CAP authenticated %s", username);
440                             lp_person(username);
441                             authenticated = 1;
442                         } else {
443                             LOG(log_info, logtype_papd, "CAP error: invalid username: '%s'", username);
444                         }
445                     } else {
446                         LOG(log_info, logtype_papd, "CAP error: could not read username");
447                     }
448                 } else {
449                     LOG(log_info, logtype_papd, "CAP error: %s", strerror(errno));
450                 }
451             } else {
452                 LOG(log_info, logtype_papd, "CAP error: %s", strerror(errno));
453             }
454         }
455
456         if ( printer->p_flags & P_AUTH_PSSP ) {
457             if ( lp.lp_person != NULL ) {
458                 authenticated = 1;
459             }
460         }
461
462         if ( authenticated == 0 ) {
463             LOG(log_error, logtype_papd, "lp_init: must authenticate" );
464             spoolerror( out, "Authentication required." );
465             return( -1 );
466         }
467
468 #ifdef ABS_PRINT
469         if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
470                 ! ABS_canprint( lp.lp_person, printer->p_role,
471                 printer->p_srvid, cost, balance )) {
472             LOG(log_error, logtype_papd, "lp_init: no ABS funds" );
473             spoolerror( out, "No ABS funds available." );
474             return( -1 );
475         }
476 #endif /* ABS_PRINT */
477     }
478
479     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
480         LOG(log_error, logtype_papd, "gethostname: %s", strerror(errno) );
481         exit( 1 );
482     }
483
484     if ( lp.lp_flags & LP_INIT ) {
485         LOG(log_error, logtype_papd, "lp_init: already inited, die!" );
486         abort();
487     }
488
489     lp.lp_flags = 0;
490     lp.lp_stream = NULL;
491     lp.lp_letter = 'A';
492
493     if ( printer->p_flags & P_SPOOLED ) {
494
495 #ifndef HAVE_CUPS
496         /* check if queuing is enabled: mode & 010 on lock file */
497         if ( stat( printer->p_lock, &st ) < 0 ) {
498             LOG(log_error, logtype_papd, "lp_init: %s: %s", printer->p_lock, strerror(errno) );
499             spoolerror( out, NULL );
500             return( -1 );
501         }
502         if ( st.st_mode & 010 ) {
503             LOG(log_info, logtype_papd, "lp_init: queuing is disabled" );
504             spoolerror( out, "Queuing is disabled." );
505             return( -1 );
506         }
507
508         if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
509             LOG(log_error, logtype_papd, "lp_init: can't create .seq" );
510             spoolerror( out, NULL );
511             return( -1 );
512         }
513
514 #ifndef SOLARIS /* flock is unsupported, I doubt this stuff works anyway with newer solaris so ignore for now */
515         if ( flock( fd, LOCK_EX ) < 0 ) {
516             LOG(log_error, logtype_papd, "lp_init: can't lock .seq" );
517             spoolerror( out, NULL );
518             return( -1 );
519         }
520 #endif
521
522         n = 0;
523         if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
524             LOG(log_error, logtype_papd, "lp_init read: %s", strerror(errno) );
525             spoolerror( out, NULL );
526             return( -1 );
527         }
528         if ( len > 0 ) {
529             for ( cp = buf; len; len--, cp++ ) {
530                 if ( *cp < '0' || *cp > '9' ) {
531                     break;
532                 }
533                 n = n * 10 + ( *cp - '0' );
534             }
535         }
536         lp.lp_seq = n;
537
538         n = ( n + 1 ) % 1000;
539         sprintf( buf, "%03d\n", n );
540         lseek( fd, 0L, 0 );
541         write( fd, buf, strlen( buf ));
542         close( fd );
543 #else
544
545         if (cups_get_printer_status ( printer ) == 0)
546         {
547             spoolerror( out, "Queuing is disabled." );
548             return( -1 );
549         }
550
551         lp.lp_seq = getpid();
552 #endif /* HAVE CUPS */
553     } else {
554         lp.lp_flags |= LP_PIPE;
555         lp.lp_seq = getpid();
556     }
557
558     lp.lp_flags |= LP_INIT;
559     return( 0 );
560 }
561
562 int lp_open( out, sat )
563     struct papfile      *out;
564     struct sockaddr_at  *sat;
565 {
566     char        name[ MAXPATHLEN ];
567     int         fd;
568     struct passwd       *pwent;
569
570 #ifdef DEBUG
571     LOG (log_debug, logtype_papd, "lp_open");
572 #endif
573
574     if ( lp.lp_flags & LP_JOBPENDING ) {
575         lp_print();
576     }
577
578     if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
579         return( -1 );
580     }
581     if ( lp.lp_flags & LP_OPEN ) {
582         /* LOG(log_error, logtype_papd, "lp_open already open" ); */
583         /* abort(); */
584         return (-1);
585     }
586
587     if ( lp.lp_flags & LP_PIPE ) {
588
589         /* go right to program */
590         if (lp.lp_person != NULL) {
591             if((pwent = getpwnam(lp.lp_person)) != NULL) {
592                 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
593                     LOG(log_info, logtype_papd, "setreuid error: %s", strerror(errno));
594                 }
595             } else {
596                 LOG(log_info, logtype_papd, "Error getting username (%s)", lp.lp_person);
597             }
598         }
599
600         lp_setup_comments(CH_UNIX);
601         if (( lp.lp_stream = popen( pipexlate(printer->p_printer), "w" )) == NULL ) {
602             LOG(log_error, logtype_papd, "lp_open popen %s: %s", printer->p_printer, strerror(errno) );
603             spoolerror( out, NULL );
604             return( -1 );
605         }
606         LOG(log_debug, logtype_papd, "lp_open: opened %s",  pipexlate(printer->p_printer) );
607     } else {
608         sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
609
610         if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
611             LOG(log_error, logtype_papd, "lp_open %s: %s", name, strerror(errno) );
612             spoolerror( out, NULL );
613             return( -1 );
614         }
615
616         if ( NULL == (lp.lp_spoolfile = (char *) malloc (strlen (name) +1)) ) {
617             LOG(log_error, logtype_papd, "malloc: %s", strerror(errno));
618             exit(1);
619         }
620         strcpy ( lp.lp_spoolfile, name);        
621
622         if (lp.lp_person != NULL) {
623             if ((pwent = getpwnam(lp.lp_person)) == NULL) {
624                 LOG(log_error, logtype_papd, "getpwnam %s: no such user", lp.lp_person);
625                 spoolerror( out, NULL );
626                 return( -1 );
627             }
628         } else {
629             if ((pwent = getpwnam(printer->p_operator)) == NULL) {
630                 LOG(log_error, logtype_papd, "getpwnam %s: no such user", printer->p_operator);
631                 spoolerror( out, NULL );
632                 return( -1 );
633             }
634         }
635
636         if (fchown(fd, pwent->pw_uid, -1) < 0) {
637             LOG(log_error, logtype_papd, "chown %s %s: %s", pwent->pw_name, name, strerror(errno));
638             spoolerror( out, NULL );
639             return( -1 );
640         }
641
642         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
643             LOG(log_error, logtype_papd, "lp_open fdopen: %s", strerror(errno) );
644             spoolerror( out, NULL );
645             return( -1 );
646         }
647 #ifdef DEBUG        
648         LOG(log_debug, logtype_papd, "lp_open: opened %s", name );
649 #endif  
650     }
651     lp.lp_flags |= LP_OPEN;
652     return( 0 );
653 }
654
655 int lp_close()
656 {
657     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
658         return 0;
659     }
660     fclose( lp.lp_stream );
661     lp.lp_stream = NULL;
662     lp.lp_flags &= ~LP_OPEN;
663     lp.lp_flags |= LP_JOBPENDING;
664     return 0;
665 }
666
667
668
669 int lp_write(in, buf, len )
670     struct papfile *in;
671     char        *buf;
672     size_t      len;
673 {
674 #define BUFSIZE 32768
675     static char tempbuf[BUFSIZE];
676     static char tempbuf2[BUFSIZE];
677     static size_t bufpos = 0;
678     static int last_line_translated = 1; /* if 0, append a \n a the start */
679     char *tbuf = buf;
680
681     /* Before we write out anything check for a pending job, e.g. cover page */
682     if (lp.lp_flags & LP_JOBPENDING)
683         lp_print();
684
685     /* foomatic doesn't handle mac line endings, so we convert them for 
686      * the Postscript headers
687      * REALLY ugly hack, remove ASAP again */
688     if ((printer->p_flags & P_FOOMATIC_HACK) && (in->pf_state & PF_TRANSLATE) && 
689         (buf[len-1] != '\n') ) {
690         if (len <= BUFSIZE) {
691             if (!last_line_translated) {
692                 tempbuf2[0] = '\n';
693                 memcpy(tempbuf2+1, buf, len++);
694             }
695             else
696                 memcpy(tempbuf2, buf, len);
697                 
698             if (tempbuf2[len-1] == '\r')
699                 tempbuf2[len-1] = '\n';
700             tempbuf2[len] = 0;
701             tbuf = tempbuf2;
702             last_line_translated = 1;
703 #ifdef DEBUG
704             LOG(log_debug, logtype_papd, "lp_write: %s", tbuf );
705 #endif
706         }
707         else {
708             LOG(log_error, logtype_papd, "lp_write: conversion buffer too small" );
709             abort();
710         }
711     }
712     else {
713         if (printer->p_flags & P_FOOMATIC_HACK && buf[len-1] == '\n') {
714             last_line_translated = 1;
715         }
716         else
717             last_line_translated = 0;
718     }
719
720     /* To be able to do commandline substitutions on piped printers
721      * we store the start of the print job in a buffer.
722      * %%EndComment triggers writing to file */
723     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
724 #ifdef DEBUG
725         LOG(log_debug, logtype_papd, "lp_write: writing to temporary buffer" );
726 #endif
727         if ((bufpos+len) > BUFSIZE) {
728             LOG(log_error, logtype_papd, "lp_write: temporary buffer too small" );
729             /* FIXME: call lp_open here? abort isn't nice... */
730             abort();
731         }
732         else {
733             memcpy(tempbuf + bufpos, tbuf, len);
734             bufpos += len;
735             if (bufpos > BUFSIZE/2)
736                 in->pf_state |= PF_STW; /* we used half of the buffer, start writing */
737             return(0);
738         }
739     }
740     else if ( bufpos) {
741         if ( fwrite( tempbuf, 1, bufpos, lp.lp_stream ) != bufpos ) {
742             LOG(log_error, logtype_papd, "lp_write: %s", strerror(errno) );
743             abort();
744         }
745         bufpos=0;
746     }
747
748     if ( fwrite( tbuf, 1, len, lp.lp_stream ) != len ) {
749         LOG(log_error, logtype_papd, "lp_write: %s", strerror(errno) );
750         abort();
751     }
752     return( 0 );
753 }
754
755 int lp_cancel()
756 {
757     char        name[ MAXPATHLEN ];
758     char        letter;
759
760     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
761         return 0;
762     }
763
764     if ( lp.lp_flags & LP_OPEN ) {
765         lp_close();
766     }
767
768     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
769         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
770         if ( unlink( name ) < 0 ) {
771             LOG(log_error, logtype_papd, "lp_cancel unlink %s: %s", name, strerror(errno) );
772         }
773     }
774
775     return 0;
776 }
777
778 /*
779  * Create printcap control file, signal printer.  Errors here should
780  * remove queue files.
781  *
782  * XXX piped?
783  */
784 int lp_print()
785 {
786 #ifndef HAVE_CUPS
787     char                buf[ MAXPATHLEN ];
788     char                tfname[ MAXPATHLEN ];
789     char                cfname[ MAXPATHLEN ];
790     char                letter;
791     int                 fd, n, s;
792     FILE                *cfile;
793 #endif /* HAVE_CUPS */
794
795     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
796         return 0;
797     }
798     lp_close();
799     lp.lp_flags &= ~LP_JOBPENDING;
800
801     if ( printer->p_flags & P_SPOOLED ) {
802 #ifndef HAVE_CUPS
803         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
804         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
805             LOG(log_error, logtype_papd, "lp_print %s: %s", tfname, strerror(errno) );
806             return 0;
807         }
808         if (( cfile = fdopen( fd, "w" )) == NULL ) {
809             LOG(log_error, logtype_papd, "lp_print %s: %s", tfname, strerror(errno) );
810             return 0;
811         }
812         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
813
814         if ( lp.lp_person ) {
815             fprintf( cfile, "P%s\n", lp.lp_person );
816         } else {
817             fprintf( cfile, "P%s\n", printer->p_operator );
818         }
819
820         if ( lp.lp_job && *lp.lp_job ) {
821             fprintf( cfile, "J%s\n", lp.lp_job );
822             fprintf( cfile, "T%s\n", lp.lp_job );
823         } else {
824             fprintf( cfile, "JMac Job\n" );
825             fprintf( cfile, "TMac Job\n" );
826         }
827
828         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
829
830         if ( lp.lp_person ) {
831             fprintf( cfile, "L%s\n", lp.lp_person );
832         } else {
833             fprintf( cfile, "L%s\n", printer->p_operator );
834         }
835
836         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
837             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
838             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
839         }
840
841         if ( lp.lp_job && *lp.lp_job ) {
842             fprintf( cfile, "N%s\n", lp.lp_job );
843         } else {
844             fprintf( cfile, "NMac Job\n" );
845         }
846         fclose( cfile );
847
848         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
849         if ( link( tfname, cfname ) < 0 ) {
850             LOG(log_error, logtype_papd, "lp_print can't link %s to %s: %s", cfname,
851                     tfname, strerror(errno) );
852             return 0;
853         }
854         unlink( tfname );
855
856         if (( s = lp_conn_unix()) < 0 ) {
857             LOG(log_error, logtype_papd, "lp_print: lp_conn_unix: %s", strerror(errno) );
858             return 0;
859         }
860
861         sprintf( buf, "\1%s\n", printer->p_printer );
862         n = strlen( buf );
863         if ( write( s, buf, n ) != n ) {
864             LOG(log_error, logtype_papd, "lp_print write: %s" , strerror(errno));
865             return 0;
866         }
867         if ( read( s, buf, 1 ) != 1 ) {
868             LOG(log_error, logtype_papd, "lp_print read: %s" , strerror(errno));
869             return 0;
870         }
871
872         lp_disconn_unix( s );
873
874         if ( buf[ 0 ] != '\0' ) {
875             LOG(log_error, logtype_papd, "lp_print lpd said %c: %s", buf[ 0 ], strerror(errno) );
876             return 0;
877         }
878 #else
879         if ( ! (lp.lp_job && *lp.lp_job) ) {
880             lp.lp_job = strdup("Mac Job");
881         }
882
883         lp_setup_comments(add_charset(cups_get_language ()));
884
885         if (lp.lp_person != NULL) {
886             cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, lp.lp_person, printer->p_cupsoptions);
887         } else if (lp.lp_created_for != NULL) {
888             cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, lp.lp_created_for, printer->p_cupsoptions);
889         } else {
890             cups_print_job ( printer->p_printer, lp.lp_spoolfile, lp.lp_job, printer->p_operator, printer->p_cupsoptions);
891         }
892
893         /*LOG(log_info, logtype_papd, "lp_print unlink %s", lp.lp_spoolfile );*/
894         unlink ( lp.lp_spoolfile );
895         return 0;
896 #endif /* HAVE_CUPS*/
897     }
898     LOG(log_info, logtype_papd, "lp_print queued" );
899     return 0;
900 }
901
902 #ifndef HAVE_CUPS
903 int lp_disconn_unix( int fd )
904 {
905     return( close( fd ));
906 }
907
908 int lp_conn_unix()
909 {
910     int                 s;
911     struct sockaddr_un  saun;
912
913     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
914         LOG(log_error, logtype_papd, "lp_conn_unix socket: %s", strerror(errno) );
915         return( -1 );
916     }
917     memset( &saun, 0, sizeof( struct sockaddr_un ));
918     saun.sun_family = AF_UNIX;
919     strcpy( saun.sun_path, _PATH_DEVPRINTER );
920     if ( connect( s, (struct sockaddr *)&saun,
921             strlen( saun.sun_path ) + 2 ) < 0 ) {
922         LOG(log_error, logtype_papd, "lp_conn_unix connect %s: %s", saun.sun_path, strerror(errno) );
923         close( s );
924         return( -1 );
925     }
926
927     return( s );
928 }
929
930 int lp_disconn_inet( int fd )
931 {
932     return( close( fd ));
933 }
934
935 int lp_conn_inet()
936 {
937     int                 privfd, port = IPPORT_RESERVED - 1;
938     struct sockaddr_in  sin;
939     struct servent      *sp;
940     struct hostent      *hp;
941
942     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
943         LOG(log_error, logtype_papd, "printer/tcp: unknown service" );
944         return( -1 );
945     }
946
947     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
948         LOG(log_error, logtype_papd, "gethostname: %s", strerror(errno) );
949         exit( 1 );
950     }
951
952     if (( hp = gethostbyname( hostname )) == NULL ) {
953         LOG(log_error, logtype_papd, "%s: unknown host", hostname );
954         return( -1 );
955     }
956
957     if (( privfd = rresvport( &port )) < 0 ) {
958         LOG(log_error, logtype_papd, "lp_connect: socket: %s", strerror(errno) );
959         close( privfd );
960         return( -1 );
961     }
962
963     memset( &sin, 0, sizeof( struct sockaddr_in ));
964     sin.sin_family = AF_INET;
965 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
966     memcpy( &sin.sin_addr, hp->h_addr, hp->h_length );
967     sin.sin_port = sp->s_port;
968
969     if ( connect( privfd, (struct sockaddr *)&sin,
970             sizeof( struct sockaddr_in )) < 0 ) {
971         LOG(log_error, logtype_papd, "lp_connect: %s", strerror(errno) );
972         close( privfd );
973         return( -1 );
974     }
975
976     return( privfd );
977 }
978
979 int lp_rmjob( job )
980     int         job;
981 {
982     char        buf[ 1024 ];
983     int         n, s;
984
985     if (( s = lp_conn_inet()) < 0 ) {
986         LOG(log_error, logtype_papd, "lp_rmjob: %s", strerror(errno) );
987         return( -1 );
988     }
989
990     if ( lp.lp_person == NULL ) {
991         return( -1 );
992     }
993
994     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
995     n = strlen( buf );
996     if ( write( s, buf, n ) != n ) {
997         LOG(log_error, logtype_papd, "lp_rmjob write: %s", strerror(errno) );
998         lp_disconn_inet( s );
999         return( -1 );
1000     }
1001     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
1002         LOG(log_debug, logtype_papd, "read %.*s", n, buf );
1003     }
1004
1005     lp_disconn_inet( s );
1006     return( 0 );
1007 }
1008
1009 char    *kw_rank = "Rank";
1010 char    *kw_active = "active";
1011
1012 char    *tag_rank = "rank: ";
1013 char    *tag_owner = "owner: ";
1014 char    *tag_job = "job: ";
1015 char    *tag_files = "files: ";
1016 char    *tag_size = "size: ";
1017 char    *tag_status = "status: ";
1018
1019 int lp_queue( out )
1020     struct papfile      *out;
1021 {
1022     char                        buf[ 1024 ], *start, *stop, *p, *q;
1023     int                         linelength, crlflength;
1024     static struct papfile       pf;
1025     int                         s;
1026     size_t                      len;
1027     ssize_t                     n;
1028         
1029     if (( s = lp_conn_unix()) < 0 ) {
1030         LOG(log_error, logtype_papd, "lp_queue: %s", strerror(errno) );
1031         return( -1 );
1032     }
1033
1034     sprintf( buf, "\3%s\n", printer->p_printer );
1035     n = strlen( buf );
1036     if ( write( s, buf, n ) != n ) {
1037         LOG(log_error, logtype_papd, "lp_queue write: %s", strerror(errno) );
1038         lp_disconn_unix( s );
1039         return( -1 );
1040     }
1041     pf.pf_state = PF_BOT;
1042
1043     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
1044         append( &pf, buf, n );
1045     }
1046
1047     for (;;) {
1048         if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
1049             /* parse */
1050             stop = start + linelength;
1051             for ( p = start; p < stop; p++ ) {
1052                 if ( *p == ' ' || *p == '\t' ) {
1053                     break;
1054                 }
1055             }
1056             if ( p >= stop ) {
1057                 CONSUME( &pf , linelength + crlflength);
1058                 continue;
1059             }
1060
1061             /*
1062              * Keys: "Rank", a number, "active"
1063              * Anything else is status.
1064              */
1065             len = p - start;
1066             if ( len == strlen( kw_rank ) &&
1067                     strncmp( kw_rank, start, len ) == 0 ) {
1068                 CONSUME( &pf, linelength + crlflength );
1069                 continue;
1070             }
1071             if (( len == strlen( kw_active ) &&
1072                     strncmp( kw_active, start, len ) == 0 ) ||
1073                     isdigit( *start )) {                /* a job line */
1074                 append( out, tag_rank, strlen( tag_rank ));
1075                 append( out, start, p - start );
1076                 append( out, "\n", 1 );
1077
1078                 for ( ; p < stop; p++ ) {
1079                     if ( *p != ' ' && *p != '\t' ) {
1080                         break;
1081                     }
1082                 }
1083                 for ( q = p; p < stop; p++ ) {
1084                     if ( *p == ' ' || *p == '\t' ) {
1085                         break;
1086                     }
1087                 }
1088                 if ( p >= stop ) {
1089                     append( out, ".\n", 2 );
1090                     CONSUME( &pf, linelength + crlflength );
1091                     continue;
1092                 }
1093                 append( out, tag_owner, strlen( tag_owner ));
1094                 append( out, q, p - q );
1095                 append( out, "\n", 1 );
1096
1097                 for ( ; p < stop; p++ ) {
1098                     if ( *p != ' ' && *p != '\t' ) {
1099                         break;
1100                     }
1101                 }
1102                 for ( q = p; p < stop; p++ ) {
1103                     if ( *p == ' ' || *p == '\t' ) {
1104                         break;
1105                     }
1106                 }
1107                 if ( p >= stop ) {
1108                     append( out, ".\n", 2 );
1109                     CONSUME( &pf , linelength + crlflength );
1110                     continue;
1111                 }
1112                 append( out, tag_job, strlen( tag_job ));
1113                 append( out, q, p - q );
1114                 append( out, "\n", 1 );
1115
1116                 for ( ; p < stop; p++ ) {
1117                     if ( *p != ' ' && *p != '\t' ) {
1118                         break;
1119                     }
1120                 }
1121                 for ( q = p, p = stop; p > q; p-- ) {
1122                     if ( *p == ' ' || *p == '\t' ) {
1123                         break;
1124                     }
1125                 }
1126                 for ( ; p > q; p-- ) {
1127                     if ( *p != ' ' && *p != '\t' ) {
1128                         break;
1129                     }
1130                 }
1131                 for ( ; p > q; p-- ) {
1132                     if ( *p == ' ' || *p == '\t' ) {
1133                         break;
1134                     }
1135                 }
1136                 if ( p <= q ) {
1137                     append( out, ".\n", 2 );
1138                     CONSUME( &pf, linelength + crlflength );
1139                     continue;
1140                 }
1141                 append( out, tag_files, strlen( tag_files ));
1142                 append( out, q, p - q );
1143                 append( out, "\n", 1 );
1144
1145                 for ( ; p < stop; p++ ) {
1146                     if ( *p != ' ' && *p != '\t' ) {
1147                         break;
1148                     }
1149                 }
1150                 append( out, tag_size, strlen( tag_size ));
1151                 append( out, p, stop - p );
1152                 append( out, "\n.\n", 3 );
1153
1154                 CONSUME( &pf, linelength + crlflength );
1155                 continue;
1156             }
1157
1158             /* status */
1159             append( out, tag_status, strlen( tag_status ));
1160             append( out, start, linelength );
1161             append( out, "\n.\n", 3 );
1162
1163             CONSUME( &pf, linelength + crlflength );
1164         } else {
1165             append( out, "*\n", 2 );
1166             lp_disconn_unix( s );
1167             return( 0 );
1168         }
1169     }
1170 }
1171 #endif /* HAVE_CUPS */