2 * Copyright (c) 1990,1994 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
10 #include <sys/types.h>
13 #include <netatalk/endian.h>
14 #include <netatalk/at.h>
15 #include <atalk/atp.h>
16 #include <atalk/pap.h>
17 #include <atalk/nbp.h>
18 #include <atalk/util.h>
27 #define _PATH_PAPRC ".paprc"
28 char *nbpfailure = "AppleTalk printer offline";
30 /* Forward Declarations */
31 void updatestatus(char *s, int len);
32 int send_file(int fd, ATP atp, int lastfile);
34 /* if there is a less hacky way to do this, please do it... */
42 #define DEBUG(x,y) (x,y)
52 if (( p = strrchr( path, '/' )) == NULL ) {
58 "Usage:\t%s [ -A address ] [ -p printername ] [ -s statusfile ] [ file ] ...\n", p );
65 static char s[ 32 + 1 + 32 + 1 + 32 ];
69 if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
70 if ( errno == ENOENT ) {
73 perror( _PATH_PAPRC );
77 while ( fgets( s, sizeof( s ), f ) != NULL ) {
78 s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
92 int waitforprinter = 0;
94 unsigned char connid, quantum, oquantum = PAP_MAXQUANTUM;
95 struct sockaddr_at sat;
101 char fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
102 struct iovec rfiov[ PAP_MAXQUANTUM ] = {
103 { fbuf[ 0 ] + 4, 0 },
104 { fbuf[ 1 ] + 4, 0 },
105 { fbuf[ 2 ] + 4, 0 },
106 { fbuf[ 3 ] + 4, 0 },
107 { fbuf[ 4 ] + 4, 0 },
108 { fbuf[ 5 ] + 4, 0 },
109 { fbuf[ 6 ] + 4, 0 },
110 { fbuf[ 7 ] + 4, 0 },
112 struct iovec sniov[ PAP_MAXQUANTUM ] = {
123 char nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
124 struct iovec rniov[ PAP_MAXQUANTUM ] = {
134 struct iovec sfiov[ PAP_MAXQUANTUM ] = {
135 { nbuf[ 0 ] + 4, 0 },
136 { nbuf[ 1 ] + 4, 0 },
137 { nbuf[ 2 ] + 4, 0 },
138 { nbuf[ 3 ] + 4, 0 },
139 { nbuf[ 4 ] + 4, 0 },
140 { nbuf[ 5 ] + 4, 0 },
141 { nbuf[ 6 ] + 4, 0 },
142 { nbuf[ 7 ] + 4, 0 },
150 struct atp_block atpb;
151 int c, err = 0, fd, cuts = 0;
152 char *obj = NULL, *type = "LaserWriter", *zone = "*";
153 struct timeval stv, tv;
154 char rbuf[ ATP_MAXDATA ];
156 unsigned short waiting, result;
157 int connattempts = 10;
164 memset(&addr, 0, sizeof(addr));
165 while (( c = getopt( ac, av, "Wwcep:s:EA:" )) != EOF ) {
181 case 'e' : /* send stdout to stderr */
198 if (!atalk_aton(optarg, &addr)) {
199 fprintf(stderr, "Bad address.\n");
211 if ( printer == NULL && (( printer = paprc()) == NULL )) {
212 fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
219 if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
220 fprintf( stderr, "%s: Bad name\n", printer );
224 fprintf( stderr, "%s: Bad name\n", printer );
228 if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
230 perror( "nbp_lookup" );
233 fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
238 printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
239 nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
242 if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
243 perror( "atp_open" );
246 if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
247 perror( "atp_open" );
251 while ( waitforidle ) {
252 char st_buf[ 1024 ]; /* XXX too big */
255 cbuf[ 1 ] = PAP_SENDSTATUS;
256 cbuf[ 2 ] = cbuf[ 3 ] = 0;
257 atpb.atp_saddr = &nn.nn_sat;
258 atpb.atp_sreqdata = cbuf;
259 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
260 atpb.atp_sreqto = 2; /* retry timer */
261 atpb.atp_sreqtries = 5; /* retry count */
262 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
263 perror( "atp_sreq" );
267 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
269 atpb.atp_saddr = &nn.nn_sat;
270 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
271 atpb.atp_rresiov = rniov;
272 atpb.atp_rresiovcnt = 1;
273 if ( atp_rresp( satp, &atpb ) < 0 ) {
274 perror( "atp_rresp" );
278 #ifndef NONZEROSTATUS
280 * The stinking LaserWriter IINTX puts crap in this
283 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
284 fprintf( stderr, "Bad status response!\n" );
287 #endif /*NONZEROSTATUS*/
289 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
290 atpb.atp_rresiovcnt != 1 ) {
291 fprintf( stderr, "Bad status response!\n" );
295 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
297 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
298 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
299 st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
300 if ( strstr( st_buf, "idle" ) != NULL ) {
303 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
304 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
309 cbuf[ 0 ] = connid = getpid() & 0xff;
310 cbuf[ 1 ] = PAP_OPEN;
311 cbuf[ 2 ] = cbuf[ 3 ] = 0;
312 cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
313 cbuf[ 5 ] = oquantum; /* flow quantum */
314 if ( gettimeofday( &stv, 0 ) < 0 ) {
315 perror( "gettimeofday" );
322 if ( gettimeofday( &tv, 0 ) < 0 ) {
323 perror( "gettimeofday" );
326 waiting = htons( tv.tv_sec - stv.tv_sec );
328 memcpy(cbuf + 6, &waiting, sizeof( waiting ));
330 atpb.atp_saddr = &nn.nn_sat;
331 atpb.atp_sreqdata = cbuf;
332 atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
333 atpb.atp_sreqto = 2; /* retry timer */
334 atpb.atp_sreqtries = 5; /* retry count */
335 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
336 perror( "atp_sreq" );
340 DEBUG( printf( "OPEN >\n" ), fflush( stdout ));
343 iov.iov_len = sizeof( rbuf );
344 atpb.atp_rresiov = &iov;
345 atpb.atp_rresiovcnt = 1;
346 if ( atp_rresp( atp, &atpb ) < 0 ) {
347 perror( "atp_rresp" );
348 if ( connattempts-- <= 0 ) {
349 fprintf( stderr, "Can't connect!\n" );
356 if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
357 rbuf[ 1 ] != PAP_OPENREPLY ) {
358 fprintf( stderr, "Bad response!\n" );
359 continue; /* This is weird, since TIDs must match... */
362 DEBUG( printf( "< OPENREPLY\n" ), fflush( stdout ));
365 printf( "%.*s\n", (int)iov.iov_len - 9, (char *) iov.iov_base + 9 );
367 updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
369 memcpy( &result, rbuf + 6, sizeof( result ));
373 memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
374 sat.sat_port = rbuf[ 4 ];
381 printf( "Connected to %.*s:%.*s@%.*s.\n",
382 nn.nn_objlen, nn.nn_obj,
383 nn.nn_typelen, nn.nn_type,
384 nn.nn_zonelen, nn.nn_zone );
387 if ( optind == ac ) {
388 send_file( 0, atp, 1 );
390 for (; optind < ac; optind++ ) {
391 if ( strcmp( av[ optind ], "-" ) == 0 ) {
393 } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
394 perror( av[ optind ] );
397 send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
408 cbuf[ 1 ] = PAP_CLOSE;
409 cbuf[ 2 ] = cbuf[ 3 ] = 0;
411 atpb.atp_saddr = &sat;
412 atpb.atp_sreqdata = cbuf;
413 atpb.atp_sreqdlen = 4; /* bytes in CloseConn request */
414 atpb.atp_sreqto = 2; /* retry timer */
415 atpb.atp_sreqtries = 5; /* retry count */
416 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
417 perror( "atp_sreq" );
421 DEBUG( printf( "CLOSE >\n" ), fflush( stdout ));
424 iov.iov_len = sizeof( rbuf );
425 atpb.atp_rresiov = &iov;
426 atpb.atp_rresiovcnt = 1;
427 if ( atp_rresp( atp, &atpb ) < 0 ) {
428 perror( "atp_rresp" );
433 if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
434 fprintf( stderr, "Bad response!\n" );
440 * The AGFA Viper Rip doesn't have the connection id in the close request.
442 if ((unsigned char)rbuf[ 0 ] != connid ) {
443 fprintf( stderr, "Bad connid in close!\n" );
446 #endif /*ZEROCONNID*/
448 DEBUG( printf( "< CLOSEREPLY\n" ), fflush( stdout ));
451 printf( "Connection closed.\n" );
458 u_int16_t seq = 0, rseq = 1;
460 int send_file( fd, atp, lastfile )
465 struct timeval stv, tv;
466 struct sockaddr_at ssat;
467 struct atp_block atpb;
469 int fiovcnt = 0, eof = 0, senteof = 0, to = 0;
471 unsigned short netseq;
473 if ( gettimeofday( &stv, 0 ) < 0 ) {
474 perror( "gettimeofday" );
482 cbuf[ 1 ] = PAP_READ;
483 if ( ++seq == 0 ) seq = 1;
484 netseq = htons( seq );
485 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
486 atpb.atp_saddr = &sat;
487 atpb.atp_sreqdata = cbuf;
488 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
489 atpb.atp_sreqto = 15; /* retry timer */
490 atpb.atp_sreqtries = -1; /* retry count */
491 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
492 perror( "atp_sreq" );
496 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
499 if ( gettimeofday( &tv, 0 ) < 0 ) {
500 perror( "gettimeofday" );
504 if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
511 cbuf[ 1 ] = PAP_TICKLE;
512 cbuf[ 2 ] = cbuf[ 3 ] = 0;
513 atpb.atp_saddr = &sat;
514 atpb.atp_sreqdata = cbuf;
515 atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
516 atpb.atp_sreqto = 0; /* retry timer */
517 atpb.atp_sreqtries = 1; /* retry count */
518 if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
519 perror( "atp_sreq" );
523 DEBUG( printf( "TICKLE >\n" ), fflush( stdout ));
526 tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
530 if ( !waitforprinter && !eof && fiovcnt == 0 ) {
533 FD_SET( atp_fileno( atp ), &fds );
535 if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
541 * A timeout has occured. Keep track of it.
545 fprintf( stderr, "Connection timed out.\n" );
554 if ( !fiovcnt && FD_ISSET( fd, &fds )) {
555 for ( i = 0; i < quantum; i++ ) {
556 rfiov[ i ].iov_len = PAP_MAXDATA;
558 if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
565 fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
566 for ( i = 0; cc > 0; i++ ) {
567 rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
568 cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
572 if ( FD_ISSET( atp_fileno( atp ), &fds )) {
574 ssat.sat_port = ATADDR_ANYPORT;
575 switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
577 atpb.atp_saddr = &ssat;
578 atpb.atp_rreqdata = cbuf;
579 atpb.atp_rreqdlen = sizeof( cbuf );
580 if ( atp_rreq( atp, &atpb ) < 0 ) {
581 perror( "atp_rreq" );
585 if ( (unsigned char)cbuf[ 0 ] != connid ) {
589 /* reset timeout counter for all valid requests */
592 switch ( cbuf[ 1 ] ) {
594 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
595 DEBUG( printf( "< READ %d\n", ntohs( netseq )), fflush( stdout ));
598 if ( rseq != ntohs( netseq )) {
599 DEBUG( printf( "| DUP %d\n", rseq ), fflush( stdout ));
602 if ( rseq++ == 0xffff ) rseq = 1;
607 port = ssat.sat_port;
612 DEBUG( printf( "< CLOSE\n" ), fflush( stdout ));
615 * Respond to the close request, and fail.
617 sniov[ 0 ].iov_len = 4;
618 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
619 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
620 ((char *)sniov[ 0 ].iov_base)[ 2 ] =
621 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
622 atpb.atp_sresiov = sniov;
623 atpb.atp_sresiovcnt = 1;
624 if ( atp_sresp( atp, &atpb ) < 0 ) {
625 perror( "atp_sresp" );
629 DEBUG( printf( "CLOSEREPLY >\n" ), fflush( stdout ));
631 fprintf( stderr, "Connection closed by foreign host.\n" );
636 DEBUG( printf( "< TICKLE\n" ), fflush( stdout ));
640 fprintf( stderr, "Bad PAP request!\n" );
646 /* reset timeout counter for all valid requests */
649 atpb.atp_saddr = &ssat;
650 for ( i = 0; i < oquantum; i++ ) {
651 rniov[ i ].iov_len = PAP_MAXDATA + 4;
653 atpb.atp_rresiov = rniov;
654 atpb.atp_rresiovcnt = oquantum;
655 if ( atp_rresp( atp, &atpb ) < 0 ) {
656 perror( "atp_rresp" );
662 * The HP LJIIISI w/ BridgePort LocalTalk card sends
663 * zero instead of the connid.
665 if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
666 fprintf( stderr, "Bad data response!\n" );
669 #endif /*ZEROCONNID*/
670 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
671 fprintf( stderr, "Bad data response!\n" );
675 for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
676 sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
677 cc += sfiov[ i ].iov_len;
679 if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
685 if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
687 DEBUG( printf( "< DATA (eof)\n" ), fflush( stdout ));
692 DEBUG( printf( "< DATA\n" ), fflush( stdout ));
699 cbuf[ 1 ] = PAP_READ;
700 if ( ++seq == 0 ) seq = 1;
701 netseq = htons( seq );
702 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
703 atpb.atp_saddr = &sat;
704 atpb.atp_sreqdata = cbuf;
705 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
706 atpb.atp_sreqto = 15; /* retry timer */
707 atpb.atp_sreqtries = -1; /* retry count */
708 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
709 perror( "atp_sreq" );
713 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
719 DEBUG( printf( "| RETRANS\n" ), fflush( stdout ));
724 perror( "atp_rsel" );
730 * Send whatever is pending.
732 if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
733 ssat.sat_port = port;
734 atpb.atp_saddr = &ssat;
736 for ( i = 0; i < fiovcnt; i++ ) {
737 sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
738 ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
739 ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
740 senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
741 ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
744 sniov[ 0 ].iov_len = 4;
745 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
746 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
747 senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
748 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
750 atpb.atp_sresiov = sniov;
751 atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
752 if ( atp_sresp( atp, &atpb ) < 0 ) {
753 perror( "atp_sresp" );
758 DEBUG( printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout ));
761 * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
762 * seem to send us an EOF on large jobs. To work around
763 * this heinous protocol violation, we won't wait for their
764 * EOF before closing.
766 if ( eof && noeof && lastfile ) {
771 * If we can't send data right now, go ahead and get the
772 * status. This is cool, because we get here reliably
773 * if there is a problem.
776 cbuf[ 1 ] = PAP_SENDSTATUS;
777 cbuf[ 2 ] = cbuf[ 3 ] = 0;
778 atpb.atp_saddr = &nn.nn_sat;
779 atpb.atp_sreqdata = cbuf;
780 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
781 atpb.atp_sreqto = 2; /* retry timer */
782 atpb.atp_sreqtries = 5; /* retry count */
783 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
784 perror( "atp_sreq" );
788 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
790 atpb.atp_saddr = &nn.nn_sat;
791 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
792 atpb.atp_rresiov = rniov;
793 atpb.atp_rresiovcnt = 1;
794 if ( atp_rresp( satp, &atpb ) < 0 ) {
795 perror( "atp_rresp" );
799 #ifndef NONZEROSTATUS
801 * The stinking LaserWriter IINTX puts crap in this
804 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
805 fprintf( stderr, "Bad status response!\n" );
808 #endif /*NONZEROSTATUS*/
810 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
811 atpb.atp_rresiovcnt != 1 ) {
812 fprintf( stderr, "Bad status response!\n" );
816 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
819 if ( waitforprinter ) {
820 char st_buf[ 1024 ]; /* XXX too big */
822 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
823 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
824 st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
825 if ( strstr( st_buf, "waiting" ) != NULL ) {
831 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
832 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
837 void updatestatus( s, len )
842 struct iovec iov[ 2 ];
848 if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
853 iov[ 0 ].iov_base = s;
854 iov[ 0 ].iov_len = len;
855 iov[ 1 ].iov_base = "\n";
856 iov[ 1 ].iov_len = 1;
857 writev( fd, iov, 2 );