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 + -
显示快捷键?