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