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