2 * $Id: pap.c,v 1.6 2001-06-29 14:14:46 rufustfirefly Exp $
4 * Copyright (c) 1990,1994 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
12 #include <sys/types.h>
15 #include <netatalk/endian.h>
16 #include <netatalk/at.h>
17 #include <atalk/atp.h>
18 #include <atalk/pap.h>
19 #include <atalk/nbp.h>
20 #include <atalk/util.h>
23 #endif /* HAVE_FCNTL_H */
31 #define _PATH_PAPRC ".paprc"
32 char *nbpfailure = "AppleTalk printer offline";
34 /* Forward Declarations */
35 void updatestatus(char *s, int len);
36 int send_file(int fd, ATP atp, int lastfile);
38 /* if there is a less hacky way to do this, please do it... */
46 #define DEBUG(x,y) (x,y)
56 if (( p = strrchr( path, '/' )) == NULL ) {
62 "Usage:\t%s [ -A address ] [ -p printername ] [ -s statusfile ] [ file ] ...\n", p );
69 static char s[ 32 + 1 + 32 + 1 + 32 ];
73 if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
74 if ( errno == ENOENT ) {
77 perror( _PATH_PAPRC );
81 while ( fgets( s, sizeof( s ), f ) != NULL ) {
82 s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
96 int waitforprinter = 0;
98 unsigned char connid, quantum, oquantum = PAP_MAXQUANTUM;
99 struct sockaddr_at sat;
105 char fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
106 struct iovec rfiov[ PAP_MAXQUANTUM ] = {
107 { fbuf[ 0 ] + 4, 0 },
108 { fbuf[ 1 ] + 4, 0 },
109 { fbuf[ 2 ] + 4, 0 },
110 { fbuf[ 3 ] + 4, 0 },
111 { fbuf[ 4 ] + 4, 0 },
112 { fbuf[ 5 ] + 4, 0 },
113 { fbuf[ 6 ] + 4, 0 },
114 { fbuf[ 7 ] + 4, 0 },
116 struct iovec sniov[ PAP_MAXQUANTUM ] = {
127 char nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
128 struct iovec rniov[ PAP_MAXQUANTUM ] = {
138 struct iovec sfiov[ PAP_MAXQUANTUM ] = {
139 { nbuf[ 0 ] + 4, 0 },
140 { nbuf[ 1 ] + 4, 0 },
141 { nbuf[ 2 ] + 4, 0 },
142 { nbuf[ 3 ] + 4, 0 },
143 { nbuf[ 4 ] + 4, 0 },
144 { nbuf[ 5 ] + 4, 0 },
145 { nbuf[ 6 ] + 4, 0 },
146 { nbuf[ 7 ] + 4, 0 },
154 struct atp_block atpb;
155 int c, err = 0, fd, cuts = 0;
156 char *obj = NULL, *type = "LaserWriter", *zone = "*";
157 struct timeval stv, tv;
158 char rbuf[ ATP_MAXDATA ];
160 unsigned short waiting, result;
161 int connattempts = 10;
168 memset(&addr, 0, sizeof(addr));
169 while (( c = getopt( ac, av, "Wwcep:s:EA:" )) != EOF ) {
185 case 'e' : /* send stdout to stderr */
202 if (!atalk_aton(optarg, &addr)) {
203 fprintf(stderr, "Bad address.\n");
215 if ( printer == NULL && (( printer = paprc()) == NULL )) {
216 fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
223 if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
224 fprintf( stderr, "%s: Bad name\n", printer );
228 fprintf( stderr, "%s: Bad name\n", printer );
232 if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
234 perror( "nbp_lookup" );
237 fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
242 printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
243 nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
246 if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
247 perror( "atp_open" );
250 if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
251 perror( "atp_open" );
255 while ( waitforidle ) {
256 char st_buf[ 1024 ]; /* XXX too big */
259 cbuf[ 1 ] = PAP_SENDSTATUS;
260 cbuf[ 2 ] = cbuf[ 3 ] = 0;
261 atpb.atp_saddr = &nn.nn_sat;
262 atpb.atp_sreqdata = cbuf;
263 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
264 atpb.atp_sreqto = 2; /* retry timer */
265 atpb.atp_sreqtries = 5; /* retry count */
266 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
267 perror( "atp_sreq" );
271 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
273 atpb.atp_saddr = &nn.nn_sat;
274 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
275 atpb.atp_rresiov = rniov;
276 atpb.atp_rresiovcnt = 1;
277 if ( atp_rresp( satp, &atpb ) < 0 ) {
278 perror( "atp_rresp" );
282 #ifndef NONZEROSTATUS
284 * The stinking LaserWriter IINTX puts crap in this
287 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
288 fprintf( stderr, "Bad status response!\n" );
291 #endif /* NONZEROSTATUS */
293 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
294 atpb.atp_rresiovcnt != 1 ) {
295 fprintf( stderr, "Bad status response!\n" );
299 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
301 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
302 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
303 st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
304 if ( strstr( st_buf, "idle" ) != NULL ) {
307 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
308 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
313 cbuf[ 0 ] = connid = getpid() & 0xff;
314 cbuf[ 1 ] = PAP_OPEN;
315 cbuf[ 2 ] = cbuf[ 3 ] = 0;
316 cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
317 cbuf[ 5 ] = oquantum; /* flow quantum */
318 if ( gettimeofday( &stv, 0 ) < 0 ) {
319 perror( "gettimeofday" );
326 if ( gettimeofday( &tv, 0 ) < 0 ) {
327 perror( "gettimeofday" );
330 waiting = htons( tv.tv_sec - stv.tv_sec );
332 memcpy(cbuf + 6, &waiting, sizeof( waiting ));
334 atpb.atp_saddr = &nn.nn_sat;
335 atpb.atp_sreqdata = cbuf;
336 atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
337 atpb.atp_sreqto = 2; /* retry timer */
338 atpb.atp_sreqtries = 5; /* retry count */
339 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
340 perror( "atp_sreq" );
344 DEBUG( printf( "OPEN >\n" ), fflush( stdout ));
347 iov.iov_len = sizeof( rbuf );
348 atpb.atp_rresiov = &iov;
349 atpb.atp_rresiovcnt = 1;
350 if ( atp_rresp( atp, &atpb ) < 0 ) {
351 perror( "atp_rresp" );
352 if ( connattempts-- <= 0 ) {
353 fprintf( stderr, "Can't connect!\n" );
360 if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
361 rbuf[ 1 ] != PAP_OPENREPLY ) {
362 fprintf( stderr, "Bad response!\n" );
363 continue; /* This is weird, since TIDs must match... */
366 DEBUG( printf( "< OPENREPLY\n" ), fflush( stdout ));
369 printf( "%.*s\n", (int)iov.iov_len - 9, (char *) iov.iov_base + 9 );
371 updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
373 memcpy( &result, rbuf + 6, sizeof( result ));
377 memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
378 sat.sat_port = rbuf[ 4 ];
385 printf( "Connected to %.*s:%.*s@%.*s.\n",
386 nn.nn_objlen, nn.nn_obj,
387 nn.nn_typelen, nn.nn_type,
388 nn.nn_zonelen, nn.nn_zone );
391 if ( optind == ac ) {
392 send_file( 0, atp, 1 );
394 for (; optind < ac; optind++ ) {
395 if ( strcmp( av[ optind ], "-" ) == 0 ) {
397 } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
398 perror( av[ optind ] );
401 send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
412 cbuf[ 1 ] = PAP_CLOSE;
413 cbuf[ 2 ] = cbuf[ 3 ] = 0;
415 atpb.atp_saddr = &sat;
416 atpb.atp_sreqdata = cbuf;
417 atpb.atp_sreqdlen = 4; /* bytes in CloseConn request */
418 atpb.atp_sreqto = 2; /* retry timer */
419 atpb.atp_sreqtries = 5; /* retry count */
420 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
421 perror( "atp_sreq" );
425 DEBUG( printf( "CLOSE >\n" ), fflush( stdout ));
428 iov.iov_len = sizeof( rbuf );
429 atpb.atp_rresiov = &iov;
430 atpb.atp_rresiovcnt = 1;
431 if ( atp_rresp( atp, &atpb ) < 0 ) {
432 perror( "atp_rresp" );
437 if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
438 fprintf( stderr, "Bad response!\n" );
444 * The AGFA Viper Rip doesn't have the connection id in the close request.
446 if ((unsigned char)rbuf[ 0 ] != connid ) {
447 fprintf( stderr, "Bad connid in close!\n" );
450 #endif /* ZEROCONNID */
452 DEBUG( printf( "< CLOSEREPLY\n" ), fflush( stdout ));
455 printf( "Connection closed.\n" );
462 u_int16_t seq = 0, rseq = 1;
464 int send_file( fd, atp, lastfile )
469 struct timeval stv, tv;
470 struct sockaddr_at ssat;
471 struct atp_block atpb;
473 int fiovcnt = 0, eof = 0, senteof = 0, to = 0;
475 unsigned short netseq;
477 if ( gettimeofday( &stv, 0 ) < 0 ) {
478 perror( "gettimeofday" );
486 cbuf[ 1 ] = PAP_READ;
487 if ( ++seq == 0 ) seq = 1;
488 netseq = htons( seq );
489 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
490 atpb.atp_saddr = &sat;
491 atpb.atp_sreqdata = cbuf;
492 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
493 atpb.atp_sreqto = 15; /* retry timer */
494 atpb.atp_sreqtries = -1; /* retry count */
495 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
496 perror( "atp_sreq" );
500 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
503 if ( gettimeofday( &tv, 0 ) < 0 ) {
504 perror( "gettimeofday" );
508 if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
515 cbuf[ 1 ] = PAP_TICKLE;
516 cbuf[ 2 ] = cbuf[ 3 ] = 0;
517 atpb.atp_saddr = &sat;
518 atpb.atp_sreqdata = cbuf;
519 atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
520 atpb.atp_sreqto = 0; /* retry timer */
521 atpb.atp_sreqtries = 1; /* retry count */
522 if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
523 perror( "atp_sreq" );
527 DEBUG( printf( "TICKLE >\n" ), fflush( stdout ));
530 tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
534 if ( !waitforprinter && !eof && fiovcnt == 0 ) {
537 FD_SET( atp_fileno( atp ), &fds );
539 if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
545 * A timeout has occured. Keep track of it.
549 fprintf( stderr, "Connection timed out.\n" );
558 if ( !fiovcnt && FD_ISSET( fd, &fds )) {
559 for ( i = 0; i < quantum; i++ ) {
560 rfiov[ i ].iov_len = PAP_MAXDATA;
562 if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
569 fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
570 for ( i = 0; cc > 0; i++ ) {
571 rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
572 cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
576 if ( FD_ISSET( atp_fileno( atp ), &fds )) {
578 ssat.sat_port = ATADDR_ANYPORT;
579 switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
581 atpb.atp_saddr = &ssat;
582 atpb.atp_rreqdata = cbuf;
583 atpb.atp_rreqdlen = sizeof( cbuf );
584 if ( atp_rreq( atp, &atpb ) < 0 ) {
585 perror( "atp_rreq" );
589 if ( (unsigned char)cbuf[ 0 ] != connid ) {
593 /* reset timeout counter for all valid requests */
596 switch ( cbuf[ 1 ] ) {
598 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
599 DEBUG( printf( "< READ %d\n", ntohs( netseq )), fflush( stdout ));
602 if ( rseq != ntohs( netseq )) {
603 DEBUG( printf( "| DUP %d\n", rseq ), fflush( stdout ));
606 if ( rseq++ == 0xffff ) rseq = 1;
611 port = ssat.sat_port;
616 DEBUG( printf( "< CLOSE\n" ), fflush( stdout ));
619 * Respond to the close request, and fail.
621 sniov[ 0 ].iov_len = 4;
622 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
623 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
624 ((char *)sniov[ 0 ].iov_base)[ 2 ] =
625 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
626 atpb.atp_sresiov = sniov;
627 atpb.atp_sresiovcnt = 1;
628 if ( atp_sresp( atp, &atpb ) < 0 ) {
629 perror( "atp_sresp" );
633 DEBUG( printf( "CLOSEREPLY >\n" ), fflush( stdout ));
635 fprintf( stderr, "Connection closed by foreign host.\n" );
640 DEBUG( printf( "< TICKLE\n" ), fflush( stdout ));
644 fprintf( stderr, "Bad PAP request!\n" );
650 /* reset timeout counter for all valid requests */
653 atpb.atp_saddr = &ssat;
654 for ( i = 0; i < oquantum; i++ ) {
655 rniov[ i ].iov_len = PAP_MAXDATA + 4;
657 atpb.atp_rresiov = rniov;
658 atpb.atp_rresiovcnt = oquantum;
659 if ( atp_rresp( atp, &atpb ) < 0 ) {
660 perror( "atp_rresp" );
666 * The HP LJIIISI w/ BridgePort LocalTalk card sends
667 * zero instead of the connid.
669 if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
670 fprintf( stderr, "Bad data response!\n" );
673 #endif /* ZEROCONNID */
674 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
675 fprintf( stderr, "Bad data response!\n" );
679 for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
680 sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
681 cc += sfiov[ i ].iov_len;
683 if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
689 if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
691 DEBUG( printf( "< DATA (eof)\n" ), fflush( stdout ));
696 DEBUG( printf( "< DATA\n" ), fflush( stdout ));
703 cbuf[ 1 ] = PAP_READ;
704 if ( ++seq == 0 ) seq = 1;
705 netseq = htons( seq );
706 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
707 atpb.atp_saddr = &sat;
708 atpb.atp_sreqdata = cbuf;
709 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
710 atpb.atp_sreqto = 15; /* retry timer */
711 atpb.atp_sreqtries = -1; /* retry count */
712 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
713 perror( "atp_sreq" );
717 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
723 DEBUG( printf( "| RETRANS\n" ), fflush( stdout ));
728 perror( "atp_rsel" );
734 * Send whatever is pending.
736 if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
737 ssat.sat_port = port;
738 atpb.atp_saddr = &ssat;
740 for ( i = 0; i < fiovcnt; i++ ) {
741 sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
742 ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
743 ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
744 senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
745 ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
748 sniov[ 0 ].iov_len = 4;
749 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
750 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
751 senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
752 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
754 atpb.atp_sresiov = sniov;
755 atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
756 if ( atp_sresp( atp, &atpb ) < 0 ) {
757 perror( "atp_sresp" );
762 DEBUG( printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout ));
765 * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
766 * seem to send us an EOF on large jobs. To work around
767 * this heinous protocol violation, we won't wait for their
768 * EOF before closing.
770 if ( eof && noeof && lastfile ) {
775 * If we can't send data right now, go ahead and get the
776 * status. This is cool, because we get here reliably
777 * if there is a problem.
780 cbuf[ 1 ] = PAP_SENDSTATUS;
781 cbuf[ 2 ] = cbuf[ 3 ] = 0;
782 atpb.atp_saddr = &nn.nn_sat;
783 atpb.atp_sreqdata = cbuf;
784 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
785 atpb.atp_sreqto = 2; /* retry timer */
786 atpb.atp_sreqtries = 5; /* retry count */
787 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
788 perror( "atp_sreq" );
792 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
794 atpb.atp_saddr = &nn.nn_sat;
795 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
796 atpb.atp_rresiov = rniov;
797 atpb.atp_rresiovcnt = 1;
798 if ( atp_rresp( satp, &atpb ) < 0 ) {
799 perror( "atp_rresp" );
803 #ifndef NONZEROSTATUS
805 * The stinking LaserWriter IINTX puts crap in this
808 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
809 fprintf( stderr, "Bad status response!\n" );
812 #endif /* NONZEROSTATUS */
814 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
815 atpb.atp_rresiovcnt != 1 ) {
816 fprintf( stderr, "Bad status response!\n" );
820 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
823 if ( waitforprinter ) {
824 char st_buf[ 1024 ]; /* XXX too big */
826 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
827 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
828 st_buf[ (int) ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
829 if ( strstr( st_buf, "waiting" ) != NULL ) {
835 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
836 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
841 void updatestatus( s, len )
846 struct iovec iov[ 2 ];
852 if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
857 iov[ 0 ].iov_base = s;
858 iov[ 0 ].iov_len = len;
859 iov[ 1 ].iov_base = "\n";
860 iov[ 1 ].iov_len = 1;
861 writev( fd, iov, 2 );