]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
033571b3733b94204092d164c8dc096c7566c77c
[netatalk.git] / etc / papd / lp.c
1 /*
2  * $Id: lp.c,v 1.30 2009-10-16 01:10:59 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 */
90 int lp_conn_inet();
91 int lp_disconn_inet( int );
92 int lp_conn_unix();
93 int lp_disconn_unix( int );
94
95 static char hostname[ MAXHOSTNAMELEN ];
96
97 extern struct sockaddr_at *sat;
98
99 static 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 #if 0
212 /* removed, it's not used and a pain to get it right from a security POV */
213 static size_t quote(char *dest, char *src, const size_t bsize, size_t len)
214 {
215 size_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
239 static 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
308 void 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
321 int 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
338 void 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
356 void 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_debug, logtype_papd, "job: %s", lp.lp_job );
365 #endif
366     
367 }
368
369 void 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
379 static 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
542 int 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_debug, 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_debug, logtype_papd, "lp_open: opened %s", name );
636 #endif  
637     }
638     lp.lp_flags |= LP_OPEN;
639     return( 0 );
640 }
641
642 int 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
656 int 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_debug, 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_debug, 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
739 int 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  */
768 int 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
887 int lp_disconn_unix( int fd )
888 {
889     return( close( fd ));
890 }
891
892 int 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
914 int lp_disconn_inet( int fd )
915 {
916     return( close( fd ));
917 }
918
919 int 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
963 int 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
992 char    *kw_rank = "Rank";
993 char    *kw_active = "active";
994
995 char    *tag_rank = "rank: ";
996 char    *tag_owner = "owner: ";
997 char    *tag_job = "job: ";
998 char    *tag_files = "files: ";
999 char    *tag_size = "size: ";
1000 char    *tag_status = "status: ";
1001
1002 int 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 */