]> arthur.barton.de Git - netatalk.git/blob - etc/papd/lp.c
Fix some issues with CAP-style printer authentication by changing where the
[netatalk.git] / etc / papd / lp.c
1 /*
2  * $Id: lp.c,v 1.13 2002-03-20 20:54:05 morgana 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_default, "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_default, "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_default, "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_default, "CAP authenticated %s", username);
219                             lp_person(username);
220                             authenticated = 1;
221                         } else {
222                             LOG(log_info, logtype_default, "CAP error: invalid username: '%s'", username);
223                         }
224                     } else {
225                         LOG(log_info, logtype_default, "CAP error: could not read username");
226                     }
227                 } else {
228                     LOG(log_info, logtype_default, "CAP error: %m");
229                 }
230             } else {
231                 LOG(log_info, logtype_default, "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_default, "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_default, "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_default, "gethostname: %m" );
260         exit( 1 );
261     }
262
263     if ( lp.lp_flags & LP_INIT ) {
264         LOG(log_error, logtype_default, "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_default, "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_default, "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_default, "lp_init: can't create .seq" );
287             spoolerror( out, NULL );
288             return( -1 );
289         }
290
291         if ( flock( fd, LOCK_EX ) < 0 ) {
292             LOG(log_error, logtype_default, "lp_init: can't lock .seq" );
293             spoolerror( out, NULL );
294             return( -1 );
295         }
296
297         n = 0;
298         if (( len = read( fd, buf, sizeof( buf ))) < 0 ) {
299             LOG(log_error, logtype_default, "lp_init read: %m" );
300             spoolerror( out, NULL );
301             return( -1 );
302         }
303         if ( len > 0 ) {
304             for ( cp = buf; len; len--, cp++ ) {
305                 if ( *cp < '0' || *cp > '9' ) {
306                     break;
307                 }
308                 n = n * 10 + ( *cp - '0' );
309             }
310         }
311         lp.lp_seq = n;
312
313         n = ( n + 1 ) % 1000;
314         sprintf( buf, "%03d\n", n );
315         lseek( fd, 0L, 0 );
316         write( fd, buf, strlen( buf ));
317         close( fd );
318     } else {
319         lp.lp_flags |= LP_PIPE;
320         lp.lp_seq = getpid();
321     }
322
323     lp.lp_flags |= LP_INIT;
324     return( 0 );
325 }
326
327 int lp_open( out, sat )
328     struct papfile      *out;
329     struct sockaddr_at  *sat;
330 {
331     char        name[ MAXPATHLEN ];
332     int         fd;
333     struct passwd       *pwent;
334
335     if (( lp.lp_flags & LP_INIT ) == 0 && lp_init( out, sat ) != 0 ) {
336         return( -1 );
337     }
338     if ( lp.lp_flags & LP_OPEN ) {
339         LOG(log_error, logtype_default, "lp_open already open" );
340         abort();
341     }
342
343     if ( lp.lp_flags & LP_PIPE ) {
344         /* go right to program */
345         if (lp.lp_person != NULL) {
346             if((pwent = getpwnam(lp.lp_person)) != NULL) {
347                 if(setreuid(pwent->pw_uid, pwent->pw_uid) != 0) {
348                     LOG(log_info, logtype_default, "setreuid error: %m");
349                 }
350             } else {
351                 LOG(log_info, logtype_default, "Error getting username (%s)", lp.lp_person);
352             }
353         }
354         if (( lp.lp_stream = popen( printer->p_printer, "w" )) == NULL ) {
355             LOG(log_error, logtype_default, "lp_open popen %s: %m", printer->p_printer );
356             spoolerror( out, NULL );
357             return( -1 );
358         }
359     } else {
360         sprintf( name, "df%c%03d%s", lp.lp_letter++, lp.lp_seq, hostname );
361
362         if (( fd = open( name, O_WRONLY|O_CREAT|O_EXCL, 0660 )) < 0 ) {
363             LOG(log_error, logtype_default, "lp_open %s: %m", name );
364             spoolerror( out, NULL );
365             return( -1 );
366         }
367
368         if (lp.lp_person != NULL) {
369             if ((pwent = getpwnam(lp.lp_person)) == NULL) {
370                 LOG(log_error, logtype_default, "getpwnam %s: no such user", lp.lp_person);
371                 spoolerror( out, NULL );
372                 return( -1 );
373             }
374         } else {
375             if ((pwent = getpwnam(printer->p_operator)) == NULL) {
376                 LOG(log_error, logtype_default, "getpwnam %s: no such user", printer->p_operator);
377                 spoolerror( out, NULL );
378                 return( -1 );
379             }
380         }
381
382         if (fchown(fd, pwent->pw_uid, -1) < 0) {
383             LOG(log_error, logtype_default, "chown %s %s: %m", pwent->pw_name, name);
384             spoolerror( out, NULL );
385             return( -1 );
386         }
387
388         if (( lp.lp_stream = fdopen( fd, "w" )) == NULL ) {
389             LOG(log_error, logtype_default, "lp_open fdopen: %m" );
390             spoolerror( out, NULL );
391             return( -1 );
392         }
393     }
394     lp.lp_flags |= LP_OPEN;
395
396     return( 0 );
397 }
398
399 int lp_close()
400 {
401     if (( lp.lp_flags & LP_INIT ) == 0 || ( lp.lp_flags & LP_OPEN ) == 0 ) {
402         return 0;
403     }
404     fclose( lp.lp_stream );
405     lp.lp_stream = NULL;
406     lp.lp_flags &= ~LP_OPEN;
407     return 0;
408 }
409
410 int lp_write( buf, len )
411     char        *buf;
412     int         len;
413 {
414     if (( lp.lp_flags & LP_OPEN ) == 0 ) {
415         return( -1 );
416     }
417
418     if ( fwrite( buf, 1, len, lp.lp_stream ) != len ) {
419         LOG(log_error, logtype_default, "lp_write: %m" );
420         abort();
421     }
422     return( 0 );
423 }
424
425 int lp_cancel()
426 {
427     char        name[ MAXPATHLEN ];
428     char        letter;
429
430     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
431         return 0;
432     }
433
434     if ( lp.lp_flags & LP_OPEN ) {
435         lp_close();
436     }
437
438     for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
439         sprintf( name, "df%c%03d%s", letter, lp.lp_seq, hostname );
440         if ( unlink( name ) < 0 ) {
441             LOG(log_error, logtype_default, "lp_cancel unlink %s: %m", name );
442         }
443     }
444
445     return 0;
446 }
447
448 /*
449  * Create printcap control file, signal printer.  Errors here should
450  * remove queue files.
451  *
452  * XXX piped?
453  */
454 int lp_print()
455 {
456     char                buf[ MAXPATHLEN ];
457     char                tfname[ MAXPATHLEN ];
458     char                cfname[ MAXPATHLEN ];
459     char                letter;
460     int                 fd, n, s;
461     FILE                *cfile;
462
463     if (( lp.lp_flags & LP_INIT ) == 0 || lp.lp_letter == 'A' ) {
464         return 0;
465     }
466     lp_close();
467
468     if ( printer->p_flags & P_SPOOLED ) {
469         sprintf( tfname, "tfA%03d%s", lp.lp_seq, hostname );
470         if (( fd = open( tfname, O_WRONLY|O_EXCL|O_CREAT, 0660 )) < 0 ) {
471             LOG(log_error, logtype_default, "lp_print %s: %m", tfname );
472             return 0;
473         }
474         if (( cfile = fdopen( fd, "w" )) == NULL ) {
475             LOG(log_error, logtype_default, "lp_print %s: %m", tfname );
476             return 0;
477         }
478         fprintf( cfile, "H%s\n", hostname );    /* XXX lp_host? */
479
480         if ( lp.lp_person ) {
481             fprintf( cfile, "P%s\n", lp.lp_person );
482         } else {
483             fprintf( cfile, "P%s\n", printer->p_operator );
484         }
485
486         if ( lp.lp_job && *lp.lp_job ) {
487             fprintf( cfile, "J%s\n", lp.lp_job );
488             fprintf( cfile, "T%s\n", lp.lp_job );
489         } else {
490             fprintf( cfile, "JMac Job\n" );
491             fprintf( cfile, "TMac Job\n" );
492         }
493
494         fprintf( cfile, "C%s\n", hostname );    /* XXX lp_host? */
495
496         if ( lp.lp_person ) {
497             fprintf( cfile, "L%s\n", lp.lp_person );
498         } else {
499             fprintf( cfile, "L%s\n", printer->p_operator );
500         }
501
502         for ( letter = 'A'; letter < lp.lp_letter; letter++ ) {
503             fprintf( cfile, "fdf%c%03d%s\n", letter, lp.lp_seq, hostname );
504             fprintf( cfile, "Udf%c%03d%s\n", letter, lp.lp_seq, hostname );
505         }
506
507         if ( lp.lp_job && *lp.lp_job ) {
508             fprintf( cfile, "N%s\n", lp.lp_job );
509         } else {
510             fprintf( cfile, "NMac Job\n" );
511         }
512         fclose( cfile );
513
514         sprintf( cfname, "cfA%03d%s", lp.lp_seq, hostname );
515         if ( link( tfname, cfname ) < 0 ) {
516             LOG(log_error, logtype_default, "lp_print can't link %s to %s: %m", cfname,
517                     tfname );
518             return 0;
519         }
520         unlink( tfname );
521
522         if (( s = lp_conn_unix()) < 0 ) {
523             LOG(log_error, logtype_default, "lp_print: lp_conn_unix: %m" );
524             return 0;
525         }
526
527         sprintf( buf, "\1%s\n", printer->p_printer );
528         n = strlen( buf );
529         if ( write( s, buf, n ) != n ) {
530             LOG(log_error, logtype_default, "lp_print write: %m" );
531             return 0;
532         }
533         if ( read( s, buf, 1 ) != 1 ) {
534             LOG(log_error, logtype_default, "lp_print read: %m" );
535             return 0;
536         }
537
538         lp_disconn_unix( s );
539
540         if ( buf[ 0 ] != '\0' ) {
541             LOG(log_error, logtype_default, "lp_print lpd said %c: %m", buf[ 0 ] );
542             return 0;
543         }
544     }
545     LOG(log_info, logtype_default, "lp_print queued" );
546     return 0;
547 }
548
549 int lp_disconn_unix( fd )
550 {
551     return( close( fd ));
552 }
553
554 int lp_conn_unix()
555 {
556     int                 s;
557     struct sockaddr_un  saun;
558
559     if (( s = socket( AF_UNIX, SOCK_STREAM, 0 )) < 0 ) {
560         LOG(log_error, logtype_default, "lp_conn_unix socket: %m" );
561         return( -1 );
562     }
563     memset( &saun, 0, sizeof( struct sockaddr_un ));
564     saun.sun_family = AF_UNIX;
565     strcpy( saun.sun_path, _PATH_DEVPRINTER );
566     if ( connect( s, (struct sockaddr *)&saun,
567             strlen( saun.sun_path ) + 2 ) < 0 ) {
568         LOG(log_error, logtype_default, "lp_conn_unix connect %s: %m", saun.sun_path );
569         close( s );
570         return( -1 );
571     }
572
573     return( s );
574 }
575
576 int lp_disconn_inet( int fd )
577 {
578     return( close( fd ));
579 }
580
581 int lp_conn_inet()
582 {
583     int                 privfd, port = IPPORT_RESERVED - 1;
584     struct sockaddr_in  sin;
585     struct servent      *sp;
586     struct hostent      *hp;
587
588     if (( sp = getservbyname( "printer", "tcp" )) == NULL ) {
589         LOG(log_error, logtype_default, "printer/tcp: unknown service\n" );
590         return( -1 );
591     }
592
593     if ( gethostname( hostname, sizeof( hostname )) < 0 ) {
594         LOG(log_error, logtype_default, "gethostname: %m" );
595         exit( 1 );
596     }
597
598     if (( hp = gethostbyname( hostname )) == NULL ) {
599         LOG(log_error, logtype_default, "%s: unknown host\n", hostname );
600         return( -1 );
601     }
602
603     if (( privfd = rresvport( &port )) < 0 ) {
604         LOG(log_error, logtype_default, "lp_connect: socket: %m" );
605         close( privfd );
606         return( -1 );
607     }
608
609     memset( &sin, 0, sizeof( struct sockaddr_in ));
610     sin.sin_family = AF_INET;
611 /*    sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); */
612     memcpy( &sin.sin_addr, hp->h_addr, hp->h_length );
613     sin.sin_port = sp->s_port;
614
615     if ( connect( privfd, (struct sockaddr *)&sin,
616             sizeof( struct sockaddr_in )) < 0 ) {
617         LOG(log_error, logtype_default, "lp_connect: %m" );
618         close( privfd );
619         return( -1 );
620     }
621
622     return( privfd );
623 }
624
625 int lp_rmjob( job )
626     int         job;
627 {
628     char        buf[ 1024 ];
629     int         n, s;
630
631     if (( s = lp_conn_inet()) < 0 ) {
632         LOG(log_error, logtype_default, "lp_rmjob: %m" );
633         return( -1 );
634     }
635
636     if ( lp.lp_person == NULL ) {
637         return( -1 );
638     }
639
640     sprintf( buf, "\5%s %s %d\n", printer->p_printer, lp.lp_person, job );
641     n = strlen( buf );
642     if ( write( s, buf, n ) != n ) {
643         LOG(log_error, logtype_default, "lp_rmjob write: %m" );
644         lp_disconn_inet( s );
645         return( -1 );
646     }
647     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
648         LOG(log_debug, logtype_default, "read %.*s", n, buf );
649     }
650
651     lp_disconn_inet( s );
652     return( 0 );
653 }
654
655 char    *kw_rank = "Rank";
656 char    *kw_active = "active";
657
658 char    *tag_rank = "rank: ";
659 char    *tag_owner = "owner: ";
660 char    *tag_job = "job: ";
661 char    *tag_files = "files: ";
662 char    *tag_size = "size: ";
663 char    *tag_status = "status: ";
664
665 int lp_queue( out )
666     struct papfile      *out;
667 {
668     char                        buf[ 1024 ], *start, *stop, *p, *q;
669     int                         linelength, crlflength;
670     static struct papfile       pf;
671     int                         n, len, s;
672         
673     if (( s = lp_conn_unix()) < 0 ) {
674         LOG(log_error, logtype_default, "lp_queue: %m" );
675         return( -1 );
676     }
677
678     sprintf( buf, "\3%s\n", printer->p_printer );
679     n = strlen( buf );
680     if ( write( s, buf, n ) != n ) {
681         LOG(log_error, logtype_default, "lp_queue write: %m" );
682         lp_disconn_unix( s );
683         return( -1 );
684     }
685     pf.pf_state = PF_BOT;
686
687     while (( n = read( s, buf, sizeof( buf ))) > 0 ) {
688         append( &pf, buf, n );
689     }
690
691     for (;;) {
692         if ( markline( &pf, &start, &linelength, &crlflength ) > 0 ) {
693             /* parse */
694             stop = start + linelength;
695             for ( p = start; p < stop; p++ ) {
696                 if ( *p == ' ' || *p == '\t' ) {
697                     break;
698                 }
699             }
700             if ( p >= stop ) {
701                 CONSUME( &pf , linelength + crlflength);
702                 continue;
703             }
704
705             /*
706              * Keys: "Rank", a number, "active"
707              * Anything else is status.
708              */
709             len = p - start;
710             if ( len == strlen( kw_rank ) &&
711                     strncmp( kw_rank, start, len ) == 0 ) {
712                 CONSUME( &pf, linelength + crlflength );
713                 continue;
714             }
715             if (( len == strlen( kw_active ) &&
716                     strncmp( kw_active, start, len ) == 0 ) ||
717                     isdigit( *start )) {                /* a job line */
718                 append( out, tag_rank, strlen( tag_rank ));
719                 append( out, start, p - start );
720                 append( out, "\n", 1 );
721
722                 for ( ; p < stop; p++ ) {
723                     if ( *p != ' ' && *p != '\t' ) {
724                         break;
725                     }
726                 }
727                 for ( q = p; p < stop; p++ ) {
728                     if ( *p == ' ' || *p == '\t' ) {
729                         break;
730                     }
731                 }
732                 if ( p >= stop ) {
733                     append( out, ".\n", 2 );
734                     CONSUME( &pf, linelength + crlflength );
735                     continue;
736                 }
737                 append( out, tag_owner, strlen( tag_owner ));
738                 append( out, q, p - q );
739                 append( out, "\n", 1 );
740
741                 for ( ; p < stop; p++ ) {
742                     if ( *p != ' ' && *p != '\t' ) {
743                         break;
744                     }
745                 }
746                 for ( q = p; p < stop; p++ ) {
747                     if ( *p == ' ' || *p == '\t' ) {
748                         break;
749                     }
750                 }
751                 if ( p >= stop ) {
752                     append( out, ".\n", 2 );
753                     CONSUME( &pf , linelength + crlflength );
754                     continue;
755                 }
756                 append( out, tag_job, strlen( tag_job ));
757                 append( out, q, p - q );
758                 append( out, "\n", 1 );
759
760                 for ( ; p < stop; p++ ) {
761                     if ( *p != ' ' && *p != '\t' ) {
762                         break;
763                     }
764                 }
765                 for ( q = p, p = stop; p > q; p-- ) {
766                     if ( *p == ' ' || *p == '\t' ) {
767                         break;
768                     }
769                 }
770                 for ( ; p > q; p-- ) {
771                     if ( *p != ' ' && *p != '\t' ) {
772                         break;
773                     }
774                 }
775                 for ( ; p > q; p-- ) {
776                     if ( *p == ' ' || *p == '\t' ) {
777                         break;
778                     }
779                 }
780                 if ( p <= q ) {
781                     append( out, ".\n", 2 );
782                     CONSUME( &pf, linelength + crlflength );
783                     continue;
784                 }
785                 append( out, tag_files, strlen( tag_files ));
786                 append( out, q, p - q );
787                 append( out, "\n", 1 );
788
789                 for ( ; p < stop; p++ ) {
790                     if ( *p != ' ' && *p != '\t' ) {
791                         break;
792                     }
793                 }
794                 append( out, tag_size, strlen( tag_size ));
795                 append( out, p, stop - p );
796                 append( out, "\n.\n", 3 );
797
798                 CONSUME( &pf, linelength + crlflength );
799                 continue;
800             }
801
802             /* status */
803             append( out, tag_status, strlen( tag_status ));
804             append( out, start, linelength );
805             append( out, "\n.\n", 3 );
806
807             CONSUME( &pf, linelength + crlflength );
808         } else {
809             append( out, "*\n", 2 );
810             lp_disconn_unix( s );
811             return( 0 );
812         }
813     }
814 }