⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ctrlsocket.cpp

📁 在Linux下使用GCC编制的FTP服务器
💻 CPP
字号:
#include <strings.h>
#include "Tools.h"
#include "CtrlSocket.h"

CCtrlSocket::CMD_INFO CCtrlSocket::m_arrCmd[]=
{
    {"SYST",&CCtrlSocket::DoSyst,"Get operating system type: SYST"},
	{"PWD",	&CCtrlSocket::DoPwd,"Get current directory: PWD"},
	{"CWD",	&CCtrlSocket::DoCwd,"Change working directory: CWD [directory-name]"},
	{"USER",&CCtrlSocket::DoUser,"Supply a username: USER username"},
	{"PASS",&CCtrlSocket::DoPass,"Supply a user password: PASS password"},
	{"TYPE",&CCtrlSocket::DoType,"Set filetype: TYPE [A | I]"},
	{"CDUP",&CCtrlSocket::DoCdup,"Change to parent directory: CDUP"},
	{"NOOP",&CCtrlSocket::DoNoop,"Do nothing: NOOP"},
	{"RETR",&CCtrlSocket::DoRetr,"Get file: RETR file-name"},
	{"STOR",&CCtrlSocket::DoStor,"Store file: STOR file-name"},
	{"LIST",&CCtrlSocket::DoList,"Get directory listing: LIST [path-name]"},
	{"DIR", &CCtrlSocket::DoList,"Get directory listing: DIR [path-name]"},
	{"PASV",&CCtrlSocket::DoPasv,"Set server in passive mode: PASV"},
	{"PORT",&CCtrlSocket::DoPort,"Specify the client port number: PORT a0,a1,a2,a3,a4,a5"},
	{"ABOR",&CCtrlSocket::DoAbor,"Abort transfer: ABOR"},
	{"QUIT",&CCtrlSocket::DoQuit,"Logout or break the connection: QUIT"},
	{"DELE",&CCtrlSocket::DoDele,"Delete file: DELE file-name"},
	{"MKD", &CCtrlSocket::DoMkd,"Make directory: MKD path-name"},
	{"RMD", &CCtrlSocket::DoRmd,"Remove directory: RMD path-name"},
	{"SIZE",&CCtrlSocket::DoSize,"Get filesize: SIZE file-name"},
	{"RNFR",&CCtrlSocket::DoRefr,"Specify old path name of file to be renamed: RNFR file-name"},
	{"RNTO",&CCtrlSocket::DoReto,"Specify new path name of file to be renamed: RNTO file-name"},
	{"HELP",&CCtrlSocket::DoHelp,"Show help: HELP [command]"},
	{"REST",NULL,"Set restart transfer marker: REST marker"}//断点续传尚未实现
};

CCtrlSocket::CCtrlSocket()
{
	m_sSend=m_sRecv="";
	m_sockData.m_pSockCtrl=this;
	pthread_mutex_init(&m_mutex,NULL);
}

CCtrlSocket::~CCtrlSocket()
{
	pthread_mutex_destroy(&m_mutex);
}

int CCtrlSocket::Start()
{
	char szBuf[1024];
	int iRet;
	int iCmdIndex,iTimeout=0;
	//
	SetTimeout(100);//每0.1秒循环一次
	m_sockData.m_iTimeout=m_iTimeout;//给数据通道套接字也设置超时
	Response(WELCOME);
	//
	while(true){
		//提取命令,追加接收队列
		iRet=Recv(szBuf,sizeof(szBuf)-1);
		if(iRet==0){
			break;//对方断开连接
		}else if(iRet>0){
			szBuf[iRet]=0;//这一句必须加上!!
			m_sRecv+=szBuf;
			iTimeout=0;
		}else if(errno!=EWOULDBLOCK){
			perror("recv fail");//出现异常
			break;
		}
		//从接收队列中提取命令,追加响应队列
		iCmdIndex=GetCmd(szBuf,sizeof(szBuf));
		if(iCmdIndex>=0){//提取了一个命令
			ReplyCmd(iCmdIndex,szBuf);
		}else if(iCmdIndex<-1){//提取失败,-1表示命令未接收完
			Response(CMD_ERR);
		}
		//读取响应队列,提交响应.
		//(响应队列由本线程和数据通道子线程互斥追加)
		if(CommitResponse()<0 && errno!=EWOULDBLOCK){
			break;//发送失败!
		}
		if(++iTimeout>(m_iTimeout/100)){//超时退出
			TRACE("timeout:%d",m_iTimeout);
			break;
		}
	}
	return 0;
}

