static char sccsid[] = "$Id: sendfax.c,v 4.21 2005/12/31 16:01:11 gert Exp $ Copyright (c) Gert Doering"; /* sendfax.c * * Send a Fax using a class 2 faxmodem. * Calls routines in faxrec.c and faxlib.c * * The code is still quite rough, but it works. * * $Log: sendfax.c,v $ * Revision 4.21 2005/12/31 16:01:11 gert * identically handle class 1 and 1.0 * * Revision 4.20 2005/05/25 14:03:55 gert * add CVS LOG * */ #include #include #include #include #include "syslibs.h" #ifndef sunos4 #include #endif #include #include #ifndef ENOENT # include #endif #include "version.h" #include "mgetty.h" #include "tio.h" #include "policy.h" #include "fax_lib.h" /* configuration */ #include "config.h" #include "conf_sf.h" /* use direct bit order in modem, that means, we have to reverse */ #define REVERSE 1 char * fac_tel_no; boolean verbose = FALSE; extern time_t call_start; /* for accounting */ /* seems to missing nearly everywhere */ #if !defined(__NetBSD__) && !defined(__OpenBSD__) time_t time _PROTO(( time_t * tloc )); #endif TIO fax_tio; char *Device = "unset"; void exit_usage _P2( (program, msg ), char * program, char * msg ) { if ( msg != NULL ) { lprintf( L_ERROR, "exit_usage: %s", msg ); fprintf( stderr, "%s: %s\n", program, msg ); } fprintf( stderr, "usage: %s [options] \n", program); fprintf( stderr, "\tvalid options: -p, -v, -l , -x , -n, -S, -r, -D \n"); lprintf( L_AUDIT, "failed: command line error" ); exit(1); } RETSIGTYPE fax_sig_goodbye _P1( (signo), int signo ) { if ( call_start == 0 ) call_start = time(NULL); lprintf( L_AUDIT, "failed: got signal %d, pid=%d, dev=%s, time=%ds, acct=\"%s\"", signo, getpid(), Device, ( time(NULL)-call_start ), c_string(acct_handle)); rmlocks(); exit(15); /* will close the fax device */ } int fax_open_device _P2( (fax_tty, use_stdin), char * fax_tty, boolean use_stdin ) { char device[MAXPATH]; int fd; if ( use_stdin ) /* fax modem on stdin */ { fd = 0; Device = ttyname(fd); /* for faxrec() */ if ( Device == NULL || *Device == '\0' ) Device = "unknown"; } else { int tries; /* ignore leading "/dev/" prefix */ if ( strncmp( fax_tty, "/dev/", 5 ) == 0 ) fax_tty += 5; if ( verbose ) printf( "Trying fax device '/dev/%s'... ", fax_tty ); tries = 0; while ( makelock( fax_tty ) != SUCCESS ) { if ( ++ tries < 3 ) { if ( verbose ) { printf( "locked... " ); fflush( stdout ); } sleep(5); } else { if ( verbose ) { printf( "locked, give up!\n" ); fflush( stdout ); } lprintf( L_MESG, "cannot lock %s", fax_tty ); return -1; } } sprintf( device, "/dev/%s", fax_tty ); if ( ( fd = open( device, O_RDWR | O_NDELAY ) ) == -1 ) { lprintf( L_ERROR, "error opening %s", device ); if ( verbose ) printf( "cannot open!\n" ); rmlocks(); return fd; } /* make device name externally visible (faxrec()) * we have to dup() it, because caller will change fax_tty */ Device = malloc( strlen(fax_tty)+1 ); if ( Device == NULL ) { perror( "sendfax: can't malloc" ); exit(2); } strcpy(Device, fax_tty); } /* unset O_NDELAY (otherwise waiting for characters */ /* would be "busy waiting", eating up all cpu) */ /* AIX has a special surprise for us: if a tcp/ip serial port is broken, * open() will succeed, but fcntl() will hang, and the fcntl() sysctl is * always restarted - so we MUST crash out from a signal handler :-( */ signal( SIGALRM, fax_sig_goodbye ); alarm(10); if ( fcntl( fd, F_SETFL, O_RDWR ) == -1 ) { lprintf( L_ERROR, "error in fcntl" ); close( fd ); if ( verbose ) printf( "cannot fcntl!\n" ); rmlocks(); return -1; } alarm(0); /* initialize baud rate, hardware handshake, ... */ tio_get( fd, &fax_tio ); /* even if we use a modem that requires Xon/Xoff flow control, * do *not* enable it here - it would interefere with the Xon * received at the top of a page. */ tio_mode_sane( &fax_tio, TRUE ); tio_set_speed( &fax_tio, c_int(speed) ); tio_mode_raw( &fax_tio ); #ifdef sun /* sunos does not rx with RTSCTS unless carrier present */ tio_set_flow_control( fd, &fax_tio, FLOW_NONE ); #else tio_set_flow_control( fd, &fax_tio, (FAXSEND_FLOW) & FLOW_HARD ); #endif if ( tio_set( fd, &fax_tio ) == ERROR ) { lprintf( L_ERROR, "error in tio_set" ); close( fd ); if ( verbose ) printf( "cannot set termio values!\n" ); rmlocks(); return -1; } /* reset parameters */ fax_to_poll = FALSE; fax_remote_id[0] = 0; fax_param[0] = 0; if ( use_stdin ) { lprintf( L_NOISE, "fax_open_device, fax on stdin" ); } else { log_init_paths( NULL, NULL, &fax_tty[ strlen(fax_tty)-3 ] ); lprintf( L_NOISE, "fax_open_device succeeded, %s -> %d", fax_tty, fd ); } if ( verbose ) printf( "OK.\n" ); return fd; } /* fax_open: loop through all devices in fax_ttys until fax_open_device() * succeeds on one of them; then return file descriptor * return "-1" of no open succeeded (all locked, permission denied, ...) */ int fax_open _P2( (fax_ttys, use_stdin), char * fax_ttys, boolean use_stdin ) { char * p, * fax_tty; int fd; p = fax_tty = fax_ttys; do { p = strchr( fax_tty, ':' ); if ( p != NULL ) *p = 0; fd = fax_open_device( fax_tty, use_stdin ); if ( p != NULL ) *p = ':'; fax_tty = p+1; } while ( p != NULL && fd == -1 ); return fd; } /* finish off - close modem device, rm lockfile */ void fax_close _P1( (fd), int fd ) { tio_flush_queue( fd, TIO_Q_BOTH ); /* unlock flow ctl. */ fax_send( "AT+FCLASS=0", fd ); delay(500); tio_flush_queue( fd, TIO_Q_BOTH ); /* unlock flow ctl. */ close( fd ); rmlocks(); } /* sendfax-specific fax initializations */ /* polling: set calling station ID, receiver on, local poll on */ static int faxpoll_client_init _P2( (fd, cid), int fd, char * cid ) { char buf[60]; if ( modem_type == Mt_class2_0 ) { sprintf( buf, "AT+FPI=\"%.40s\"", cid ); if ( mdm_command( buf, fd ) == ERROR ) return ERROR; if ( mdm_command( "AT+FSP=1", fd ) == ERROR ) return ERROR; } else { sprintf( buf, "AT+FCIG=\"%.40s\"", cid ); if ( mdm_command( buf, fd ) == ERROR ) return ERROR; if ( mdm_command( "AT+FSPL=1", fd ) == ERROR ) return ERROR; } if ( mdm_command( "AT+FCR=1", fd ) == ERROR ) return ERROR; return NOERROR; } int main _P2( (argc, argv), int argc, char ** argv ) { int argidx; int fd; char buf[1000]; int i; int tries; /* number of unsuccessful tries */ int total_bytes = 0; /* number of bytes sent */ int total_pages = 0; /* number of pages (files) sent */ int total_resent= 0; /* number of pages resent */ /* initialize logging */ log_init_paths( argv[0], FAX_LOG, NULL ); /* parse switches (-> conf_sf.c) and read global config file */ if ( sendfax_parse_args( argc, argv ) == ERROR ) { exit_usage( argv[0], NULL ); } /* read config file (defaults) */ sendfax_get_config( NULL ); lprintf( L_MESG, "sendfax: %s", mgetty_version ); lprintf( L_NOISE, "%s compiled at %s, %s", __FILE__, __DATE__, __TIME__ ); /* for simplicity, put a few config things into global variables */ verbose = c_bool( verbose ); argidx = optind; /* fax number given? */ if ( argidx == argc ) { exit_usage( argv[0], "no fax number specified" ); } fac_tel_no = argv[ argidx++ ]; lprintf( L_MESG, "sending fax to %s", fac_tel_no ); /* check, if all the arguments passed are normal files and * readable */ for ( i=argidx; iphase B) */ else ppm = pp_eop; /* over & out (->hangup) */ } else /* not last page -> */ ppm = pp_mps; /* another page next */ fax_page_tx_status = -1; /* set by fax_send_page() */ if ( fax_send_page( argv[ argidx ], &total_bytes, &fax_tio, ppm, fd ) == ERROR ) { break; } /* after the page punctuation command, the modem * will send us a +FPTS: page transmit status. * The ppm value is written to fax_page_tx_status by * fax_send_page() / fax_send_ppm() * If the other side requests retransmission, do so. */ switch ( fax_page_tx_status ) { case 1: break; /* page good */ /* page bad - r. req. */ case 2: if ( c_int(max_tries) <= 0 ) /* ignore */ { fprintf( stderr, "WARNING: page bad (RTN), ignoring\n" ); lprintf( L_WARN, "WARNING: RTN ignored\n" ); } else /* try again */ { fprintf( stderr, "ERROR: RTN: page bad - retrain requested\n" ); tries ++; if ( tries >= c_int(max_tries) ) /* max tries reached */ { if ( c_bool(max_tries_ctd) ) /* go on */ { fprintf( stderr, "WARNING: maximum number of retries reached, going on\n" ); lprintf( L_WARN, "max. tries (%d) reached, going on", tries ); } else /* abort */ { fprintf( stderr, "ERROR: too many retries - aborting send\n" ); fax_hangup_code = -1; fax_hangup = 1; } } else { if ( verbose ) printf( "sending page again (retry %d)\n", tries ); total_resent++; continue; /* don't go to next page */ } } break; case 3: fprintf( stderr, "WARNING: RTP: page good, but retrain requested\n" ); break; case 4: case 5: fprintf( stderr, "WARNING: procedure interrupt requested - don't know how to handle it\n" ); break; case -1: /* something broke */ lprintf( L_WARN, "fpts:-1" ); break; default:fprintf( stderr, "WARNING: invalid code: +FPTS:%d\n", fax_page_tx_status ); break; } if ( fax_hangup && fax_hangup_code != 0 ) break; /* page transmitted successfully, rename file to ".done" */ if ( c_bool( rename_files ) ) { char done[MAXPATH+6]; if ( strlen( argv[argidx] ) > sizeof(done)-6 ) fprintf( stderr, "file name %s too long\n", argv[argidx] ); else { sprintf( done, "%s.done", argv[argidx] ); if ( rename( argv[argidx], done ) == -1 ) lprintf( L_ERROR, "can't rename work file to %s", done ); } } argidx++; /* next page */ tries=0; /* no tries yet */ } /* end main page loop */ if ( argidx < argc || ( fax_hangup && fax_hangup_code != 0 ) ) { lprintf( L_AUDIT, "failed transmitting %s: phone=\"%s\", +FHS:%02d, dev=%s, time=%ds, acct=\"%s\"", argv[argidx], fac_tel_no, fax_hangup_code, Device, ( time(NULL)-call_start ), c_string(acct_handle) ); fprintf( stderr, "\n%s: FAILED to transmit '%s'.\n", argv[0], argv[argidx] ); if ( fax_hangup_code == -1 ) fprintf( stderr, "(number of tries exhausted)\n" ); else fprintf( stderr, "Transmission error: +FHNG:%2d (%s)\n", fax_hangup_code, fax_strerror( fax_hangup_code ) ); fax_close( fd ); exit(12); } /* OK, handle (optional) fax polling now. * Fax polling will only be tried if user specified "-p" and the * faxmodem sent us a "+FPOLL" response */ if ( c_bool(fax_poll_wanted) ) { int pagenum = 0; if ( verbose ) printf( "starting fax poll\n" ); if ( ! fax_to_poll ) { printf( "remote does not have document to poll!\n" ); } else { /* class 2.0 modems use the correct byte order, Rockwell- * compatible class 2 modems get it wrong. */ if ( modem_type == Mt_class2_0 ) fax_set_bor( fd, 1 ); /* switch to fax receiver flow control */ tio_set_flow_control( fd, &fax_tio, (FAXREC_FLOW) & (FLOW_HARD|FLOW_XON_IN) ); tio_set( fd, &fax_tio ); if ( fax_get_pages( fd, &pagenum, c_string(poll_dir), -1, -1, -1 ) == ERROR ) { fprintf( stderr, "warning: polling failed\n" ); lprintf( L_AUDIT, "failed: polling failed, phone=\"%s\", +FHS:%02d, dev=%s, time=%ds, acct=\"%s\"", fac_tel_no, fax_hangup_code, Device, ( time(NULL)-call_start ), c_string(acct_handle) ); fax_close( fd ); exit(12); } } if ( verbose ) printf( "%d pages successfully polled!\n", pagenum ); } fax_close( fd ); lprintf( L_AUDIT, "success, phone=\"%s\", dev=%s, time=%ds, pages=%d(+%d), bytes=%d, acct=\"%s\"", fac_tel_no, Device, ( time(NULL)-call_start ), total_pages, total_resent, total_bytes, c_string(acct_handle) ); return 0; }