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";
31 #define DEBUG(x,y) (x,y)
41 if (( p = strrchr( path, '/' )) == NULL ) {
47 "Usage:\t%s [ -A address ] [ -p printername ] [ -s statusfile ] [ file ] ...\n", p );
54 static char s[ 32 + 1 + 32 + 1 + 32 ];
58 if (( f = fopen( _PATH_PAPRC, "r" )) == NULL ) {
59 if ( errno == ENOENT ) {
62 perror( _PATH_PAPRC );
66 while ( fgets( s, sizeof( s ), f ) != NULL ) {
67 s[ strlen( s ) - 1 ] = '\0'; /* remove trailing newline */
81 int waitforprinter = 0;
83 unsigned char connid, quantum, oquantum = PAP_MAXQUANTUM;
84 struct sockaddr_at sat;
90 char fbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
91 struct iovec rfiov[ PAP_MAXQUANTUM ] = {
101 struct iovec sniov[ PAP_MAXQUANTUM ] = {
112 char nbuf[ PAP_MAXQUANTUM ][ 4 + PAP_MAXDATA ];
113 struct iovec rniov[ PAP_MAXQUANTUM ] = {
123 struct iovec sfiov[ PAP_MAXQUANTUM ] = {
124 { nbuf[ 0 ] + 4, 0 },
125 { nbuf[ 1 ] + 4, 0 },
126 { nbuf[ 2 ] + 4, 0 },
127 { nbuf[ 3 ] + 4, 0 },
128 { nbuf[ 4 ] + 4, 0 },
129 { nbuf[ 5 ] + 4, 0 },
130 { nbuf[ 6 ] + 4, 0 },
131 { nbuf[ 7 ] + 4, 0 },
139 struct atp_block atpb;
140 int c, err = 0, fd, cuts = 0;
141 char *obj = NULL, *type = "LaserWriter", *zone = "*";
142 struct timeval stv, tv;
143 char rbuf[ ATP_MAXDATA ];
145 unsigned short waiting, result;
146 int connattempts = 10;
153 memset(&addr, 0, sizeof(addr));
154 while (( c = getopt( ac, av, "Wwcep:s:EA:" )) != EOF ) {
170 case 'e' : /* send stdout to stderr */
187 if (!atalk_aton(optarg, &addr)) {
188 fprintf(stderr, "Bad address.\n");
200 if ( printer == NULL && (( printer = paprc()) == NULL )) {
201 fprintf( stderr, "No printer specified and ./.paprc not found.\n" );
208 if ( nbp_name( printer, &obj, &type, &zone ) < 0 ) {
209 fprintf( stderr, "%s: Bad name\n", printer );
213 fprintf( stderr, "%s: Bad name\n", printer );
217 if ( nbp_lookup( obj, type, zone, &nn, 1, &addr ) <= 0 ) {
219 perror( "nbp_lookup" );
222 fprintf( stderr, "%s:%s@%s: NBP Lookup failed\n", obj, type, zone );
227 printf( "Trying %u.%d:%d ...\n", ntohs( nn.nn_sat.sat_addr.s_net ),
228 nn.nn_sat.sat_addr.s_node, nn.nn_sat.sat_port );
231 if (( atp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
232 perror( "atp_open" );
235 if (( satp = atp_open( ATADDR_ANYPORT, &addr )) == NULL ) {
236 perror( "atp_open" );
240 while ( waitforidle ) {
241 char st_buf[ 1024 ]; /* XXX too big */
244 cbuf[ 1 ] = PAP_SENDSTATUS;
245 cbuf[ 2 ] = cbuf[ 3 ] = 0;
246 atpb.atp_saddr = &nn.nn_sat;
247 atpb.atp_sreqdata = cbuf;
248 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
249 atpb.atp_sreqto = 2; /* retry timer */
250 atpb.atp_sreqtries = 5; /* retry count */
251 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
252 perror( "atp_sreq" );
256 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
258 atpb.atp_saddr = &nn.nn_sat;
259 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
260 atpb.atp_rresiov = rniov;
261 atpb.atp_rresiovcnt = 1;
262 if ( atp_rresp( satp, &atpb ) < 0 ) {
263 perror( "atp_rresp" );
267 #ifndef NONZEROSTATUS
269 * The stinking LaserWriter IINTX puts crap in this
272 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
273 fprintf( stderr, "Bad status response!\n" );
276 #endif /*NONZEROSTATUS*/
278 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
279 atpb.atp_rresiovcnt != 1 ) {
280 fprintf( stderr, "Bad status response!\n" );
284 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
286 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
287 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
288 st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
289 if ( strstr( st_buf, "idle" ) != NULL ) {
292 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
293 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
298 cbuf[ 0 ] = connid = getpid() & 0xff;
299 cbuf[ 1 ] = PAP_OPEN;
300 cbuf[ 2 ] = cbuf[ 3 ] = 0;
301 cbuf[ 4 ] = atp_sockaddr( atp )->sat_port;
302 cbuf[ 5 ] = oquantum; /* flow quantum */
303 if ( gettimeofday( &stv, 0 ) < 0 ) {
304 perror( "gettimeofday" );
311 if ( gettimeofday( &tv, 0 ) < 0 ) {
312 perror( "gettimeofday" );
315 waiting = htons( tv.tv_sec - stv.tv_sec );
317 memcpy(cbuf + 6, &waiting, sizeof( waiting ));
319 atpb.atp_saddr = &nn.nn_sat;
320 atpb.atp_sreqdata = cbuf;
321 atpb.atp_sreqdlen = 8; /* bytes in OpenConn request */
322 atpb.atp_sreqto = 2; /* retry timer */
323 atpb.atp_sreqtries = 5; /* retry count */
324 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
325 perror( "atp_sreq" );
329 DEBUG( printf( "OPEN >\n" ), fflush( stdout ));
332 iov.iov_len = sizeof( rbuf );
333 atpb.atp_rresiov = &iov;
334 atpb.atp_rresiovcnt = 1;
335 if ( atp_rresp( atp, &atpb ) < 0 ) {
336 perror( "atp_rresp" );
337 if ( connattempts-- <= 0 ) {
338 fprintf( stderr, "Can't connect!\n" );
345 if ( iov.iov_len < 8 || (unsigned char)rbuf[ 0 ] != connid ||
346 rbuf[ 1 ] != PAP_OPENREPLY ) {
347 fprintf( stderr, "Bad response!\n" );
348 continue; /* This is weird, since TIDs must match... */
351 DEBUG( printf( "< OPENREPLY\n" ), fflush( stdout ));
354 printf( "%.*s\n", iov.iov_len - 9, (char *) iov.iov_base + 9 );
356 updatestatus( (char *) iov.iov_base + 9, iov.iov_len - 9 );
358 memcpy( &result, rbuf + 6, sizeof( result ));
362 memcpy( &sat, &nn.nn_sat, sizeof( struct sockaddr_at ));
363 sat.sat_port = rbuf[ 4 ];
370 printf( "Connected to %.*s:%.*s@%.*s.\n",
371 nn.nn_objlen, nn.nn_obj,
372 nn.nn_typelen, nn.nn_type,
373 nn.nn_zonelen, nn.nn_zone );
376 if ( optind == ac ) {
377 send_file( 0, atp, 1 );
379 for (; optind < ac; optind++ ) {
380 if ( strcmp( av[ optind ], "-" ) == 0 ) {
382 } else if (( fd = open( av[ optind ], O_RDONLY )) < 0 ) {
383 perror( av[ optind ] );
386 send_file( fd, atp, ( optind == ac - 1 ) ? 1 : 0 );
397 cbuf[ 1 ] = PAP_CLOSE;
398 cbuf[ 2 ] = cbuf[ 3 ] = 0;
400 atpb.atp_saddr = &sat;
401 atpb.atp_sreqdata = cbuf;
402 atpb.atp_sreqdlen = 4; /* bytes in CloseConn request */
403 atpb.atp_sreqto = 2; /* retry timer */
404 atpb.atp_sreqtries = 5; /* retry count */
405 if ( atp_sreq( atp, &atpb, 1, ATP_XO ) < 0 ) {
406 perror( "atp_sreq" );
410 DEBUG( printf( "CLOSE >\n" ), fflush( stdout ));
413 iov.iov_len = sizeof( rbuf );
414 atpb.atp_rresiov = &iov;
415 atpb.atp_rresiovcnt = 1;
416 if ( atp_rresp( atp, &atpb ) < 0 ) {
417 perror( "atp_rresp" );
422 if ( iov.iov_len != 4 || rbuf[ 1 ] != PAP_CLOSEREPLY ) {
423 fprintf( stderr, "Bad response!\n" );
429 * The AGFA Viper Rip doesn't have the connection id in the close request.
431 if ((unsigned char)rbuf[ 0 ] != connid ) {
432 fprintf( stderr, "Bad connid in close!\n" );
435 #endif /*ZEROCONNID*/
437 DEBUG( printf( "< CLOSEREPLY\n" ), fflush( stdout ));
440 printf( "Connection closed.\n" );
447 u_int16_t seq = 0, rseq = 1;
449 send_file( fd, atp, lastfile )
454 struct timeval stv, tv;
455 struct sockaddr_at ssat;
456 struct atp_block atpb;
458 int fiovcnt = 0, eof = 0, senteof = 0, to = 0;
460 unsigned short netseq;
462 if ( gettimeofday( &stv, 0 ) < 0 ) {
463 perror( "gettimeofday" );
471 cbuf[ 1 ] = PAP_READ;
472 if ( ++seq == 0 ) seq = 1;
473 netseq = htons( seq );
474 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
475 atpb.atp_saddr = &sat;
476 atpb.atp_sreqdata = cbuf;
477 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
478 atpb.atp_sreqto = 15; /* retry timer */
479 atpb.atp_sreqtries = -1; /* retry count */
480 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
481 perror( "atp_sreq" );
485 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
488 if ( gettimeofday( &tv, 0 ) < 0 ) {
489 perror( "gettimeofday" );
493 if (( tv.tv_sec - stv.tv_sec ) >= 60 ) {
500 cbuf[ 1 ] = PAP_TICKLE;
501 cbuf[ 2 ] = cbuf[ 3 ] = 0;
502 atpb.atp_saddr = &sat;
503 atpb.atp_sreqdata = cbuf;
504 atpb.atp_sreqdlen = 4; /* bytes in Tickle request */
505 atpb.atp_sreqto = 0; /* retry timer */
506 atpb.atp_sreqtries = 1; /* retry count */
507 if ( atp_sreq( satp, &atpb, 0, 0 ) < 0 ) {
508 perror( "atp_sreq" );
512 DEBUG( printf( "TICKLE >\n" ), fflush( stdout ));
515 tv.tv_sec = stv.tv_sec + 60 - tv.tv_sec;
519 if ( !waitforprinter && !eof && fiovcnt == 0 ) {
522 FD_SET( atp_fileno( atp ), &fds );
524 if (( cc = select( FD_SETSIZE, &fds, 0, 0, &tv )) < 0 ) {
530 * A timeout has occured. Keep track of it.
534 fprintf( stderr, "Connection timed out.\n" );
543 if ( !fiovcnt && FD_ISSET( fd, &fds )) {
544 for ( i = 0; i < quantum; i++ ) {
545 rfiov[ i ].iov_len = PAP_MAXDATA;
547 if (( cc = readv( fd, rfiov, quantum )) < 0 ) {
554 fiovcnt = cc / PAP_MAXDATA + ( cc % PAP_MAXDATA > 0 );
555 for ( i = 0; cc > 0; i++ ) {
556 rfiov[ i ].iov_len = ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
557 cc -= ( cc > PAP_MAXDATA ) ? PAP_MAXDATA : cc;
561 if ( FD_ISSET( atp_fileno( atp ), &fds )) {
563 ssat.sat_port = ATADDR_ANYPORT;
564 switch( atp_rsel( atp, &ssat, ATP_TRESP | ATP_TREQ )) {
566 atpb.atp_saddr = &ssat;
567 atpb.atp_rreqdata = cbuf;
568 atpb.atp_rreqdlen = sizeof( cbuf );
569 if ( atp_rreq( atp, &atpb ) < 0 ) {
570 perror( "atp_rreq" );
574 if ( (unsigned char)cbuf[ 0 ] != connid ) {
578 /* reset timeout counter for all valid requests */
581 switch ( cbuf[ 1 ] ) {
583 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
584 DEBUG( printf( "< READ %d\n", ntohs( netseq )), fflush( stdout ));
587 if ( rseq != ntohs( netseq )) {
588 DEBUG( printf( "| DUP %d\n", rseq ), fflush( stdout ));
591 if ( rseq++ == 0xffff ) rseq = 1;
596 port = ssat.sat_port;
601 DEBUG( printf( "< CLOSE\n" ), fflush( stdout ));
604 * Respond to the close request, and fail.
606 sniov[ 0 ].iov_len = 4;
607 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
608 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_CLOSEREPLY;
609 ((char *)sniov[ 0 ].iov_base)[ 2 ] =
610 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
611 atpb.atp_sresiov = sniov;
612 atpb.atp_sresiovcnt = 1;
613 if ( atp_sresp( atp, &atpb ) < 0 ) {
614 perror( "atp_sresp" );
618 DEBUG( printf( "CLOSEREPLY >\n" ), fflush( stdout ));
620 fprintf( stderr, "Connection closed by foreign host.\n" );
625 DEBUG( printf( "< TICKLE\n" ), fflush( stdout ));
629 fprintf( stderr, "Bad PAP request!\n" );
635 /* reset timeout counter for all valid requests */
638 atpb.atp_saddr = &ssat;
639 for ( i = 0; i < oquantum; i++ ) {
640 rniov[ i ].iov_len = PAP_MAXDATA + 4;
642 atpb.atp_rresiov = rniov;
643 atpb.atp_rresiovcnt = oquantum;
644 if ( atp_rresp( atp, &atpb ) < 0 ) {
645 perror( "atp_rresp" );
651 * The HP LJIIISI w/ BridgePort LocalTalk card sends
652 * zero instead of the connid.
654 if ( ((unsigned char *)rniov[ 0 ].iov_base)[ 0 ] != connid ) {
655 fprintf( stderr, "Bad data response!\n" );
658 #endif /*ZEROCONNID*/
659 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_DATA ) {
660 fprintf( stderr, "Bad data response!\n" );
664 for ( cc = 0, i = 0; i < atpb.atp_rresiovcnt; i++ ) {
665 sfiov[ i ].iov_len = rniov[ i ].iov_len - 4;
666 cc += sfiov[ i ].iov_len;
668 if ( cc && writev( 1, sfiov, atpb.atp_rresiovcnt ) < cc ) {
674 if ( ((char *)rniov[ 0 ].iov_base)[ 2 ] ) {
676 DEBUG( printf( "< DATA (eof)\n" ), fflush( stdout ));
681 DEBUG( printf( "< DATA\n" ), fflush( stdout ));
688 cbuf[ 1 ] = PAP_READ;
689 if ( ++seq == 0 ) seq = 1;
690 netseq = htons( seq );
691 memcpy( cbuf + 2, &netseq, sizeof( netseq ));
692 atpb.atp_saddr = &sat;
693 atpb.atp_sreqdata = cbuf;
694 atpb.atp_sreqdlen = 4; /* bytes in SendData request */
695 atpb.atp_sreqto = 15; /* retry timer */
696 atpb.atp_sreqtries = -1; /* retry count */
697 if ( atp_sreq( atp, &atpb, oquantum, ATP_XO ) < 0 ) {
698 perror( "atp_sreq" );
702 DEBUG( printf( "READ %d >\n", seq ), fflush( stdout ));
708 DEBUG( printf( "| RETRANS\n" ), fflush( stdout ));
713 perror( "atp_rsel" );
719 * Send whatever is pending.
721 if ( !waitforprinter && !senteof && data && ( fiovcnt || eof )) {
722 ssat.sat_port = port;
723 atpb.atp_saddr = &ssat;
725 for ( i = 0; i < fiovcnt; i++ ) {
726 sniov[ i ].iov_len = rfiov[ i ].iov_len + 4;
727 ((char *)sniov[ i ].iov_base)[ 0 ] = connid;
728 ((char *)sniov[ i ].iov_base)[ 1 ] = PAP_DATA;
729 senteof = ((char *)sniov[ i ].iov_base)[ 2 ] = eof;
730 ((char *)sniov[ i ].iov_base)[ 3 ] = 0;
733 sniov[ 0 ].iov_len = 4;
734 ((char *)sniov[ 0 ].iov_base)[ 0 ] = connid;
735 ((char *)sniov[ 0 ].iov_base)[ 1 ] = PAP_DATA;
736 senteof = ((char *)sniov[ 0 ].iov_base)[ 2 ] = eof;
737 ((char *)sniov[ 0 ].iov_base)[ 3 ] = 0;
739 atpb.atp_sresiov = sniov;
740 atpb.atp_sresiovcnt = fiovcnt ? fiovcnt : 1;
741 if ( atp_sresp( atp, &atpb ) < 0 ) {
742 perror( "atp_sresp" );
747 DEBUG( printf( "DATA %s\n", eof ? "(eof) >" : ">" ), fflush( stdout ));
750 * The Apple LaserWriter IIf, the HP LWIIISi, and IV, don't
751 * seem to send us an EOF on large jobs. To work around
752 * this heinous protocol violation, we won't wait for their
753 * EOF before closing.
755 if ( eof && noeof && lastfile ) {
760 * If we can't send data right now, go ahead and get the
761 * status. This is cool, because we get here reliably
762 * if there is a problem.
765 cbuf[ 1 ] = PAP_SENDSTATUS;
766 cbuf[ 2 ] = cbuf[ 3 ] = 0;
767 atpb.atp_saddr = &nn.nn_sat;
768 atpb.atp_sreqdata = cbuf;
769 atpb.atp_sreqdlen = 4; /* bytes in SendStatus request */
770 atpb.atp_sreqto = 2; /* retry timer */
771 atpb.atp_sreqtries = 5; /* retry count */
772 if ( atp_sreq( satp, &atpb, 1, 0 ) < 0 ) {
773 perror( "atp_sreq" );
777 DEBUG( printf( "SENDSTATUS >\n" ), fflush( stdout ));
779 atpb.atp_saddr = &nn.nn_sat;
780 rniov[ 0 ].iov_len = PAP_MAXDATA + 4;
781 atpb.atp_rresiov = rniov;
782 atpb.atp_rresiovcnt = 1;
783 if ( atp_rresp( satp, &atpb ) < 0 ) {
784 perror( "atp_rresp" );
788 #ifndef NONZEROSTATUS
790 * The stinking LaserWriter IINTX puts crap in this
793 if ( ((char *)rniov[ 0 ].iov_base)[ 0 ] != 0 ) {
794 fprintf( stderr, "Bad status response!\n" );
797 #endif /*NONZEROSTATUS*/
799 if ( ((char *)rniov[ 0 ].iov_base)[ 1 ] != PAP_STATUS ||
800 atpb.atp_rresiovcnt != 1 ) {
801 fprintf( stderr, "Bad status response!\n" );
805 DEBUG( printf( "< STATUS\n" ), fflush( stdout ));
808 if ( waitforprinter ) {
809 char st_buf[ 1024 ]; /* XXX too big */
811 memcpy( st_buf, (char *) rniov[ 0 ].iov_base + 9,
812 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
813 st_buf[ ((char *)rniov[ 0 ].iov_base)[ 8 ]] = '\0';
814 if ( strstr( st_buf, "waiting" ) != NULL ) {
820 updatestatus( (char *) rniov[ 0 ].iov_base + 9,
821 ((char *)rniov[ 0 ].iov_base)[ 8 ] );
826 updatestatus( s, len )
831 struct iovec iov[ 2 ];
837 if (( fd = open( status, O_WRONLY|O_TRUNC )) < 0 ) {
842 iov[ 0 ].iov_base = s;
843 iov[ 0 ].iov_len = len;
844 iov[ 1 ].iov_base = "\n";
845 iov[ 1 ].iov_len = 1;
846 writev( fd, iov, 2 );