www.pudn.com > NETINFO.rar > ftp.cpp
#include "ftp.h"
BOOL AFXAPI CompareElements(const FTP_DATA_MATCH* pElement1, const FTP_DATA_MATCH* pElement2)
{
return memcmp(pElement1, pElement2, sizeof(FTP_DATA_MATCH)) == 0;
}
BOOL DeleteMatch(CFtpDataMatchList* pMatchList, FTP_DATA_MATCH& match)
{
POSITION pos = pMatchList->Find(match);
if( pos != NULL)
{
pMatchList->RemoveAt(pos);
return TRUE;
}
else
return FALSE;
}
BOOL AddMatch(CFtpDataMatchList* pMatchList, FTP_DATA_MATCH& match)
{
DeleteMatch(pMatchList, match);
if( pMatchList->AddTail(match) )
return TRUE;
else
return FALSE;
}
BOOL FindMatch(const CFtpDataMatchList* pMatchList, const IP* ip, const TCP*tcp, CFtpConnection**ppFtp)
{
if( !tcp->syn || !tcp->ack )
return FALSE;
POSITION pos = pMatchList->GetHeadPosition();
while( pos )
{
FTP_DATA_MATCH match = pMatchList->GetNext(pos);
if( (match.match.src.low.ip.b1 <= ip->dst.b1 && ip->dst.b1 <= match.match.src.high.ip.b1 )
&& (match.match.src.low.ip.b2 <= ip->dst.b2 && ip->dst.b2 <= match.match.src.high.ip.b2 )
&& (match.match.src.low.ip.b3 <= ip->dst.b3 && ip->dst.b3 <= match.match.src.high.ip.b3 )
&& (match.match.src.low.ip.b4 <= ip->dst.b4 && ip->dst.b4 <= match.match.src.high.ip.b4 )
&& (match.match.src.low.port <= tcp->dstPort && tcp->dstPort <= match.match.src.high.port )
&& (match.match.dst.low.ip.b1 <= ip->src.b1 && ip->src.b1 <= match.match.dst.high.ip.b1 )
&& (match.match.dst.low.ip.b2 <= ip->src.b2 && ip->src.b2 <= match.match.dst.high.ip.b2 )
&& (match.match.dst.low.ip.b3 <= ip->src.b3 && ip->src.b3 <= match.match.dst.high.ip.b3 )
&& (match.match.dst.low.ip.b4 <= ip->src.b4 && ip->src.b4 <= match.match.dst.high.ip.b4 )
&& (match.match.dst.low.port <= tcp->srcPort && tcp->srcPort <= match.match.dst.high.port ) )
{
*ppFtp = match.ftp;
return TRUE;
}
}
return FALSE;
}
//get temporary file name that doesn't exist now
void FtpGetTempFileName(const char filename[MAX_PATH], DWORD startPos, char tempfilename[MAX_PATH])
{
sprintf(tempfilename, "%s.tmp%d", filename, startPos);
while( ExistFileOrDirectory(tempfilename) )
{
sprintf(tempfilename, "%s.tmp%d_%d", filename, startPos, rand() );
}
}
CFtpDataConnection::CFtpDataConnection(CFtpConnection* pFtp, const ETHERNET *ether, const IP* ip,
NetInfo* pNi, const NETINFO_CALLBACKS *pFuncs, DWORD dwAttachData)
:CConnection(ether, ip, pNi, pFuncs, dwAttachData)
{
m_ftp = pFtp;
m_ftp->OnDataCreate(this);
}
CFtpDataConnection::~CFtpDataConnection()
{
m_ftp->OnDataDelete(this);
}
BOOL CFtpDataConnection::OnData(int sender, int receiver, const BYTE *pData, DWORD length)
{
m_ftp->OnDataReceived(this, m_addr+sender, m_addr+receiver, pData, length);
return TRUE;
}
CFtpConnection::CFtpConnection(CMapKeyToConnect *pHashTbl, CFtpDataMatchList* pMatchList,
const ETHERNET *ether, const IP* ip,
NetInfo* pNi, const NETINFO_CALLBACKS *pFuncs, DWORD dwAttachData)
:CLineConnection(ether, ip, pNi, pFuncs, dwAttachData)
{
strcpy(m_szWorkDir, "/");
memset(&m_match, 0, sizeof(m_match));
m_match.ftp = this;
m_bMatchAdded = FALSE;
m_cmd = FTP_COMMAND_NONE;
memset(m_szLastCmd, 0, sizeof(m_szLastCmd));
m_dwStartPos = 0;
m_ftpData = NULL;
memset(&m_ftpDataKey, 0, sizeof(m_ftpDataKey));
m_pHashTbl = pHashTbl;
m_pMatchList = pMatchList;
}
void CFtpConnection::ClearMatchContext()
{
if( m_bMatchAdded )
{
if( m_ftpData )
{
m_pHashTbl->RemoveKey(m_ftpDataKey);
delete m_ftpData;
m_ftpData = NULL;
}
DeleteMatch(m_pMatchList, m_match);
m_bMatchAdded = FALSE;
}
}
CFtpConnection::~CFtpConnection()
{
ClearMatchContext();
//notify user the end of the current file's transferring
if( m_pFuncs && m_pFuncs->OnFtpFileTransferEnd )
m_pFuncs->OnFtpFileTransferEnd(m_pNi, this);
}
void CFtpConnection::OnLine(int sender, int receiver, const char* line, int lineLen)
{
if( sender == m_connecter ) //ftp client sending command
{
strcpy(m_szLastCmd, line);
m_szLastCmd[lineLen-2] = 0; //remove ending CRLF
//notify user command of the client
if( m_pFuncs && m_pFuncs->OnFtpCommand )
m_pFuncs->OnFtpCommand(m_pNi, this, m_szLastCmd);
//set default command type
m_cmd = FTP_COMMAND_OTHERS;
if( memicmp(line, "CWD ", 4) == 0 )
m_cmd = FTP_COMMAND_CWD;
else if( memicmp(line, "CDUP", 4) == 0 )
m_cmd = FTP_COMMAND_CDUP;
else if( memicmp(line, "PWD", 3) == 0 )
m_cmd = FTP_COMMAND_PWD;
//get active data tcp match
else if( memicmp(line, "PORT ", 5) == 0 )
{
ClearMatchContext();
int b1,b2,b3,b4,p1,p2;
if( sscanf(line + 5, "%d,%d,%d,%d,%d,%d", &b1, &b2, &b3, &b4, &p1, &p2) == 6 )
{
m_match.match.src.low.ip = m_addr[m_listener].ip;
m_match.match.src.low.port = 0;
m_match.match.src.high.ip = m_addr[m_listener].ip;
m_match.match.src.high.port = 0xffff;
m_match.match.dst.low.ip.b1 = (BYTE)b1;
m_match.match.dst.low.ip.b2 = (BYTE)b2;
m_match.match.dst.low.ip.b3 = (BYTE)b3;
m_match.match.dst.low.ip.b4 = (BYTE)b4;
m_match.match.dst.low.port = (WORD)(p1*256+p2);
m_match.match.dst.high = m_match.match.dst.low;
//add match
if( AddMatch(m_pMatchList, m_match) )
m_bMatchAdded = TRUE;
}
}
else if( memicmp(line, "PASV", 4) == 0 )
m_cmd = FTP_COMMAND_PASV;
else if( memicmp(line, "REST ", 5) == 0 )
m_dwStartPos = (DWORD)atol(line + 5);
else if( memicmp(line, "RETR ", 5) == 0
|| memicmp(line, "STOR ", 5) == 0 )
{
if( memicmp(line, "RETR ", 5) == 0 )
m_cmd = FTP_COMMAND_RETR;
else if( memicmp(line, "STOR ", 5) == 0 )
m_cmd = FTP_COMMAND_STOR;
//prepare file name
char temp[MAX_PATH], fullUnixFileName[MAX_PATH];
strcpy(temp, line);
temp[lineLen - 2] = 0;
for(const char* pFileName= temp+5; isspace(*pFileName); pFileName++)NULL; //skip space
if( *pFileName == '/' )
strcpy(fullUnixFileName, pFileName);
else if( strcmp(m_szWorkDir, "/") == 0 )
sprintf(fullUnixFileName, "/%s", pFileName);
else
sprintf(fullUnixFileName, "%s/%s", m_szWorkDir, pFileName);
//notify user a file is ready to transfered
if( m_pFuncs && m_pFuncs->OnFtpFileTransferBegin )
m_pFuncs->OnFtpFileTransferBegin(m_pNi, this, m_dwStartPos,
m_cmd == FTP_COMMAND_STOR, fullUnixFileName);
}
}
else //ftp server sending reply
{
//check if it's a effective reply line
if( isdigit(line[0]) && isdigit(line[1]) && isdigit(line[2]) && line[3] == ' ' )
{
char szReply[MAX_FTP_LINE];
strcpy(szReply, line);
szReply[lineLen-2] = 0;
//notify user the reply of the server
if( m_pFuncs && m_pFuncs->OnFtpReply )
m_pFuncs->OnFtpReply(m_pNi, this, m_szLastCmd, szReply);
//get passive data connection match
if( m_cmd == FTP_COMMAND_PASV )
{
if( line[0] == '2' )
{
ClearMatchContext();
int b1,b2,b3,b4,p1,p2;
int cursor = 4;
while( line[cursor] != 0 )
{
if( sscanf(line + cursor, "%d,%d,%d,%d,%d,%d", &b1, &b2, &b3, &b4, &p1, &p2) == 6 )
{
m_match.match.src.low.ip = m_addr[m_connecter].ip;
m_match.match.src.low.port = 0;
m_match.match.src.high.ip = m_addr[m_connecter].ip;
m_match.match.src.high.port = 0xffff;
m_match.match.dst.low.ip.b1 = (BYTE)b1;
m_match.match.dst.low.ip.b2 = (BYTE)b2;
m_match.match.dst.low.ip.b3 = (BYTE)b3;
m_match.match.dst.low.ip.b4 = (BYTE)b4;
m_match.match.dst.low.port = (WORD)(p1*256+p2);
m_match.match.dst.high = m_match.match.dst.low;
if( AddMatch(m_pMatchList, m_match) )
m_bMatchAdded = TRUE;
break;
}
else
cursor++;
}
}
}
else if( m_cmd == FTP_COMMAND_RETR || m_cmd == FTP_COMMAND_STOR )
{
if( line[0] == '1' )
return;
else
{
//notify user the end of the current file's transferring
if( m_pFuncs && m_pFuncs->OnFtpFileTransferEnd )
m_pFuncs->OnFtpFileTransferEnd(m_pNi, this);
}
}
else if( m_cmd == FTP_COMMAND_CWD )
{
if( line[0] == '2' )
{
for(const char* pDir= m_szLastCmd+4; isspace(*pDir); pDir++)NULL; //skip space
char temp[MAX_PATH];
if( pDir[0] == '/' )
strcpy(m_szWorkDir, pDir);
else
{
sprintf(temp, "%s/%s", m_szWorkDir, pDir);
strcpy(m_szWorkDir, temp);
}
}
}
else if( m_cmd == FTP_COMMAND_CDUP )
{
if( line[0] == '2' )
{
//look for the last '/'
for(int i=strlen(m_szWorkDir)-1; i>=0 && m_szWorkDir[i]!='/'; i--)NULL;
if( i>=1 )
m_szWorkDir[i] = 0;
else
strcpy(m_szWorkDir, "/");
}
}
else if( m_cmd == FTP_COMMAND_PWD )
{
if( line[0] == '2' )
{
for(const char* pFirstQuote = line+4; *pFirstQuote!=0 && *pFirstQuote!='"'; pFirstQuote++)NULL;
if( *pFirstQuote != '"' ) return;
for(const char* pSecondQuote = pFirstQuote+1; *pSecondQuote!=0 && *pSecondQuote!='"'; pSecondQuote++)NULL;
if( *pSecondQuote != '"' ) return;
memcpy(m_szWorkDir, pFirstQuote+1, pSecondQuote - pFirstQuote - 1);
m_szWorkDir[pSecondQuote - pFirstQuote - 1] = 0;
}
}
}
}
}
void CFtpConnection::OnDataCreate(CFtpDataConnection* pFtpData)
{
m_ftpData = pFtpData;
memcpy(&m_ftpDataKey, pFtpData->m_addr, sizeof(TCP_ADDR)*2);
}
void CFtpConnection::OnDataDelete(CFtpDataConnection* pFtpData)
{
m_ftpData = NULL;
memset(&m_ftpDataKey, 0, sizeof(TCP_ADDR)*2);
//notify user the end of the current file's transferring
if( m_pFuncs && m_pFuncs->OnFtpFileTransferEnd )
m_pFuncs->OnFtpFileTransferEnd(m_pNi, this);
}
void CFtpConnection::OnDataReceived(CFtpDataConnection* pFtpData,
const TCP_ADDR* sender, const TCP_ADDR* receiver,
const BYTE* pData, DWORD length)
{
if( m_pFuncs && m_pFuncs->OnFtpFileData
&& ( m_cmd == FTP_COMMAND_RETR || m_cmd == FTP_COMMAND_STOR) )
m_pFuncs->OnFtpFileData(m_pNi, this, pData, length);
}
HTCPCONNECT GetFtpDataConnect(HFTPCONNECT hFtp)
{
return hFtp->m_ftpData;
}