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