/* lsz - send files with x/y/zmodem Copyright (C) until 1988 Chuck Forsberg (Omen Technology INC) Copyright (C) 1994 Matt Porter, Michael D. Black Copyright (C) 1996, 1997 Uwe Ohse This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. originally written by Chuck Forsberg */ #include "zglobal.h" /* char *getenv(); */ #define SS_NORMAL 0 #include #include #include #include #include #include #include #ifndef R_OK # define R_OK 4 #endif #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) # include size_t mm_size; void *mm_addr=NULL; #else # undef HAVE_MMAP #endif #include "timing.h" #include "long-options.h" #include "xstrtoul.h" #include "error.h" #ifndef STRICT_PROTOTYPES extern time_t time(); extern char *strerror(); #endif #ifndef HAVE_ERRNO_DECLARATION extern int errno; #endif unsigned Baudrate=2400; /* Default, should be set by first mode() call */ unsigned Txwindow; /* Control the size of the transmitted window */ unsigned Txwspac; /* Spacing between zcrcq requests */ unsigned Txwcnt; /* Counter used to space ack requests */ size_t Lrxpos; /* Receiver's last reported offset */ int errors; enum zm_type_enum protocol; int under_rsh=FALSE; extern int turbo_escape; static int no_unixmode; int Canseek=1; /* 1: can; 0: only rewind, -1: neither */ static int zsendfile __P ((struct zm_fileinfo *zi, const char *buf, size_t blen)); static int getnak __P ((void)); static int wctxpn __P ((struct zm_fileinfo *)); static int wcs __P ((const char *oname, const char *remotename)); static size_t zfilbuf __P ((struct zm_fileinfo *zi)); static size_t filbuf __P ((char *buf, size_t count)); static int getzrxinit __P ((void)); static int calc_blklen __P ((long total_sent)); static int sendzsinit __P ((void)); static int wctx __P ((struct zm_fileinfo *)); static int zsendfdata __P ((struct zm_fileinfo *)); static int getinsync __P ((struct zm_fileinfo *, int flag)); static void countem __P ((int argc, char **argv)); static void chkinvok __P ((const char *s)); static void usage __P ((int exitcode, const char *what)); static int zsendcmd __P ((const char *buf, size_t blen)); static void saybibi __P ((void)); static int wcsend __P ((int argc, char *argp[])); static int wcputsec __P ((char *buf, int sectnum, size_t cseclen)); static void usage1 __P ((int exitcode)); #ifdef ENABLE_SYSLOG #define DO_SYSLOG(message) do { \ if (enable_syslog) { \ const char *shortname; \ if (!zi->fname) \ shortname="no.name"; \ else { \ shortname=strrchr(zi->fname,'/'); \ if (!shortname) \ shortname=zi->fname; \ else \ shortname++; \ } \ lsyslog message ; \ } \ } while(0) #else #define DO_SYSLOG(message) do { } while(0) #endif #define ZSDATA(x,y,z) \ do { if (Crc32t) {zsda32(x,y,z); } else {zsdata(x,y,z);}} while(0) #ifdef HAVE_MMAP #define DATAADR (mm_addr ? ((char *)mm_addr)+zi->bytes_sent : txbuf) #else #define DATAADR (txbuf) #endif int Filesleft; long Totalleft; size_t buffersize=16384; #ifdef HAVE_MMAP int use_mmap=1; #endif /* * Attention string to be executed by receiver to interrupt streaming data * when an error is detected. A pause (0336) may be needed before the * ^C (03) or after it. */ #ifdef READCHECK char Myattn[] = { 0 }; #else char Myattn[] = { 03, 0336, 0 }; #endif FILE *input_f; #define MAX_BLOCK 8192 char *txbuf; long vpos = 0; /* Number of bytes read from file */ char Lastrx; char Crcflg; int Verbose=0; int Restricted=0; /* restricted; no /.. or ../ in filenames */ int Quiet=0; /* overrides logic that would otherwise set verbose */ int Ascii=0; /* Add CR's for brain damaged programs */ int Fullname=0; /* transmit full pathname */ int Unlinkafter=0; /* Unlink file after it is sent */ int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */ int firstsec; int errcnt=0; /* number of files unreadable */ size_t blklen=128; /* length of transmitted records */ int Optiong; /* Let it rip no wait for sector ACK's */ int Totsecs; /* total number of sectors this file */ int Filcnt=0; /* count of number of files opened */ int Lfseen=0; unsigned Rxbuflen = 16384; /* Receiver's max buffer length */ unsigned Tframlen = 0; /* Override for tx frame length */ unsigned blkopt=0; /* Override value for zmodem blklen */ int Rxflags = 0; int Rxflags2 = 0; size_t bytcnt; int Wantfcs32 = TRUE; /* want to send 32 bit FCS */ char Lzconv; /* Local ZMODEM file conversion request */ char Lzmanag; /* Local ZMODEM file management request */ int Lskipnocor; char Lztrans; char zconv; /* ZMODEM file conversion request */ char zmanag; /* ZMODEM file management request */ char ztrans; /* ZMODEM file transport request */ int command_mode; /* Send a command, then exit. */ int Cmdtries = 11; int Cmdack1; /* Rx ACKs command, then do it */ int Exitcode; int enable_timesync=0; size_t Lastsync; /* Last offset to which we got a ZRPOS */ int Beenhereb4; /* How many times we've been ZRPOS'd same place */ int no_timeout=FALSE; size_t max_blklen=1024; size_t start_blklen=0; int zmodem_requested; time_t stop_time=0; char *tcp_server_address=0; int tcp_socket=-1; int tcp_flag=0; int error_count; #define OVERHEAD 18 #define OVER_ERR 20 #define MK_STRING(x) #x #ifdef ENABLE_SYSLOG # if defined(ENABLE_SYSLOG_FORCE) || defined(ENABLE_SYSLOG_DEFAULT) int enable_syslog=TRUE; # else int enable_syslog=FALSE; # endif #endif jmp_buf intrjmp; /* For the interrupt on RX CAN */ static long min_bps; static long min_bps_time; static int io_mode_fd=0; static int zrqinits_sent=0; static int play_with_sigint=0; /* called by signal interrupt or terminate to clean things up */ RETSIGTYPE bibi (int n) { canit(STDOUT_FILENO); fflush (stdout); io_mode (io_mode_fd,0); if (n == 99) error (0, 0, _ ("io_mode(,2) in rbsb.c not implemented\n")); else error (0, 0, _ ("caught signal %d; exiting"), n); if (n == SIGQUIT) abort (); exit (128 + n); } /* Called when ZMODEM gets an interrupt (^C) */ static RETSIGTYPE onintr(int n) { signal(SIGINT, SIG_IGN); n++; /* use it */ longjmp(intrjmp, -1); } int Zctlesc; /* Encode control characters */ const char *program_name = "sz"; int Zrwindow = 1400; /* RX window size (controls garbage count) */ static struct option const long_options[] = { {"append", no_argument, NULL, '+'}, {"twostop", no_argument, NULL, '2'}, {"try-8k", no_argument, NULL, '8'}, {"start-8k", no_argument, NULL, '9'}, {"try-4k", no_argument, NULL, '4'}, {"start-4k", no_argument, NULL, '5'}, {"ascii", no_argument, NULL, 'a'}, {"binary", no_argument, NULL, 'b'}, {"bufsize", required_argument, NULL, 'B'}, {"cmdtries", required_argument, NULL, 'C'}, {"command", required_argument, NULL, 'c'}, {"immediate-command", required_argument, NULL, 'i'}, {"dot-to-slash", no_argument, NULL, 'd'}, {"full-path", no_argument, NULL, 'f'}, {"escape", no_argument, NULL, 'e'}, {"rename", no_argument, NULL, 'E'}, {"help", no_argument, NULL, 'h'}, {"crc-check", no_argument, NULL, 'H'}, {"1024", no_argument, NULL, 'k'}, {"1k", no_argument, NULL, 'k'}, {"packetlen", required_argument, NULL, 'L'}, {"framelen", required_argument, NULL, 'l'}, {"min-bps", required_argument, NULL, 'm'}, {"min-bps-time", required_argument, NULL, 'M'}, {"newer", no_argument, NULL, 'n'}, {"newer-or-longer", no_argument, NULL, 'N'}, {"16-bit-crc", no_argument, NULL, 'o'}, {"disable-timeouts", no_argument, NULL, 'O'}, {"disable-timeout", no_argument, NULL, 'O'}, /* i can't get it right */ {"protect", no_argument, NULL, 'p'}, {"resume", no_argument, NULL, 'r'}, {"restricted", no_argument, NULL, 'R'}, {"quiet", no_argument, NULL, 'q'}, {"stop-at", required_argument, NULL, 's'}, {"syslog", optional_argument, NULL , 2}, {"timesync", no_argument, NULL, 'S'}, {"timeout", required_argument, NULL, 't'}, {"turbo", no_argument, NULL, 'T'}, {"unlink", no_argument, NULL, 'u'}, {"unrestrict", no_argument, NULL, 'U'}, {"verbose", no_argument, NULL, 'v'}, {"windowsize", required_argument, NULL, 'w'}, {"xmodem", no_argument, NULL, 'X'}, {"ymodem", no_argument, NULL, 1}, {"zmodem", no_argument, NULL, 'Z'}, {"overwrite", no_argument, NULL, 'y'}, {"overwrite-or-skip", no_argument, NULL, 'Y'}, {"delay-startup", required_argument, NULL, 4}, {"tcp-server", no_argument, NULL, 6}, {"tcp-client", required_argument, NULL, 7}, {"no-unixmode", no_argument, NULL, 8}, {NULL, 0, NULL, 0} }; static void show_version(void) { printf ("%s (%s) %s\n", program_name, PACKAGE, VERSION); } int main(int argc, char **argv) { char *cp; int npats; int dm; int i; int stdin_files; char **patts; int c; const char *Cmdstr=NULL; /* Pointer to the command string */ unsigned int startup_delay=0; if (((cp = getenv("ZNULLS")) != NULL) && *cp) Znulls = atoi(cp); if (((cp=getenv("SHELL"))!=NULL) && (strstr(cp, "rsh") || strstr(cp, "rksh") || strstr(cp, "rbash") || strstr(cp,"rshell"))) { under_rsh=TRUE; Restricted=1; } if ((cp=getenv("ZMODEM_RESTRICTED"))!=NULL) Restricted=1; from_cu(); chkinvok(argv[0]); #ifdef ENABLE_SYSLOG openlog(program_name,LOG_PID,ENABLE_SYSLOG); #endif setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); parse_long_options (argc, argv, show_version, usage1); Rxtimeout = 600; while ((c = getopt_long (argc, argv, "2+48abB:C:c:dfeEghHi:kL:l:m:M:NnOopRrqsSt:TUuvw:XYy", long_options, (int *) 0))!=EOF) { unsigned long int tmp; char *tmpptr; enum strtol_error s_err; switch (c) { case 0: break; case '+': Lzmanag = ZF1_ZMAPND; break; case '2': Twostop = TRUE; break; case '8': if (max_blklen==8192) start_blklen=8192; else max_blklen=8192; break; case '9': /* this is a longopt .. */ start_blklen=8192; max_blklen=8192; break; case '4': if (max_blklen==4096) start_blklen=4096; else max_blklen=4096; break; case '5': /* this is a longopt .. */ start_blklen=4096; max_blklen=4096; break; case 'a': Lzconv = ZCNL; Ascii = TRUE; break; case 'b': Lzconv = ZCBIN; break; case 'B': if (0==strcmp(optarg,"auto")) buffersize= (size_t) -1; else buffersize=strtol(optarg,NULL,10); #ifdef HAVE_MMAP use_mmap=0; #endif break; case 'C': s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); Cmdtries = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("command tries"), s_err); break; case 'i': Cmdack1 = ZCACK1; /* **** FALL THROUGH TO **** */ case 'c': command_mode = TRUE; Cmdstr = optarg; break; case 'd': ++Dottoslash; /* **** FALL THROUGH TO **** */ case 'f': Fullname=TRUE; break; case 'e': Zctlesc = 1; break; case 'E': Lzmanag = ZF1_ZMCHNG; break; case 'h': usage(0,NULL); break; case 'H': Lzmanag = ZF1_ZMCRC; break; case 'k': start_blklen=1024; break; case 'L': s_err = xstrtoul (optarg, NULL, 0, &tmp, "ck"); blkopt = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("packetlength"), s_err); if (blkopt<24 || blkopt>MAX_BLOCK) { char meld[256]; sprintf(meld, _("packetlength out of range 24..%ld"), (long) MAX_BLOCK); usage(2,meld); } break; case 'l': s_err = xstrtoul (optarg, NULL, 0, &tmp, "ck"); Tframlen = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("framelength"), s_err); if (Tframlen<32 || Tframlen>MAX_BLOCK) { char meld[256]; sprintf(meld, _("framelength out of range 32..%ld"), (long) MAX_BLOCK); usage(2,meld); } break; case 'm': s_err = xstrtoul (optarg, &tmpptr, 0, &tmp, "km"); min_bps = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("min_bps"), s_err); if (min_bps<0) usage(2,_("min_bps must be >= 0")); break; case 'M': s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); min_bps_time = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("min_bps_time"), s_err); if (min_bps_time<=1) usage(2,_("min_bps_time must be > 1")); break; case 'N': Lzmanag = ZF1_ZMNEWL; break; case 'n': Lzmanag = ZF1_ZMNEW; break; case 'o': Wantfcs32 = FALSE; break; case 'O': no_timeout = TRUE; break; case 'p': Lzmanag = ZF1_ZMPROT; break; case 'r': if (Lzconv == ZCRESUM) Lzmanag = ZF1_ZMCRC; else Lzconv = ZCRESUM; break; case 'R': Restricted = TRUE; break; case 'q': Quiet=TRUE; Verbose=0; break; case 's': if (isdigit((unsigned char) (*optarg))) { struct tm *tm; time_t t; int hh,mm; char *nex; hh = strtoul (optarg, &nex, 10); if (hh>23) usage(2,_("hour to large (0..23)")); if (*nex!=':') usage(2, _("unparsable stop time\n")); nex++; mm = strtoul (optarg, &nex, 10); if (mm>59) usage(2,_("minute to large (0..59)")); t=time(NULL); tm=localtime(&t); tm->tm_hour=hh; tm->tm_min=hh; stop_time=mktime(tm); if (stop_time1000) usage(2,_("timeout out of range 10..1000")); break; case 'u': ++Unlinkafter; break; case 'U': if (!under_rsh) Restricted=0; else error(1,0, _("security violation: can't do that under restricted shell\n")); break; case 'v': ++Verbose; break; case 'w': s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); Txwindow = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("window size"), s_err); if (Txwindow < 256) Txwindow = 256; Txwindow = (Txwindow/64) * 64; Txwspac = Txwindow/4; if (blkopt > Txwspac || (!blkopt && Txwspac < MAX_BLOCK)) blkopt = Txwspac; break; case 'X': protocol=ZM_XMODEM; break; case 1: protocol=ZM_YMODEM; break; case 'Z': protocol=ZM_ZMODEM; break; case 'Y': Lskipnocor = TRUE; /* **** FALLL THROUGH TO **** */ case 'y': Lzmanag = ZF1_ZMCLOB; break; case 2: #ifdef ENABLE_SYSLOG # ifndef ENABLE_SYSLOG_FORCE if (optarg && (!strcmp(optarg,"off") || !strcmp(optarg,"no"))) { if (under_rsh) error(0,0, _("cannot turnoff syslog")); else enable_syslog=FALSE; } else enable_syslog=TRUE; # else error(0,0, _("cannot turnoff syslog")); # endif #endif break; case 4: s_err = xstrtoul (optarg, NULL, 0, &tmp, NULL); startup_delay = tmp; if (s_err != LONGINT_OK) STRTOL_FATAL_ERROR (optarg, _("startup delay"), s_err); break; case 6: tcp_flag=2; break; case 7: tcp_flag=3; tcp_server_address=(char *)strdup(optarg); if (!tcp_server_address) { error(1,0,_("out of memory")); } break; case 8: no_unixmode=1; break; default: usage (2,NULL); break; } } if (getuid()!=geteuid()) { error(1,0, _("this program was never intended to be used setuid\n")); } txbuf=malloc(MAX_BLOCK); if (!txbuf) error(1,0,_("out of memory")); zsendline_init(); if (start_blklen==0) { if (protocol == ZM_ZMODEM) { start_blklen=1024; if (Tframlen) { start_blklen=max_blklen=Tframlen; } } else start_blklen=128; } if (argc<2) usage(2,_("need at least one file to send")); if (startup_delay) sleep(startup_delay); #ifdef HAVE_SIGINTERRUPT /* we want interrupted system calls to fail and not to be restarted. */ siginterrupt(SIGALRM,1); #endif npats = argc - optind; patts=&argv[optind]; if (npats < 1 && !command_mode) usage(2,_("need at least one file to send")); if (command_mode && Restricted) { printf(_("Can't send command in restricted mode\n")); exit(1); } if (Fromcu && !Quiet) { if (Verbose == 0) Verbose = 2; } vfile("%s %s\n", program_name, VERSION); if (tcp_flag==2) { char buf[256]; #ifdef MAXHOSTNAMELEN char hn[MAXHOSTNAMELEN]; #else char hn[256]; #endif char *p,*q; int d; /* tell receiver to receive via tcp */ d=tcp_server(buf); p=strchr(buf+1,'<'); p++; q=strchr(p,'>'); *q=0; if (gethostname(hn,sizeof(hn))==-1) { error(1,0, _("hostname too long\n")); } fprintf(stdout,"connect with lrz --tcp-client \"%s:%s\"\n",hn,p); fflush(stdout); /* ok, now that this file is sent we can switch to tcp */ tcp_socket=tcp_accept(d); dup2(tcp_socket,0); dup2(tcp_socket,1); } if (tcp_flag==3) { char buf[256]; char *p; p=strchr(tcp_server_address,':'); if (!p) error(1,0, _("illegal server address\n")); *p++=0; sprintf(buf,"[%s] <%s>\n",tcp_server_address,p); fprintf(stdout,"connecting to %s\n",buf); fflush(stdout); /* we need to switch to tcp mode */ tcp_socket=tcp_connect(buf); dup2(tcp_socket,0); dup2(tcp_socket,1); } { /* we write max_blocklen (data) + 18 (ZModem protocol overhead) * + escape overhead (about 4 %), so buffer has to be * somewhat larger than max_blklen */ char *s=malloc(max_blklen+1024); if (!s) { zperr(_("out of memory")); exit(1); } #ifdef SETVBUF_REVERSED setvbuf(stdout,_IOFBF,s,max_blklen+1024); #else setvbuf(stdout,s,_IOFBF,max_blklen+1024); #endif } blklen=start_blklen; for (i=optind,stdin_files=0;i1) { usage(1,_("can read only one file from stdin")); } else if (stdin_files==1) { io_mode_fd=1; } io_mode(io_mode_fd,1); readline_setup(io_mode_fd, 128, 256); if (signal(SIGINT, bibi) == SIG_IGN) signal(SIGINT, SIG_IGN); else { signal(SIGINT, bibi); play_with_sigint=1; } signal(SIGTERM, bibi); signal(SIGPIPE, bibi); signal(SIGHUP, bibi); if ( protocol!=ZM_XMODEM) { if (protocol==ZM_ZMODEM) { printf("rz\r"); fflush(stdout); } countem(npats, patts); if (protocol == ZM_ZMODEM) { /* throw away any input already received. This doesn't harm * as we invite the receiver to send it's data again, and * might be useful if the receiver has already died or * if there is dirt left if the line */ #ifdef HAVE_SELECT struct timeval t; unsigned char throwaway; fd_set f; #endif purgeline(io_mode_fd); #ifdef HAVE_SELECT t.tv_sec = 0; t.tv_usec = 0; FD_ZERO(&f); FD_SET(io_mode_fd,&f); while (select(1,&f,NULL,NULL,&t)) { if (0==read(io_mode_fd,&throwaway,1)) /* EOF ... */ break; } #endif purgeline(io_mode_fd); stohdr(0L); if (command_mode) Txhdr[ZF0] = ZCOMMAND; zshhdr(ZRQINIT, Txhdr); zrqinits_sent++; #if defined(ENABLE_TIMESYNC) if (Rxflags2 != ZF1_TIMESYNC) /* disable timesync if there are any flags we don't know. * dsz/gsz seems to use some other flags! */ enable_timesync=FALSE; if (Rxflags2 & ZF1_TIMESYNC && enable_timesync) { Totalleft+=6; /* TIMESYNC never needs more */ Filesleft++; } #endif } } fflush(stdout); if (Cmdstr) { if (getzrxinit()) { Exitcode=0200; canit(STDOUT_FILENO); } else if (zsendcmd(Cmdstr, strlen(Cmdstr)+1)) { Exitcode=0200; canit(STDOUT_FILENO); } } else if (wcsend(npats, patts)==ERROR) { Exitcode=0200; canit(STDOUT_FILENO); } fflush(stdout); io_mode(io_mode_fd,0); if (Exitcode) dm=Exitcode; else if (errcnt) dm=1; else dm=0; if (Verbose) { fputs("\r\n",stderr); if (dm) fputs(_("Transfer incomplete\n"),stderr); else fputs(_("Transfer complete\n"),stderr); } exit(dm); /*NOTREACHED*/ } static int send_pseudo(const char *name, const char *data) { char *tmp; const char *p; int ret=0; /* ok */ size_t plen; int fd; int lfd; p = getenv ("TMPDIR"); if (!p) p = getenv ("TMP"); if (!p) p = "/tmp"; tmp=malloc(PATH_MAX+1); if (!tmp) error(1,0,_("out of memory")); plen=strlen(p); memcpy(tmp,p,plen); tmp[plen++]='/'; lfd=0; do { if (lfd++==10) { free(tmp); vstringf (_ ("send_pseudo %s: cannot open tmpfile %s: %s"), name, tmp, strerror (errno)); vstring ("\r\n"); return 1; } sprintf(tmp+plen,"%s.%lu.%d",name,(unsigned long) getpid(),lfd); fd=open(tmp,O_WRONLY|O_CREAT|O_EXCL,0700); /* is O_EXCL guaranted to not follow symlinks? * I don`t know ... so be careful */ if (fd!=-1) { struct stat st; if (0!=lstat(tmp,&st)) { vstringf (_ ("send_pseudo %s: cannot lstat tmpfile %s: %s"), name, tmp, strerror (errno)); vstring ("\r\n"); unlink(tmp); close(fd); fd=-1; } else { if (S_ISLNK(st.st_mode)) { vstringf (_ ("send_pseudo %s: avoiding symlink trap"),name); vstring ("\r\n"); unlink(tmp); close(fd); fd=-1; } } } } while (fd==-1); if (write(fd,data,strlen(data))!=(signed long) strlen(data) || close(fd)!=0) { vstringf (_ ("send_pseudo %s: cannot write to tmpfile %s: %s"), name, tmp, strerror (errno)); vstring ("\r\n"); free(tmp); return 1; } if (wcs (tmp,name) == ERROR) { if (Verbose) vstringf (_ ("send_pseudo %s: failed"),name); else { if (Verbose) vstringf (_ ("send_pseudo %s: ok"),name); Filcnt--; } vstring ("\r\n"); ret=1; } unlink (tmp); free(tmp); return ret; } static int wcsend (int argc, char *argp[]) { int n; Crcflg = FALSE; firstsec = TRUE; bytcnt = (size_t) -1; for (n = 0; n < argc; ++n) { Totsecs = 0; if (wcs (argp[n],NULL) == ERROR) return ERROR; } #if defined(ENABLE_TIMESYNC) if (Rxflags2 & ZF1_TIMESYNC && enable_timesync) { /* implement Peter Mandrellas extension */ char buf[60]; time_t t = time (NULL); struct tm *tm = localtime (&t); /* sets timezone */ strftime (buf, sizeof (buf) - 1, "%H:%M:%S", tm); if (Verbose) { vstring ("\r\n"); vstringf (_("Answering TIMESYNC at %s"),buf); } #if defined(HAVE_TIMEZONE_VAR) sprintf(buf+strlen(buf),"%ld\r\n", timezone / 60); if (Verbose) vstringf (" (%s %ld)\r\n", _ ("timezone"), timezone / 60); #else if (Verbose) vstringf (" (%s)\r\n", _ ("timezone unknown")); #endif send_pseudo("/$time$.t",buf); } #endif Totsecs = 0; if (Filcnt == 0) { /* bitch if we couldn't open ANY files */ #if 0 /* i *really* do not like this */ if (protocol != ZM_XMODEM) { const char *Cmdstr; /* Pointer to the command string */ command_mode = TRUE; Cmdstr = "echo \"lsz: Can't open any requested files\""; if (getnak ()) { Exitcode = 0200; canit(STDOUT_FILENO); } if (!zmodem_requested) canit(STDOUT_FILENO); else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr))) { Exitcode = 0200; canit(STDOUT_FILENO); } Exitcode = 1; return OK; } #endif canit(STDOUT_FILENO); vstring ("\r\n"); vstringf (_ ("Can't open any requested files.")); vstring ("\r\n"); return ERROR; } if (zmodem_requested) saybibi (); else if (protocol != ZM_XMODEM) { struct zm_fileinfo zi; char *pa; pa=alloca(PATH_MAX+1); *pa='\0'; zi.fname = pa; zi.modtime = 0; zi.mode = 0; zi.bytes_total = 0; zi.bytes_sent = 0; zi.bytes_received = 0; zi.bytes_skipped = 0; wctxpn (&zi); } return OK; } static int wcs(const char *oname, const char *remotename) { #if !defined(S_ISDIR) int c; #endif struct stat f; char *name; struct zm_fileinfo zi; #ifdef HAVE_MMAP int dont_mmap_this=0; #endif #ifdef ENABLE_SYSLOG const char *shortname; shortname=strrchr(oname,'/'); if (shortname) shortname++; else shortname=oname; #endif if (Restricted) { /* restrict pathnames to current tree or uucppublic */ if ( strstr(oname, "../") #ifdef PUBDIR || (oname[0]== '/' && strncmp(oname, MK_STRING(PUBDIR), strlen(MK_STRING(PUBDIR)))) #endif ) { canit(STDOUT_FILENO); vchar('\r'); error(1,0, _("security violation: not allowed to upload from %s"),oname); } } if (0==strcmp(oname,"-")) { char *p=getenv("ONAME"); name=alloca(PATH_MAX+1); if (p) { strcpy(name, p); } else { sprintf(name, "s%lu.lsz", (unsigned long) getpid()); } input_f=stdin; #ifdef HAVE_MMAP dont_mmap_this=1; #endif } else if ((input_f=fopen(oname, "r"))==NULL) { int e=errno; error(0,e, _("cannot open %s"),oname); ++errcnt; return OK; /* pass over it, there may be others */ } else { name=alloca(PATH_MAX+1); strcpy(name, oname); } #ifdef HAVE_MMAP if (!use_mmap || dont_mmap_this) #endif { static char *s=NULL; static size_t last_length=0; struct stat st; if (fstat(fileno(input_f),&st)==-1) st.st_size=1024*1024; if (buffersize==(size_t) -1 && s) { if ((size_t) st.st_size > last_length) { free(s); s=NULL; last_length=0; } } if (!s && buffersize) { last_length=16384; if (buffersize==(size_t) -1) { if (st.st_size>0) last_length=st.st_size; } else last_length=buffersize; /* buffer whole pages */ last_length=(last_length+4095)&0xfffff000; s=malloc(last_length); if (!s) { zpfatal(_("out of memory")); exit(1); } } if (s) { #ifdef SETVBUF_REVERSED setvbuf(input_f,_IOFBF,s,last_length); #else setvbuf(input_f,s,_IOFBF,last_length); #endif } } vpos = 0; /* Check for directory or block special files */ fstat(fileno(input_f), &f); #if defined(S_ISDIR) if (S_ISDIR(f.st_mode) || S_ISBLK(f.st_mode)) { #else c = f.st_mode & S_IFMT; if (c == S_IFDIR || c == S_IFBLK) { #endif error(0,0, _("is not a file: %s"),name); fclose(input_f); return OK; } if (remotename) { /* disqualify const */ union { const char *c; char *s; } cheat; cheat.c=remotename; zi.fname=cheat.s; } else zi.fname=name; zi.modtime=f.st_mtime; zi.mode=f.st_mode; #if defined(S_ISFIFO) zi.bytes_total= (S_ISFIFO(f.st_mode)) ? DEFBYTL : f.st_size; #else zi.bytes_total= c == S_IFIFO ? DEFBYTL : f.st_size; #endif zi.bytes_sent=0; zi.bytes_received=0; zi.bytes_skipped=0; zi.eof_seen=0; timing(1,NULL); ++Filcnt; switch (wctxpn(&zi)) { case ERROR: #ifdef ENABLE_SYSLOG if (enable_syslog) lsyslog(LOG_INFO, _("%s/%s: error occured"),protname(),shortname); #endif return ERROR; case ZSKIP: error(0,0, _("skipped: %s"),name); #ifdef ENABLE_SYSLOG if (enable_syslog) lsyslog(LOG_INFO, _("%s/%s: skipped"),protname(),shortname); #endif return OK; } if (!zmodem_requested && wctx(&zi)==ERROR) { #ifdef ENABLE_SYSLOG if (enable_syslog) lsyslog(LOG_INFO, _("%s/%s: error occured"),protname(),shortname); #endif return ERROR; } if (Unlinkafter) unlink(oname); if (Verbose > 1 #ifdef ENABLE_SYSLOG || enable_syslog #endif ) { long bps; double d=timing(0,NULL); if (d==0) /* can happen if timing() uses time() */ d=0.5; bps=zi.bytes_sent/d; vchar('\r'); if (Verbose > 1) vstringf(_("Bytes Sent:%7ld BPS:%-8ld \n"), (long) zi.bytes_sent,bps); #ifdef ENABLE_SYSLOG if (enable_syslog) lsyslog(LOG_INFO, "%s/%s: %ld Bytes, %ld BPS",shortname, protname(), (long) zi.bytes_sent,bps); #endif } return 0; } /* * generate and transmit pathname block consisting of * pathname (null terminated), * file length, mode time and file mode in octal * as provided by the Unix fstat call. * N.B.: modifies the passed name, may extend it! */ static int wctxpn(struct zm_fileinfo *zi) { register char *p, *q; char *name2; struct stat f; name2=alloca(PATH_MAX+1); if (protocol==ZM_XMODEM) { if (Verbose && *zi->fname && fstat(fileno(input_f), &f)!= -1) { vstringf(_("Sending %s, %ld blocks: "), zi->fname, (long) (f.st_size>>7)); } vstringf(_("Give your local XMODEM receive command now.")); vstring("\r\n"); return OK; } if (!zmodem_requested) if (getnak()) { vfile("getnak failed"); DO_SYSLOG((LOG_INFO, "%s/%s: getnak failed", shortname,protname())); return ERROR; } q = (char *) 0; if (Dottoslash) { /* change . to . */ for (p=zi->fname; *p; ++p) { if (*p == '/') q = p; else if (*p == '.') *(q=p) = '/'; } if (q && strlen(++q) > 8) { /* If name>8 chars */ q += 8; /* make it .ext */ strcpy(name2, q); /* save excess of name */ *q = '.'; strcpy(++q, name2); /* add it back */ } } for (p=zi->fname, q=txbuf ; *p; ) if ((*q++ = *p++) == '/' && !Fullname) q = txbuf; *q++ = 0; p=q; while (q < (txbuf + MAX_BLOCK)) *q++ = 0; /* note that we may lose some information here in case mode_t is wider than an * int. But i believe sending %lo instead of %o _could_ break compatability */ if (!Ascii && (input_f!=stdin) && *zi->fname && fstat(fileno(input_f), &f)!= -1) sprintf(p, "%lu %lo %o 0 %d %ld", (long) f.st_size, f.st_mtime, (unsigned int)((no_unixmode) ? 0 : f.st_mode), Filesleft, Totalleft); if (Verbose) vstringf(_("Sending: %s\n"),txbuf); Totalleft -= f.st_size; if (--Filesleft <= 0) Totalleft = 0; if (Totalleft < 0) Totalleft = 0; /* force 1k blocks if name won't fit in 128 byte block */ if (txbuf[125]) blklen=1024; else { /* A little goodie for IMP/KMD */ txbuf[127] = (f.st_size + 127) >>7; txbuf[126] = (f.st_size + 127) >>15; } if (zmodem_requested) return zsendfile(zi,txbuf, 1+strlen(p)+(p-txbuf)); if (wcputsec(txbuf, 0, 128)==ERROR) { vfile("wcputsec failed"); DO_SYSLOG((LOG_INFO, "%s/%s: wcputsec failed", shortname,protname())); return ERROR; } return OK; } static int getnak(void) { int firstch; int tries=0; Lastrx = 0; for (;;) { tries++; switch (firstch = READLINE_PF(100)) { case ZPAD: if (getzrxinit()) return ERROR; Ascii = 0; /* Receiver does the conversion */ return FALSE; case TIMEOUT: /* 30 seconds are enough */ if (tries==3) { zperr(_("Timeout on pathname")); return TRUE; } /* don't send a second ZRQINIT _directly_ after the * first one. Never send more then 4 ZRQINIT, because * omen rz stops if it saw 5 of them */ if ((zrqinits_sent>1 || tries>1) && zrqinits_sent<4) { /* if we already sent a ZRQINIT we are using zmodem * protocol and may send further ZRQINITs */ stohdr(0L); zshhdr(ZRQINIT, Txhdr); zrqinits_sent++; } continue; case WANTG: io_mode(io_mode_fd,2); /* Set cbreak, XON/XOFF, etc. */ Optiong = TRUE; blklen=1024; case WANTCRC: Crcflg = TRUE; case NAK: return FALSE; case CAN: if ((firstch = READLINE_PF(20)) == CAN && Lastrx == CAN) return TRUE; default: break; } Lastrx = firstch; } } static int wctx(struct zm_fileinfo *zi) { register size_t thisblklen; register int sectnum, attempts, firstch; firstsec=TRUE; thisblklen = blklen; vfile("wctx:file length=%ld", (long) zi->bytes_total); while ((firstch=READLINE_PF(Rxtimeout))!=NAK && firstch != WANTCRC && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN) ; if (firstch==CAN) { zperr(_("Receiver Cancelled")); return ERROR; } if (firstch==WANTCRC) Crcflg=TRUE; if (firstch==WANTG) Crcflg=TRUE; sectnum=0; for (;;) { if (zi->bytes_total <= (zi->bytes_sent + 896L)) thisblklen = 128; if ( !filbuf(txbuf, thisblklen)) break; if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR) return ERROR; zi->bytes_sent += thisblklen; } fclose(input_f); attempts=0; do { purgeline(io_mode_fd); sendline(EOT); flushmo(); ++attempts; } while ((firstch=(READLINE_PF(Rxtimeout)) != ACK) && attempts < RETRYMAX); if (attempts == RETRYMAX) { zperr(_("No ACK on EOT")); return ERROR; } else return OK; } static int wcputsec(char *buf, int sectnum, size_t cseclen) { int checksum, wcj; char *cp; unsigned oldcrc; int firstch; int attempts; firstch=0; /* part of logic to detect CAN CAN */ if (Verbose>1) { vchar('\r'); if (protocol==ZM_XMODEM) { vstringf(_("Xmodem sectors/kbytes sent: %3d/%2dk"), Totsecs, Totsecs/8 ); } else { vstringf(_("Ymodem sectors/kbytes sent: %3d/%2dk"), Totsecs, Totsecs/8 ); } } for (attempts=0; attempts <= RETRYMAX; attempts++) { Lastrx= firstch; sendline(cseclen==1024?STX:SOH); sendline(sectnum); sendline(-sectnum -1); oldcrc=checksum=0; for (wcj=cseclen,cp=buf; --wcj>=0; ) { sendline(*cp); oldcrc=updcrc((0377& *cp), oldcrc); checksum += *cp++; } if (Crcflg) { oldcrc=updcrc(0,updcrc(0,oldcrc)); sendline((int)oldcrc>>8); sendline((int)oldcrc); } else sendline(checksum); flushmo(); if (Optiong) { firstsec = FALSE; return OK; } firstch = READLINE_PF(Rxtimeout); gotnak: switch (firstch) { case CAN: if(Lastrx == CAN) { cancan: zperr(_("Cancelled")); return ERROR; } break; case TIMEOUT: zperr(_("Timeout on sector ACK")); continue; case WANTCRC: if (firstsec) Crcflg = TRUE; case NAK: zperr(_("NAK on sector")); continue; case ACK: firstsec=FALSE; Totsecs += (cseclen>>7); return OK; case ERROR: zperr(_("Got burst for sector ACK")); break; default: zperr(_("Got %02x for sector ACK"), firstch); break; } for (;;) { Lastrx = firstch; if ((firstch = READLINE_PF(Rxtimeout)) == TIMEOUT) break; if (firstch == NAK || firstch == WANTCRC) goto gotnak; if (firstch == CAN && Lastrx == CAN) goto cancan; } } zperr(_("Retry Count Exceeded")); return ERROR; } /* fill buf with count chars padding with ^Z for CPM */ static size_t filbuf(char *buf, size_t count) { int c; size_t m; if ( !Ascii) { m = read(fileno(input_f), buf, count); if (m <= 0) return 0; while (m < count) buf[m++] = 032; return count; } m=count; if (Lfseen) { *buf++ = 012; --m; Lfseen = 0; } while ((c=getc(input_f))!=EOF) { if (c == 012) { *buf++ = 015; if (--m == 0) { Lfseen = TRUE; break; } } *buf++ =c; if (--m == 0) break; } if (m==count) return 0; else while (m--!=0) *buf++ = CPMEOF; return count; } /* Fill buffer with blklen chars */ static size_t zfilbuf (struct zm_fileinfo *zi) { size_t n; n = fread (txbuf, 1, blklen, input_f); if (n < blklen) zi->eof_seen = 1; else { /* save one empty paket in case file ends ob blklen boundary */ int c = getc(input_f); if (c != EOF || !feof(input_f)) ungetc(c, input_f); else zi->eof_seen = 1; } return n; } static void usage1 (int exitcode) { usage (exitcode, NULL); } static void usage(int exitcode, const char *what) { FILE *f=stdout; if (exitcode) { if (what) fprintf(stderr, "%s: %s\n",program_name,what); fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name); exit(exitcode); } fprintf(f, _("%s version %s\n"), program_name, VERSION); fprintf(f,_("Usage: %s [options] file ...\n"), program_name); fprintf(f,_(" or: %s [options] -{c|i} COMMAND\n"),program_name); fputs(_("Send file(s) with ZMODEM/YMODEM/XMODEM protocol\n"),f); fputs(_( " (X) = option applies to XMODEM only\n" " (Y) = option applies to YMODEM only\n" " (Z) = option applies to ZMODEM only\n" ),f); /* splitted into two halves for really bad compilers */ fputs(_( " -+, --append append to existing destination file (Z)\n" " -2, --twostop use 2 stop bits\n" " -4, --try-4k go up to 4K blocksize\n" " --start-4k start with 4K blocksize (doesn't try 8)\n" " -8, --try-8k go up to 8K blocksize\n" " --start-8k start with 8K blocksize\n" " -a, --ascii ASCII transfer (change CR/LF to LF)\n" " -b, --binary binary transfer\n" " -B, --bufsize N buffer N bytes (N==auto: buffer whole file)\n" " -c, --command COMMAND execute remote command COMMAND (Z)\n" " -C, --command-tries N try N times to execute a command (Z)\n" " -d, --dot-to-slash change '.' to '/' in pathnames (Y/Z)\n" " --delay-startup N sleep N seconds before doing anything\n" " -e, --escape escape all control characters (Z)\n" " -E, --rename force receiver to rename files it already has\n" " -f, --full-path send full pathname (Y/Z)\n" " -i, --immediate-command CMD send remote CMD, return immediately (Z)\n" " -h, --help print this usage message\n" " -k, --1k send 1024 byte packets (X)\n" " -L, --packetlen N limit subpacket length to N bytes (Z)\n" " -l, --framelen N limit frame length to N bytes (l>=L) (Z)\n" " -m, --min-bps N stop transmission if BPS below N\n" " -M, --min-bps-time N for at least N seconds (default: 120)\n" ),f); fputs(_( " -n, --newer send file if source newer (Z)\n" " -N, --newer-or-longer send file if source newer or longer (Z)\n" " -o, --16-bit-crc use 16 bit CRC instead of 32 bit CRC (Z)\n" " -O, --disable-timeouts disable timeout code, wait forever\n" " -p, --protect protect existing destination file (Z)\n" " -r, --resume resume interrupted file transfer (Z)\n" " -R, --restricted restricted, more secure mode\n" " -q, --quiet quiet (no progress reports)\n" " -s, --stop-at {HH:MM|+N} stop transmission at HH:MM or in N seconds\n" " --tcp-server open socket, wait for connection (Z)\n" " --tcp-client ADDR:PORT open socket, connect to ... (Z)\n" " -u, --unlink unlink file after transmission\n" " -U, --unrestrict turn off restricted mode (if allowed to)\n" " -v, --verbose be verbose, provide debugging information\n" " -w, --windowsize N Window is N bytes (Z)\n" " -X, --xmodem use XMODEM protocol\n" " -y, --overwrite overwrite existing files\n" " -Y, --overwrite-or-skip overwrite existing files, else skip\n" " --ymodem use YMODEM protocol\n" " -Z, --zmodem use ZMODEM protocol\n" "\n" "short options use the same arguments as the long ones\n" ),f); exit(exitcode); } /* * Get the receiver's init parameters */ static int getzrxinit(void) { static int dont_send_zrqinit=1; int old_timeout=Rxtimeout; int n; struct stat f; size_t rxpos; int timeouts=0; Rxtimeout=100; /* 10 seconds */ /* XXX purgeline(io_mode_fd); this makes _real_ trouble. why? -- uwe */ for (n=10; --n>=0; ) { /* we might need to send another zrqinit in case the first is * lost. But *not* if getting here for the first time - in * this case we might just get a ZRINIT for our first ZRQINIT. * Never send more then 4 ZRQINIT, because * omen rz stops if it saw 5 of them. */ if (zrqinits_sent<4 && n!=10 && !dont_send_zrqinit) { zrqinits_sent++; stohdr(0L); zshhdr(ZRQINIT, Txhdr); } dont_send_zrqinit=0; switch (zgethdr(Rxhdr, 1,&rxpos)) { case ZCHALLENGE: /* Echo receiver's challenge numbr */ stohdr(rxpos); zshhdr(ZACK, Txhdr); continue; case ZCOMMAND: /* They didn't see our ZRQINIT */ /* ??? Since when does a receiver send ZCOMMAND? -- uwe */ continue; case ZRINIT: Rxflags = 0377 & Rxhdr[ZF0]; Rxflags2 = 0377 & Rxhdr[ZF1]; Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32)); { int old=Zctlesc; Zctlesc |= Rxflags & TESCCTL; /* update table - was initialised to not escape */ if (Zctlesc && !old) zsendline_init(); } Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8); if ( !(Rxflags & CANFDX)) Txwindow = 0; vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen); if ( play_with_sigint) signal(SIGINT, SIG_IGN); io_mode(io_mode_fd,2); /* Set cbreak, XON/XOFF, etc. */ #ifndef READCHECK /* Use MAX_BLOCK byte frames if no sample/interrupt */ if (Rxbuflen < 32 || Rxbuflen > MAX_BLOCK) { Rxbuflen = MAX_BLOCK; vfile("Rxbuflen=%d", Rxbuflen); } #endif /* Override to force shorter frame length */ if (Tframlen && Rxbuflen > Tframlen) Rxbuflen = Tframlen; if ( !Rxbuflen) Rxbuflen = 1024; vfile("Rxbuflen=%d", Rxbuflen); /* If using a pipe for testing set lower buf len */ fstat(0, &f); #if defined(S_ISCHR) if (! (S_ISCHR(f.st_mode))) { #else if ((f.st_mode & S_IFMT) != S_IFCHR) { #endif Rxbuflen = MAX_BLOCK; } /* * If input is not a regular file, force ACK's to * prevent running beyond the buffer limits */ if ( !command_mode) { fstat(fileno(input_f), &f); #if defined(S_ISREG) if (!(S_ISREG(f.st_mode))) { #else if ((f.st_mode & S_IFMT) != S_IFREG) { #endif Canseek = -1; /* return ERROR; */ } } /* Set initial subpacket length */ if (blklen < 1024) { /* Command line override? */ if (Baudrate > 300) blklen = 256; if (Baudrate > 1200) blklen = 512; if (Baudrate > 2400) blklen = 1024; } if (Rxbuflen && blklen>Rxbuflen) blklen = Rxbuflen; if (blkopt && blklen > blkopt) blklen = blkopt; vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen); vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac); Rxtimeout=old_timeout; return (sendzsinit()); case ZCAN: case TIMEOUT: if (timeouts++==0) continue; /* force one other ZRQINIT to be sent */ return ERROR; case ZRQINIT: if (Rxhdr[ZF0] == ZCOMMAND) continue; default: zshhdr(ZNAK, Txhdr); continue; } } return ERROR; } /* Send send-init information */ static int sendzsinit(void) { int c; if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL))) return OK; errors = 0; for (;;) { stohdr(0L); if (Zctlesc) { Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr); } else zsbhdr(ZSINIT, Txhdr); ZSDATA(Myattn, 1+strlen(Myattn), ZCRCW); c = zgethdr(Rxhdr, 1,NULL); switch (c) { case ZCAN: return ERROR; case ZACK: return OK; default: if (++errors > 19) return ERROR; continue; } } } /* Send file name and related info */ static int zsendfile(struct zm_fileinfo *zi, const char *buf, size_t blen) { unsigned long crc; size_t rxpos; /* we are going to send a ZFILE. There cannot be much useful * stuff in the line right now (*except* ZCAN?). */ #if 0 purgeline(io_mode_fd); /* might possibly fix stefan glasers problems */ #endif for (;;) { int gotblock; int gotchar; Txhdr[ZF0] = Lzconv; /* file conversion request */ Txhdr[ZF1] = Lzmanag; /* file management request */ if (Lskipnocor) Txhdr[ZF1] |= ZF1_ZMSKNOLOC; Txhdr[ZF2] = Lztrans; /* file transport request */ Txhdr[ZF3] = 0; zsbhdr(ZFILE, Txhdr); ZSDATA(buf, blen, ZCRCW); again: gotblock = zgethdr(Rxhdr, 1, &rxpos); switch (gotblock) { case ZRINIT: while ((gotchar = READLINE_PF(50)) > 0) if (gotchar == ZPAD) { goto again; } /* **** FALL THRU TO **** */ default: continue; case ZRQINIT: /* remote site is sender! */ if (Verbose) vstringf(_("got ZRQINIT")); DO_SYSLOG((LOG_INFO, "%s/%s: got ZRQINIT - sz talks to sz", shortname,protname())); return ERROR; case ZCAN: if (Verbose) vstringf(_("got ZCAN")); DO_SYSLOG((LOG_INFO, "%s/%s: got ZCAN - receiver canceled", shortname,protname())); return ERROR; case TIMEOUT: DO_SYSLOG((LOG_INFO, "%s/%s: got TIMEOUT", shortname,protname())); return ERROR; case ZABORT: DO_SYSLOG((LOG_INFO, "%s/%s: got ZABORT", shortname,protname())); return ERROR; case ZFIN: DO_SYSLOG((LOG_INFO, "%s/%s: got ZFIN", shortname,protname())); return ERROR; case ZCRC: crc = 0xFFFFFFFFL; #ifdef HAVE_MMAP if (use_mmap && !mm_addr) { struct stat st; /* mmap on files of 0 length can give 0 as result .. under linux 2.2.9 at least */ if (fstat (fileno (input_f), &st) == 0 && st.st_size!=0) { mm_size = st.st_size; mm_addr = mmap (0, mm_size, PROT_READ, MAP_SHARED, fileno (input_f), 0); if ((caddr_t) mm_addr == (caddr_t) - 1) mm_addr = NULL; else { fclose (input_f); input_f = NULL; } } } if (mm_addr) { size_t i; size_t count; char *p=mm_addr; count=(rxpos < mm_size && rxpos > 0)? rxpos: mm_size; for (i=0;i= 0) { if (rxpos==0) { struct stat st; if (0==fstat(fileno(input_f),&st)) { rxpos=st.st_size; } else rxpos=-1; } while (rxpos-- && ((gotchar = getc(input_f)) != EOF)) crc = UPDC32(gotchar, crc); crc = ~crc; clearerr(input_f); /* Clear EOF */ fseek(input_f, 0L, 0); } stohdr(crc); zsbhdr(ZCRC, Txhdr); goto again; case ZSKIP: if (input_f) { fclose(input_f); input_f=NULL; } #ifdef HAVE_MMAP else if (mm_addr) { munmap(mm_addr,mm_size); mm_addr=NULL; } #endif vfile("receiver skipped"); DO_SYSLOG((LOG_INFO, "%s/%s: receiver skipped", shortname, protname())); return ZSKIP; case ZRPOS: /* * Suppress zcrcw request otherwise triggered by * lastsync==bytcnt */ #ifdef HAVE_MMAP if (!mm_addr) #endif if (rxpos && fseek(input_f, (long) rxpos, 0)) { int er=errno; vfile("fseek failed: %s", strerror(er)); DO_SYSLOG((LOG_INFO, "%s/%s: fseek failed: %s", shortname, protname(), strerror(er))); return ERROR; } if (rxpos) zi->bytes_skipped=rxpos; bytcnt = zi->bytes_sent = rxpos; Lastsync = rxpos -1; return zsendfdata(zi); } } } /* Send the data in the file */ static int zsendfdata (struct zm_fileinfo *zi) { static int c; int newcnt; static int junkcount; /* Counts garbage chars received by TX */ static size_t last_txpos = 0; static long last_bps = 0; static long not_printed = 0; static long total_sent = 0; static time_t low_bps=0; #ifdef HAVE_MMAP if (use_mmap && !mm_addr) { struct stat st; if (fstat (fileno (input_f), &st) == 0 && st.st_size!=0) { mm_size = st.st_size; mm_addr = mmap (0, mm_size, PROT_READ, MAP_SHARED, fileno (input_f), 0); if ((caddr_t) mm_addr == (caddr_t) - 1) mm_addr = NULL; else { fclose (input_f); input_f = NULL; } } } #endif if (play_with_sigint) signal (SIGINT, onintr); Lrxpos = 0; junkcount = 0; Beenhereb4 = 0; somemore: if (setjmp (intrjmp)) { if (play_with_sigint) signal (SIGINT, onintr); waitack: junkcount = 0; c = getinsync (zi, 0); gotack: switch (c) { default: if (input_f) { fclose (input_f); input_f=NULL; } DO_SYSLOG((LOG_INFO, "%s/%s: got %d", shortname, protname(), c)); return ERROR; case ZCAN: if (input_f) { fclose (input_f); input_f=NULL; } DO_SYSLOG((LOG_INFO, "%s/%s: got ZCAN", shortname, protname())); return ERROR; case ZSKIP: if (input_f) { fclose (input_f); input_f=NULL; } DO_SYSLOG((LOG_INFO, "%s/%s: got ZSKIP", shortname, protname())); return ZSKIP; case ZACK: case ZRPOS: break; case ZRINIT: return OK; } #ifdef READCHECK /* * If the reverse channel can be tested for data, * this logic may be used to detect error packets * sent by the receiver, in place of setjmp/longjmp * rdchk(fdes) returns non 0 if a character is available */ while (rdchk (io_mode_fd)) { #ifdef READCHECK_READS switch (checked) #else switch (READLINE_PF (1)) #endif { case CAN: case ZPAD: c = getinsync (zi, 1); goto gotack; case XOFF: /* Wait a while for an XON */ case XOFF | 0200: READLINE_PF (100); } } #endif } newcnt = Rxbuflen; Txwcnt = 0; stohdr (zi->bytes_sent); zsbhdr (ZDATA, Txhdr); do { size_t n; int e; unsigned old = blklen; blklen = calc_blklen (total_sent); total_sent += blklen + OVERHEAD; if (Verbose > 2 && blklen != old) vstringf (_("blklen now %d\n"), blklen); #ifdef HAVE_MMAP if (mm_addr) { if (zi->bytes_sent + blklen < mm_size) n = blklen; else { n = mm_size - zi->bytes_sent; zi->eof_seen = 1; } } else #endif n = zfilbuf (zi); if (zi->eof_seen) { e = ZCRCE; if (Verbose>3) vstring("e=ZCRCE/eof seen"); } else if (junkcount > 3) { e = ZCRCW; if (Verbose>3) vstring("e=ZCRCW/junkcount > 3"); } else if (bytcnt == Lastsync) { e = ZCRCW; if (Verbose>3) vstringf("e=ZCRCW/bytcnt == Lastsync == %ld", (unsigned long) Lastsync); #if 0 /* what is this good for? Rxbuflen/newcnt normally are short - so after * a few KB ZCRCW will be used? (newcnt is never incremented) */ } else if (Rxbuflen && (newcnt -= n) <= 0) { e = ZCRCW; if (Verbose>3) vstringf("e=ZCRCW/Rxbuflen(newcnt=%ld,n=%ld)", (unsigned long) newcnt,(unsigned long) n); #endif } else if (Txwindow && (Txwcnt += n) >= Txwspac) { Txwcnt = 0; e = ZCRCQ; if (Verbose>3) vstring("e=ZCRCQ/Window"); } else { e = ZCRCG; if (Verbose>3) vstring("e=ZCRCG"); } if ((Verbose > 1 || min_bps || stop_time) && (not_printed > (min_bps ? 3 : 7) || zi->bytes_sent > last_bps / 2 + last_txpos)) { int minleft = 0; int secleft = 0; time_t now; last_bps = (zi->bytes_sent / timing (0,&now)); if (last_bps > 0) { minleft = (zi->bytes_total - zi->bytes_sent) / last_bps / 60; secleft = ((zi->bytes_total - zi->bytes_sent) / last_bps) % 60; } if (min_bps) { if (low_bps) { if (last_bps=min_bps_time) { /* too bad */ if (Verbose) { vstringf(_("zsendfdata: bps rate %ld below min %ld"), last_bps, min_bps); vstring("\r\n"); } DO_SYSLOG((LOG_INFO, "%s/%s: bps rate low: %ld <%ld", shortname, protname(), last_bps, min_bps)); return ERROR; } } else low_bps=0; } else if (last_bps < min_bps) { low_bps=now; } } if (stop_time && now>=stop_time) { /* too bad */ if (Verbose) { vstring(_("zsendfdata: reached stop time")); vstring("\r\n"); } DO_SYSLOG((LOG_INFO, "%s/%s: reached stop time", shortname, protname())); return ERROR; } if (Verbose > 1) { vchar ('\r'); vstringf (_("Bytes Sent:%7ld/%7ld BPS:%-8ld ETA %02d:%02d "), (long) zi->bytes_sent, (long) zi->bytes_total, last_bps, minleft, secleft); } last_txpos = zi->bytes_sent; } else if (Verbose) not_printed++; ZSDATA (DATAADR, n, e); bytcnt = zi->bytes_sent += n; if (e == ZCRCW) goto waitack; #ifdef READCHECK /* * If the reverse channel can be tested for data, * this logic may be used to detect error packets * sent by the receiver, in place of setjmp/longjmp * rdchk(fdes) returns non 0 if a character is available */ fflush (stdout); while (rdchk (io_mode_fd)) { #ifdef READCHECK_READS switch (checked) #else switch (READLINE_PF (1)) #endif { case CAN: case ZPAD: c = getinsync (zi, 1); if (c == ZACK) break; /* zcrce - dinna wanna starta ping-pong game */ ZSDATA (txbuf, 0, ZCRCE); goto gotack; case XOFF: /* Wait a while for an XON */ case XOFF | 0200: READLINE_PF (100); default: ++junkcount; } } #endif /* READCHECK */ if (Txwindow) { size_t tcount = 0; while ((tcount = zi->bytes_sent - Lrxpos) >= Txwindow) { vfile ("%ld (%ld,%ld) window >= %u", tcount, (long) zi->bytes_sent, (long) Lrxpos, Txwindow); if (e != ZCRCQ) ZSDATA (txbuf, 0, e = ZCRCQ); c = getinsync (zi, 1); if (c != ZACK) { ZSDATA (txbuf, 0, ZCRCE); goto gotack; } } vfile ("window = %ld", tcount); } } while (!zi->eof_seen); if (play_with_sigint) signal (SIGINT, SIG_IGN); for (;;) { stohdr (zi->bytes_sent); zsbhdr (ZEOF, Txhdr); switch (getinsync (zi, 0)) { case ZACK: continue; case ZRPOS: goto somemore; case ZRINIT: return OK; case ZSKIP: if (input_f) { fclose (input_f); input_f=NULL; } DO_SYSLOG((LOG_INFO, "%s/%s: got ZSKIP", shortname, protname())); return c; default: if (input_f) { fclose (input_f); input_f=NULL; } DO_SYSLOG((LOG_INFO, "%s/%s: got %d", shortname, protname(), c)); return ERROR; } } } static int calc_blklen(long total_sent) { static long total_bytes=0; static int calcs_done=0; static long last_error_count=0; static int last_blklen=0; static long last_bytes_per_error=0; unsigned long best_bytes=0; long best_size=0; long this_bytes_per_error; long d; unsigned int i; if (total_bytes==0) { /* called from countem */ total_bytes=total_sent; return 0; } /* it's not good to calc blklen too early */ if (calcs_done++ < 5) { if (error_count && start_blklen >1024) return last_blklen=1024; else last_blklen/=2; return last_blklen=start_blklen; } if (!error_count) { /* that's fine */ if (start_blklen==max_blklen) return start_blklen; this_bytes_per_error=LONG_MAX; goto calcit; } if (error_count!=last_error_count) { /* the last block was bad. shorten blocks until one block is * ok. this is because very often many errors come in an * short period */ if (error_count & 2) { last_blklen/=2; if (last_blklen < 32) last_blklen = 32; else if (last_blklen > 512) last_blklen=512; if (Verbose > 3) vstringf(_("calc_blklen: reduced to %d due to error\n"), last_blklen); } last_error_count=error_count; last_bytes_per_error=0; /* force recalc */ return last_blklen; } this_bytes_per_error=total_sent / error_count; /* we do not get told about every error, because * there may be more than one error per failed block. * but one the other hand some errors are reported more * than once: If a modem buffers more than one block we * get at least two ZRPOS for the same position in case * *one* block has to be resent. * so don't do this: * this_bytes_per_error/=2; */ /* there has to be a margin */ if (this_bytes_per_error<100) this_bytes_per_error=100; /* be nice to the poor machine and do the complicated things not * too often */ if (last_bytes_per_error>this_bytes_per_error) d=last_bytes_per_error-this_bytes_per_error; else d=this_bytes_per_error-last_bytes_per_error; if (d<4) { if (Verbose > 3) { vstringf(_("calc_blklen: returned old value %d due to low bpe diff\n"), last_blklen); vstringf(_("calc_blklen: old %ld, new %ld, d %ld\n"), last_bytes_per_error,this_bytes_per_error,d ); } return last_blklen; } last_bytes_per_error=this_bytes_per_error; calcit: if (Verbose > 3) vstringf(_("calc_blklen: calc total_bytes=%ld, bpe=%ld, ec=%ld\n"), total_bytes,this_bytes_per_error,(long) error_count); for (i=32;i<=max_blklen;i*=2) { long ok; /* some many ok blocks do we need */ long failed; /* and that's the number of blocks not transmitted ok */ unsigned long transmitted; ok=total_bytes / i + 1; failed=((long) i + OVERHEAD) * ok / this_bytes_per_error; transmitted=total_bytes + ok * OVERHEAD + failed * ((long) i+OVERHEAD+OVER_ERR); if (Verbose > 4) vstringf(_("calc_blklen: blklen %d, ok %ld, failed %ld -> %lu\n"), i,ok,failed,transmitted); if (transmitted < best_bytes || !best_bytes) { best_bytes=transmitted; best_size=i; } } if (best_size > 2*last_blklen) best_size=2*last_blklen; last_blklen=best_size; if (Verbose > 3) vstringf(_("calc_blklen: returned %d as best\n"), last_blklen); return last_blklen; } /* * Respond to receiver's complaint, get back in sync with receiver */ static int getinsync(struct zm_fileinfo *zi, int flag) { size_t rxpos; for (;;) { int gotblock; gotblock = zgethdr(Rxhdr, 0, &rxpos); switch (gotblock) { case ZCAN: case ZABORT: case ZFIN: case TIMEOUT: return ERROR; case ZRPOS: /* ************************************* */ /* If sending to a buffered modem, you */ /* might send a break at this point to */ /* dump the modem's buffer. */ if (input_f) clearerr(input_f); /* In case file EOF seen */ #ifdef HAVE_MMAP if (!mm_addr) #endif if (fseek(input_f, (long) rxpos, 0)) return ERROR; zi->eof_seen = 0; bytcnt = Lrxpos = zi->bytes_sent = rxpos; if (Lastsync == rxpos) { error_count++; } Lastsync = rxpos; return ZRPOS; case ZACK: Lrxpos = rxpos; if (flag || zi->bytes_sent == rxpos) return ZACK; continue; case ZRINIT: case ZSKIP: if (input_f) { fclose (input_f); input_f=NULL; } #ifdef HAVE_MMAP else if (mm_addr) { munmap(mm_addr,mm_size); mm_addr=NULL; } #endif return ZSKIP; case ERROR: default: error_count++; zsbhdr(ZNAK, Txhdr); continue; } } } /* Say "bibi" to the receiver, try to do it cleanly */ static void saybibi(void) { for (;;) { stohdr(0L); /* CAF Was zsbhdr - minor change */ zshhdr(ZFIN, Txhdr); /* to make debugging easier */ switch (zgethdr(Rxhdr, 0,NULL)) { case ZFIN: sendline('O'); sendline('O'); flushmo(); case ZCAN: case TIMEOUT: return; } } } /* Send command and related info */ static int zsendcmd(const char *buf, size_t blen) { int c; pid_t cmdnum; size_t rxpos; cmdnum = getpid(); errors = 0; for (;;) { stohdr((size_t) cmdnum); Txhdr[ZF0] = Cmdack1; zsbhdr(ZCOMMAND, Txhdr); ZSDATA(buf, blen, ZCRCW); listen: Rxtimeout = 100; /* Ten second wait for resp. */ c = zgethdr(Rxhdr, 1, &rxpos); switch (c) { case ZRINIT: goto listen; /* CAF 8-21-87 */ case ERROR: case TIMEOUT: if (++errors > Cmdtries) return ERROR; continue; case ZCAN: case ZABORT: case ZFIN: case ZSKIP: case ZRPOS: return ERROR; default: if (++errors > 20) return ERROR; continue; case ZCOMPL: Exitcode = rxpos; saybibi(); return OK; case ZRQINIT: vfile("******** RZ *******"); system("rz"); vfile("******** SZ *******"); goto listen; } } } /* * If called as lsb use YMODEM protocol */ static void chkinvok (const char *s) { const char *p; p = s; while (*p == '-') s = ++p; while (*p) if (*p++ == '/') s = p; if (*s == 'v') { Verbose = 1; ++s; } program_name = s; if (*s == 'l') s++; /* lsz -> sz */ protocol = ZM_ZMODEM; if (s[0] == 's' && s[1] == 'x') protocol = ZM_XMODEM; if (s[0] == 's' && (s[1] == 'b' || s[1] == 'y')) { protocol = ZM_YMODEM; } } static void countem (int argc, char **argv) { struct stat f; for (Totalleft = 0, Filesleft = 0; --argc >= 0; ++argv) { f.st_size = -1; if (Verbose > 2) { vstringf ("\nCountem: %03d %s ", argc, *argv); } if (access (*argv, R_OK) >= 0 && stat (*argv, &f) >= 0) { #if defined(S_ISDIR) if (!S_ISDIR(f.st_mode) && !S_ISBLK(f.st_mode)) { #else int c; c = f.st_mode & S_IFMT; if (c != S_IFDIR && c != S_IFBLK) { #endif ++Filesleft; Totalleft += f.st_size; } } else if (strcmp (*argv, "-") == 0) { ++Filesleft; Totalleft += DEFBYTL; } if (Verbose > 2) vstringf (" %ld", (long) f.st_size); } if (Verbose > 2) vstringf (_("\ncountem: Total %d %ld\n"), Filesleft, Totalleft); calc_blklen (Totalleft); } /* End of lsz.c */