]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
Solaris 'compile hell' stuff...
[netatalk.git] / etc / papd / lp.c
1 /*
2  * $Id: lp.c,v 1.14.8.1 2004-02-20 20:53:14 bfernhomberg Exp $
3  *
4  * Copyright (c) 1990,1994 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * Portions:
8  * Copyright (c) 1983 Regents of the University of California.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  */
39
40 /*
41  * Interface to lpr system.
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif /* HAVE_CONFIG_H */
47
48 #include <sys/param.h>
49 #include <atalk/logger.h>
50 #include <sys/time.h>
51 #include <sys/socket.h>
52 #include <sys/stat.h>
53 #include <ctype.h>
54 #ifdef HAVE_UNISTD_H
55 #include <unistd.h>
56 #endif /* HAVE_UNISTD_H */
57
58 #if defined( sun ) && defined( __svr4__ )
59 #include </usr/ucbinclude/sys/file.h>
60 #else /* sun && __svr4__ */
61 #include <sys/file.h>
62 #endif /* sun && __svr4__ */
63 #include <sys/un.h>
64 #include <netinet/in.h>
65 #undef s_net
66 #include <netatalk/at.h>
67 #include <atalk/atp.h>
68 #include <atalk/paths.h>
69
70 #ifdef ABS_PRINT
71 #include <math.h>
72 #endif /* ABS_PRINT */
73
74 #include <stdio.h>
75 #include <stdlib.h>
76 #include <string.h>
77 #include <netdb.h>
78 #ifdef HAVE_FCNTL_H
79 #include <fcntl.h>
80 #endif /* HAVE_FCNTL_H */
81 #include <pwd.h>
82
83 #include "printer.h"
84 #include "file.h"
85 #include "lp.h"
86
87 /* These functions aren't used outside of lp.c */
88 int lp_conn_inet();
89 int lp_disconn_inet( int );
90 int lp_conn_unix();
91 int lp_disconn_unix( int );
92
93 char    hostname[ MAXHOSTNAMELEN ];
94
95 extern struct sockaddr_at *sat;
96
97 struct lp {
98     int                 lp_flags;
99     FILE                *lp_stream;
100     int                 lp_seq;
101     char                lp_letter;
102     char                *lp_person;
103     char                *lp_host;
104     char                *lp_job;
105 } lp;
106 #define LP_INIT         (1<<0)
107 #define LP_OPEN         (1<<1)
108 #define LP_PIPE         (1<<2)
109 #define LP_CONNECT      (1<<3)
110 #define LP_QUEUE        (1<<4)
111
112 void lp_person( person )
113     char        *person;
114 {
115     if ( lp.lp_person != NULL ) {
116         free( lp.lp_person );
117     }
118     if (( lp.lp_person = (char *)malloc( strlen( person ) + 1 )) == NULL ) {
119         LOG(log_error, logtype_papd, "malloc: %m" );
120         exit( 1 );
121     }
122     strcpy( lp.lp_person, person );
123 }
124
125 #ifdef ABS_PRINT
126 int lp_pagecost()
127 {
128     char        cost[ 22 ];
129     char        balance[ 22 ];
130     int         err;
131
132     if ( lp.lp_person == NULL ) {
133         return( -1 );
134     }
135     err = ABS_canprint( lp.lp_person, printer->p_role, printer->p_srvid,
136             cost, balance );
137     printer->p_pagecost = floor( atof( cost ) * 10000.0 );
138     printer->p_balance = atof( balance ) + atof( cost );
139     return( err < 0 ? -1 : 0 );
140 }
141 #endif /* ABS_PRINT */
142
143 void lp_host( host )
144     char        *host;
145 {
146     if ( lp.lp_host != NULL ) {
147         free( lp.lp_host );
148     }
149     if (( lp.lp_host = (char *)malloc( strlen( host ) + 1 )) == NULL ) {
150         LOG(log_error, logtype_papd, "malloc: %m" );
151         exit( 1 );
152     }
153     strcpy( lp.lp_host, host );
154 }
155
156 void lp_job( job )
157     char        *job;
158 {
159     char        *p, *q;
160
161     if ( lp.lp_job != NULL ) {
162         free( lp.lp_job );
163     }
164     if (( lp.lp_job = (char *)malloc( strlen( job ) + 1 )) == NULL ) {
165         LOG(log_error, logtype_papd, "malloc: %m" );
166         exit( 1 );
167     }
168     for ( p = job, q = lp.lp_job; *p != '\0'; p++, q++ ) {
169         if ( !isascii( *p ) || !isprint( *p ) || *p == '\\' ) {
170             *q = '.';
171         } else {
172             *q = *p;
173         }
174     }
175     *q = '\0';
176 }
177
178 int lp_init( out, sat )
179     struct papfile      *out;
180     struct sockaddr_at  *sat;
181 {
182     int         fd, n, len;
183     char        *cp, buf[ BUFSIZ ];
184     struct stat st;
185     int         authenticated = 0;
186 #ifdef ABS_PRINT
187     char        cost[ 22 ];
188     char        balance[ 22 ];
189 #endif /* ABS_PRINT */
190
191     if ( printer->p_flags & P_AUTH ) {
192         authenticated = 0;
193
194         /* cap style "log on to afp server before printing" authentication */
195
196         if ( printer->p_authprintdir && (printer->p_flags & P_AUTH_CAP) ) {
197             int addr_net = ntohs( sat->sat_addr.s_net );
198             int addr_node  = sat->sat_addr.s_node;
199             char addr_filename[256];
200             char auth_string[256];
201             char *username, *afpdpid;
202             struct stat cap_st;
203             FILE *cap_file;
204
205             memset( auth_string, 0, 256 );
206             sprintf(addr_filename, "%s/net%d.%dnode%d", 
207                 printer->p_authprintdir, addr_net/256, addr_net%256, 
208                 addr_node);
209             if (stat(addr_filename, &cap_st) == 0) {
210                 if ((cap_file = fopen(addr_filename, "r")) != NULL) {
211                     if (fgets(auth_string, 256, cap_file) != NULL) {
212                         username = auth_string;
213                         if ((afpdpid = strrchr( auth_string, ':' )) != NULL) {
214                             *afpdpid = '\0';
215                             afpdpid++;
216                         }
217                         if (getpwnam(username) != NULL ) {
218                             LOG(log_info, logtype_papd, "CAP authenticated %s", username);
219                             lp_person(username);
220                             authenticated = 1;
221                         } else {
222                             LOG(log_info, logtype_papd, "CAP error: invalid username: '%s'", username);
223                         }
224                     } else {
225                         LOG(log_info, logtype_papd, "CAP error: could not read username");
226                     }
227                 } else {
228                     LOG(log_info, logtype_papd, "CAP error: %m");
229                 }
230             } else {
231                 LOG(log_info, logtype_papd, "CAP error: %m");
232             }
233         }
234
235         if ( printer->p_flags & P_AUTH_PSSP ) {
236             if ( lp.lp_person != NULL ) {
237                 authenticated = 1;
238             }
239         }
240
241         if ( authenticated == 0 ) {
242             LOG(log_error, logtype_papd, "lp_init: must authenticate" );
243             spoolerror( out, "Authentication required." );
244             return( -1 );
245         }
246
247 #ifdef ABS_PRINT
248         if (( printer->p_flags & P_ACCOUNT ) && printer->p_pagecost > 0 &&
249                 ! ABS_canprint( lp.lp_person, printer->p_role,
250                 printer->p_srvid, cost, balance )) {
251             LOG(log_error, logtype_papd, "lp_init: no ABS funds" );
252             spoolerror( out, "No ABS funds available." );
253             return( -1 );
254         }
255 #endif /* ABS_PRINT */
256     }
257
258     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
259         LOG(log_error, logtype_papd, "gethostname: %m" );
260         exit( 1 );
261     }
262
263     if ( lp.lp_flags & LP_INIT ) {
264         LOG(log_error, logtype_papd, "lp_init: already inited, die!" );
265         abort();
266     }
267
268     lp.lp_flags = 0;
269     lp.lp_stream = NULL;
270     lp.lp_letter = 'A';
271
272     if ( printer->p_flags & P_SPOOLED ) {
273         /* check if queuing is enabled: mode & 010 on lock file */
274         if ( stat( printer->p_lock, &st ) < 0 ) {
275             LOG(log_error, logtype_papd, "lp_init: %s: %m", printer->p_lock );
276             spoolerror( out, NULL );
277             return( -1 );
278         }
279         if ( st.st_mode & 010 ) {
280             LOG(log_info, logtype_papd, "lp_init: queuing is disabled" );
281             spoolerror( out, "Queuing is disabled." );
282             return( -1 );
283         }
284
285         if (( fd = open( ".seq", O_RDWR|O_CREAT, 0661 )) < 0 ) {
286             LOG(log_error, logtype_papd, "lp_init: can't create .seq" );
287             spoolerror( out, NULL );
288             return( -1 );
289         }
290
291 #ifndef SOLARIS /* flock is unsupported, I doubt this stuff work anyway with newer solaris so ignore for now */
292         if ( flock( fd, LOCK_EX ) < 0 ) {
293             LOG(log_error, logtype_papd, "lp_init: can't lock .seq" );
294             spoolerror( out, NULL );
295             return( -1 );
296         }
297 #endif
298
299         n = 0;
300         if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
301             LOG(log_error, logtype_papd, "lp_init read: %m" );
302             spoolerror( out, NULL );
303             return( -1 );
304         }
305         if ( len > 0 ) {
306             for ( cp = buf; len; len--, cp++ ) {
307                 if ( *cp < '0' || *cp > '9' ) {
308                     break;
309                 }
310                 n = n * 10 + ( *cp - '0' );
311             }
312         }
313         lp.lp_seq = n;
314
315         n = ( n + 1 ) % 1000;
316         sprintf( buf, "%03d\n", n );
317         lseek( fd, 0L, 0 );
318         write( fd, buf, strlen( buf ));
319         close( fd );
320     } else {
321         lp.lp_flags |= LP_PIPE;
322         lp.lp_seq = getpid();
323     }
324
325     lp.lp_flags |= LP_INIT;
326     return( 0 );
327 }
328
329 int lp_open( out, sat )
330     struct papfile      *out;
331     struct sockaddr_at  *sat;
332 {
333     char        name[ MAXPATHLEN ];
334     int         fd;
335     struct passwd       *pwent;
336
337     if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
338         return( -1 );
339     }
340     if ( lp.lp_flags & LP_OPEN ) {
341         LOG(log_error, logtype_papd, "lp_open already open" );
342         abort();
343     }
344
345     if ( lp.lp_flags & LP_PIPE ) {
346         /* go right to program */
347         if (lp.lp_person != NULL) {
348             if((pwent = getpwnam(lp.lp_person)) != NULL) {
349                 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
350                     LOG(log_info, logtype_papd, "setreuid error: %m");
351                 }
352             } else {
353                 LOG(log_info, logtype_papd, "Error getting username (%s)", lp.lp_person);
354             }
355         }
356         if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
357             LOG(log_error, logtype_papd, "lp_open popen %s: %m", printer->p_printer );
358             spoolerror( out, NULL );
359             return( -1 );
360         }
361     } else {
362         sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
363
364         if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
365             LOG(log_error, logtype_papd, "lp_open %s: %m", name );
366             spoolerror( out, NULL );
367             return( -1 );
368         }
369
370         if (lp.lp_person != NULL) {
371             if ((pwent = getpwnam(lp.lp_person)) == NULL) {
372                 LOG(log_error, logtype_papd, "getpwnam %s: no such user", lp.lp_person);
373                 spoolerror( out, NULL );
374                 return( -1 );
375             }
376         } else {
377             if ((pwent = getpwnam(printer->p_operator)) == NULL) {
378                 LOG(log_error, logtype_papd, "getpwnam %s: no such user", printer->p_operator);
379                 spoolerror( out, NULL );
380                 return( -1 );
381             }
382         }
383
384         if (fchown(fd, pwent->pw_uid, -1) < 0) {
385             LOG(log_error, logtype_papd, "chown %s %s: %m", pwent->pw_name, name);
386             spoolerror( out, NULL );
387             return( -1 );
388         }
389
390         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
391             LOG(log_error, logtype_papd, "lp_open fdopen: %m" );
392             spoolerror( out, NULL );
393             return( -1 );
394         }
395     }
396     lp.lp_flags |= LP_OPEN;
397
398     return( 0 );
399 }
400
401 int lp_close()
402 {
403     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
404         return 0;
405     }
406     fclose( lp.lp_stream );
407     lp.lp_stream = NULL;
408     lp.lp_flags &= ~LP_OPEN;
409     return 0;
410 }
411
412 int lp_write( buf, len )
413     char        *buf;
414     int         len;
415 {
416     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
417         return( -1 );
418     }
419
420     if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
421         LOG(log_error, logtype_papd, "lp_write: %m" );
422         abort();
423     }
424     return( 0 );
425 }
426
427 int lp_cancel()
428 {
429     char        name[ MAXPATHLEN ];
430     char        letter;
431
432     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
433         return 0;
434     }
435
436     if ( lp.lp_flags & LP_OPEN ) {
437         lp_close();
438     }
439
440     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
441         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
442         if ( unlink( name ) < 0 ) {
443             LOG(log_error, logtype_papd, "lp_cancel unlink %s: %m", name );
444         }
445     }
446
447     return 0;
448 }
449
450 /*
451  * Create printcap control file, signal printer.  Errors here should
452  * remove queue files.
453  *
454  * XXX piped?
455  */
456 int lp_print()
457 {
458     char                buf[ MAXPATHLEN ];
459     char                tfname[ MAXPATHLEN ];
460     char                cfname[ MAXPATHLEN ];
461     char                letter;
462     int                 fd, n, s;
463     FILE                *cfile;
464
465     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
466         return 0;
467     }
468     lp_close();
469
470     if ( printer->p_flags & P_SPOOLED ) {
471         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
472         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
473             LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
474             return 0;
475         }
476         if (( cfile = fdopen( fd, "w" )) == NULL ) {
477             LOG(log_error, logtype_papd, "lp_print %s: %m", tfname );
478             return 0;
479         }
480         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
481
482         if ( lp.lp_person ) {
483             fprintf( cfile, "P%s\n", lp.lp_person );
484         } else {
485             fprintf( cfile, "P%s\n", printer->p_operator );
486         }
487
488         if ( lp.lp_job && *lp.lp_job ) {
489             fprintf( cfile, "J%s\n", lp.lp_job );
490             fprintf( cfile, "T%s\n", lp.lp_job );
491         } else {
492             fprintf( cfile, "JMac Job\n" );
493             fprintf( cfile, "TMac Job\n" );
494         }
495
496         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
497
498         if ( lp.lp_person ) {
499             fprintf( cfile, "L%s\n", lp.lp_person );
500         } else {
501             fprintf( cfile, "L%s\n", printer->p_operator );
502         }
503
504         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
505             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
506             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
507         }
508
509         if ( lp.lp_job && *lp.lp_job ) {
510             fprintf( cfile, "N%s\n", lp.lp_job );
511         } else {
512             fprintf( cfile, "NMac Job\n" );
513         }
514         fclose( cfile );
515
516         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
517         if ( link( tfname, cfname ) < 0 ) {
518             LOG(log_error, logtype_papd, "lp_print can't link %s to %s: %m", cfname,
519                     tfname );
520             return 0;
521         }
522         unlink( tfname );
523
524         if (( s = lp_conn_unix()) < 0 ) {
525             LOG(log_error, logtype_papd, "lp_print: lp_conn_unix: %m" );
526             return 0;
527         }
528
529         sprintf( buf, "\1%s\n", printer->p_printer );
530         n = strlen( buf );
531         if ( write( s, buf, n ) != n ) {
532             LOG(log_error, logtype_papd, "lp_print write: %m" );
533             return 0;
534         }
535         if ( read( s, buf, 1 ) != 1 ) {
536             LOG(log_error, logtype_papd, "lp_print read: %m" );
537             return 0;
538         }
539
540         lp_disconn_unix( s );
541
542         if ( buf[ 0 ] != '\0' ) {
543             LOG(log_error, logtype_papd, "lp_print lpd said %c: %m", buf[ 0 ] );
544             return 0;
545         }
546     }
547     LOG(log_info, logtype_papd, "lp_print queued" );
548     return 0;
549 }
550
551 int lp_disconn_unix( fd )
552 {
553     return( close( fd ));
554 }
555
556 int lp_conn_unix()
557 {
558     int                 s;
559     struct sockaddr_un  saun;
560
561     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
562         LOG(log_error, logtype_papd, "lp_conn_unix socket: %m" );
563         return( -1 );
564     }
565     memset( &saun, 0, sizeof( struct sockaddr_un ));
566     saun.sun_family = AF_UNIX;
567     strcpy( saun.sun_path, _PATH_DEVPRINTER );
568     if ( connect( s, (struct sockaddr *)&saun,
569             strlen( saun.sun_path ) + 2 ) < 0 ) {
570         LOG(log_error, logtype_papd, "lp_conn_unix connect %s: %m", saun.sun_path );
571         close( s );
572         return( -1 );
573     }
574
575     return( s );
576 }
577
578 int lp_disconn_inet( int fd )
579 {
580     return( close( fd ));
581 }
582
583 int lp_conn_inet()
584 {
585     int                 privfd, port = IPPORT_RESERVED - 1;
586     struct sockaddr_in  sin;
587     struct servent      *sp;
588     struct hostent      *hp;
589
590     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
591         LOG(log_error, logtype_papd, "printer/tcp: unknown service\n" );
592         return( -1 );
593     }
594
595     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
596         LOG(log_error, logtype_papd, "gethostname: %m" );
597         exit( 1 );
598     }
599
600     if (( hp = gethostbyname( hostname )) == NULL ) {
601         LOG(log_error, logtype_papd, "%s: unknown host\n", hostname );
602         return( -1 );
603     }
604
605     if (( privfd = rresvport( &port )) < 0 ) {
606         LOG(log_error, logtype_papd, "lp_connect: socket: %m" );
607         close( privfd );
608         return( -1 );
609     }
610
611     memset( &sin, 0, sizeof( struct sockaddr_in ));
612     sin.sin_family = AF_INET;
613 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
614     memcpy( &sin.sin_addr, hp->h_addr, hp->h_length );
615     sin.sin_port = sp->s_port;
616
617     if ( connect( privfd, (struct sockaddr *)&sin,
618             sizeof( struct sockaddr_in )) < 0 ) {
619         LOG(log_error, logtype_papd, "lp_connect: %m" );
620         close( privfd );
621         return( -1 );
622     }
623
624     return( privfd );
625 }
626
627 int lp_rmjob( job )
628     int         job;
629 {
630     char        buf[ 1024 ];
631     int         n, s;
632
633     if (( s = lp_conn_inet()) < 0 ) {
634         LOG(log_error, logtype_papd, "lp_rmjob: %m" );
635         return( -1 );
636     }
637
638     if ( lp.lp_person == NULL ) {
639         return( -1 );
640     }
641
642     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
643     n = strlen( buf );
644     if ( write( s, buf, n ) != n ) {
645         LOG(log_error, logtype_papd, "lp_rmjob write: %m" );
646         lp_disconn_inet( s );
647         return( -1 );
648     }
649     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
650         LOG(log_debug, logtype_papd, "read %.*s", n, buf );
651     }
652
653     lp_disconn_inet( s );
654     return( 0 );
655 }
656
657 char    *kw_rank = "Rank";
658 char    *kw_active = "active";
659
660 char    *tag_rank = "rank: ";
661 char    *tag_owner = "owner: ";
662 char    *tag_job = "job: ";
663 char    *tag_files = "files: ";
664 char    *tag_size = "size: ";
665 char    *tag_status = "status: ";
666
667 int lp_queue( out )
668     struct papfile      *out;
669 {
670     char                        buf[ 1024 ], *start, *stop, *p, *q;
671     int                         linelength, crlflength;
672     static struct papfile       pf;
673     int                         n, len, s;
674         
675     if (( s = lp_conn_unix()) < 0 ) {
676         LOG(log_error, logtype_papd, "lp_queue: %m" );
677         return( -1 );
678     }
679
680     sprintf( buf, "\3%s\n", printer->p_printer );
681     n = strlen( buf );
682     if ( write( s, buf, n ) != n ) {
683         LOG(log_error, logtype_papd, "lp_queue write: %m" );
684         lp_disconn_unix( s );
685         return( -1 );
686     }
687     pf.pf_state = PF_BOT;
688
689     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
690         append( &pf, buf, n );
691     }
692
693     for (;;) {
694         if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
695             /* parse */
696             stop = start + linelength;
697             for ( p = start; p < stop; p++ ) {
698                 if ( *p == ' ' || *p == '\t' ) {
699                     break;
700                 }
701             }
702             if ( p >= stop ) {
703                 CONSUME( &pf , linelength + crlflength);
704                 continue;
705             }
706
707             /*
708              * Keys: "Rank", a number, "active"
709              * Anything else is status.
710              */
711             len = p - start;
712             if ( len == strlen( kw_rank ) &&
713                     strncmp( kw_rank, start, len ) == 0 ) {
714                 CONSUME( &pf, linelength + crlflength );
715                 continue;
716             }
717             if (( len == strlen( kw_active ) &&
718                     strncmp( kw_active, start, len ) == 0 ) ||
719                     isdigit( *start )) {                /* a job line */
720                 append( out, tag_rank, strlen( tag_rank ));
721                 append( out, start, p - start );
722                 append( out, "\n", 1 );
723
724                 for ( ; p < stop; p++ ) {
725                     if ( *p != ' ' && *p != '\t' ) {
726                         break;
727                     }
728                 }
729                 for ( q = p; p < stop; p++ ) {
730                     if ( *p == ' ' || *p == '\t' ) {
731                         break;
732                     }
733                 }
734                 if ( p >= stop ) {
735                     append( out, ".\n", 2 );
736                     CONSUME( &pf, linelength + crlflength );
737                     continue;
738                 }
739                 append( out, tag_owner, strlen( tag_owner ));
740                 append( out, q, p - q );
741                 append( out, "\n", 1 );
742
743                 for ( ; p < stop; p++ ) {
744                     if ( *p != ' ' && *p != '\t' ) {
745                         break;
746                     }
747                 }
748                 for ( q = p; p < stop; p++ ) {
749                     if ( *p == ' ' || *p == '\t' ) {
750                         break;
751                     }
752                 }
753                 if ( p >= stop ) {
754                     append( out, ".\n", 2 );
755                     CONSUME( &pf , linelength + crlflength );
756                     continue;
757                 }
758                 append( out, tag_job, strlen( tag_job ));
759                 append( out, q, p - q );
760                 append( out, "\n", 1 );
761
762                 for ( ; p < stop; p++ ) {
763                     if ( *p != ' ' && *p != '\t' ) {
764                         break;
765                     }
766                 }
767                 for ( q = p, p = stop; p > q; p-- ) {
768                     if ( *p == ' ' || *p == '\t' ) {
769                         break;
770                     }
771                 }
772                 for ( ; p > q; p-- ) {
773                     if ( *p != ' ' && *p != '\t' ) {
774                         break;
775                     }
776                 }
777                 for ( ; p > q; p-- ) {
778                     if ( *p == ' ' || *p == '\t' ) {
779                         break;
780                     }
781                 }
782                 if ( p <= q ) {
783                     append( out, ".\n", 2 );
784                     CONSUME( &pf, linelength + crlflength );
785                     continue;
786                 }
787                 append( out, tag_files, strlen( tag_files ));
788                 append( out, q, p - q );
789                 append( out, "\n", 1 );
790
791                 for ( ; p < stop; p++ ) {
792                     if ( *p != ' ' && *p != '\t' ) {
793                         break;
794                     }
795                 }
796                 append( out, tag_size, strlen( tag_size ));
797                 append( out, p, stop - p );
798                 append( out, "\n.\n", 3 );
799
800                 CONSUME( &pf, linelength + crlflength );
801                 continue;
802             }
803
804             /* status */
805             append( out, tag_status, strlen( tag_status ));
806             append( out, start, linelength );
807             append( out, "\n.\n", 3 );
808
809             CONSUME( &pf, linelength + crlflength );
810         } else {
811             append( out, "*\n", 2 );
812             lp_disconn_unix( s );
813             return( 0 );
814         }
815     }
816 }