int CCtrlSocket::ReplyCmd(int iCmdIndex,const char *pszArg)
{
	CMD_INFO *pCmdInfo=m_arrCmd+iCmdIndex;
	FN_DO pfnDo;
	//
	if(!m_user.IsLoaded() && 
	   strcmp(pCmdInfo->pszCmd,"USER")!=0 && 
	   strcmp(pCmdInfo->pszCmd,"PASS")!=0){
		Response(LOGIN_ERR);
		return -1;
	}
	pfnDo=pCmdInfo->pfnDo;
	if(pfnDo){
		return (this->*pfnDo)(pszArg);
	}else{
		Response(IMPL_ERR);
		return 0;			
	}
}

int CCtrlSocket::DoUser(const char *pszArg)
{
	m_user.m_sUser=pszArg;		
	Response(USER_OK);
	return 0;
}

int CCtrlSocket::DoPass(const char *pszArg)
{
	int iRet;
	if(m_user.m_sUser==""){
		Response(LOGIN_ERR);
		return -1;
	}
	iRet=m_user.Load(pszArg);
	if(iRet==0){
		Response(LOGIN_OK);
	}else if(iRet==-1){
		Response(LOGIN_ERR);
	}else{
		Response(INTER_ERR);
	}
	return iRet;
}

int CCtrlSocket::DoSyst(const char *pszArg)
{
	Response(SYST_OK);
	return 0;
}

int CCtrlSocket::DoRetr(const char *pszArg)	
{
	if(m_user.BuildRetr(m_sockData.m_sFile,pszArg)<0){
		Response(FILE_ERR);				
		return -1;
	}
	return m_sockData.Run(CDataSocket::RETR);
}

int CCtrlSocket::DoStor(const char *pszArg)	
{
	int iRet=m_user.BuildStor(m_sockData.m_sFile,pszArg);
	if(iRet<0){
		Response(FILE_ERR);				
		return -1;
	}
	return m_sockData.Run(CDataSocket::STOR);
}

int CCtrlSocket::DoList(const char *pszArg)	
{
	if(m_user.BuildList(m_sockData.m_sList)<0){
		Response(FILE_ERR);				
		return -1;
	}
	return m_sockData.Run(CDataSocket::LIST);
}

int CCtrlSocket::DoPwd(const char *pszArg)	
{
	Response("257 \"%s\" is current directory.",
			 m_user.GetDir());
	return 0;
}

int CCtrlSocket::DoCwd(const char *pszArg)	
{
	if(m_user.ChangeDir(pszArg)<0){
		Response(DIR_ERR);
		return -1;
	}
	Response(DIR_OK);
	return 0;
}

int CCtrlSocket::DoDele(const char *pszArg)
{
	if(m_user.DeleFile(pszArg)<0){
		Response(FILE_ERR);
		return -1;
	}
	Response(FILE_OK);
	return 0;
}

int CCtrlSocket::DoMkd(const char *pszArg)
{
	if(m_user.Mkdir(pszArg)<0){
		Response(DIR_ERR);
		return -1;
	}
	Response(DIR_OK);
	return 0;
}

int CCtrlSocket::DoRmd(const char *pszArg)
{
	if(m_user.Rmdir(pszArg)<0){
		Response(DIR_ERR);
		return -1;
	}
	Response(DIR_OK);
	return 0;
}

int CCtrlSocket::DoSize(const char *pszArg)
{
	int iSize=m_user.GetFileSize(pszArg);
	if(iSize<0){
		Response(FILE_ERR);
		return -1;
	}
	Response("213 %d",iSize);
	return 0;
}

//rename file/direcory from ...
int CCtrlSocket::DoRefr(const char *pszArg)
{
	if(m_user.RenameFrom(pszArg)<0){
		Response(FILE_ERR);
		return -1;
	}
	Response(RNFR_OK);
	return 0;
}

//rename file/directory to ...
int CCtrlSocket::DoReto(const char *pszArg)
{
	if(m_user.RenameTo(pszArg)<0){
		Response(FILE_ERR);
		return -1;
	}
	Response(RNTO_OK);
	return 0;
}

int CCtrlSocket::DoCdup(const char *pszArg)
{
	return DoCwd("..");
}

int CCtrlSocket::DoType(const char *pszArg)	
{
	Response(CMD_OK);
	return 0;
}

int CCtrlSocket::DoNoop(const char *pszArg)	
{
	Response(CMD_OK);
	return 0;
}

int CCtrlSocket::DoPasv(const char *pszArg)	
{
	return m_sockData.PreparePasv();
}

int CCtrlSocket::DoPort(const char *pszArg)	
{
	return m_sockData.PreparePort(pszArg);
}

int CCtrlSocket::DoQuit(const char *pszArg)
{
	Response(QUIT_OK);
	CommitResponse();
	exit(0);
	return 0;
}

int CCtrlSocket::DoAbor(const char *pszArg)
{
	return m_sockData.Stop();//中断传输线程
}

