ftpcontrolsocket.cpp
来自「一个支持FTP,SFTP的客户端程序」· C++ 代码 · 共 2,319 行 · 第 1/5 页
CPP
2,319 行
DoClose();
return;
}
}
m_Operation.nOpState = CONNECT_SSL_WAITDONE;
return;
}
else if (m_Operation.nOpState == CONNECT_SSL_WAITDONE)
{
m_Operation.nOpState = CONNECT_INIT;
}
else if (m_Operation.nOpState == CONNECT_SSL_PBSZ)
{
if (!Send("PROT P"))
return;
m_Operation.nOpState = CONNECT_SSL_PROT;
return;
}
else if (m_Operation.nOpState == CONNECT_SSL_PROT)
{
int code = GetReplyCode();
if (code == 2 || code == 3)
m_bProtP = true;
ShowStatus(IDS_STATUSMSG_CONNECTED, 0);
m_pOwner->SetConnected(TRUE);
ResetOperation(FZ_REPLY_OK);
return;
}
else if (m_Operation.nOpState==CONNECT_GSS_FAILED ||
m_Operation.nOpState == CONNECT_GSS_NEEDPASS ||
m_Operation.nOpState == CONNECT_GSS_NEEDUSER)
{
if (!m_RecvBuffer.empty() && m_RecvBuffer.front() != "")
{
//Incoming reply from server during async is not allowed
DoClose();
return;
}
}
else if (m_Operation.nOpState == CONNECT_FEAT)
{
if (m_CurrentServer.nUTF8 == 1)
m_bUTF8 = true;
if (m_bUTF8 && m_hasClntCmd)
{
// Some servers refuse to enable UTF8 if client does not send CLNT command
// to fix compatibility with Internet Explorer, but in the process breaking
// compatibility with other clients.
// Rather than forcing MS to fix Internet Explorer, letting other clients
// suffer is a questionable decision in my opinion.
if (Send("CLNT FileZilla"))
m_Operation.nOpState = CONNECT_CLNT;
return;
}
if (m_bUTF8)
{
// Handle servers that disobey RFC 2640 that have UTF8 in the FEAT
// response but do not use UTF8 unless OPTS UTF8 ON gets send.
// However these servers obey a conflicting ietf draft:
// http://www.ietf.org/proceedings/02nov/I-D/draft-ietf-ftpext-utf-8-option-00.txt
// servers are, amongst others, G6 FTP Server and RaidenFTPd.
if (Send("OPTS UTF8 ON"))
m_Operation.nOpState = CONNECT_OPTSUTF8;
return;
}
if (Send("SYST"))
m_Operation.nOpState = CONNECT_SYST;
return;
}
else if (m_Operation.nOpState == CONNECT_CLNT)
{
// See above why we send this command
if (Send("OPTS UTF8 ON"))
m_Operation.nOpState = CONNECT_OPTSUTF8;
return;
}
else if (m_Operation.nOpState == CONNECT_OPTSUTF8)
{
if (Send("SYST"))
m_Operation.nOpState = CONNECT_SYST;
return;
}
else if (m_Operation.nOpState == CONNECT_SYST)
{
if (GetReplyCode() == 2)
{
const CString reply = m_RecvBuffer.front();
if (reply.GetLength() > 7 && reply.Mid(3, 4) == " MVS")
m_mayBeMvsFilesystem = true;
else if (reply.GetLength() >= 11 && reply.Mid(3, 8) == " BS-2000")
m_mayBeBS2000Filesystem = true;
}
if ((m_CurrentServer.nServerType & FZ_SERVERTYPE_LAYERMASK) & (FZ_SERVERTYPE_LAYER_SSL_IMPLICIT | FZ_SERVERTYPE_LAYER_SSL_EXPLICIT | FZ_SERVERTYPE_LAYER_TLS_EXPLICIT))
{
m_Operation.nOpState = CONNECT_SSL_PBSZ;
Send("PBSZ 0");
return;
}
ShowStatus(IDS_STATUSMSG_CONNECTED, 0);
m_pOwner->SetConnected(TRUE);
ResetOperation(FZ_REPLY_OK);
return;
}
else if (!bSkipReply)
{
int res=GetReplyCode();
if(res!=2 && res!=3 && m_Operation.nOpState>=0) // get initial connect msg off server
{
int nError=FZ_REPLY_ERROR;
if (res==5 && logonseq[logontype][m_Operation.nOpState]==1)
nError|=FZ_REPLY_CRITICALERROR;
DoClose(nError);
return;
}
}
CString hostname = m_CurrentServer.host;
if (m_CurrentServer.port != 21)
hostname.Format(hostname+ ":%d", m_CurrentServer.port); // add port to hostname (only if port is not 21)
USES_CONVERSION;
//**** GSS Authentication ****
if (m_Operation.nOpState==CONNECT_GSS_INIT) //authenticate
{
int i = m_pGssLayer->GetClientAuth(T2CA(m_CurrentServer.host));
if (i==-1)
m_Operation.nOpState = CONNECT_GSS_AUTHDONE;
else if (i != GSSAPI_AUTHENTICATION_SUCCEEDED)
{
m_Operation.nOpState = CONNECT_GSS_FAILED;
CAsyncRequestData *pData=new CAsyncRequestData;
pData->nRequestType=FZ_ASYNCREQUEST_GSS_AUTHFAILED;
pData->nRequestID=m_pOwner->GetNextAsyncRequestID();
if (!PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_ASYNCREQUEST, FZ_ASYNCREQUEST_GSS_AUTHFAILED), (LPARAM)pData))
delete pData;
}
else
{
// we got authentication, we need to check whether we have forwardable tickets
//ShowStatus(IDS_STATUSMSG_GSSAUTH, 0);
PostMessage(m_pOwner->m_hOwnerWnd,m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_SECURESERVER, TRUE), 0);
if (Send("CWD ."))
m_Operation.nOpState = CONNECT_GSS_CWD;
}
return;
}
else if (m_Operation.nOpState == CONNECT_GSS_AUTHDONE)
{
if (!m_pGssLayer->AuthSuccessful())
{
m_Operation.nOpState = CONNECT_GSS_FAILED;
CAsyncRequestData *pData=new CAsyncRequestData;
pData->nRequestType = FZ_ASYNCREQUEST_GSS_AUTHFAILED;
pData->nRequestID = m_pOwner->GetNextAsyncRequestID();
if (!PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_ASYNCREQUEST, FZ_ASYNCREQUEST_GSS_AUTHFAILED), (LPARAM)pData))
delete pData;
return;
}
else
{
// we got authentication, we need to check whether we have forwardable tickets
//ShowStatus(IDS_STATUSMSG_GSSAUTH, 0);
PostMessage(m_pOwner->m_hOwnerWnd,m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_SECURESERVER, TRUE), 0);
if (Send("CWD ."))
m_Operation.nOpState = CONNECT_GSS_CWD;
return;
}
}
else if (m_Operation.nOpState == CONNECT_GSS_CWD)
{ // authentication succeeded, we're now get the response to the CWD command
if (GetReplyCode() == 2) // we're logged on
{
if (Send("FEAT"))
m_Operation.nOpState = CONNECT_FEAT;
return;
}
else
//GSS authentication complete but we still have to go through the standard logon procedure
m_Operation.nOpState = CONNECT_INIT;
}
if (m_Operation.nOpState==CONNECT_INIT)
{
if (logontype)
{
CString str;
str.Format(IDS_STATUSMSG_FWCONNECT,hostname);
ShowStatus(str,0);
}
m_Operation.nOpState++;
}
else if (m_Operation.nOpState >= 0 && !bSkipReply)
{
m_Operation.nOpState=logonseq[logontype][m_Operation.nOpState+GetReplyCode()-1]; //get next command from array
switch(m_Operation.nOpState)
{
case ER: // ER means summat has gone wrong
DoClose();
return;
case LO: //LO means we are logged on
if (Send("FEAT"))
m_Operation.nOpState = CONNECT_FEAT;
return;
}
}
// go through appropriate logon procedure
int i = logonseq[logontype][m_Operation.nOpState];
if (m_pGssLayer)
{
if ((i == 0 || i == 6 || i == 9 || i == 10) &&
(m_CurrentServer.user == "anonymous" || m_CurrentServer.user == ""))
{
//Extract user from kerberos ticket
char str[256];
if (m_pGssLayer->GetUserFromKrbTicket(str))
m_CurrentServer.user = str;
if (m_CurrentServer.user == "")
{
CGssNeedUserRequestData *pData = new CGssNeedUserRequestData;
pData->nRequestID = m_pOwner->GetNextAsyncRequestID();
pData->nOldOpState = m_Operation.nOpState;
m_Operation.nOpState = CONNECT_GSS_NEEDUSER;
if (!PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_ASYNCREQUEST, FZ_ASYNCREQUEST_GSS_NEEDUSER), (LPARAM)pData))
delete pData;
return;
}
}
else if ((i == 1 || i == 11) && (m_CurrentServer.pass == COptions::GetOption(OPTION_ANONPWD) || m_CurrentServer.pass == ""))
{
CGssNeedPassRequestData *pData=new CGssNeedPassRequestData;
pData->nRequestID=m_pOwner->GetNextAsyncRequestID();
pData->nOldOpState = m_Operation.nOpState;
m_Operation.nOpState = CONNECT_GSS_NEEDPASS;
if (!PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_ASYNCREQUEST, FZ_ASYNCREQUEST_GSS_NEEDPASS), (LPARAM)pData))
delete pData;
return;
}
}
switch(logonseq[logontype][m_Operation.nOpState])
{
case 0:
temp="USER "+m_CurrentServer.user;
break;
case 1:
temp="PASS "+m_CurrentServer.pass;
break;
case 2:
temp="ACCT "+CCrypt::decrypt(COptions::GetOption(OPTION_FWPASS));
break;
case 3:
temp="USER "+COptions::GetOption(OPTION_FWUSER);
break;
case 4:
temp="PASS "+CCrypt::decrypt(COptions::GetOption(OPTION_FWPASS));
break;
case 5:
temp="SITE "+hostname;
break;
case 6:
temp="USER "+m_CurrentServer.user+"@"+hostname;
break;
case 7:
temp="OPEN "+hostname;
break;
case 8:
temp="USER "+COptions::GetOption(OPTION_FWUSER)+"@"+hostname;
break;
case 9:
temp="USER "+m_CurrentServer.user+"@"+hostname+" "+COptions::GetOption(OPTION_FWUSER);
break;
case 10:
temp="USER "+m_CurrentServer.user+"@"+COptions::GetOption(OPTION_FWUSER)+"@"+hostname;
break;
case 11:
temp="PASS "+m_CurrentServer.pass+"@"+CCrypt::decrypt(COptions::GetOption(OPTION_FWPASS));
break;
case 12:
if (m_CurrentServer.account == _T(""))
temp = _T("ACCT default");
else
temp = _T("ACCT ") + m_CurrentServer.account;
break;
}
// send command, get response
if(!Send(temp))
return;
}
#define BUFFERSIZE 4096
void CFtpControlSocket::OnReceive(int nErrorCode)
{
LogMessage(__FILE__, __LINE__, this,FZ_LOG_DEBUG, _T("OnReceive(%d) OpMode=%d OpState=%d"), nErrorCode, m_Operation.nOpMode, m_Operation.nOpState);
m_LastRecvTime=CTime::GetCurrentTime();
PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_SOCKETSTATUS, FZ_SOCKETSTATUS_RECV), 0);
if (!m_pOwner->IsConnected())
{
if (!m_Operation.nOpMode)
{
LogMessage(__FILE__, __LINE__, this, FZ_LOG_INFO, _T("Socket has been closed, don't process receive") );
return;
}
m_MultiLine = "";
CString str;
str.Format(IDS_STATUSMSG_CONNECTEDWITH, m_ServerName);
ShowStatus(str, 0);
m_pOwner->SetConnected(TRUE);
}
char *buffer = new char[BUFFERSIZE];
int numread = Receive(buffer, BUFFERSIZE);
while (numread != SOCKET_ERROR && numread)
{
for (int i=0; i < numread; i++)
{
if ((buffer[i] == '\r') || (buffer[i] == '\n') || (buffer[i] == 0))
{
if (!m_RecvBuffer.empty() && m_RecvBuffer.back() != "")
{
USES_CONVERSION;
if (m_bUTF8)
{
// convert from UTF-8 to ANSI
LPCSTR utf8 = (LPCSTR)m_RecvBuffer.back();
if (!utf8_valid((const unsigned char*)utf8, strlen(utf8)))
{
if (m_CurrentServer.nUTF8 != 1)
{
LogMessage(__FILE__, __LINE__, this, FZ_LOG_WARNING, _T("Server does not send proper UTF-8, falling back to local charset"));
m_bUTF8 = false;
}
ShowStatus(A2CT(utf8), 3);
}
else
{
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
if (!len)
m_RecvBuffer.back() = "";
else
{
LPWSTR p1 = new WCHAR[len + 1];
MultiByteToWideChar(CP_UTF8, 0, utf8, -1 , (LPWSTR)p1, len + 1);
ShowStatus(W2CT(p1), 3);
delete [] p1;
}
}
}
else
ShowStatus(A2CT(m_RecvBuffer.back()), 3);
//Check for multi-line responses
if (m_RecvBuffer.back().GetLength() > 3)
{
if (m_MultiLine != "")
{
if (m_RecvBuffer.back().Left(4) != m_MultiLine)
{
DiscardLine(m_RecvBuffer.back());
m_RecvBuffer.pop_back();
}
else // end of multi-line found
{
m_MultiLine = "";
m_pOwner->PostThreadMessage(m_pOwner->m_nInternalMessageID, FZAPI_THREADMSG_PROCESSREPLY, 0);
}
}
// start of new multi-line
else if (m_RecvBuffer.back()[3] == '-')
{
// DDD<SP> is the end of a multi-line response
m_MultiLine = m_RecvBuffer.back().Left(3) + ' ';
m_RecvBuffer.pop_back();
}
else
m_pOwner->PostThreadMessage(m_pOwner->m_nInternalMessageID, FZAPI_THREADMSG_PROCESSREPLY, 0);
}
else
m_RecvBuffer.pop_back();
m_RecvBuffer.push_back("");
}
}
else
{
//The command may only be 2000 chars long. This ensures that a malicious user can't
//send extremely large commands to fill the memory of the server
if (m_RecvBuffer.empty())
m_RecvBuffer.push_back("");
if (m_RecvBuffer.back().GetLength() < 2000)
m_RecvBuffer.back() += buffer[i];
}
}
if (numread < BUFFERSIZE)
break;
numread = Receive(buffer,BUFFERSIZE);
if (numread && numread != SOCKET_ERROR)
{
m_LastRecvTime = CTime::GetCurrentTime();
PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_SOCKETSTATUS, FZ_SOCKETSTATUS_RECV), 0);
}
}
delete buffer;
if (numread == SOCKET_ERROR)
if (GetLastError() != WSAEWOULDBLOCK)
{
ShowStatus(IDS_STATUSMSG_DISCONNECTED,1);
DoClose();
return;
}
}
void CFtpControlSocket::ProcessReply()
{
if (m_RecvBuffer.empty())
return;
if (m_awaitsReply)
{
if (m_sendBuffer)
TriggerEvent(FD_WRITE);
m_awaitsReply = false;
}
CString reply = GetReply();
if ( reply == _T("") )
return;
// After Cancel, we might have to skip a reply
if (m_skipReply)
{
m_skipReply = false;
m_RecvBuffer.pop_front();
return;
}
if (m_bKeepAliveActive)
{
m_bKeepAliveActive = FALSE;
m_pOwner->PostThreadMessage(m_pOwner->m_nInternalMessageID,FZAPI_THREADMSG_POSTKEEPALIVE,0);
}
else if (m_Operation.nOpMode&CSMODE_CONNECT)
LogOnToServer();
else if (m_Operation.nOpMode& (CSMODE_COMMAND|CSMODE_CHMOD) )
{
if (GetReplyCode()== 2 || GetReplyCode()== 3)
ResetOperation(FZ_REPLY_OK);
else
ResetOperation(FZ_REPLY_ERROR);
}
else if (m_Operation.nOpMode&CSMODE_TRANSFER)
{
FileTransfer(0);
}
else if (m_Operation.nOpMode&CSMODE_LIST)
List(FALSE);
else if (m_Operation.nOpMode&CSMODE_DELETE)
Delete( _T(""),CServerPath());
else if (m_Operation.nOpMode&CSMODE_RMDIR)
RemoveDir( _T(""),CServerPath());
else if (m_Operation.nOpMode&CSMODE_MKDIR)
MakeDir(CServerPath());
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?