|
|
3.15b
/* sexyz.c */
/* Synchronet External X/Y/ZMODEM Transfer Protocols */
/* $Id: sexyz.c,v 1.1.1.2 2018/04/24 16:43:07 root Exp $ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
* *
* 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 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to [email protected] *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
/*
* ZMODEM code based on zmtx/zmrx v1.02 (C) Mattheij Computer Service 1994
* by Jacques Mattheij <[email protected]>
*/
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h> /* isdigit */
#include <sys/stat.h>
#ifdef __unix__
#include <termios.h>
#include <signal.h>
#endif
/* xpdev */
#include "conwrap.h"
#include "genwrap.h"
#include "semwrap.h"
#include "dirwrap.h"
#include "filewrap.h"
#include "sockwrap.h"
#include "str_list.h"
#include "ini_file.h"
#include "eventwrap.h"
#include "threadwrap.h"
/* sbbs */
#include "ringbuf.h"
#include "telnet.h"
#include "nopen.h"
/* sexyz */
#include "sexyz.h"
#define SINGLE_THREADED FALSE
#define MIN_OUTBUF_SIZE 1024
#define MAX_OUTBUF_SIZE (64*1024)
#define INBUF_SIZE (64*1024)
#define MAX_FILE_SIZE 0 /* Default value for max recv file size, 0 = unlimited*/
/***************/
/* Global Vars */
/***************/
long mode=0; /* Program mode */
long zmode=0L; /* Zmodem mode */
uchar block[XMODEM_MAX_BLOCK_SIZE]; /* Block buffer */
ulong block_num; /* Block number */
char* dszlog;
BOOL dszlog_path=TRUE; /* Log complete path to filename */
BOOL dszlog_short=FALSE; /* Log Micros~1 short filename */
BOOL dszlog_quotes=FALSE; /* Quote filenames in DSZLOG */
int log_level=LOG_INFO;
BOOL use_syslog=FALSE;
int64_t max_file_size=MAX_FILE_SIZE;
xmodem_t xm;
zmodem_t zm;
FILE* errfp;
FILE* statfp;
FILE* logfp=NULL;
char revision[16];
SOCKET sock=INVALID_SOCKET;
BOOL telnet=TRUE;
#ifdef __unix__
BOOL stdio=FALSE;
#endif
BOOL terminate=FALSE;
BOOL debug_tx=FALSE;
BOOL debug_rx=FALSE;
BOOL debug_telnet=FALSE;
BOOL pause_on_exit=FALSE;
BOOL pause_on_abend=FALSE;
BOOL newline=TRUE;
BOOL connected=TRUE;
time_t progress_interval;
RingBuf outbuf;
#if defined(RINGBUF_EVENT)
#define outbuf_empty outbuf.empty_event
#else
xpevent_t outbuf_empty;
#endif
unsigned outbuf_drain_timeout;
long outbuf_size;
uchar inbuf[INBUF_SIZE];
unsigned inbuf_pos=0;
unsigned inbuf_len=0;
unsigned flows=0;
unsigned select_errors=0;
#ifdef _WINSOCKAPI_
/* Note: Don't call WSACleanup() or TCP session will close! */
WSADATA WSAData;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
fprintf(statfp,"%s %s\n",WSAData.szDescription, WSAData.szSystemStatus);
return(TRUE);
}
fprintf(errfp,"!WinSock startup ERROR %d\n", status);
return(FALSE);
}
#else /* No WINSOCK */
#define winsock_startup() (TRUE)
#endif
static int lputs(void* unused, int level, const char* str)
{
FILE* fp=statfp;
int ret;
#if defined(_WIN32) && defined(_DEBUG)
if(log_level==LOG_DEBUG)
OutputDebugString(str);
#endif
if(level>log_level)
return 0;
if(level<LOG_NOTICE)
fp=errfp;
if(!newline) {
fprintf(fp,"\n");
newline=TRUE;
}
if(level<LOG_NOTICE)
ret=fprintf(fp,"!%s\n",str);
else
ret=fprintf(fp,"%s\n",str);
#if defined(__unix__)
if(use_syslog) {
char* msg;
char* p;
if((msg=strdup(str))!=NULL) {
REPLACE_CHARS(msg,'\r',' ',p);
REPLACE_CHARS(msg,'\n',' ',p);
syslog(level,"%s",msg);
free(msg);
}
}
#endif
return ret;
}
static int lprintf(int level, const char *fmt, ...)
{
char sbuf[1024];
va_list argptr;
if(level>log_level)
return 0;
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
va_end(argptr);
return(lputs(NULL,level,sbuf));
}
void break_handler(int type)
{
lprintf(LOG_NOTICE,"-> Aborted Locally (signal: %d)",type);
/* Flag to indicate local (as opposed to remote) abort */
zm.local_abort=TRUE;
/* Stop any transfers in progress immediately */
xm.cancelled=TRUE;
zm.cancelled=TRUE;
}
#if defined(_WIN32)
BOOL WINAPI ControlHandler(unsigned long CtrlType)
{
break_handler((int)CtrlType);
return TRUE;
}
#endif
char* dszlog_filename(char* str)
{
char* p=str;
static char path[MAX_PATH+1];
#ifdef _WIN32
char sfpath[MAX_PATH+1];
if(dszlog_short) {
SAFECOPY(sfpath,str);
GetShortPathName(str,sfpath,sizeof(sfpath));
p=sfpath;
}
#endif
if(!dszlog_path)
p=getfname(p);
if(!dszlog_quotes)
return(p);
SAFEPRINTF(path,"\"%s\"",p);
return(path);
}
static char *chr(uchar ch)
{
static char str[25];
if(mode&ZMODEM) {
switch(ch) {
case ZRQINIT: return("ZRQINIT");
case ZRINIT: return("ZRINIT");
case ZSINIT: return("ZSINIT");
case ZACK: return("ZACK");
case ZFILE: return("ZFILE");
case ZSKIP: return("ZSKIP");
case ZNAK: return("ZNAK");
case ZABORT: return("ZABORT");
case ZFIN: return("ZFIN");
case ZRPOS: return("ZRPOS");
case ZDATA: return("ZDATA");
case ZEOF: return("ZEOF");
case ZPAD: return("ZPAD");
case ZDLE: return("ZDLE");
case ZDLEE: return("ZDLEE");
case ZBIN: return("ZBIN");
case ZHEX: return("ZHEX");
case ZBIN32: return("ZBIN32");
case ZRESC: return("ZRESC");
case ZCRCE: return("ZCRCE");
case ZCRCG: return("ZCRCG");
case ZCRCQ: return("ZCRCQ");
case ZCRCW: return("ZCRCW");
}
} else {
switch(ch) {
case SOH: return("SOH");
case STX: return("STX");
case ETX: return("ETX");
case EOT: return("EOT");
case ACK: return("ACK");
case NAK: return("NAK");
case CAN: return("CAN");
}
}
if(ch>=' ' && ch<='~')
sprintf(str,"'%c' (%02Xh)",ch,ch);
else
sprintf(str,"%u (%02Xh)",ch,ch);
return(str);
}
void dump(BYTE* buf, int len)
{
char str[128];
int i,j;
size_t slen=0;
slen=sprintf(str,"TX: ");
for(i=0;i<len;i+=j) {
for(j=0;i+j<len && j<32;j++)
slen+=sprintf(str+slen,"%02X ",buf[i+j]);
lprintf(LOG_DEBUG,"%s",str);
slen=sprintf(str,"TX: ");
}
}
int sendbuf(SOCKET s, void *buf, size_t buflen)
{
size_t sent=0;
int ret;
fd_set socket_set;
for(;;) {
#ifdef __unix__
if(stdio)
ret=write(STDOUT_FILENO, (char *)buf+sent,buflen-sent);
else
#endif
ret=sendsocket(s,(char *)buf+sent,buflen-sent);
if(ret==SOCKET_ERROR) {
switch(ERROR_VALUE) {
case EAGAIN:
case ENOBUFS:
#if (EAGAIN != EWOULDBLOCK)
case EWOULDBLOCK:
#endif
/* Block until we can send */
FD_ZERO(&socket_set);
#ifdef __unix__
if(stdio)
FD_SET(STDIN_FILENO,&socket_set);
else
#endif
FD_SET(sock,&socket_set);
if((ret=select(sock+1,NULL,&socket_set,NULL,NULL))<1) {
if(ret==SOCKET_ERROR && ERROR_VALUE != EINTR) {
lprintf(LOG_ERR,"ERROR %d selecting socket", ERROR_VALUE);
goto disconnect;
}
}
break;
default:
goto disconnect;
}
}
else {
sent += ret;
if(sent >= buflen)
return(sent);
}
}
disconnect:
lprintf(LOG_DEBUG,"DISCONNECTED line %u", __LINE__);
connected=FALSE;
if(sent)
return sent;
return SOCKET_ERROR;
}
void send_telnet_cmd(SOCKET sock, uchar cmd, uchar opt)
{
uchar buf[3];
buf[0]=TELNET_IAC;
buf[1]=cmd;
buf[2]=opt;
if(debug_telnet)
lprintf(LOG_DEBUG,"Sending telnet command: %s %s"
,telnet_cmd_desc(buf[1]),telnet_opt_desc(buf[2]));
if(sendbuf(sock,buf,sizeof(buf))!=sizeof(buf) && debug_telnet)
lprintf(LOG_ERR,"FAILED");
}
#define DEBUG_TELNET FALSE
/*
* Returns -1 on disconnect or the number of bytes read.
* Does not muck around with ERROR_VALUE (hopefully)
*/
static int recv_buffer(int timeout /* seconds */)
{
int i;
fd_set socket_set;
struct timeval tv;
int magic_errno;
for(;;) {
if(inbuf_len > inbuf_pos)
return(inbuf_len-inbuf_pos);
#ifdef __unix__
if(stdio) {
i=read(STDIN_FILENO,inbuf,sizeof(inbuf));
/* Look like a socket using MAGIC! */
switch(i) {
case 0:
i=-1;
magic_errno=EAGAIN;
break;
case -1:
magic_errno=errno;
break;
}
}
else
#endif
{
i=recv(sock,inbuf,sizeof(inbuf),0);
if(i==SOCKET_ERROR)
magic_errno=ERROR_VALUE;
}
if(i==SOCKET_ERROR) {
switch(magic_errno) {
case EAGAIN:
case EINTR:
#if (EAGAIN != EWOULDBLOCK)
case EWOULDBLOCK:
#endif
// Call select()
if(timeout) {
FD_ZERO(&socket_set);
#ifdef __unix__
if(stdio)
FD_SET(STDIN_FILENO,&socket_set);
else
#endif
FD_SET(sock,&socket_set);
tv.tv_sec=timeout;
timeout=0;
tv.tv_usec=0;
if((i=select(sock+1,&socket_set,NULL,NULL,&tv))<1) {
if(i==SOCKET_ERROR) {
lprintf(LOG_ERR,"ERROR %d selecting socket", magic_errno);
connected=FALSE;
}
else
lprintf(LOG_WARNING,"Receive timeout (%u seconds)", timeout);
}
else
continue;
}
return 0;
default:
lprintf(LOG_DEBUG,"DISCONNECTED line %u, error=%u", __LINE__,magic_errno);
connected=FALSE;
return -1;
}
return -1; // Impossible.
}
else if(i==0) {
lprintf(LOG_DEBUG,"DISCONNECTED line %u", __LINE__);
connected=FALSE;
return -1;
}
else {
inbuf_len=i;
return i;
}
};
}
/****************************************************************************/
/* Receive a byte from remote (single-threaded version) */
/****************************************************************************/
int recv_byte(void* unused, unsigned timeout /* seconds */)
{
int i;
uchar ch;
static uchar telnet_cmd;
static int telnet_cmdlen;
while((inbuf_len || connected) && !terminate) {
if(inbuf_len) {
ch=inbuf[inbuf_pos++];
i=1;
if(inbuf_pos >= inbuf_len)
inbuf_pos=inbuf_len=0;
}
else {
i=recv_buffer(timeout);
switch(i) {
case -1:
i=0;
break;
case 0:
return NOINP;
default:
i=1;
continue;
}
}
if(i!=sizeof(ch)) {
if(i==0) {
lprintf(LOG_WARNING,"Socket Disconnected");
} else
lprintf(LOG_ERR,"recv error %d (%d)",i,ERROR_VALUE);
return(NOINP);
}
if(telnet) {
if(ch==TELNET_IAC) {
#if DEBUG_TELNET
lprintf(LOG_DEBUG,"T<%s> ",telnet_cmd_desc(ch));
#endif
if(telnet_cmdlen==0) {
telnet_cmdlen=1;
continue;
}
if(telnet_cmdlen==1) {
telnet_cmdlen=0;
if(debug_rx)
lprintf(LOG_DEBUG,"RX: %s",chr(TELNET_IAC));
return(TELNET_IAC);
}
}
if(telnet_cmdlen) {
telnet_cmdlen++;
#if DEBUG_TELNET
if(telnet_cmdlen==2)
lprintf(LOG_DEBUG,"T<%s> ",telnet_cmd_desc(ch));
else
lprintf(LOG_DEBUG,"T<%s> ",telnet_opt_desc(ch));
#endif
if(debug_telnet && telnet_cmdlen==3)
lprintf(LOG_DEBUG,"Received telnet command: %s %s"
,telnet_cmd_desc(telnet_cmd),telnet_opt_desc(ch));
if(telnet_cmdlen==3 && telnet_cmd==TELNET_DO)
send_telnet_cmd(sock, TELNET_WILL,ch);
/*
else if(telnet_cmdlen==3 && telnet_cmd==TELNET_WILL)
send_telnet_cmd(sock, TELNET_DO,ch);
*/
telnet_cmd=ch;
if((telnet_cmdlen==2 && ch<TELNET_WILL) || telnet_cmdlen>2) {
telnet_cmdlen=0;
/* Code disabled. Why? ToDo */
/* break; */
}
continue;
}
}
if(debug_rx)
lprintf(LOG_DEBUG,"RX: %s",chr(ch));
return(ch);
}
return(NOINP);
}
#if !SINGLE_THREADED
/*************************/
/* Send a byte to remote */
/*************************/
int send_byte(void* unused, uchar ch, unsigned timeout)
{
uchar buf[2] = { TELNET_IAC, TELNET_IAC };
unsigned len=1;
DWORD result;
if(telnet && ch==TELNET_IAC) /* escape IAC char */
len=2;
else
buf[0]=ch;
if(RingBufFree(&outbuf)<len) {
#if !defined(RINGBUF_EVENT)
ResetEvent(outbuf_empty);
#endif
fprintf(statfp,"FLOW");
flows++;
result=WaitForEvent(outbuf_empty,timeout*1000);
fprintf(statfp,"\b\b\b\b \b\b\b\b");
if(result!=WAIT_OBJECT_0) {
lprintf(LOG_WARNING
,"!TIMEOUT (%d) waiting for output buffer to flush (%u seconds, %u bytes)\n"
,result, timeout, RingBufFull(&outbuf));
fprintf(statfp
,"\n!TIMEOUT (%d) waiting for output buffer to flush (%u seconds, %u bytes)\n"
,result, timeout, RingBufFull(&outbuf));
newline=TRUE;
if((result=RingBufFree(&outbuf))<len) {
lprintf(LOG_ERR,"Still not enough space in ring buffer (need %d, avail=%d)",len,result);
return(-1);
}
}
if((result=RingBufFree(&outbuf))<len) {
lprintf(LOG_ERR,"Not enough space in ring buffer (need %d, avail=%d) although empty event is set!",len,result);
return(-1);
}
}
if((result=RingBufWrite(&outbuf,buf,len))!=len) {
lprintf(LOG_ERR,"RingBufWrite() returned %d, expected %d",result,len);
}
#if 0
if(debug_tx)
lprintf(LOG_DEBUG,"TX: %s",chr(ch));
#endif
return(0);
}
#else
/*************************/
/* Send a byte to remote */
/*************************/
int send_byte(void* unused, uchar ch, unsigned timeout)
{
uchar buf[2] = { TELNET_IAC, TELNET_IAC };
int len=1;
int i;
if(telnet && ch==TELNET_IAC) /* escape IAC char */
len=2;
else
buf[0]=ch;
i=sendbuf(sock,buf,len);
if(i==len) {
if(debug_tx)
lprintf(LOG_DEBUG,"TX: %s",chr(ch));
return(0);
}
return(-1);
}
#endif
static void output_thread(void* arg)
{
char stats[128];
BYTE buf[MAX_OUTBUF_SIZE];
int i;
ulong avail;
uint64_t total_sent=0;
uint64_t total_pkts=0;
ulong short_sends=0;
ulong bufbot=0;
ulong buftop=0;
#if 0 /* def _DEBUG */
fprintf(statfp,"output thread started\n");
#endif
while(sock!=INVALID_SOCKET && !terminate) {
if(bufbot==buftop)
avail=RingBufFull(&outbuf);
else
avail=buftop-bufbot;
if(!avail) {
#if !defined(RINGBUF_EVENT)
SetEvent(outbuf_empty);
#endif
sem_wait(&outbuf.sem);
if(outbuf.highwater_mark)
sem_trywait_block(&outbuf.highwater_sem,outbuf_drain_timeout);
continue;
}
if(bufbot==buftop) { /* linear buf empty, read from ring buf */
if(avail>sizeof(buf)) {
lprintf(LOG_ERR,"Insufficient linear output buffer (%lu > %lu)"
,avail, sizeof(buf));
avail=sizeof(buf);
}
buftop=RingBufRead(&outbuf, buf, avail);
bufbot=0;
}
i=sendbuf(sock, (char*)buf+bufbot, buftop-bufbot);
if(i==SOCKET_ERROR) {
if(ERROR_VALUE == ENOTSOCK)
lprintf(LOG_ERR,"client socket closed on send");
else if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_ERR,"connection reset by peer on send");
else if(ERROR_VALUE==ECONNABORTED)
lprintf(LOG_ERR,"connection aborted by peer on send");
else
lprintf(LOG_ERR,"ERROR %d sending on socket %d"
,ERROR_VALUE, sock);
break;
}
if(debug_tx)
dump(buf+bufbot,i);
if(i!=(int)(buftop-bufbot)) {
lprintf(LOG_ERR,"Short socket send (%u instead of %u)"
,i ,buftop-bufbot);
short_sends++;
}
bufbot+=i;
total_sent+=i;
total_pkts++;
}
if(total_sent)
sprintf(stats,"(sent %"PRIu64" bytes in %"PRIu64" blocks, %"PRIu64" average, %lu short, %lu errors)"
,total_sent, total_pkts, total_sent/total_pkts, short_sends, select_errors);
else
stats[0]=0;
lprintf(LOG_DEBUG,"output thread terminated\n%s", stats);
}
/* Flush output buffer */
void flush(void* unused)
{
#ifdef __unix__
if(stdio)
fflush(stdout);
#endif
}
BOOL is_connected(void* unused)
{
if(inbuf_len > inbuf_pos)
return TRUE;
return connected;
}
BOOL data_waiting(void* unused, unsigned timeout /* seconds */)
{
if(recv_buffer(timeout) > 0)
return TRUE;
return FALSE;
}
/****************************************************************************/
/* Returns the total number of blocks required to send the file */
/****************************************************************************/
uint64_t num_blocks(unsigned block_num, uint64_t offset, uint64_t len, unsigned block_size)
{
uint64_t blocks;
uint64_t remain = len - offset;
blocks=block_num + (remain/block_size);
if(remain%block_size)
blocks++;
return(blocks);
}
/************************************************/
/* Dump the current block contents - for debug */
/************************************************/
void dump_block(long block_size)
{
long l;
for(l=0;l<block_size;l++)
fprintf(statfp,"%02X ",block[l]);
fprintf(statfp,"\n");
}
void xmodem_progress(void* unused, unsigned block_num, int64_t offset, int64_t fsize, time_t start)
{
unsigned cps;
uint64_t total_blocks;
long l;
long t;
time_t now;
static time_t last_progress;
now=time(NULL);
if(now-last_progress>=progress_interval || offset >= fsize || newline) {
t=now-start;
if(t<=0)
t=1;
if((cps=(unsigned)(offset/t))==0)
cps=1; /* cps so far */
l=(long)(fsize/cps); /* total transfer est time */
l-=t; /* now, it's est time left */
if(l<0) l=0;
if(mode&SEND) {
total_blocks=num_blocks(block_num,offset,fsize,xm.block_size);
fprintf(statfp,"\rBlock (%lu%s): %lu/%"PRId64" Byte: %"PRId64" "
"Time: %lu:%02lu/%lu:%02lu %u cps %lu%% "
,xm.block_size%1024L ? xm.block_size: xm.block_size/1024L
,xm.block_size%1024L ? "" : "K"
,block_num
,total_blocks
,offset
,t/60L
,t%60L
,l/60L
,l%60L
,cps
,fsize?(long)(((float)offset/(float)fsize)*100.0):100
);
} else if(mode&YMODEM) {
fprintf(statfp,"\rBlock (%lu%s): %lu Byte: %"PRId64" "
"Time: %lu:%02lu/%lu:%02lu %u cps %lu%% "
,xm.block_size%1024L ? xm.block_size: xm.block_size/1024L
,xm.block_size%1024L ? "" : "K"
,block_num
,offset
,t/60L
,t%60L
,l/60L
,l%60L
,cps
,fsize?(long)(((float)offset/(float)fsize)*100.0):100
);
} else { /* XModem receive */
fprintf(statfp,"\rBlock (%lu%s): %lu Byte: %"PRId64" "
"Time: %lu:%02lu %u cps "
,xm.block_size%1024L ? xm.block_size: xm.block_size/1024L
,xm.block_size%1024L ? "" : "K"
,block_num
,offset
,t/60L
,t%60L
,cps
);
}
newline=FALSE;
last_progress=now;
}
}
/*
* show the progress of the transfer like this:
* zmtx: sending file "garbage" 4096 bytes ( 20%)
*/
void zmodem_progress(void* cbdata, int64_t current_pos)
{
unsigned cps;
long l;
long t;
time_t now;
static time_t last_progress;
now=time(NULL);
if(now-last_progress>=progress_interval || current_pos >= zm.current_file_size || newline) {
t=now-zm.transfer_start_time;
if(t<=0)
t=1;
if(zm.transfer_start_pos>current_pos)
zm.transfer_start_pos=0;
if((cps=(unsigned)((current_pos-zm.transfer_start_pos)/t))==0)
cps=1; /* cps so far */
l=(long)(zm.current_file_size/cps); /* total transfer est time */
l-=t; /* now, it's est time left */
if(l<0) l=0;
fprintf(statfp,"\rKByte: %"PRId64"/%"PRId64" %u/CRC-%u "
"Time: %lu:%02lu/%lu:%02lu %u cps %lu%% "
,current_pos/1024
,zm.current_file_size/1024
,zm.block_size
,mode&RECV ? (zm.receive_32bit_data ? 32:16) :
(zm.can_fcs_32 && !zm.want_fcs_16) ? 32:16
,t/60L
,t%60L
,l/60L
,l%60L
,cps
,zm.current_file_size?(long)(((float)current_pos/(float)zm.current_file_size)*100.0):100
);
newline=FALSE;
last_progress=now;
}
}
static int send_files(char** fname, uint fnames)
{
char path[MAX_PATH+1];
int i;
uint errors;
uint fnum;
uint cps;
glob_t g;
int gi;
BOOL success=TRUE;
uint64_t fsize;
uint64_t sent_bytes;
uint64_t total_bytes=0;
time_t t,startfile;
time_t startall;
FILE* fp;
startall=time(NULL);
/****************************************************/
/* Search through all to find total files and bytes */
/****************************************************/
for(fnum=0;fnum<fnames;fnum++) {
if(glob(fname[fnum],0,NULL,&g)) {
lprintf(LOG_WARNING,"%s not found",fname[fnum]);
continue;
}
for(i=0;i<(int)g.gl_pathc;i++) {
if(isdir(g.gl_pathv[i]))
continue;
xm.total_files++;
xm.total_bytes+=flength(g.gl_pathv[i]);
}
globfree(&g);
}
if(xm.total_files<1) {
lprintf(LOG_ERR,"No files to send");
return(-1);
}
if(xm.total_files>1)
lprintf(LOG_INFO,"Sending %u files (%"PRId64" KB total)"
,xm.total_files,xm.total_bytes/1024);
zm.files_remaining = xm.total_files;
zm.bytes_remaining = xm.total_bytes;
/***********************************************/
/* Send every file matching names or filespecs */
/***********************************************/
for(fnum=0;fnum<fnames;fnum++) {
if(glob(fname[fnum],0,NULL,&g)) {
lprintf(LOG_WARNING,"%s not found",fname[fnum]);
continue;
}
for(gi=0;gi<(int)g.gl_pathc;gi++) {
SAFECOPY(path,g.gl_pathv[gi]);
if(isdir(path))
continue;
if((fp=fnopen(NULL,path,O_RDONLY|O_BINARY))==NULL
&& (fp=fopen(path,"rb"))==NULL) {
lprintf(LOG_ERR,"Error %d opening %s for read",errno,path);
continue;
}
setvbuf(fp,NULL,_IOFBF,0x10000);
fsize=filelength(fileno(fp));
errors=0;
startfile=time(NULL);
lprintf(LOG_INFO,"Sending %s (%"PRId64" KB) via %cMODEM"
,path,fsize/1024
,mode&XMODEM ? 'X' : mode&YMODEM ? 'Y' : 'Z');
if(mode&ZMODEM)
success=zmodem_send_file(&zm, path, fp, /* ZRQINIT? */fnum==0, &startfile, &sent_bytes);
else /* X/Ymodem */
success=xmodem_send_file(&xm, path, fp, &startfile, &sent_bytes);
fclose(fp);
if((t=time(NULL)-startfile)<=0)
t=1;
if((cps=(unsigned)(sent_bytes/t))==0)
cps=1;
if(success) {
xm.sent_files++;
xm.sent_bytes+=fsize;
if(zm.file_skipped)
lprintf(LOG_WARNING,"File Skipped");
else
lprintf(LOG_INFO,"Successful - Time: %lu:%02lu CPS: %lu"
,t/60,t%60,cps);
if(xm.total_files-xm.sent_files)
lprintf(LOG_INFO,"Remaining - Time: %lu:%02lu Files: %u KBytes: %"PRId64
,((xm.total_bytes-xm.sent_bytes)/cps)/60
,((xm.total_bytes-xm.sent_bytes)/cps)%60
,xm.total_files-xm.sent_files
,(xm.total_bytes-xm.sent_bytes)/1024
);
} else
lprintf(LOG_WARNING,"File Transfer %s", zm.local_abort ? "Aborted" : "Failure");
/* DSZLOG entry */
if(logfp) {
lprintf(LOG_DEBUG,"Updating DSZLOG: %s", dszlog);
fprintf(logfp,"%c %7"PRId64" %5u bps %6lu cps %3u errors %5u %4u "
"%s -1\n"
,(mode&ZMODEM && zm.file_skipped) ? 's'
: success ? (mode&ZMODEM ? 'z':'S')
: 'E'
,sent_bytes
,115200 /* baud */
,cps
,mode&ZMODEM ? zm.errors : xm.errors
,flows
,mode&ZMODEM ? zm.block_size : xm.block_size
,dszlog_filename(path));
fflush(logfp);
}
total_bytes += sent_bytes;
if(zm.local_abort) {
xm.cancelled=FALSE;
xmodem_cancel(&xm);
break;
}
if(xm.cancelled || zm.cancelled || !success)
break;
} /* while(gi<(int)g.gl_pathc) */
if(gi<(int)g.gl_pathc)/* error occurred */
break;
}
if(mode&ZMODEM && !zm.cancelled && is_connected(NULL) && (success || total_bytes))
zmodem_get_zfin(&zm);
if(fnum<fnames) /* error occurred */
return(-1);
if(!success)
return(-1);
if(mode&XMODEM)
return(0);
if(mode&YMODEM) {
if(xmodem_get_mode(&xm)) {
lprintf(LOG_INFO,"Sending YMODEM termination block");
memset(block,0,XMODEM_MIN_BLOCK_SIZE); /* send short block for terminator */
xmodem_put_block(&xm, block, XMODEM_MIN_BLOCK_SIZE /* block_size */, 0 /* block_num */);
if(xmodem_get_ack(&xm, /* tries: */6, /* block_num: */0) != ACK) {
lprintf(LOG_WARNING,"Failed to receive ACK after terminating block");
}
}
}
if(xm.total_files>1) {
t=time(NULL)-startall;
if(!t) t=1;
lprintf(LOG_INFO,"Overall - Time %02lu:%02lu KBytes: %"PRId64" CPS: %lu"
,t/60,t%60,total_bytes/1024,total_bytes/t);
}
return(0); /* success */
}
static int receive_files(char** fname_list, int fnames)
{
char str[MAX_PATH+1];
char fname[MAX_PATH+1];
int i;
int fnum=0;
uint errors;
uint total_files=0;
uint cps;
uint wr;
BOOL success=FALSE;
long fmode;
long serial_num=-1;
int64_t file_bytes=0,file_bytes_left=0;
int64_t total_bytes=0;
FILE* fp;
time_t t,startfile,ftime;
if(fnames>1)
lprintf(LOG_INFO,"Receiving %u files",fnames);
outbuf.highwater_mark=0; /* don't delay ACK/NAK transmits */
/* Purge input buffer */
while(is_connected(NULL) && (i=recv_byte(NULL,0))!=NOINP)
lprintf(LOG_WARNING,"Throwing out received: %s",chr((uchar)i));
while(is_connected(NULL)) {
file_bytes=0x7fffffff;
if(mode&XMODEM) {
SAFECOPY(str,fname_list[0]); /* we'll have at least one fname */
file_bytes_left=file_bytes;
}
else {
if(mode&YMODEM) {
lprintf(LOG_INFO,"Fetching YMODEM header block");
for(errors=0;errors<=xm.max_errors && !xm.cancelled;errors++) {
xmodem_put_nak(&xm, /* expected_block: */ 0);
if(xmodem_get_block(&xm, block, /* expected_block: */ 0) == SUCCESS) {
send_byte(NULL,ACK,10);
break;
}
if(errors+1>xm.max_errors/3 && mode&CRC && !(mode&GMODE)) {
lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode");
mode&=~CRC;
}
}
if(errors>xm.max_errors || xm.cancelled) {
lprintf(LOG_ERR,"Error fetching YMODEM header block");
xmodem_cancel(&xm);
return(1);
}
if(!block[0]) {
lprintf(LOG_INFO,"Received YMODEM termination block");
return(0);
}
ftime=total_files=0;
total_bytes=0;
i=sscanf(block+strlen(block)+1,"%"PRId64" %lo %lo %lo %u %"PRId64
,&file_bytes /* file size (decimal) */
,&ftime /* file time (octal unix format) */
,&fmode /* file mode (not used) */
,&serial_num /* program serial number */
,&total_files /* remaining files to be sent */
,&total_bytes /* remaining bytes to be sent */
);
lprintf(LOG_DEBUG,"YMODEM header (%u fields): %s", i, block+strlen(block)+1);
SAFECOPY(fname,block);
} else { /* Zmodem */
lprintf(LOG_INFO,"Waiting for ZMODEM sender...");
i=zmodem_recv_init(&zm);
if(zm.cancelled)
return(1);
if(i<0)
return(-1);
switch(i) {
case ZFILE:
SAFECOPY(fname,zm.current_file_name);
file_bytes = zm.current_file_size;
ftime = zm.current_file_time;
total_files = zm.files_remaining;
total_bytes = zm.bytes_remaining;
break;
case ZFIN:
case ZCOMPL:
return(!success);
default:
return(-1);
}
}
file_bytes_left=file_bytes;
if(!total_files)
total_files=fnames-fnum;
if(!total_files)
total_files=1;
if(total_bytes<file_bytes)
total_bytes=file_bytes;
lprintf(LOG_DEBUG,"Incoming filename: %.64s ",fname);
if(mode&RECVDIR)
sprintf(str,"%s%s",fname_list[0],getfname(fname));
else {
SAFECOPY(str,getfname(fname));
for(i=0;i<fnames;i++) {
if(!fname_list[i][0]) /* name blank or already used */
continue;
if(!stricmp(getfname(fname_list[i]),str)) {
SAFECOPY(str,fname_list[i]);
fname_list[i][0]=0;
break;
}
}
if(i==fnames) { /* Not found in list */
if(fnames)
fprintf(statfp," - Not in receive list!");
if(!fnames || fnum>=fnames || !fname_list[fnum][0])
SAFECOPY(str,getfname(fname)); /* worst case */
else {
SAFECOPY(str,fname_list[fnum]);
fname_list[fnum][0]=0;
}
}
}
fprintf(statfp,"File size: %"PRId64" bytes\n", file_bytes);
if(total_files>1)
fprintf(statfp,"Remaining: %"PRId64" bytes in %u files\n", total_bytes, total_files);
}
lprintf(LOG_DEBUG,"Receiving: %.64s ",str);
fnum++;
if(!(mode&RECVDIR) && fnames && fnum>fnames) {
lprintf(LOG_WARNING,"Attempt to send more files than specified");
xmodem_cancel(&xm);
break;
}
if(fexistcase(str) && !(mode&OVERWRITE)) {
lprintf(LOG_WARNING,"%s already exists",str);
if(mode&ZMODEM) {
zmodem_send_zskip(&zm);
continue;
}
xmodem_cancel(&xm);
return(1);
}
if(!(mode&XMODEM) && max_file_size!=0 && file_bytes > max_file_size) {
lprintf(LOG_WARNING,"%s file size (%"PRId64") exceeds specified maximum: %"PRId64" bytes", str, file_bytes, max_file_size);
if(mode&ZMODEM) {
zmodem_send_zskip(&zm);
continue;
}
xmodem_cancel(&xm);
return(1);
}
if((fp=fnopen(NULL,str,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY))==NULL
&& (fp=fopen(str,"wb"))==NULL) {
lprintf(LOG_ERR,"Error %d creating %s",errno,str);
if(mode&ZMODEM) {
zmodem_send_zskip(&zm);
continue;
}
xmodem_cancel(&xm);
return(1);
}
if(mode&XMODEM)
lprintf(LOG_INFO,"Receiving %s via XMODEM%s %s"
,str
,mode&GMODE ? "-G" : ""
,mode&CRC ? "CRC-16":"Checksum");
else
lprintf(LOG_INFO,"Receiving %s (%"PRId64" KB) via %s %s"
,str
,file_bytes/1024
,mode&YMODEM ? mode&GMODE ? "YMODEM-G" : "YMODEM" :"ZMODEM"
,mode&ZMODEM ? "" : (mode&CRC ? "CRC-16" : "Checksum"));
startfile=time(NULL);
success=FALSE;
if(mode&ZMODEM) {
errors=zmodem_recv_file_data(&zm,fp,0);
if(errors<=zm.max_errors && !zm.cancelled)
success=TRUE;
} else {
errors=0;
block_num=1;
xmodem_put_nak(&xm, block_num);
while(is_connected(NULL)) {
off_t pos=ftello(fp);
if(max_file_size!=0 && pos>=max_file_size) {
lprintf(LOG_WARNING,"Specified maximum file size (%"PRId64" bytes) reached at offset %"PRId64
,max_file_size, pos);
break;
}
xmodem_progress(NULL,block_num,pos,file_bytes,startfile);
i=xmodem_get_block(&xm, block, block_num);
if(i!=SUCCESS) {
if(i==EOT) { /* end of transfer */
success=TRUE;
xmodem_put_ack(&xm);
break;
}
if(i==CAN) { /* Cancel */
xm.cancelled=TRUE;
break;
}
if(++errors>xm.max_errors || (mode&GMODE)) {
lprintf(LOG_ERR,"Too many errors (%u)",errors);
xmodem_cancel(&xm);
break;
}
if(i!=NOT_XMODEM
&& block_num==1 && errors>xm.max_errors/3 && mode&CRC && !(mode&GMODE)) {
lprintf(LOG_NOTICE,"Falling back to 8-bit Checksum mode (error=%d)", i);
mode&=~CRC;
}
xmodem_put_nak(&xm, block_num);
continue;
}
if(!(mode&GMODE))
send_byte(NULL,ACK,10);
if(file_bytes_left<=0L) { /* No more bytes to receive */
lprintf(LOG_WARNING,"Sender attempted to send more bytes than were specified in header");
break;
}
wr=xm.block_size;
if(wr>(uint)file_bytes_left)
wr=(uint)file_bytes_left;
if(fwrite(block,1,wr,fp)!=wr) {
lprintf(LOG_ERR,"ERROR %d writing %u bytes at file offset %"PRIu64
,errno, wr, (uint64_t)ftello(fp));
xmodem_cancel(&xm);
return(1);
}
file_bytes_left-=wr;
block_num++;
}
}
/* Use correct file size */
if(mode&ZMODEM)
file_bytes = zm.current_file_size; /* file can grow in transit */
else {
fflush(fp);
if(file_bytes < filelength(fileno(fp))) {
lprintf(LOG_INFO,"Truncating file to %"PRIu64" bytes", file_bytes);
chsize(fileno(fp),(ulong)file_bytes); /* <--- 4GB limit */
} else
file_bytes = filelength(fileno(fp));
}
fclose(fp);
t=time(NULL)-startfile;
if(!t) t=1;
if(zm.file_skipped)
lprintf(LOG_WARNING,"File Skipped");
else if(success)
lprintf(LOG_INFO,"Successful - Time: %lu:%02lu CPS: %lu"
,t/60,t%60,(ulong)(file_bytes/t));
else
lprintf(LOG_ERR,"File Transfer %s"
,zm.local_abort ? "Aborted": zm.cancelled ? "Cancelled":"Failure");
if(!(mode&XMODEM) && ftime)
setfdate(str,ftime);
if(logfp) {
lprintf(LOG_DEBUG,"Updating DSZLOG: %s", dszlog);
fprintf(logfp,"%c %6lu %5u bps %4lu cps %3u errors %5u %4u "
"%s %d\n"
,success ? (mode&ZMODEM ? 'Z' : 'R') : 'E'
,file_bytes
,115200 /* baud */
,file_bytes/t
,errors
,flows
,mode&ZMODEM ? zm.block_size : xm.block_size
,dszlog_filename(str)
,serial_num);
fflush(logfp);
}
if(zm.local_abort) {
lprintf(LOG_DEBUG,"Locally aborted, sending cancel to remote");
if(mode&ZMODEM)
zmodem_send_zabort(&zm);
xm.cancelled=FALSE;
xmodem_cancel(&xm);
break;
}
if(mode&XMODEM) /* maximum of one file */
break;
if((cps=(unsigned)(file_bytes/t))==0)
cps=1;
total_files--;
total_bytes-=file_bytes;
if(total_files>1 && total_bytes)
lprintf(LOG_INFO,"Remaining - Time: %lu:%02lu Files: %u KBytes: %"PRIu64
,(total_bytes/cps)/60
,(total_bytes/cps)%60
,total_files
,total_bytes/1024
);
}
return(!success); /* 0=success */
}
void bail(int code)
{
if(logfp!=NULL)
fclose(logfp);
if(pause_on_exit || (pause_on_abend && code!=0)) {
printf("Hit enter to continue...");
getchar();
}
exit(code);
}
static const char* usage=
"usage: sexyz <socket> [-opts] <cmd> [file | path | @list]\n"
"\n"
#ifdef __unix__
"socket = TCP socket descriptor (leave blank for stdio mode)\n"
#else
"socket = TCP socket descriptor\n"
#endif
"\n"
"opts = -y allow overwriting of existing files when receiving\n"
" -o disable Zmodem CRC-32 mode (use CRC-16)\n"
" -s disable Zmodem streaming (Slow Zmodem)\n"
" -k enable X/Ymodem-1K send mode\n"
" -c enable Xmodem-CRC receive mode\n"
" -g enable X/Ymodem-G receive mode (no error recovery)\n"
" -2 set maximum Zmodem block size to 2K\n"
" -4 set maximum Zmodem block size to 4K\n"
" -8 set maximum Zmodem block size to 8K (ZedZap)\n"
" -m# set maximum receive file size to # bytes (0=unlimited, default=%u)\n"
" -! to pause after abnormal exit (error)\n"
#ifdef __unix__
" -telnet to enable Telnet mode (the default except in stdio mode)\n"
#else
" -telnet to enable Telnet mode (the default)\n"
#endif
" -rlogin or -ssh or -raw to disable Telnet mode\n"
"\n"
"cmd = v to display detailed version information\n"
" sx to send Xmodem rx to receive Xmodem\n"
" sX to send Xmodem-1K rc to receive Xmodem-CRC\n"
" sy to send Ymodem ry to receive Ymodem\n"
" sY to send Ymodem-1K rg to receive Ymodem-G\n"
" sz to send Zmodem rz to receive Zmodem\n"
"\n"
"file = filename to send or receive\n"
"path = directory to receive files into\n"
"list = name of text file with list of filenames to send or receive\n";
#ifdef __unix__
struct termios tio_default; /* Initial term settings */
#ifdef NEEDS_CFMAKERAW
static void
cfmakeraw(struct termios *t)
{
t->c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
t->c_oflag &= ~OPOST;
t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
t->c_cflag &= ~(CSIZE|PARENB);
t->c_cflag |= CS8;
}
#endif
static void fixterm(void)
{
tcsetattr(STDIN_FILENO,TCSANOW,&tio_default);
}
static void init_stdio(void)
{
struct termios tio_raw;
if(isatty(STDERR_FILENO))
fclose(stderr);
if (isatty(STDIN_FILENO)) {
tcgetattr(STDIN_FILENO,&tio_default);
tio_raw = tio_default;
/* cfmakeraw(&tio_raw); */
tio_raw.c_iflag &= ~(IMAXBEL|IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
tio_raw.c_oflag &= ~OPOST;
tio_raw.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
tio_raw.c_cflag &= ~(CSIZE|PARENB);
tio_raw.c_cflag |= CS8;
tcsetattr(STDIN_FILENO,TCSANOW,&tio_raw);
setvbuf(stdout, NULL, _IOFBF, 0);
atexit(fixterm);
}
}
BOOL RingBufIsEmpty(void *buf)
{
return(RingBufFull(buf)==0);
}
#endif
/***************/
/* Entry Point */
/***************/
int main(int argc, char **argv)
{
char str[MAX_PATH+1];
char fname[MAX_PATH+1];
char ini_fname[MAX_PATH+1];
char* p;
char* arg;
int i;
int retval;
uint fnames=0;
FILE* fp;
BOOL tcp_nodelay;
char compiler[32];
BOOL telnet_requested=FALSE;
str_list_t fname_list;
fname_list=strListInit();
DESCRIBE_COMPILER(compiler);
errfp=stderr;
#ifdef __unix__
statfp=stderr;
#else
statfp=stdout;
#endif
sscanf("$Revision: 1.1.1.2 $", "%*s %s", revision);
fprintf(statfp,"\nSynchronet External X/Y/ZMODEM v%s-%s"
" Copyright %s Rob Swindell\n\n"
,revision
,PLATFORM_DESC
,__DATE__+7
);
xmodem_init(&xm,NULL,&mode,lputs,xmodem_progress,send_byte,recv_byte,is_connected,NULL,flush);
zmodem_init(&zm,NULL,lputs,zmodem_progress,send_byte,recv_byte,is_connected,NULL,data_waiting,flush);
xm.log_level=&log_level;
zm.log_level=&log_level;
/* Generate path/sexyz[.host].ini from path/sexyz[.exe] */
SAFECOPY(str,argv[0]);
p=getfname(str);
SAFECOPY(fname,p);
*p=0;
if((p=getfext(fname))!=NULL)
*p=0;
strcat(fname,".ini");
iniFileName(ini_fname,sizeof(ini_fname),str,fname);
if((fp=fopen(ini_fname,"r"))!=NULL)
fprintf(statfp,"Reading %s\n",ini_fname);
tcp_nodelay =iniReadBool(fp,ROOT_SECTION,"TCP_NODELAY",TRUE);
telnet =iniReadBool(fp,ROOT_SECTION,"Telnet",TRUE);
debug_tx =iniReadBool(fp,ROOT_SECTION,"DebugTx",FALSE);
debug_rx =iniReadBool(fp,ROOT_SECTION,"DebugRx",FALSE);
debug_telnet =iniReadBool(fp,ROOT_SECTION,"DebugTelnet",FALSE);
pause_on_exit =iniReadBool(fp,ROOT_SECTION,"PauseOnExit",FALSE);
pause_on_abend =iniReadBool(fp,ROOT_SECTION,"PauseOnAbend",FALSE);
log_level =iniReadLogLevel(fp,ROOT_SECTION,"LogLevel",log_level);
use_syslog =iniReadBool(fp,ROOT_SECTION,"SysLog",use_syslog);
outbuf.highwater_mark =(ulong)iniReadBytes(fp,ROOT_SECTION,"OutbufHighwaterMark",1,1100);
outbuf_drain_timeout =iniReadInteger(fp,ROOT_SECTION,"OutbufDrainTimeout",10);
outbuf_size =(ulong)iniReadBytes(fp,ROOT_SECTION,"OutbufSize",1,16*1024);
progress_interval =iniReadInteger(fp,ROOT_SECTION,"ProgressInterval",1);
max_file_size =iniReadBytes(fp,ROOT_SECTION,"MaxFileSize",/* unit: */1,MAX_FILE_SIZE);
if(iniReadBool(fp,ROOT_SECTION,"Debug",FALSE))
log_level=LOG_DEBUG;
xm.send_timeout =iniReadInteger(fp,"Xmodem","SendTimeout",xm.send_timeout); /* seconds */
xm.recv_timeout =iniReadInteger(fp,"Xmodem","RecvTimeout",xm.recv_timeout); /* seconds */
xm.byte_timeout =iniReadInteger(fp,"Xmodem","ByteTimeout",xm.byte_timeout); /* seconds */
xm.ack_timeout =iniReadInteger(fp,"Xmodem","AckTimeout",xm.ack_timeout); /* seconds */
xm.block_size =(ulong)iniReadBytes(fp,"Xmodem","BlockSize",1,xm.block_size); /* 128 or 1024 */
xm.max_block_size =(ulong)iniReadBytes(fp,"Xmodem","MaxBlockSize",1,xm.max_block_size); /* 128 or 1024 */
xm.max_errors =iniReadInteger(fp,"Xmodem","MaxErrors",xm.max_errors);
xm.g_delay =iniReadInteger(fp,"Xmodem","G_Delay",xm.g_delay);
xm.crc_mode_supported =iniReadBool(fp,"Xmodem","SendCRC",xm.crc_mode_supported);
xm.g_mode_supported =iniReadBool(fp,"Xmodem","SendG",xm.g_mode_supported);
xm.fallback_to_xmodem =iniReadInteger(fp,"Ymodem","FallbackToXmodem", xm.fallback_to_xmodem);
zm.init_timeout =iniReadInteger(fp,"Zmodem","InitTimeout",zm.init_timeout); /* seconds */
zm.send_timeout =iniReadInteger(fp,"Zmodem","SendTimeout",zm.send_timeout); /* seconds */
zm.recv_timeout =iniReadInteger(fp,"Zmodem","RecvTimeout",zm.recv_timeout); /* seconds */
zm.crc_timeout =iniReadInteger(fp,"Zmodem","CrcTimeout",zm.crc_timeout); /* seconds */
zm.block_size =(ulong)iniReadBytes(fp,"Zmodem","BlockSize",1,zm.block_size); /* 1024 */
zm.max_block_size =(ulong)iniReadBytes(fp,"Zmodem","MaxBlockSize",1,zm.max_block_size); /* 1024 or 8192 */
zm.max_errors =iniReadInteger(fp,"Zmodem","MaxErrors",zm.max_errors);
zm.recv_bufsize =(ulong)iniReadBytes(fp,"Zmodem","RecvBufSize",1,0);
zm.no_streaming =!iniReadBool(fp,"Zmodem","Streaming",TRUE);
zm.want_fcs_16 =!iniReadBool(fp,"Zmodem","CRC32",TRUE);
zm.escape_telnet_iac =iniReadBool(fp,"Zmodem","EscapeTelnetIAC",TRUE);
zm.escape_8th_bit =iniReadBool(fp,"Zmodem","Escape8thBit",FALSE);
zm.escape_ctrl_chars =iniReadBool(fp,"Zmodem","EscapeCtrlChars",FALSE);
dszlog_path =iniReadBool(fp,"DSZLOG","Path",TRUE);
dszlog_short =iniReadBool(fp,"DSZLOG","Short",FALSE);
dszlog_quotes =iniReadBool(fp,"DSZLOG","Quotes",FALSE);
if(fp!=NULL)
fclose(fp);
if(zm.recv_bufsize > 0xffff)
zm.recv_bufsize = 0xffff;
if(outbuf_size < MIN_OUTBUF_SIZE)
outbuf_size = MIN_OUTBUF_SIZE;
else if(outbuf_size > MAX_OUTBUF_SIZE)
outbuf_size = MAX_OUTBUF_SIZE;
fprintf(statfp,"Output buffer size: %u\n", outbuf_size);
RingBufInit(&outbuf, outbuf_size);
#if !defined(RINGBUF_EVENT)
outbuf_empty=CreateEvent(NULL,/* ManualReset */TRUE, /*InitialState */TRUE,NULL);
#ifdef __unix__
outbuf_empty->cbdata=&outbuf;
outbuf_empty->verify=RingBufIsEmpty;
#endif
#endif
#if 0
if(argc>1) {
fprintf(statfp,"Command line: ");
for(i=1;i<argc;i++)
fprintf(statfp,"%s ",argv[i]);
fprintf(statfp,"\n",statfp);
}
#endif
for(i=1;i<argc;i++) {
if(sock==INVALID_SOCKET && isdigit(argv[i][0])) {
sock=atoi(argv[i]);
continue;
}
if(!(mode&(SEND|RECV))) {
if(toupper(argv[i][0])=='S' || toupper(argv[i][0])=='R') { /* cmd */
if(toupper(argv[i][0])=='R')
mode|=RECV;
else
mode|=SEND;
switch(argv[i][1]) {
case 'c':
case 'C':
mode|=XMODEM|CRC;
break;
case 'x':
xm.block_size=XMODEM_MIN_BLOCK_SIZE;
case 'X':
mode|=XMODEM;
break;
case 'b': /* sz/rz compatible */
case 'B':
case 'y':
xm.block_size=XMODEM_MIN_BLOCK_SIZE;
case 'Y':
mode|=(YMODEM|CRC);
break;
case 'k': /* Ymodem-Checksum for debug/test purposes only */
mode|=YMODEM;
break;
case 'g':
case 'G':
mode|=(YMODEM|CRC|GMODE);
break;
case 'z':
case 'Z':
mode|=(ZMODEM|CRC);
break;
default:
fprintf(statfp,"Unrecognized command '%s'\n\n",argv[i]);
fprintf(statfp,usage,MAX_FILE_SIZE);
bail(1);
return -1;
}
continue;
}
if(toupper(argv[i][0])=='V') {
fprintf(statfp,"%-8s %s\n",getfname(__FILE__) ,revision);
fprintf(statfp,"%-8s %s\n",getfname(xmodem_source()),xmodem_ver(str));
fprintf(statfp,"%-8s %s\n",getfname(zmodem_source()),zmodem_ver(str));
#ifdef _DEBUG
fprintf(statfp,"Debug\n");
#endif
fprintf(statfp,"Compiled %s %.5s with %s\n",__DATE__,__TIME__,compiler);
fprintf(statfp,"%s\n",os_version(str));
bail(0);
return 0;
}
arg=argv[i];
if(*arg=='-') {
while(*arg=='-')
arg++;
if(stricmp(arg,"telnet")==0) {
telnet_requested=TRUE;
telnet=TRUE;
continue;
}
if(stricmp(arg,"rlogin")==0 || stricmp(arg,"ssh")==0 || stricmp(arg,"raw")==0) {
telnet_requested=FALSE;
telnet=FALSE;
continue;
}
if(stricmp(arg,"debug")==0) {
log_level=LOG_DEBUG;
continue;
}
if(stricmp(arg,"syslog")==0) {
use_syslog=TRUE;
continue;
}
if(stricmp(arg,"quotes")==0) {
dszlog_quotes=TRUE;
continue;
}
switch(toupper(*arg)) {
case 'K': /* sz/rz compatible */
xm.block_size=XMODEM_MAX_BLOCK_SIZE;
break;
case 'C': /* sz/rz compatible */
mode|=CRC;
break;
case '2':
zm.max_block_size=2048;
break;
case '4':
zm.max_block_size=4096;
break;
case '8': /* ZedZap */
zm.max_block_size=8192;
break;
case 'O': /* disable Zmodem CRC-32 */
zm.want_fcs_16=TRUE;
break;
case 'S': /* disable Zmodem streaming */
zm.no_streaming=TRUE;
break;
case 'G': /* Ymodem-G or Xmodem-G (a.k.a. Qmodem-G) */
mode|=(GMODE|CRC);
break;
case 'Y':
mode|=OVERWRITE;
break;
case '!':
pause_on_abend=TRUE;
break;
case 'M': /* MaxFileSize */
max_file_size=strtoul(arg++,NULL,0); /* TODO: use strtoull() ? */
break;
}
}
}
else if((argv[i][0]=='+' || argv[i][0]=='@') && fexist(argv[i]+1)) {
if(mode&RECVDIR) {
fprintf(statfp,"!Cannot specify both directory and filename\n");
bail(1);
return -1;
}
sprintf(str,"%s",argv[i]+1);
if((fp=fopen(str,"r"))==NULL) {
fprintf(statfp,"!Error %d opening filelist: %s\n",errno,str);
bail(1);
return -1;
}
while(!feof(fp) && !ferror(fp)) {
if(!fgets(str,sizeof(str),fp))
break;
truncsp(str);
strListAppend(&fname_list,strdup(str),fnames++);
}
fclose(fp);
}
else if(mode&(SEND|RECV)){
if(isdir(argv[i])) { /* is a directory */
if(mode&RECVDIR) {
fprintf(statfp,"!Only one directory can be specified\n");
bail(1);
return -1;
}
if(fnames) {
fprintf(statfp,"!Cannot specify both directory and filename\n");
bail(1);
return -1;
}
if(mode&SEND) {
fprintf(statfp,"!Cannot send directory '%s'\n",argv[i]);
bail(1);
return -1;
}
mode|=RECVDIR;
}
strListAppend(&fname_list,argv[i],fnames++);
}
}
if(max_file_size)
fprintf(statfp,"Maximum receive file size: %"PRIi64"\n", max_file_size);
if(!(mode&(SEND|RECV))) {
fprintf(statfp,"!No command specified\n\n");
fprintf(statfp,usage,MAX_FILE_SIZE);
bail(1);
return -1;
}
if(mode&(SEND|XMODEM) && !fnames) { /* Sending with any or recv w/Xmodem */
fprintf(statfp,"!Must specify filename or filelist\n\n");
fprintf(statfp,usage,MAX_FILE_SIZE);
bail(1);
return -1;
}
if(sock==INVALID_SOCKET || sock<1) {
#ifdef __unix__
if(STDOUT_FILENO > STDIN_FILENO)
sock=STDOUT_FILENO;
else
sock=STDIN_FILENO;
stdio=TRUE;
fprintf(statfp,"No socket descriptor specified, using STDIO\n");
if(!telnet_requested)
telnet=FALSE;
init_stdio();
#else
fprintf(statfp,"!No socket descriptor specified\n\n");
fprintf(errfp,usage,MAX_FILE_SIZE);
bail(1);
return -1;
#endif
}
#ifdef __unix__
else
statfp=stdout;
#endif
if(!telnet)
zm.escape_telnet_iac = FALSE;
zm.max_file_size = max_file_size;
/* Code disabled. Why? ToDo */
/* if(mode&RECVDIR)
backslash(fname[0]); */
if(!winsock_startup())
bail(-1);
/* Enable the Nagle Algorithm */
#ifdef __unix__
if(!stdio) {
#endif
lprintf(LOG_DEBUG,"Setting TCP_NODELAY to %d",tcp_nodelay);
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char*)&tcp_nodelay,sizeof(tcp_nodelay));
#ifdef __unix__
}
#endif
/* Set non-blocking mode */
#ifdef __unix__
if(stdio) {
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
}
else
#endif
{
i=1;
ioctlsocket(sock, FIONBIO, &i);
}
if(!socket_check(sock, NULL, NULL, 0)) {
lprintf(LOG_WARNING,"No socket connection");
bail(-1);
return -1;
}
if((dszlog=getenv("DSZLOG"))!=NULL) {
if((logfp=fopen(dszlog,"w"))==NULL) {
lprintf(LOG_WARNING,"Error %d opening DSZLOG file: %s",errno,dszlog);
bail(-1);
return -1;
}
}
/* Install Ctrl-C/Break signal handler here */
#if defined(_WIN32)
SetConsoleCtrlHandler(ControlHandler, TRUE /* Add */);
#elif defined(__unix__)
signal(SIGQUIT,break_handler);
signal(SIGINT,break_handler);
signal(SIGTERM,break_handler);
signal(SIGHUP,SIG_IGN);
/* Don't die on SIGPIPE */
signal(SIGPIPE,SIG_IGN);
#endif
#if !SINGLE_THREADED
_beginthread(output_thread,0,NULL);
#endif
if(mode&RECV)
retval=receive_files(fname_list, fnames);
else
retval=send_files(fname_list, fnames);
#if !SINGLE_THREADED
lprintf(LOG_DEBUG,"Waiting for output buffer to empty... ");
if(RingBufFull(&outbuf)) {
#if !defined(RINGBUF_EVENT)
ResetEvent(outbuf_empty);
#endif
if(WaitForEvent(outbuf_empty,5000)!=WAIT_OBJECT_0)
lprintf(LOG_DEBUG,"FAILURE");
}
#endif
terminate=TRUE; /* stop output thread */
/* Code disabled. Why? ToDo */
/* sem_post(outbuf.sem);
sem_post(outbuf.highwater_sem); */
fprintf(statfp,"Exiting - Error level: %d, flows: %u, select_errors=%u"
,retval, flows, select_errors);
fprintf(statfp,"\n");
bail(retval);
return retval;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.