// FTPClient.cpp: implementation of the CFTPClient class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SFTP.h"
#include "FTPClient.h"
#include "sftpdoc.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#define SAFEDELTE(x) if(x!=NULL){delete x;x = NULL;}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CFTPClient::CFTPClient(CSFTPDoc *pDoc)
{
m_pCtrlRxarch = NULL;
m_pCtrlTxarch = NULL;
m_pCtrlsokfile = NULL;
m_Ctrlsok = NULL;
m_pDoc = pDoc;
}
CFTPClient::~CFTPClient()
{
SAFEDELTE(m_pCtrlRxarch);
SAFEDELTE(m_pCtrlTxarch);
SAFEDELTE(m_pCtrlsokfile);
SAFEDELTE(m_Ctrlsok);
}
void CFTPClient::Receive()
{
}
BOOL CFTPClient::Logon(CString hostname, int hostport, CString username, CString password, CString acct, CString fwhost, CString fwusername, CString fwpassword, int fwport, int logontype)
{
// 创建显示登录信息的窗口,如果已经创建只要显示即可
if(m_dlgMsg.m_hWnd == NULL){
m_dlgMsg.Create(AfxGetMainWnd());
}
else
m_dlgMsg.ShowWindow(SW_SHOW);
int port,logonpoint=0;
const int LO=-2, ER=-1;
CString buf,temp;
const int NUMLOGIN=9; // currently supports 9 different login sequences
int logonseq[NUMLOGIN][100] = {
// 该数组用来存储防火墙设置,设置USER命令的行参数
{0,LO,3, 1,LO,6, 2,LO,ER}, // 无防火墙
{3,6,3, 4,6,ER, 5,ER,9, 0,LO,12, 1,LO,15, 2,LO,ER}, // 站点主机名
{3,6,3, 4,6,ER, 6,LO,9, 1,LO,12, 2,LO,ER}, // 登录后的USER命令
{7,3,3, 0,LO,6, 1,LO,9, 2,LO,ER}, // 打开代理
{3,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER},
{6,LO,3, 1,LO,6, 2,LO,ER},
{8,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER},
{9,ER,3, 1,LO,6, 2,LO,ER},
{10,LO,3, 11,LO,6, 2,LO,ER}
};
if(logontype<0 || logontype>=NUMLOGIN)
return FALSE; // 非法连接类型
// 如果不通过防火墙,直接连接,否则连接到服务器
if(!logontype) {
temp=hostname;
port=hostport;
}
else {
temp=fwhost;
port=fwport;
}
if(hostport!=21)
hostname.Format(hostname+":%d",hostport); // 如果端口不是21,在主机名后面加上端口号
// 建立到远端的连接,并准备好序列化对象
if(!ConnectRemote(temp,port))
return false;
if(!FTPcommand(""))
return FALSE;
// FTPCommand命令用来向服务器发送一条命令,这里发送空行,用来获得基本的
// 服务器信息,对于不同类型的请求发不同的命令序列,通过身份认证
while(1) {
switch(logonseq[logontype][logonpoint]) {
case 0:
temp="USER "+username;
break;
case 1:
temp="PASS "+password;
break;
case 2:
temp="ACCT "+acct;
break;
case 3:
temp="USER "+fwusername;
break;
case 4:
temp="PASS "+fwpassword;
break;
case 5:
temp="SITE "+hostname;
break;
case 6:
temp="USER "+username+"@"+hostname;
break;
case 7:
temp="OPEN "+hostname;
break;
case 8:
temp="USER "+fwusername+"@"+hostname;
break;
case 9:
temp="USER "+username+"@"+hostname+" "+fwusername;
break;
case 10:
temp="USER "+username+"@"+fwusername+"@"+hostname;
break;
case 11:
temp="PASS "+password+"@"+fwpassword;
break;
}
// 发出命令并且等待服务器响应
if(!WriteStr(temp)) // 向服务器发送一条命令
return FALSE;
if(!ReadStr()) // 从服务器接收数据
return FALSE;
// 只要下面的响应才是有效的
if(m_fc!=2 && m_fc!=3)
return FALSE;
logonpoint=logonseq[logontype][logonpoint+m_fc-1]; //从数组中获取下一条命令
switch(logonpoint) {
case ER: // ER说明发生错误
m_retmsg.LoadString(IDS_FTPMSG1);
return FALSE;
case LO: // LO说明已经成功登录服务器
return TRUE;
}
}
}
BOOL CFTPClient::ConnectRemote(CString serverhost, int serverport)
{
SAFEDELTE(m_pCtrlRxarch);
SAFEDELTE(m_pCtrlTxarch);
SAFEDELTE(m_pCtrlsokfile);
SAFEDELTE(m_Ctrlsok);
if(m_Ctrlsok == NULL)
m_Ctrlsok = new CFTPSocket;
if( !(m_Ctrlsok->Create()) )
return FALSE;
if(!(m_Ctrlsok->Connect(serverhost,serverport)))
return FALSE;
if(!(m_pCtrlsokfile=new CSocketFile(m_Ctrlsok)))
return FALSE;
if(!(m_pCtrlRxarch=new CArchive(m_pCtrlsokfile,CArchive::load)))
return FALSE;
if(!(m_pCtrlTxarch=new CArchive(m_pCtrlsokfile,CArchive::store)))
return FALSE;
return TRUE;
}
BOOL CFTPClient::WriteStr(CString outputstring)
{
TRY {
m_pCtrlTxarch->WriteString(outputstring+"\r\n");
m_pCtrlTxarch->Flush();
}
CATCH(CException,e) {
return FALSE;
}
END_CATCH
return TRUE;
}
int CFTPClient::ReadStr()
{
int retcode;
if(!ReadStr2())
return FALSE;
if(m_retmsg.GetLength() < 4 || m_retmsg.GetAt(3) != '-')
return TRUE;
retcode=atol(m_retmsg);
while(1) { //handle multi-line server responses
if(m_retmsg.GetLength() > 3 && (m_retmsg.GetAt(3) == ' ' && atol(m_retmsg) == retcode))
return TRUE;
if(!ReadStr2()) return FALSE;
}
}
BOOL CFTPClient::ReadStr2()
{
TRY {
if(!m_pCtrlRxarch->ReadString(m_retmsg)) {
return FALSE;
}
}
CATCH(CException,e) {
return FALSE;
}
END_CATCH
if(m_retmsg.GetLength() > 0)
m_fc = m_retmsg.GetAt(0)-48;
m_dlgMsg.TextOut(m_retmsg);
// get 1st digit of the return code (indicates primary result)
return TRUE;
}
BOOL CFTPClient::FTPcommand(CString command)
{
if(command != "" && !WriteStr(command))
return FALSE;
if((!ReadStr()) || (m_fc != 2))
return FALSE;
return TRUE;
}
BOOL CFTPClient::List()
{
CString lhost,temp,rhost;
UINT localsock,i;
CFile datafile;
// 创建阻塞的套接字,来建立用于接收服务器的二进制数据流的连接
CSocket sockSrvr;
// 这一步Socket用于接收数据
CAsyncSocket datachannel;
int num, sum;
const int BUFSIZE = 4096;
DWORD lpArgument=0;
m_buf.RemoveAll(); // 使用动态数组储存数据,便于操作
m_buf.SetSize(BUFSIZE);
// 该命令请求与服务器建立二进制连接
if(!FTPcommand("TYPE I"))
return FALSE; // 请求二进制模式
m_retmsg.LoadString(IDS_FTPMSG6);
// 通过已经建立的连接获得本地地址
if(!m_Ctrlsok->GetSockName(lhost,localsock))
return FALSE;;
while(1) {
// 把IP地址中的"."转换为","以满足协议要求
if((i=lhost.Find("."))==-1) break;
lhost.SetAt(i,',');
}
// 创建监听Socket并且开始监听(让MFC选择端口),等待服务器的反向连接
if((!sockSrvr.Create(0, SOCK_STREAM, NULL))
|| (!sockSrvr.Listen()))
return FALSE;
if(!sockSrvr.GetSockName(temp,localsock))
return FALSE; // 获取MFC选择的端口
// 将端口号转换为两个字节加到本地IP上
lhost.Format(lhost+",%d,%d", localsock / 256, localsock % 256);
// 向服务器发送PORT反定向命令,并且让服务器按该IP和端口建立反向连接
if(!FTPcommand("PORT "+lhost))
return FALSE; // 发送请求目录命令
if(!WriteStr("LIST") )
return FALSE;
if(!ReadStr())
return FALSE; // 获取RETR/STOR命令的响应
if(!sockSrvr.Accept(datachannel))
return FALSE; // 接收到服务器的反向连接,准备异步
//Socket来接收数据把Socket设为阻塞模式
if((!datachannel.AsyncSelect(0)) ||
(!datachannel.IOCtl(FIONBIO,&lpArgument))) {
m_retmsg.LoadString(IDS_FTPMSG6);
return FALSE;
}
sum = 0;
while(1) { // 从服务器读取数据,并放入动态数组中
TRY {
if(!(num = datachannel.Receive(m_buf.GetData() + sum, BUFSIZE, 0))
|| num == SOCKET_ERROR)
break; // 数据流结束或网络失败
TRACE("Received :%d\n", num);
Sleep(0);
sum += num;
m_buf.SetSize(sum + BUFSIZE);
}
CATCH (CException,e) {
m_retmsg.LoadString(IDS_FTPMSG5);
return FALSE;
}
END_CATCH
}
datachannel.Close();
ProcessList(); // 处理接收到的数据并且显示
if(!FTPcommand(""))
return FALSE; // 检查是否成功地完成了列表传输
return TRUE;
}
void CFTPClient::ProcessList()
{
m_pDoc->RemoveAll();
int ndx = 0;
while(GetLine(ndx)){
// m_dlgMsg.TextOut(m_strLine);
m_pDoc->AddLine(m_strLine);
}
}
BOOL CFTPClient::GetLine(int& ndx)
{
m_strLine.Em