int CCtrlSocket::DoHelp(const char *pszArg)
{
	int iCmdIndex,iCount;
	//
	iCount=sizeof(m_arrCmd)/sizeof(m_arrCmd[0]);
	if(*pszArg){
		iCmdIndex=FindCmdInfo(pszArg);
		if(iCmdIndex<0){
			Response(CMD_ERR);
		}else{
			Response("214 %s",m_arrCmd[iCmdIndex].pszInfo);
		}
	}else{
		for(iCmdIndex=0; iCmdIndex<iCount; ++iCmdIndex)
			Response(m_arrCmd[iCmdIndex].pszCmd);
		Response("214 HELP command successful.");
	}
	return 0;
}


int CCtrlSocket::FindCmdInfo(const char *pszCmd)
{
	int i,iCount;
	//
	iCount=sizeof(m_arrCmd)/sizeof(m_arrCmd[0]);
	for(i=0;i<iCount;i++)
		if(strcasecmp(m_arrCmd[i].pszCmd,pszCmd) == 0)
			return i;
	return -1;
}

int CCtrlSocket::GetCmd(char *pszArg,int iArgSize)
{
	size_t nDelim;
	char *pszTail,*pszMid;
	int iCmdIndex;
	//
	nDelim=m_sRecv.find("\r\n");
	if(nDelim==string::npos)
		return -1;
	if(nDelim<3 || (int)nDelim>iArgSize-1){//命令肯定有问题
		m_sRecv.erase(0,nDelim+2);
		return -2;
	}
	memcpy(pszArg,m_sRecv.c_str(),nDelim);
	pszArg[nDelim]=0;
	m_sRecv.erase(0,nDelim+2);
	pszTail=pszArg+nDelim;
	if((pszMid=strchr(pszArg,' ')))
		*pszMid=0;
	else
		pszMid=pszTail-1;
	iCmdIndex=FindCmdInfo(pszArg);
	if(iCmdIndex>=0){
		memmove(pszArg,pszMid+1,pszTail-pszMid);
		return iCmdIndex;
	}
	return -3;
}

void CCtrlSocket::Response(RPS_NO iRpsNo)
{
	static const RPS_INFO arrRps[]=
	{
		{CONN_OK,	"150 Connection accepted"},
		{ASC_OK, 	"150 Opening ASCII mode data connection for transfer."},
		{BIN_OK, 	"150 Opening BINARY mode data connection for transfer."},
		{PORT_OK,	"200 Port command successful"},
		{CMD_OK,	"200 OK"},
		{QUIT_OK,	"200 GoodBye!"},
		{SYST_OK,	"215 UNIX emulated by Gotter FTP Server."},
		{WELCOME,	"220 Gotter FTP Server v1.0,you are welcome!."},
		{TRAN_OK,	"226 Transfer complete"},
		{ABOR_OK,	"226 ABOR command successful."},
		{LOGIN_OK,	"230 User successfully logged in."},
		{FILE_OK,	"250 File operated successfully."},
		{DIR_OK,	"250 Directory operated successfully."},
		{RNTO_OK,	"250 renamed."},
		{USER_OK,	"331 User name ok, need password."},
		{RNFR_OK,	"350 ready for destination name."},
		{CONN_ERR,	"426 connection error."},
		{INTER_ERR,	"450 Internal error."},
		{ARG_ERR,	"501 Invalid parameter."},
		{CMD_ERR,	"501 Unknown command."},
		{IMPL_ERR,	"502 Command not implemented."},
		{SEQ_ERR,	"503 Bad sequence of commands."},
		{LOGIN_ERR,	"530 Not logged in."},
		{RIGHT_ERR,	"550 Permission denied"},
		{FILE_ERR,	"550 File operated unsuccessfully."},
		{DIR_ERR,	"550 Directory operated unsuccessfully."}
	};
	int i,iCount=sizeof(arrRps)/sizeof(arrRps[0]);
	for(i=0;i<iCount;i++){
		if(arrRps[i].iRpsNo==iRpsNo){
			Response(arrRps[i].pszInfo);
			break;
		}
	}
	if(i>=iCount){//不要出现这种情况
		Response(INTER_ERR);
		TRACE("Not response code!\n");
	}
}

void CCtrlSocket::Response(const char *pszFormat,...)
{
	static char szBuf[1024];
	va_list args;
   	va_start(args,pszFormat);
    vsnprintf(szBuf,sizeof(szBuf)-3,pszFormat,args);
	va_end(args);
	strcat(szBuf,"\r\n");
	AppendResponse(szBuf);		
}

void CCtrlSocket::AppendResponse(const char *pszResponse)
{
	pthread_mutex_lock(&m_mutex);
	m_sSend+=pszResponse;
	pthread_mutex_unlock(&m_mutex);
}

int CCtrlSocket::CommitResponse()
{
	pthread_mutex_lock(&m_mutex);
	int iRet=0;
	if(m_sSend.length()>0){
		iRet=Send(m_sSend.c_str(),m_sSend.length());
		if(iRet>0){
			m_sSend.erase(0,iRet);
		}
	}
	pthread_mutex_unlock(&m_mutex);
	return iRet;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -