You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bforce/source/bforce/prot_common.c

1560 lines
38 KiB
C

/*
* binkleyforce -- unix FTN mailer project
*
* Copyright (c) 1998-2000 Alexander Belkin, 2:5020/1398.11
*
* 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.
*
* $Id$
*/
#include "includes.h"
#include "confread.h"
#include "logger.h"
#include "io.h"
#include "util.h"
#include "session.h"
#include "prot_common.h"
#include "outbound.h"
#include "freq.h"
const char *Protocols[] =
{
"Unknown", "Xmodem", "Zmodem", "ZedZap", "DirZap",
"Janus", "Hydra", "Tcp", "BinkP"
};
/*****************************************************************************
* Get first file from files queue not marked as sent. Extract files in the
* order: netmail, arcmail, other files, file request. Files of equal type
* extract in the order of flavor descending
*
* Arguments:
* dest pointer to the pointer to put file information to
* pi pointer to the protocol information structure
* q pointer to the files queue
*
* Return value:
* Zero value on success and non-zero if nothing to send
*/
static int prot_get_next_file(s_filelist **dest, s_protinfo *pi, s_fsqueue *q)
{
s_filelist *ptrl = NULL;
s_filelist *best = NULL;
*dest = NULL;
for( ptrl = q->fslist; ptrl; ptrl = ptrl->next )
if( ptrl->status == STATUS_WILLSEND )
{
if( pi->reqs_only )
{
if( (ptrl->type & TYPE_REQUEST) )
{
*dest = ptrl;
return 0;
}
}
else if( best )
{
if( (ptrl->type & TYPE_NETMAIL) )
{
if( (best->type & TYPE_NETMAIL) )
{
if( ptrl->flavor > best->flavor )
best = ptrl;
} else
best = ptrl;
}
else if( (ptrl->type & TYPE_ARCMAIL) )
{
if( (best->type & TYPE_ARCMAIL) && (ptrl->flavor > best->flavor) )
best = ptrl;
else if( !(best->type & TYPE_NETMAIL)
&& !(best->type & TYPE_ARCMAIL) )
best = ptrl;
}
else if( !(ptrl->type & TYPE_REQUEST) && (best->type & TYPE_REQUEST) )
best = ptrl;
}
else
best = ptrl;
}
if( best )
{
*dest = best;
return 0;
}
for( ptrl = pi->filelist; ptrl; ptrl = ptrl->next )
if( ptrl->status == STATUS_WILLSEND )
{
*dest = ptrl;
return 0;
}
return 1;
}
/*****************************************************************************
* Calculate total files number and size we are going to send. It will be
* used by some protocols that supports reporting of total traffic to the
* remote side (e.g. Zmodem protocols could)
*
* Arguments:
* pi pointer to the protocol information structure
* q pointer to the files queue
*
* Return value:
* None
*/
/*
void prot_update_traffic(s_protinfo *pi, const s_fsqueue *q)
{
s_filelist *ptrl = NULL;
pi->send_bytesleft = 0;
pi->send_filesleft = 0;
if( !state.sopts.holdall )
{
for( ptrl = q->fslist; ptrl; ptrl = ptrl->next )
if( ptrl->status == STATUS_WILLSEND )
{
pi->send_filesleft += 1;
pi->send_bytesleft += ptrl->size;
}
for( ptrl = pi->filelist; ptrl; ptrl = ptrl->next )
if( ptrl->status == STATUS_WILLSEND )
{
pi->send_filesleft += 1;
pi->send_bytesleft += ptrl->size;
}
}
}
*/
/*****************************************************************************
* Open next file for sending. Set all necessary information. And do all
* necessary checks.
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* Zero value on success and non-zero if have nothing to send
*/
int p_tx_fopen(s_protinfo *pi)
{
FILE *fp;
struct stat st;
s_filelist *ptrl = NULL;
if( state.sopts.holdall )
return 1;
get_next_file:
if( prot_get_next_file(&ptrl, pi, &state.queue) || !ptrl )
return 1;
/* Mark this file as "processed" */
ptrl->status = STATUS_SENDING;
pi->send_left_num -= 1;
pi->send_left_size -= ptrl->size;
if( pi->send_left_size < 0 )
pi->send_left_size = 0;
if( pi->send_left_num < 0 )
pi->send_left_num = 0;
/* Reset MinCPS time counter */
pi->tx_low_cps_time = 0;
DEB((D_PROT, "p_tx_fopen: now opening \"%s\"", ptrl->fname));
if( stat(ptrl->fname, &st) )
{
logerr("send: cannot stat file \"%s\"", ptrl->fname);
goto get_next_file;
}
if( (fp = file_open(ptrl->fname, "r")) == NULL )
{
logerr("send: cannot open file \"%s\"", ptrl->fname);
goto get_next_file;
}
/*
* Add new entry to the send files queue
*/
if( pi->sentfiles && pi->n_sentfiles > 0 )
{
pi->sentfiles = (s_finfo *)xrealloc(pi->sentfiles, sizeof(s_finfo)*(pi->n_sentfiles+1));
memset(&pi->sentfiles[pi->n_sentfiles], '\0', sizeof(s_finfo));
pi->send = &pi->sentfiles[pi->n_sentfiles++];
}
else
{
pi->sentfiles = (s_finfo *)xmalloc(sizeof(s_finfo));
memset(pi->sentfiles, '\0', sizeof(s_finfo));
pi->send = pi->sentfiles;
pi->n_sentfiles = 1;
}
/*
* Set file information
*/
pi->send->fp = fp;
pi->send->local_name = (char*)xstrcpy(file_getname(ptrl->fname));
pi->send->net_name = recode_file_out(p_convfilename(pi->send->local_name, ptrl->type));
pi->send->fname = (char*)xstrcpy(ptrl->fname);
pi->send->mod_time = (ptrl->type & TYPE_REQUEST) ? time(NULL) : st.st_mtime;
pi->send->mod_time = localtogmt(pi->send->mod_time);
pi->send->mod_time += pi->send->mod_time%2;
pi->send->mode = st.st_mode;
pi->send->bytes_total = st.st_size;
pi->send->start_time = time(NULL);
pi->send->eofseen = FALSE;
pi->send->status = FSTAT_PROCESS;
pi->send->type = ptrl->type;
pi->send->action = ptrl->action;
pi->send->flodsc = ptrl->flodsc;
if( strcmp(pi->send->local_name, pi->send->net_name) == 0 )
{
log("send: \"%s\" %d bytes",
pi->send->local_name,
pi->send->bytes_total);
}
else
{
log("send: \"%s\" %d bytes -> \"%s\"",
pi->send->local_name,
pi->send->bytes_total,
pi->send->net_name);
}
return 0;
}
void prot_traffic_update(s_traffic *traf, size_t size, int xtime, int type)
{
if( type & TYPE_REQANSW )
{
traf->freqed_size += size;
traf->freqed_time += xtime;
traf->freqed_num++;
}
else if( type & TYPE_NETMAIL )
{
traf->netmail_size += size;
traf->netmail_time += xtime;
traf->netmail_num++;
}
else if( type & TYPE_ARCMAIL )
{
traf->arcmail_size += size;
traf->arcmail_time += xtime;
traf->arcmail_num++;
}
else
{
traf->files_size += size;
traf->files_time += xtime;
traf->files_num++;
}
}
size_t prot_traffic_total_size(const s_traffic *traff)
{
return traff->netmail_size + traff->arcmail_size
+ traff->files_size + traff->freqed_size;
}
int prot_traffic_total_num(const s_traffic *traff)
{
return traff->netmail_num + traff->arcmail_num
+ traff->files_num + traff->freqed_num;
}
/*****************************************************************************
* Close sending file. If file was sent successfully than make appropriate
* actions (e.g. unlink/truncate file)
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* Zero value on success and non-zero to indicate error
*/
int p_tx_fclose(s_protinfo *pi)
{
long trans_time = 0;
long cps = 0;
if( pi->send->fp )
{
(void)file_close(pi->send->fp);
pi->send->fp = NULL;
}
/*
* Calculate send time, CPS, etc
*/
trans_time = (long)time(NULL) - (long)pi->send->start_time;
if( trans_time > 0 )
cps = (long)((pi->send->bytes_sent - pi->send->bytes_skipped) / trans_time);
log("sent: \"%s\" %ld [%s]: %ld seconds, %ld CPS",
pi->send->net_name,
(long)(pi->send->bytes_sent - pi->send->bytes_skipped),
(pi->send->status == FSTAT_SKIPPED) ? "skipped" :
(pi->send->status == FSTAT_REFUSED) ? "refused" :
(pi->send->status == FSTAT_SUCCESS) ? "ok" : "error",
(long)(trans_time > 0) ? trans_time : 0,
(long)(cps > 0) ? cps : 0);
/*
* Update outgoing traffic structure
*/
prot_traffic_update(&pi->traffic_sent,
pi->send->bytes_sent - pi->send->bytes_skipped,
trans_time, pi->send->type);
/*
* Calculate time of sending FREQ'ed files
*/
if( (pi->send->type & TYPE_REQANSW) == TYPE_REQANSW )
pi->send_freqtime += trans_time;
/*
* Do necessary actions
*/
if( pi->send->status == FSTAT_SUCCESS || pi->send->status == FSTAT_SKIPPED )
{
FILE *tfp;
if( (pi->send->type & TYPE_FROMFLO) )
out_flo_marksent(pi->send->fname,
state.queue.flotab[pi->send->flodsc].fname);
switch(pi->send->action) {
case ACTION_NOTHING:
DEB((D_PROT, "p_tx_fclose: do nothing with file \"%s\"", pi->send->fname));
break;
case ACTION_UNLINK:
case ACTION_FORCEUNLINK:
DEB((D_PROT, "p_tx_fclose: unlinking file \"%s\"", pi->send->fname));
if( unlink(pi->send->fname) && errno != ENOENT )
logerr("send: cannot unlink file \"%s\"", pi->send->fname);
break;
case ACTION_TRUNCATE:
DEB((D_PROT, "p_tx_fclose: truncating file \"%s\"", pi->send->fname));
if( (tfp = fopen(pi->send->fname, "w")) )
fclose(tfp);
else if( errno != ENOENT )
logerr("send: cannot truncate file \"%s\"", pi->send->fname);
break;
}
}
return 0;
}
/*****************************************************************************
* Read next part of file to the buffer. User by transmitter.
*
* Arguments:
* buffer pointer to the buffer
* buflen buffer size
* pi pointer to the protocol information structure
*
* Return value:
* >= 0 - number of bytes read
* -1 - error reading file (must try to send file later)
* -2 - file must be skipped (send never)
*/
int p_tx_readfile(char *buffer, size_t buflen, s_protinfo *pi)
{
int n;
struct stat st;
long ftell_pos = ftell(pi->send->fp);
pi->send->eofseen = FALSE; /* clear EOF flag */
/*
* Sanity check: sync our transmitter position
* with the position returned by the ftell()
*/
if( ftell_pos < 0 )
{
log("Error: ftell() return %ld for the file \"%s\"",
ftell_pos, pi->send->fname);
pi->send->status = FSTAT_REFUSED;
return -1;
}
/*
* Try receive file later, if such error occurs
*/
if( pi->send->bytes_sent != ftell_pos )
{
log("internal error: invalid transmitting offset");
log("pi->send->bytes_sent = %ld, ftell() = %ld",
(long)pi->send->bytes_sent, ftell_pos);
pi->send->bytes_received = ftell_pos;
return -1;
}
if( stat(pi->send->fname, &st) == -1 && errno == ENOENT )
{
log("send: file not found! do you want to skip it?");
pi->send->status = FSTAT_SKIPPED;
return -2;
}
else if( (n = fread(buffer, 1, buflen, pi->send->fp)) < buflen )
{
if( feof(pi->send->fp) )
pi->send->eofseen = TRUE;
else
{
log("send: error reading file from disk (ferror = %d)",
ferror(pi->send->fp));
pi->send->status = FSTAT_REFUSED;
return -1;
}
}
return n;
}
/*****************************************************************************
* Write buffer to the file. Used by receiver.
*
* Arguments:
* buffer pointer to the buffer
* buflen number of bytes in buffer
* pi pointer to the protocol information structure
*
* Return value:
* 0 - successfully written
* -1 - error writing file (receive later)
* -2 - file must be skipped (receive never)
*/
int p_rx_writefile(const char *buffer, size_t buflen, s_protinfo *pi)
{
struct stat st;
long ftell_pos = ftell(pi->recv->fp);
/*
* Sanity check: sync our receiver position
* with the position returned by the ftell()
*/
if( ftell_pos < 0 )
{
log("Error: ftell() return %ld for the file \"%s\"",
ftell_pos, pi->recv->fname);
pi->recv->status = FSTAT_REFUSED;
return -1;
}
/*
* Try receive file later, if such error occurs
*/
if( pi->recv->bytes_received != ftell_pos )
{
log("internal error: invalid receiving offset");
log("pi->recv->bytes_received = %ld, ftell() = %ld",
(long)pi->recv->bytes_received, ftell_pos);
pi->recv->bytes_received = ftell_pos;
return -1;
}
if( stat(pi->recv->fname, &st) == -1 )
{
fflush(pi->recv->fp);
if( stat(pi->recv->fname, &st) == -1 && errno == ENOENT )
{
log("recv: skip file \"%s\" (file not found)", pi->recv->fname);
pi->recv->status = FSTAT_SKIPPED;
return -2;
}
}
if( buflen > 0 && fwrite(buffer, buflen, 1, pi->recv->fp) != 1 )
{
log("recv: error writing file \"%s\" to disk (ferror = %d)",
pi->recv->fname, ferror(pi->recv->fp));
pi->recv->status = FSTAT_REFUSED;
return -1;
}
return 0;
}
/*****************************************************************************
* Move file from temporary inbound to main
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* 0 - successfully processed file
* 1 - error occured (mailer must refuse this file (receive later))
*/
static int p_move2inbound(s_protinfo *pi)
{
int rc = 0;
char *realname = NULL;
char *uniqname = NULL;
char *destname = NULL;
if( state.inbound && pi->recv->local_name )
{
realname = (char *)xstrcpy(state.inbound);
realname = (char *)xstrcat(realname, pi->recv->local_name);
} else
return 1;
if( access(realname, F_OK) == -1 )
{
destname = realname;
}
else
{
if( (uniqname = prot_unique_name(state.inbound,
pi->recv->local_name, pi->recv->type)) == NULL )
{
log("recv: cannot get unique name for \"%s\"",
pi->recv->local_name);
free(realname);
return 1;
}
if( !strcmp(realname, uniqname) )
{
log("recv: overwriting \"%s\"",
file_getname(uniqname));
}
else
{
log("recv: rename \"%s\" -> \"%s\"",
pi->recv->local_name, file_getname(uniqname));
}
destname = uniqname;
}
if( (rc = rename(pi->recv->fname, destname)) == -1 )
{
logerr("recv: cannot rename \"%s\" -> \"%s\"",
pi->recv->fname, destname);
}
else
{
mode_t fmode = 0;
/*
* Change new file permissions
*/
if( (pi->recv->type & TYPE_NETMAIL) == TYPE_NETMAIL )
fmode = conf_filemode(cf_mode_netmail);
else if( (pi->recv->type & TYPE_ARCMAIL) == TYPE_ARCMAIL )
fmode = conf_filemode(cf_mode_arcmail);
else if( (pi->recv->type & TYPE_REQUEST) == TYPE_REQUEST )
fmode = conf_filemode(cf_mode_request);
else if( (pi->recv->type & TYPE_TICFILE) == TYPE_TICFILE )
fmode = conf_filemode(cf_mode_ticfile);
if( fmode == 0 )
fmode = conf_filemode(cf_mode_default);
if( chmod(destname, fmode ? fmode : (S_IRUSR|S_IWUSR)) == -1 )
logerr("recv: cannot set permissions for file \"%s\"",
destname);
}
if( realname )
free(realname);
if( uniqname )
free(uniqname);
return rc ? 1 : 0;
}
/*****************************************************************************
* Compare the two file informations. One specified by file information
* structure (s_finfo) and another specified by other variables.
*
* Arguments:
* finf pointer to the file information
* fn file name
* sz file size
* tm file modification time
*
* Return value:
* Zero value if two files with such parameters seems to be the same
* file, and non-zero if it is different files
*/
int p_compfinfo(s_finfo *finf, const char *fn, size_t sz, time_t tm)
{
return ( sz == finf->bytes_total && tm == finf->mod_time
&& strcmp(fn, finf->net_name) == 0 ) ? 0 : 1;
}
/*****************************************************************************
* Open file for receiving. If file with such name allready exist:
* 1) if timestamp and size identical to existing one, then skip;
* 2) if only timestamp is equal and size of existing file is shorter then
* it possible was an unfinished transfer - try to recover;
* 3) in all other cases rename receiving file.
*
* Arguments:
* pi pointer to the protocol information structure
* fn file name |
* sz file size | \ as it was reported by
* tm file last modification time | / remote side (need checks)
* mode file permissions |
*
* Return value:
* 0 - successful call
* 1 - refuse this file (receive later)
* 2 - skip this file (never receive it)
*/
int p_rx_fopen(s_protinfo *pi, char *fn, size_t sz, time_t tm, mode_t mode)
{
const char *openmode = "w"; /* Set open mode to overwrite */
struct stat st;
const char *p_skiplist = NULL;
const char *p_delaylist = NULL;
size_t minfree = 0;
size_t needed_bytes_total = 0;
/*
* Check. May be we are receiving this file allready
*/
if( pi->recv && p_compfinfo(pi->recv, fn, sz, tm) == 0 )
{
log("recv: got duplicated file information");
if( pi->recv->fp )
return 0;
else if( pi->recv->status == FSTAT_SKIPPED )
return 2;
else if( pi->recv->status == FSTAT_REFUSED )
return 1;
}
/* Reset mincps time counter */
pi->rx_low_cps_time = 0;
/*
* Add new entry to the receive files queue
*/
if( pi->rcvdfiles && pi->n_rcvdfiles > 0 )
{
pi->rcvdfiles = (s_finfo *)xrealloc(pi->rcvdfiles, sizeof(s_finfo)*(pi->n_rcvdfiles+1));
memset(&pi->rcvdfiles[pi->n_rcvdfiles], '\0', sizeof(s_finfo));
pi->recv = &pi->rcvdfiles[pi->n_rcvdfiles++];
}
else
{
pi->rcvdfiles = (s_finfo *)xmalloc(sizeof(s_finfo));
memset(pi->rcvdfiles, '\0', sizeof(s_finfo));
pi->recv = pi->rcvdfiles;
pi->n_rcvdfiles = 1;
}
/*
* Set file information
*/
pi->recv->net_name = (char*)xstrcpy(fn);
pi->recv->local_name = xstrcpy(file_getname(fn));
pi->recv->bytes_total = sz;
pi->recv->mod_time = tm;
pi->recv->mode = mode;
pi->recv->start_time = time(NULL);
pi->recv->eofseen = FALSE;
pi->recv->status = FSTAT_PROCESS;
pi->recv->type = out_filetype(pi->recv->net_name);
pi->recv->action = ACTION_NOTHING;
pi->recv->flodsc = 0;
/*
* Convert file name to local charset
*/
recode_file_in(pi->recv->local_name);
/*
* Remove undesirable characters from file name
*/
file_name_makesafe(pi->recv->local_name);
/*
* Upper case file names convert to lower case.
*/
if( string_isupper(pi->recv->local_name) )
string_tolower(pi->recv->local_name);
if( strcmp(pi->recv->local_name, pi->recv->net_name) )
{
log("recv: \"%s\" %d bytes -> \"%s\"",
string_printable(pi->recv->net_name),
pi->recv->bytes_total,
string_printable(pi->recv->local_name));
}
else
{
log("recv: \"%s\" %d bytes",
string_printable(pi->recv->local_name),
pi->recv->bytes_total);
}
/*
* Get `DelayFilesIn', `Skipfiles' and `MinFree' values
*/
p_skiplist = conf_string(cf_skip_files_recv);
p_delaylist = conf_string(cf_delay_files_recv);
minfree = conf_number(cf_min_free_space);
/*
* Should we delay(refuse) this file NOW?
*/
if( p_delaylist && !checkmasks(p_delaylist, pi->recv->net_name) )
{
log("recv: file from delaylist -> refuse");
pi->recv->status = FSTAT_REFUSED;
return(1);
}
/*
* Should we skip this file NOW?
*/
if( p_skiplist && !checkmasks(p_skiplist, pi->recv->net_name) )
{
log("recv: file from skiplist -> skip");
pi->recv->status = FSTAT_SKIPPED;
return(2);
}
/*
* Check. May be we allready have this file
*/
if( (pi->recv->type & TYPE_REQUEST) || (pi->recv->type & TYPE_NETMAIL) )
{
pi->recv->fname = p_gettmpname(state.tinbound, pi->recv->local_name,
state.node.addr, pi->recv->bytes_total,
pi->recv->mod_time);
}
else /* It is not netmail or filerequest */
{
char *fname;
fname = (char *)xstrcpy(state.inbound);
fname = (char *)xstrcat(fname, pi->recv->local_name);
if( stat(fname, &st ) == 0 )
{
/* Skip file if we allready have it's copy */
if( pi->recv->mod_time == localtogmt(st.st_mtime)
&& pi->recv->bytes_total == st.st_size )
{
log("recv: allready have \"%s\"", fname);
pi->recv->status = FSTAT_SKIPPED;
}
}
free(fname); fname = NULL;
if( pi->recv->status == FSTAT_SKIPPED )
return 2;
pi->recv->fname = p_gettmpname(state.tinbound, pi->recv->local_name,
state.node.addr, pi->recv->bytes_total,
pi->recv->mod_time);
/* Now make same check for temporary inbound */
if( stat(pi->recv->fname, &st) == 0 )
{
/* Skip if it is the same file */
if( pi->recv->mod_time == localtogmt(st.st_mtime)
&& pi->recv->bytes_total == st.st_size )
{
/*
* We will skip it and try to move it into inbound
* directory, do you know who could left it here ? :)
*/
log("recv: allready have \"%s\"", pi->recv->fname);
if( p_move2inbound(pi) == 0 )
pi->recv->status = FSTAT_SKIPPED;
else
pi->recv->status = FSTAT_REFUSED;
free(pi->recv->fname);
pi->recv->fname = NULL;
return pi->recv->status == FSTAT_SKIPPED ? 2 : 1;
}
else if( pi->recv->mod_time == localtogmt(st.st_mtime)
&& pi->recv->bytes_total >= st.st_size )
{
pi->recv->bytes_skipped = st.st_size;
pi->recv->bytes_received = pi->recv->bytes_skipped;
log("recv: receiving \"%s\" from offset %ld",
pi->recv->fname, (long)pi->recv->bytes_skipped);
openmode = "a";
}
}
}
/*
* Check, there is enough space in our inbound
*/
if (openmode == "a") needed_bytes_total = minfree + pi->recv->bytes_total - pi->recv->bytes_skipped;
else needed_bytes_total = minfree + pi->recv->bytes_total;
if( minfree > 0 && getfreespace(state.inbound) < needed_bytes_total )
{
log("recv: not enough free space in inbound -> refuse");
pi->recv->status = FSTAT_REFUSED;
return(1);
}
DEB((D_PROT, "p_rx_fopen: call fopen(\"%s\", \"%s\")",
pi->recv->fname, openmode));
if( (pi->recv->fp = file_open(pi->recv->fname, openmode)) == NULL )
{
logerr("recv: cannot open \"%s\" -> refuse", pi->recv->fname);
pi->recv->status = FSTAT_REFUSED;
free(pi->recv->fname);
pi->recv->fname = NULL;
return 1;
}
if( pi->buffer && pi->buflen > 0 )
{
#ifdef SETVBUF_REVERSED
setvbuf(pi->recv->fp, _IOFBF, pi->buffer, pi->buflen);
#else
setvbuf(pi->recv->fp, pi->buffer, _IOFBF, pi->buflen);
#endif
}
return 0;
}
/*****************************************************************************
* Close receiving file. If it was completly received - move it to the main
* inbound directory.
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* 0 - successfull call
* 1 - error occured, refuse this file (receive later)
*/
int p_rx_fclose(s_protinfo *pi)
{
long trans_time = 0;
long cps = 0;
int rc = 0;
struct stat st;
DEB((D_PROT, "p_rx_fclose: close file \"%s\"", pi->recv->fname));
if( pi->recv->fp )
{
rc = file_close(pi->recv->fp); pi->recv->fp = NULL;
if( rc )
{
/*
* This may be any sort of errors, including
* random data corruptions. Return error.
*/
logerr("recv: cannot close file \"%s\"", pi->recv->fname);
return 1;
}
}
if( pi->recv->status == FSTAT_SKIPPED )
{
/*
* Remove skipped file from temp. inbound
*/
if( unlink(pi->recv->fname) == -1 && errno != ENOENT )
logerr("recv: cannot remove skipped file \"%s\"", pi->recv->fname);
}
else
{
/*
* Set original file modification time
*/
if( pi->recv->mod_time )
{
struct utimbuf tvp;
tvp.actime = time(NULL);
tvp.modtime = gmttolocal(pi->recv->mod_time);
utime(pi->recv->fname, &tvp);
}
/*
* Set default file permission to 0600
*/
if( chmod(pi->recv->fname, (S_IRUSR | S_IWUSR)) == -1 )
logerr("recv: cannot change mode for file \"%s\"", pi->recv->fname);
}
/*
* Update incoming traffic structure
*/
prot_traffic_update(&pi->traffic_rcvd,
pi->recv->bytes_received - pi->recv->bytes_skipped,
trans_time, pi->recv->type);
/*
* If file was received successfuly
*/
if( pi->recv->status == FSTAT_SUCCESS )
{
/*
* Sanity check. Compare real file length with the
* expected length (reported by the remote side)
*/
if( stat(pi->recv->fname, &st) == -1 )
{
log("cannot stat received file \"%s\"", pi->recv->fname);
pi->recv->status = FSTAT_REFUSED;
}
else if( st.st_size != pi->recv->bytes_total )
{
log("received file has invalid size %ld (%ld expected)",
(long)st.st_size, (long)pi->recv->bytes_total);
pi->recv->status = FSTAT_REFUSED;
}
else if( state.reqstat == REQS_ALLOW
&& (pi->recv->type & TYPE_REQUEST) )
{
/* Run our freq processor */
req_proc(pi->recv->fname, &pi->filelist);
unlink(pi->recv->fname);
}
else if( p_move2inbound(pi) )
{
log("recv: can't move file into main inbound -> refuse");
pi->recv->status = FSTAT_REFUSED;
}
}
/*
* Calculate receive time, CPS, etc
*/
trans_time = (long)time(NULL) - (long)pi->recv->start_time;
if( trans_time > 0 )
cps = (long)((pi->recv->bytes_received - pi->recv->bytes_skipped) / trans_time);
log("rcvd: \"%s\" %ld [%s]: %ld seconds, %ld CPS",
pi->recv->local_name,
(long)(pi->recv->bytes_received - pi->recv->bytes_skipped),
(pi->recv->status == FSTAT_SKIPPED) ? "skipped" :
(pi->recv->status == FSTAT_REFUSED) ? "refused" :
(pi->recv->status == FSTAT_SUCCESS) ? "ok" : "error",
(long)(trans_time > 0) ? trans_time : 0,
(long)(cps > 0) ? cps : 0);
return pi->recv->status == FSTAT_SUCCESS ? 0 : 1;
}
/*****************************************************************************
* Check current receiving/sending speeds (CPS), check time limits.
*
* Arguments:
* pi pointer to the protocol information structure
* bidir if hydra, check only one CPS value
*
* Return value:
* PRC_NOERROR - all ok
* PRC_STOPTIME - abort session due to time limits
* PRC_CPSTOOLOW - abort session due low speed
*/
int p_info(s_protinfo *pi, int bidir)
{
int rc = 0, rx_cps_low = 0, tx_cps_low = 0;
time_t tx_now, rx_now, now;
now = time(NULL);
if( pi->send && pi->send->status == FSTAT_PROCESS )
{
tx_now = (long)time(NULL) - (long)pi->send->start_time;
pi->tx_cps = (pi->send->bytes_sent - pi->send->bytes_skipped) / (tx_now ? tx_now : 1);
/* Is it FREQ answer? */
if( pi->freq_timelimit > 0 && (pi->send->type & TYPE_REQANSW) )
{
/*
* Check FREQ time limit not reached?
*/
if( pi->send_freqtime + tx_now > pi->freq_timelimit )
{
log("reached FREQ time limit %d second(s)", pi->freq_timelimit);
rc = PRC_STOPTIME;
}
}
/* Check is transmiting CPS to low */
if( pi->min_tx_cps )
{
if( pi->tx_low_cps_time )
{
if( pi->tx_cps < pi->min_tx_cps )
{
if( now - pi->tx_low_cps_time >= pi->min_cps_time )
{
tx_cps_low = 1;
}
}
else
{
pi->tx_low_cps_time = 0;
}
}
else if( pi->tx_cps < pi->min_tx_cps )
{
pi->tx_low_cps_time = now;
}
}
} /* end of if( txi ) */
if( pi->recv && pi->recv->status == FSTAT_PROCESS )
{
rx_now = (long)time(NULL) - (long)pi->recv->start_time;
pi->rx_cps = (pi->recv->bytes_received - pi->recv->bytes_skipped) / (rx_now ? rx_now : 1);
/* Check is receiving CPS to low */
if( pi->min_rx_cps )
{
if( pi->rx_low_cps_time )
{
if( pi->rx_cps < pi->min_rx_cps )
{
if( now - pi->rx_low_cps_time >= pi->min_cps_time )
{
rx_cps_low = 1;
}
}
else
{
pi->rx_low_cps_time = 0;
}
}
else if( pi->rx_cps < pi->min_rx_cps )
{
pi->rx_low_cps_time = now;
}
}
} /* end of if( rxi ) */
/* check maybe now is a good time to stop it ? =) */
if( pi->stop_time && now >= pi->stop_time )
{
log("reached stop time");
rc = PRC_STOPTIME;
}
if ( tx_cps_low )
{
if ( !bidir || rx_cps_low ) {
log("transmitting speed below %d cps", pi->min_tx_cps);
rc = PRC_CPSTOOLOW;
}
}
if ( rx_cps_low )
{
if ( !bidir || tx_cps_low ) {
log("receiving speed below %d cps", pi->min_rx_cps);
rc = PRC_CPSTOOLOW;
}
}
return(rc);
}
/*****************************************************************************
* Log short session statistic. Output overall Send/Received bytes, avreage
* CPS, on-line time
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* None
*/
void p_log_txrxstat(const s_protinfo *pi)
{
char abuf[BF_MAXADDRSTR+1];
s_faddr tmpaddr = state.node.addr;
int total_time = time_elapsed(pi->start_time);
int files_sent = prot_traffic_total_num(&pi->traffic_sent);
int files_rcvd = prot_traffic_total_num(&pi->traffic_rcvd);
size_t bytes_sent = prot_traffic_total_size(&pi->traffic_sent);
size_t bytes_rcvd = prot_traffic_total_size(&pi->traffic_rcvd);
size_t total_traff = bytes_sent + bytes_rcvd;
size_t total_cps = (total_traff / (total_time ? total_time : 1));
tmpaddr.domain[0] = '\0'; /* remove domain from address */
log("%s, S/R %ld/%ld, %ld/%ld bytes in %ld seconds, %ld CPS",
ftn_addrstr(abuf, tmpaddr),
files_sent, files_rcvd,
(long)bytes_sent, (long)bytes_rcvd,
(long)((total_time > 0) ? total_time : 0),
(long)((total_cps > 0) ? total_cps : 0));
}
/*****************************************************************************
* Do session cleanup. Remove temporary files, remove useless file requests
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* None
*/
void p_session_cleanup(s_protinfo *pi, bool success)
{
int i;
s_filelist *ptrl;
if( success )
{
/*
* Remove sent file requests after successfull sessions
*/
for( i = 0; i < pi->n_sentfiles; i++ )
if( (pi->sentfiles[i].type & TYPE_REQUEST)
&& (pi->sentfiles[i].status == FSTAT_SUCCESS) )
{
if( unlink(pi->sentfiles[i].fname) == -1 && errno != ENOENT )
{
logerr("can't unlink '.req' file \"%s\"",
pi->sentfiles[i].fname);
}
}
}
/*
* Delete files with action ACTION_FORCEUNLINK
*/
for( ptrl = pi->filelist; ptrl; ptrl = ptrl->next )
{
/* It must be allready deleted if it is sent */
if( ptrl->action == ACTION_FORCEUNLINK && ptrl->status != STATUS_SENT )
{
if( unlink(ptrl->fname) && errno != ENOENT )
logerr("cannot unlink temporary file \"%s\"", ptrl->fname);
}
}
}
/*****************************************************************************
* Do all necessary convertions and checks with file names before sending it
*
* Arguments:
* origname pointer to the original file name
* type file type (netmail, arcmail, etc.)
*
* Return value:
* Pointer to the new file name (must be freed)
*/
char *p_convfilename(const char *origname, int type)
{
const char *p;
char *dest = NULL;
long crc = 0L;
s_faddr addr;
if( (type & TYPE_NETMAIL) )
{
/*
* Generate random file name for netmail packets
*/
dest = (char*)xmalloc(32);
sprintf(dest, "%08lx.pkt", (long)rand());
}
else if( (type & TYPE_ASONAME) == TYPE_ASONAME
&& (type & TYPE_ARCMAIL) == TYPE_ARCMAIL )
{
/*
* Generate new file name for arcmail from AmigaDos style
* outbound, such files looks like "2.5020.1682.9.fr1".
*/
if( (p = strrchr(origname, '.')) )
crc = getcrc32ccitt(origname, p - origname - 1);
else
crc = getcrc32ccitt(origname, strlen(origname));
dest = (char*)xmalloc(32);
sprintf(dest, "%08lx%s", (long)crc, p?p:".fr0");
}
else if( (type & TYPE_ASONAME) == TYPE_ASONAME
&& (type & TYPE_REQUEST) == TYPE_REQUEST )
{
/*
* Generate new file name for file request from AmigaDos
* style outbound, such files looks like "2.5020.1682.0.req".
*/
dest = (char*)xmalloc(32);
if( out_parse_name_aso(&addr, origname) == 0 )
sprintf(dest, "%04x%04x.req", addr.net, addr.node);
else
sprintf(dest, "%08lx.req", (long)rand());
}
else
{
/*
* All other files send with their real name, but:
* 1) remove "nonprintable" characters
* 2) convert files to 8+3 format if remote has no long
* file names support (indicated by $state.sopts.fnc)
*/
if( state.sopts.fnc )
{
dest = (char*)xmalloc(13);
file_get_dos_name(dest, origname);
}
else
{
dest = (char*)xstrcpy(origname);
if( string_isupper(dest) )
string_tolower(dest);
}
}
return dest;
}
/*****************************************************************************
* Get temporary file name with full path.
*
* Arguments:
* inbound path to temporary inbound directory
* filename file name
* addr remote address \
* sz file size > Used to generate unique file names
* tm file time /
*
* Return value:
* Pointer to the temporary file name (must be freed)
*/
char *p_gettmpname(const char *inbound, char *filename, s_faddr addr,
size_t sz, time_t tm)
{
char *dest;
char abuf[BF_MAXADDRSTR+1];
char bbuf[BF_MAXADDRSTR+30];
char cbuf[16];
sprintf(bbuf, "%s %lx %lx", ftn_addrstr(abuf, addr), (long)sz, (long)tm);
sprintf(cbuf, "%lx", (long)getcrc32ccitt(bbuf, strlen(bbuf)));
dest = xstrcpy(inbound);
dest = xstrcat(dest, filename);
dest = xstrcat(dest, ".");
dest = xstrcat(dest, cbuf);
return dest;
}
#define MAX_TRIES 1024
/*****************************************************************************
* Get new file name for received file. Use it when we allready have
* file with such name in inbound directory.
*
* Rename file with the next algorithm:
*
* - For file request. Overwrite existing file
*
* - For netmail or ticfile. Generate new unique
* file name. (1234abcde.pkt -> ab04d51f.pkt)
*
* - For arcmail. Rotate extension chars to make
* unique name. (057d63a6.su0 -> 057d63a6.su1)
*
* - For other files. Try to add a numeric value
* as additional extension ("net5020.ndl.1")
*
* Arguments:
* dirname pointer to the directory (inbound)
* fname pointer to the file name
* type file type (netmail, arcmail, etc.)
*
* Return value:
* Pointer to the new file name with path (must be freed), and
* NULL to indicate error
*/
char *prot_unique_name(char *dirname, char *fname, int type)
{
int try = 0;
int offs = 0;
char *result = NULL;
char *p;
if( (type & TYPE_REQUEST) )
{
result = (char *)xstrcpy(dirname);
result = (char *)xstrcat(result, fname);
}
else if( (type & TYPE_NETMAIL) || (type & TYPE_TICFILE) )
{
char *ext = (type & TYPE_NETMAIL) ? "pkt" : "tic";
offs = strlen(dirname);
result = (char *)xmalloc(offs + 36);
strncpy(result, dirname, offs + 35);
result[offs + 35] = '\0';
do
{
++try;
sprintf(result+offs, "%08lx.%s", (long)rand(), ext);
}
while( !access(result, F_OK) && try < MAX_TRIES );
}
else if( (type & TYPE_ARCMAIL) )
{
result = (char *)xstrcpy(dirname);
result = (char *)xstrcat(result, fname);
p = result + strlen(result) - 1;
do
{
++try;
if( (*p >= '0') && (*p <= '8') )
++*p;
else if( (*p >= 'A') && (*p <= 'Y') )
++*p;
else if( (*p >= 'a') && (*p <= 'y') )
++*p;
else if( (*p == '9') )
*p = 'a';
else if( (*p == 'z') )
*p = 'A';
else if( --p < result || *p == '.' || *p == '/' )
{
free(result);
result = NULL;
break;
}
}
while( !access(result, F_OK) && try < MAX_TRIES );
}
else
{
result = (char *)xstrcpy(dirname);
result = (char *)xstrcat(result, fname);
offs = strlen(result);
result = (char *)xrealloc(result, offs + 16);
if( result[offs-1] != '.' )
{
result[offs++] = '.';
result[offs ] = '\0';
}
do
{
++try;
sprintf(result+offs, "%d", try);
}
while( !access(result, F_OK) && try < MAX_TRIES );
}
if( try >= MAX_TRIES )
{
if( result )
free(result);
return NULL;
}
return result;
}
/*****************************************************************************
* Release memory used by file information structure
*
* Arguments:
* fi pointer to the file information structure
*
* Return value:
* None
*/
void deinit_finfo(s_finfo *fi)
{
if( fi->fp )
fclose(fi->fp);
if( fi->local_name )
free(fi->local_name);
if( fi->net_name )
free(fi->net_name);
if( fi->fname )
free(fi->fname);
memset(fi, '\0', sizeof(s_finfo));
}
/*****************************************************************************
* Init protocol information structure. Set all necessary options, such as
* minimal CPSes, start/stop time, etc.
*
* Arguments:
* pi pointer to the protocol information structure
* caller are we caller?
*
* Return value:
* None
*/
void init_protinfo(s_protinfo *pi, bool caller)
{
long tmp;
long sesslimit;
memset(pi, '\0', sizeof(s_protinfo));
pi->start_time = time(NULL);
if( (pi->buflen = conf_number(cf_recv_buffer_size)) > 0 )
pi->buflen = (pi->buflen / 2048) * 2048;
else
pi->buflen = 32768;
if( pi->buflen > 0 )
pi->buffer = (char*)xmalloc(pi->buflen);
if( (tmp = conf_number(cf_min_cps_time)) > 0 )
pi->min_cps_time = tmp;
else
pi->min_cps_time = 60;
/*
* Set abort time if session limit was specified
*/
if( caller )
sesslimit = conf_number(cf_session_limit_out);
else
sesslimit = conf_number(cf_session_limit_in);
if( sesslimit > 0 )
pi->stop_time = time(0) + sesslimit;
/*
* Set minimal transmit speed (CPS)
*/
if( (tmp = conf_number(cf_min_cps_send)) > 0 )
pi->min_tx_cps = tmp;
else
{
if( state.handshake->protocol == PROT_ZMODEM
|| state.handshake->protocol == PROT_ZEDZAP
|| state.handshake->protocol == PROT_DIRZAP )
tmp = conf_connlist(cf_zmodem_mincps_send, state.connspeed);
else if( state.handshake->protocol == PROT_HYDRA )
tmp = conf_connlist(cf_hydra_mincps_send, state.connspeed);
else
tmp = 0;
if( tmp > 0 )
pi->min_tx_cps = tmp;
}
/*
* Set minimal receive speed (CPS)
*/
if( (tmp = conf_number(cf_min_cps_recv)) > 0 )
pi->min_rx_cps = tmp;
else
{
if( state.handshake->protocol == PROT_ZMODEM
|| state.handshake->protocol == PROT_ZEDZAP
|| state.handshake->protocol == PROT_DIRZAP )
tmp = conf_connlist(cf_zmodem_mincps_recv, state.connspeed);
else if( state.handshake->protocol == PROT_HYDRA )
tmp = conf_connlist(cf_hydra_mincps_recv, state.connspeed);
else
tmp = 0;
if( tmp > 0 )
pi->min_rx_cps = tmp;
}
/*
* Report about selected limits
*/
if( sesslimit > 0 && (pi->min_tx_cps > 0 || pi->min_rx_cps > 0) )
log("use restrictions: %d/%d cps, %d seconds", pi->min_tx_cps, pi->min_rx_cps, sesslimit);
else if( pi->min_tx_cps > 0 || pi->min_rx_cps > 0 )
log("use restrictions: %d/%d cps", pi->min_tx_cps, pi->min_rx_cps);
else if( sesslimit > 0 )
log("use restrictions: %d seconds", sesslimit);
DEB((D_PROT, "protinfo_init: min_tx_cps = %ld, min_rx_cps = %ld",
(long)pi->min_tx_cps, (long)pi->min_rx_cps));
DEB((D_PROT, "protinfo_init: buffersize = %ld, stoptime = %ld",
(long)pi->min_tx_cps, (long)pi->min_rx_cps));
}
/*****************************************************************************
* Release memory used by protocol information structure
*
* Arguments:
* pi pointer to the protocol information structure
*
* Return value:
* None
*/
void deinit_protinfo(s_protinfo *pi)
{
int i;
for( i = 0; i < pi->n_sentfiles; i++ )
deinit_finfo(&pi->sentfiles[i]);
for( i = 0; i < pi->n_rcvdfiles; i++ )
deinit_finfo(&pi->rcvdfiles[i]);
if( pi->buffer )
free(pi->buffer);
if( pi->sentfiles )
free(pi->sentfiles);
if( pi->rcvdfiles )
free(pi->rcvdfiles);
memset(pi, '\0', sizeof(s_protinfo));
}