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