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; 
}