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