-
Rob Swindell authored
So Clang-FreeBSD was warning (in compiles of scfg/scfg*.c by Deuce): result of comparison of constant 100000 with expression of type 'uint16_t' (aka 'unsigned short') is always true Why? Cause a uint16_t's max value is 65535 (less than 100000). Sure we could have just lowered the UIFC max number of config items to 65535, but that would have been too easy. And why are these compared-with values of type uint16_t to begin with? Because most ctrl/*.cnf lists (of configuration items) were limited to 65535 entries cause ... 16-bit DOS, historically. Now that *.cnf files aren't used, we could just increase these scfg_t.*_total type sizes from 16 to 32-bits, yeah? The result is this commit. I went to (signed) int so we could still keep -1 as the special illegal sub/dir num value (e.g. INVALID_SUB, which is sometimes used to indicate the email message base). Theoretically, 2 billion configuration items could be supported in these lists, but SCFG will limit you to 100000 anyway. So there's a whole lot of s/uint/int in this commit. I'd be very surprised if this doesn't result in some new GCC/Clang warnings, but at least the old "comparison of constant 100000" warnings are now gone!
Rob Swindell authoredSo Clang-FreeBSD was warning (in compiles of scfg/scfg*.c by Deuce): result of comparison of constant 100000 with expression of type 'uint16_t' (aka 'unsigned short') is always true Why? Cause a uint16_t's max value is 65535 (less than 100000). Sure we could have just lowered the UIFC max number of config items to 65535, but that would have been too easy. And why are these compared-with values of type uint16_t to begin with? Because most ctrl/*.cnf lists (of configuration items) were limited to 65535 entries cause ... 16-bit DOS, historically. Now that *.cnf files aren't used, we could just increase these scfg_t.*_total type sizes from 16 to 32-bits, yeah? The result is this commit. I went to (signed) int so we could still keep -1 as the special illegal sub/dir num value (e.g. INVALID_SUB, which is sometimes used to indicate the email message base). Theoretically, 2 billion configuration items could be supported in these lists, but SCFG will limit you to 100000 anyway. So there's a whole lot of s/uint/int in this commit. I'd be very surprised if this doesn't result in some new GCC/Clang warnings, but at least the old "comparison of constant 100000" warnings are now gone!
execnet.cpp 18.60 KiB
/* execnet.cpp */
/* Synchronet command shell/module TCP/IP Network functions */
/* $Id: execnet.cpp,v 1.34 2019/08/04 17:49:51 deuce Exp $ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 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:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/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 mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
#include "cmdshell.h"
#define TIMEOUT_SOCK_READLINE 30 /* seconds */
#define TIMEOUT_SOCK_LISTEN 30 /* seconds */
#define TIMEOUT_FTP_RESPONSE 300 /* seconds */
/* TODO: IPv6 */
int sbbs_t::exec_net(csi_t* csi)
{
char str[512],rsp[512],buf[1025],ch,*p,**pp,**pp1,**pp2;
ushort w;
int i;
BOOL rd;
int32_t *lp,*lp1,*lp2;
time_t start;
switch(*(csi->ip++)) { /* sub-op-code stored as next byte */
case CS_SOCKET_OPEN:
lp=getintvar(csi,*(int32_t *)csi->ip);
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(csi->sockets>=MAX_SOCKETS)
return(0);
if(lp!=NULL) {
SOCKET sock=open_socket(PF_INET, SOCK_STREAM, NULL);
if(sock!=INVALID_SOCKET) {
SOCKADDR_IN addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = htonl(startup->outgoing4.s_addr);
addr.sin_family = AF_INET;
if((i=bind(sock, (struct sockaddr *) &addr, sizeof (addr)))!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(sock);
return(0);
}
*lp=sock;
for(i=0;i<csi->sockets;i++)
if(!csi->socket[i])
break;
csi->socket[i]=*lp;
if(i==csi->sockets)
csi->sockets++;
csi->logic=LOGIC_TRUE;
}
}
return(0);
case CS_SOCKET_CLOSE:
lp=getintvar(csi,*(int32_t *)csi->ip);
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(lp && *lp) {
csi->logic=close_socket((SOCKET)*lp);
csi->socket_error=ERROR_VALUE;
for(i=0;i<csi->sockets;i++)
if(csi->socket[i]==(SOCKET)*lp)
csi->socket[i]=0;
*lp=0;
}
return(0);
case CS_SOCKET_CHECK:
lp=getintvar(csi,*(int32_t *)csi->ip);
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(lp==NULL || *lp==INVALID_SOCKET)
return(0);
if(socket_check(*lp,NULL,NULL,0)==TRUE)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
case CS_SOCKET_CONNECT:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* address */
csi->ip+=4;
w=*(ushort *)csi->ip; /* port */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !*lp || !pp || !*pp || !w)
return(0);
ulong ip_addr;
if((ip_addr=resolve_ip(*pp))==INADDR_NONE)
return(0);
SOCKADDR_IN addr;
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = ip_addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(w);
if((i=connect(*lp, (struct sockaddr *)&addr, sizeof(addr)))!=0) {
csi->socket_error=ERROR_VALUE;
return(0);
}
csi->logic=LOGIC_TRUE;
return(0);
case CS_SOCKET_ACCEPT:
lp1=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
csi->socket_error=0;
/* TODO */
return(0);
case CS_SOCKET_NREAD:
lp1=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
lp2=getintvar(csi,*(int32_t *)csi->ip); /* var */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp1 || !lp2)
return(0);
if(ioctlsocket(*lp1, FIONREAD, (ulong*)lp2)==0)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
case CS_SOCKET_PEEK:
case CS_SOCKET_READ:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* buffer */
csi->ip+=4;
w=*(ushort *)csi->ip; /* length */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(w<1 || w>sizeof(buf)-1)
w=sizeof(buf)-1;
int rcv_count;
if((rcv_count=recv(*lp,buf,w
,*(csi->ip-13)==CS_SOCKET_PEEK ? MSG_PEEK : 0))>0) {
csi->logic=LOGIC_TRUE;
buf[rcv_count]=0;
if(csi->etx) {
p=strchr(buf,csi->etx);
if(p) *p=0;
}
*pp=copystrvar(csi,*pp,buf);
} else
csi->socket_error=ERROR_VALUE;
return(0);
case CS_SOCKET_READLINE:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* buffer */
csi->ip+=4;
w=*(ushort *)csi->ip; /* length */
csi->ip+=2;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(w<1 || w>sizeof(buf)-1)
w=sizeof(buf)-1;
start=time(NULL);
for(i=0;i<w;) {
if(!online)
return(1);
if(!socket_check(*lp,&rd,NULL,1000))
return(0);
if(!rd) {
if(time(NULL)-start>TIMEOUT_SOCK_READLINE) {
lprintf(LOG_WARNING,"!socket_readline: timeout (%d) exceeded"
,TIMEOUT_SOCK_READLINE);
return(0);
}
continue;
}
if(recv(*lp, &ch, 1, 0)!=1) {
csi->socket_error=ERROR_VALUE;
return(0);
}
if(ch=='\n' && i>=1)
break;
buf[i++]=ch;
}
if(i>0 && buf[i-1]=='\r')
buf[i-1]=0;
else
buf[i]=0;
if(csi->etx) {
p=strchr(buf,csi->etx);
if(p) *p=0;
}
*pp=copystrvar(csi,*pp,buf);
csi->logic=LOGIC_TRUE;
return(0);
case CS_SOCKET_WRITE:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* buffer */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp || !(*pp))
return(0);
if(sendsocket(*lp,*pp,strlen(*pp))>0)
csi->logic=LOGIC_TRUE;
else
csi->socket_error=ERROR_VALUE;
return(0);
/* FTP Functions */
case CS_FTP_LOGIN:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp1=getstrvar(csi,*(int32_t *)csi->ip); /* username */
csi->ip+=4;
pp2=getstrvar(csi,*(int32_t *)csi->ip); /* password */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(!ftp_cmd(csi,*lp,NULL,rsp))
return(0);
if(atoi(rsp)!=220)
return(0);
sprintf(str,"USER %s",*pp1);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==331) { /* Password needed */
sprintf(str,"PASS %s",*pp2);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
}
if(atoi(rsp)==230) /* Login successful */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_LOGOUT:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp)
return(0);
if(!ftp_cmd(csi,*lp,"QUIT",rsp))
return(0);
if(atoi(rsp)==221) /* Logout successful */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_PWD:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp)
return(0);
if(!ftp_cmd(csi,*lp,"PWD",rsp))
return(0);
if(atoi(rsp)==257) /* pathname */
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_CWD:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* path */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
sprintf(str,"CWD %s",*pp);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==250)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_DIR:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* path */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
if(ftp_get(csi,*lp,*pp,NULL /* unused */, true /* DIR */)==true)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_DELETE:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp=getstrvar(csi,*(int32_t *)csi->ip); /* path */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp)
return(0);
sprintf(str,"DELE %s",*pp);
if(!ftp_cmd(csi,*lp,str,rsp))
return(0);
if(atoi(rsp)==250)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_GET:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp1=getstrvar(csi,*(int32_t *)csi->ip); /* src path */
csi->ip+=4;
pp2=getstrvar(csi,*(int32_t *)csi->ip); /* dest path */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(ftp_get(csi,*lp,*pp1,*pp2)==true)
csi->logic=LOGIC_TRUE;
return(0);
case CS_FTP_PUT:
lp=getintvar(csi,*(int32_t *)csi->ip); /* socket */
csi->ip+=4;
pp1=getstrvar(csi,*(int32_t *)csi->ip); /* src path */
csi->ip+=4;
pp2=getstrvar(csi,*(int32_t *)csi->ip); /* dest path */
csi->ip+=4;
csi->logic=LOGIC_FALSE;
csi->socket_error=0;
if(!lp || !pp1 || !pp2)
return(0);
if(ftp_put(csi,*lp,*pp1,*pp2)==true)
csi->logic=LOGIC_TRUE;
return(0);
default:
errormsg(WHERE,ERR_CHK,"net sub-instruction",*(csi->ip-1));
return(0);
}
}
/* FTP Command/Response function */
bool sbbs_t::ftp_cmd(csi_t* csi, SOCKET sock, const char* cmdsrc, char* rsp)
{
char cmd[512];
int len;
BOOL data_avail;
time_t start;
if(cmdsrc!=NULL) {
sprintf(cmd,"%s\r\n",cmdsrc);
if(csi->ftp_mode&CS_FTP_ECHO_CMD)
bputs(cmd);
len=strlen(cmd);
if(sendsocket(sock,cmd,len)!=len) {
csi->socket_error=ERROR_VALUE;
return(FALSE);
}
}
if(rsp!=NULL) {
int rd;
char ch;
while(1) {
rd=0;
start=time(NULL);
while(rd<500) {
if(!online)
return(FALSE);
if(!socket_check(sock,&data_avail,NULL,1000))
return(FALSE);
if(!data_avail) {
if(time(NULL)-start>TIMEOUT_FTP_RESPONSE) {
lprintf(LOG_WARNING,"!ftp_cmd: TIMEOUT_FTP_RESPONSE (%d) exceeded"
,TIMEOUT_FTP_RESPONSE);
return(FALSE);
}
continue;
}
if(recv(sock, &ch, 1, 0)!=1) {
csi->socket_error=ERROR_VALUE;
return(FALSE);
}
if(ch=='\n' && rd>=1)
break;
rsp[rd++]=ch;
}
rsp[rd-1]=0;
if(csi->ftp_mode&CS_FTP_ECHO_RSP)
bprintf("%s\r\n",rsp);
if(rsp[0]!=' ' && rsp[3]!='-')
break;
}
}
return(TRUE);
}
SOCKET sbbs_t::ftp_data_sock(csi_t* csi, SOCKET ctrl_sock, SOCKADDR_IN* addr)
{
char cmd[512];
char rsp[512];
char* p;
socklen_t addr_len;
SOCKET data_sock;
int ip_b[4];
int port_b[2];
union {
DWORD dw;
BYTE b[sizeof(DWORD)];
} ip_addr;
union {
WORD w;
BYTE b[sizeof(WORD)];
} port;
if(csi->ftp_mode&CS_FTP_ASCII)
strcpy(cmd,"TYPE A");
else /* BINARY */
strcpy(cmd,"TYPE I");
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=200) {
return(INVALID_SOCKET);
}
if((data_sock=open_socket(PF_INET, SOCK_STREAM, "ftp"))==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
return(INVALID_SOCKET);
}
memset(addr,0,sizeof(SOCKADDR_IN));
addr->sin_addr.s_addr = htonl(startup->outgoing4.s_addr);
addr->sin_family = AF_INET;
if(bind(data_sock, (struct sockaddr *)addr,xp_sockaddr_len(addr))!= 0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
if(csi->ftp_mode&CS_FTP_PASV) {
if(!ftp_cmd(csi,ctrl_sock,"PASV",rsp)
|| atoi(rsp)!=227 /* PASV response */) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
p=strchr(rsp,'(');
if(p==NULL) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
p++;
if(sscanf(p,"%u,%u,%u,%u,%u,%u"
,&ip_b[0],&ip_b[1],&ip_b[2],&ip_b[3]
,&port_b[0],&port_b[1])!=6) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(INVALID_SOCKET);
}
ip_addr.b[0]=ip_b[0]; ip_addr.b[1]=ip_b[1];
ip_addr.b[2]=ip_b[2]; ip_addr.b[3]=ip_b[3];
port.b[0]=port_b[0]; port.b[1]=port_b[1];
addr->sin_addr.s_addr=ip_addr.dw;
addr->sin_port=port.w;
} else { /* Normal (Active) FTP */
addr_len=sizeof(SOCKADDR_IN);
if(getsockname(data_sock, (struct sockaddr *)addr,&addr_len)!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
SOCKADDR_IN ctrl_addr;
addr_len=sizeof(ctrl_addr);
if(getsockname(ctrl_sock, (struct sockaddr *)&ctrl_addr,&addr_len)!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
if(listen(data_sock, 1)!= 0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(INVALID_SOCKET);
}
ip_addr.dw=ntohl(ctrl_addr.sin_addr.s_addr);
port.w=ntohs(addr->sin_port);
sprintf(cmd,"PORT %u,%u,%u,%u,%u,%u"
,ip_addr.b[3]
,ip_addr.b[2]
,ip_addr.b[1]
,ip_addr.b[0]
,port.b[1]
,port.b[0]
);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=200 /* PORT response */) {
close_socket(data_sock);
return(INVALID_SOCKET);
}
}
return(data_sock);
}
bool sbbs_t::ftp_get(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest, bool dir)
{
char cmd[512];
char rsp[512];
char buf[4097];
int rd;
BOOL data_avail;
ulong total=0;
SOCKET data_sock;
union xp_sockaddr addr;
socklen_t addr_len;
FILE* fp=NULL;
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET)
return(false);
if(csi->ftp_mode&CS_FTP_PASV) {
#if 0 // Debug
bprintf("Connecting to %s:%hd\r\n"
,inet_ntoa(addr.in.sin_addr)
,ntohs(addr.in.sin_port));
#endif
/* TODO: IPv6 */
if(connect(data_sock,&addr.addr,sizeof(SOCKADDR_IN))!=0) {
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(false);
}
}
if(dir)
sprintf(cmd,"LIST %s",src);
else
sprintf(cmd,"RETR %s",src);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=150 /* Open data connection */) {
close_socket(data_sock);
return(false);
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
SOCKET accept_sock;
addr_len=sizeof(addr);
if((accept_sock=accept_socket(data_sock,&addr,&addr_len))
==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
return(false);
}
close_socket(data_sock);
data_sock=accept_sock;
}
if(!dir)
if((fp=fopen(dest,"wb"))==NULL) {
close_socket(data_sock);
return(false);
}
while(online) {
if(!socket_check(ctrl_sock,NULL,NULL,0))
break; /* Control connection lost */
if(!socket_check(data_sock,&data_avail,NULL,100))
break; /* Data connection lost */
if(!data_avail)
continue;
if((rd=recv(data_sock, buf, sizeof(buf)-1, 0))<1)
break;
if(dir) {
buf[rd]=0;
bputs(buf);
} else
fwrite(buf,1,rd,fp);
total+=rd;
if(!dir && csi->ftp_mode&CS_FTP_HASH)
outchar('#');
}
if(!dir && csi->ftp_mode&CS_FTP_HASH) {
CRLF;
}
if(fp!=NULL)
fclose(fp);
close_socket(data_sock);
if(!ftp_cmd(csi,ctrl_sock,NULL,rsp)
|| atoi(rsp)!=226 /* Download complete */)
return(false);
bprintf("ftp: %lu bytes received.\r\n", total);
return(true);
}
bool sbbs_t::ftp_put(csi_t* csi, SOCKET ctrl_sock, char* src, char* dest)
{
char cmd[512];
char rsp[512];
char path[MAX_PATH+1];
char buf[4097];
int rd;
ulong total=0;
SOCKET data_sock;
union xp_sockaddr addr;
socklen_t addr_len;
FILE* fp=NULL;
bool error=false;
SAFECOPY(path,src);
if(!fexistcase(path))
return(false);
if((data_sock=ftp_data_sock(csi, ctrl_sock, &addr.in))==INVALID_SOCKET) {
bprintf("ftp: failure, line %d",__LINE__);
return(false);
}
if(csi->ftp_mode&CS_FTP_PASV) {
#if 0 // Debug
bprintf("Connecting to %s:%hd\r\n"
,inet_ntoa(addr.in.sin_addr)
,ntohs(addr.in.sin_port));
#endif
if(connect(data_sock,&addr.addr,sizeof(addr.in))!=0) {
bprintf("ftp: failure, line %d",__LINE__);
csi->socket_error=ERROR_VALUE;
close_socket(data_sock);
return(false);
}
}
if((fp=fopen(path,"rb"))==NULL) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
return(false);
}
sprintf(cmd,"STOR %s",dest);
if(!ftp_cmd(csi,ctrl_sock,cmd,rsp)
|| atoi(rsp)!=150 /* Open data connection */) {
bprintf("ftp: failure, line %d",__LINE__);
close_socket(data_sock);
fclose(fp);
return(false);
}
if(!(csi->ftp_mode&CS_FTP_PASV)) { /* Normal (Active) FTP */
if(!socket_readable(data_sock, TIMEOUT_SOCK_LISTEN * 1000)) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
fclose(fp);
return(false);
}
SOCKET accept_sock;
addr_len=sizeof(addr);
if((accept_sock=accept_socket(data_sock,&addr,&addr_len))
==INVALID_SOCKET) {
csi->socket_error=ERROR_VALUE;
closesocket(data_sock);
fclose(fp);
return(false);
}
close_socket(data_sock);
data_sock=accept_sock;
}
while(online && !feof(fp)) {
rd=fread(buf,sizeof(char),sizeof(buf),fp);
if(rd<1) /* EOF or READ error */
break;
if(!socket_check(ctrl_sock,NULL,NULL,0))
break; /* Control connection lost */
if(sendsocket(data_sock,buf,rd)<1) {
error=true;
break;
}
total+=rd;
if(csi->ftp_mode&CS_FTP_HASH)
outchar('#');
}
if(csi->ftp_mode&CS_FTP_HASH) {
CRLF;
}
fclose(fp);
close_socket(data_sock);
if(!ftp_cmd(csi,ctrl_sock,NULL,rsp)
|| atoi(rsp)!=226 /* Upload complete */)
return(false);
if(!error)
bprintf("ftp: %lu bytes sent.\r\n", total);
return(!error);
}