📄 ftp_ctrl.c
字号:
/*---------------------------------------------------------------------
*
* Program: AC_FTP.EXE Asynch Ftp Client (TCP)
*
* filename: FTP_CTRL.C
*
* copyright by Bob Quinn, 1995
*
* Description:
* Client application that uses "file transfer protocol" (ftp)
* service as described by RFC 959.
*
* This module contains the functions that deal with the FTP
* control connection (for sending commands to FTP server,
* receiving replies from FTP server, and processing them.
*
* This software is not subject to any export provision of
* the United States Department of Commerce, and may be
* exported to any country or planet.
*
* Permission is granted to anyone to use this software for any
* purpose on any computer system, and to alter it and redistribute
* it freely, subject to the following restrictions:
*
* 1. The author is not responsible for the consequences of
* use of this software, no matter how awful, even if they
* arise from flaws in it.
*
* 2. The origin of this software must not be misrepresented,
* either by explicit claim or by omission. Since few users
* ever read sources, credits must appear in the documentation.
*
* 3. Altered versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
* Since few users ever read sources, credits must appear in
* the documentation.
*
* 4. This notice may not be removed or altered.
*
---------------------------------------------------------------------*/
#include "..\wsa_xtra.h"
#include <windows.h>
#include <windowsx.h>
#include "..\winsockx.h"
#include <string.h> /* for _fmemcpy() & _fmemset() */
#include <winsock.h>
#include "resource.h"
#include <direct.h> /* for Microsoft find file structure */
#include "ac_ftp.h"
/*---------------------------------------------------------------
* Function: InitCtrlConn()
*
* Description: get a TCP socket, register for async notification,
* and then connect to Ftp server
*/
SOCKET InitCtrlConn(PSOCKADDR_IN pstName, HWND hDlg, u_int nAsyncMsg)
{
int nRet;
SOCKET hCtrlSock;
if (bDebug)
OutputDebugString("InitCtrlConn()\n");
/* Get a TCP socket for control connection */
hCtrlSock = socket (AF_INET, SOCK_STREAM, 0);
if (hCtrlSock == INVALID_SOCKET) {
WSAperror(WSAGetLastError(), "socket()", hInst);
} else {
/* Request async notification for most events */
nRet = WSAAsyncSelect(hCtrlSock, hDlg, nAsyncMsg,
(FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE));
if (nRet == SOCKET_ERROR) {
WSAperror(WSAGetLastError(), "WSAAsyncSelect()", hInst);
closesocket(hCtrlSock);
hCtrlSock = INVALID_SOCKET;
} else {
/* Initiate non-blocking connect to server */
pstName->sin_family = PF_INET;
pstName->sin_port = htons(IPPORT_FTP);
nRet = connect(hCtrlSock,(LPSOCKADDR)pstName,SOCKADDR_LEN);
if (nRet == SOCKET_ERROR) {
int WSAErr = WSAGetLastError();
/* Anything but "would block" error is bad */
if (WSAErr != WSAEWOULDBLOCK) {
/* Report error and clean up */
WSAperror(WSAErr, "connect()", hInst);
closesocket(hCtrlSock);
hCtrlSock = INVALID_SOCKET;
}
}
}
}
return (hCtrlSock);
} /* end InitCtrlConn() */
/*--------------------------------------------------------------
* Function: SendFtpCmd()
*
* Description: Format and send an FTP command to the server
*/
int SendFtpCmd(void)
{
int nRet, nLen, nBytesSent = 0;
int nFtpCmd = astFtpCmd[1].nFtpCmd;
if (bDebug) {
wsprintf(achTempBuf,
"SendFtpCmd() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%d\n",
nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
OutputDebugString (achTempBuf);
}
/* Create a command string (if we don't already have one) */
if (szFtpCmd[0] == 0) {
switch (nFtpCmd) {
case PORT:
wsprintf (szFtpCmd, "PORT %d,%d,%d,%d,%d,%d\r\n",
stDLclName.sin_addr.S_un.S_un_b.s_b1, /* local addr */
stDLclName.sin_addr.S_un.S_un_b.s_b2,
stDLclName.sin_addr.S_un.S_un_b.s_b3,
stDLclName.sin_addr.S_un.S_un_b.s_b4,
stDLclName.sin_port & 0xFF, /* local port */
(stDLclName.sin_port & 0xFF00)>>8);
break;
case CWD:
case DELE:
case PASS:
case RETR:
case STOR:
case TYPE:
case USER:
/* Ftp commmand and parameter */
wsprintf (szFtpCmd, "%s %s\r\n",
aszFtpCmd[nFtpCmd], &(astFtpCmd[1].szFtpParm));
break;
case ABOR:
case LIST:
case PWD:
case QUIT:
/* Solitary Ftp command string (no parameter) */
wsprintf (szFtpCmd, "%s\r\n", aszFtpCmd[nFtpCmd]);
break;
default:
return (0); /* we have a bogus command! */
}
}
nLen = strlen(szFtpCmd);
if (hCtrlSock != INVALID_SOCKET) {
/* Send the ftp command to control socket */
while (nBytesSent < nLen) {
nRet = send(hCtrlSock, (LPSTR)szFtpCmd, nLen-nBytesSent, 0);
if (nRet == SOCKET_ERROR) {
int WSAErr = WSAGetLastError();
/* If "would block" error we'll pickup again with async
* FD_WRITE notification, but any other error is bad news */
if (WSAErr != WSAEWOULDBLOCK)
WSAperror(WSAErr, "SendFtpCmd()", hInst);
break;
}
nBytesSent += nRet;
}
}
/* if we sent it all, update our status and move everything up
* in command queue */
if (nBytesSent == nLen) {
int i;
if (nFtpCmd == PASS) /* hide password */
memset (szFtpCmd+5, 'x', 10);
if (bLogFile) /* log command */
_lwrite (hLogFile, szFtpCmd, strlen(szFtpCmd));
GetDlgItemText (hWinMain, IDC_REPLY, /* display command */
achRplyBuf, RPLY_SIZE-strlen(szFtpCmd));
wsprintf (achTempBuf, "%s%s", szFtpCmd, achRplyBuf);
SetDlgItemText (hWinMain, IDC_REPLY, achTempBuf);
szFtpCmd[0] = 0; /* disable Ftp command string */
/* move everything up in the command queue */
for (i=0; i < nQLen; i++) {
astFtpCmd[i].nFtpCmd = astFtpCmd[i+1].nFtpCmd;
astFtpCmd[i+1].nFtpCmd = 0; /* reset old command */
if (*(astFtpCmd[i+1].szFtpParm)) {
memcpy (astFtpCmd[i].szFtpParm, astFtpCmd[i+1].szFtpParm, CMD_SIZE);
*(astFtpCmd[i+1].szFtpParm) = 0; /* terminate old string */
} else {
*(astFtpCmd[i].szFtpParm) = 0; /* terminate unused string */
}
}
nQLen--; /* decrement the queue length */
switch (nFtpCmd) {
case (USER):
SetDlgItemText (hWinMain, IDC_STATUS,"Status: connecting");
break;
case (STOR):
SetDlgItemText (hWinMain, IDC_STATUS,"Status: sending a file");
break;
case (RETR):
SetDlgItemText (hWinMain, IDC_STATUS,"Status: receiving a file");
break;
case (LIST):
SetDlgItemText (hWinMain, IDC_STATUS,"Status: receiving directory");
break;
case (QUIT):
SetDlgItemText (hWinMain, IDC_SERVER, "Server: none");
SetDlgItemText (hWinMain, IDC_STATUS, "Status: not connected");
break;
}
}
return (nBytesSent);
} /* end SendFtpCmd() */
/*--------------------------------------------------------------
* Function: QueueFtpCmd()
*
* Description: Put FTP command in command queue for sending after we
* receive responses to pending commands or now if nothing is pending
*/
BOOL QueueFtpCmd(int nFtpCmd, LPSTR szFtpParm) {
if (bDebug) {
wsprintf(achTempBuf,
"QueueFtpCmd() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, pend:%d\n",
nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd,
recv(hCtrlSock, achTempBuf, BUF_SIZE, MSG_PEEK));
OutputDebugString (achTempBuf);
}
if ((nFtpCmd == ABOR) || (nFtpCmd == QUIT)) {
AbortFtpCmd();
if (hCtrlSock != INVALID_SOCKET)
SetDlgItemText (hWinMain, IDC_STATUS,"Status: connected");
} else if (nQLen == MAX_CMDS) {
/* Notify user if they can't fit in the queue */
MessageBox (hWinMain, "Ftp command queue is full, try again later",
"Can't Queue Command", MB_OK | MB_ICONASTERISK);
return (FALSE); /* not queued */
}
nQLen++; /* increment Ftp command counter */
/* Save command vitals */
astFtpCmd[nQLen].nFtpCmd = nFtpCmd;
if (szFtpParm)
lstrcpy (astFtpCmd[nQLen].szFtpParm, szFtpParm);
if (!(astFtpCmd[0].nFtpCmd) && astFtpCmd[1].nFtpCmd) {
/* If nothing pending reply, then send the next command */
SendFtpCmd();
}
return (TRUE); /* queued! */
} /* end QueueFtpCmd() */
/*--------------------------------------------------------------
* Function: AbortFtpCmd()
*
* Description: Clean up routine to abort a pending FTP command and
* clear the command queue
*/
void AbortFtpCmd(void) {
int i;
if (hLstnSock != INVALID_SOCKET) {/* Close listen socket */
closesocket(hLstnSock);
hLstnSock = INVALID_SOCKET;
}
if (hDataSock != INVALID_SOCKET){ /* Close data socket */
CloseFtpConn(&hDataSock,
(astFtpCmd[0].nFtpCmd != STOR) ? achInBuf : (PSTR)0,
INPUT_SIZE, hWinMain);
EndData();
}
for (i=0;i<MAX_CMDS;i++) /* Clear command queue */
astFtpCmd[i].nFtpCmd = 0;
nQLen = 0;
} /* end AbortFtpCmd() */
/*--------------------------------------------------------------
* Function: RecvFtpRply()
*
* Description: Read the FTP reply from server (and log it)
*/
int RecvFtpRply(SOCKET hCtrlSock, LPSTR szFtpRply, int nLen)
{
int nRet=0;
if (bDebug) {
wsprintf(achTempBuf,
"RecvFtpRply() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%d\n",
nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
OutputDebugString (achTempBuf);
}
if (hCtrlSock != INVALID_SOCKET) {
memset(szFtpRply,0,nLen); /* Init receive buffer */
/* Read as much as we can */
nRet = recv(hCtrlSock,(LPSTR)szFtpRply,nLen,0);
if (nRet == SOCKET_ERROR) {
int WSAErr = WSAGetLastError();
if (WSAErr != WSAEWOULDBLOCK) {
WSAperror (WSAErr, "RecvFtpRply()", hInst);
}
} else if (bLogFile) /* log reply */
_lwrite (hLogFile, szFtpRply, nRet);
}
return (nRet);
} /* end RecvFtpReply() */
/*--------------------------------------------------------------
* Function: ProcessFtpRply()
*
* Description: Figure out what happened, and what to do next.
*/
void ProcessFtpRply (LPSTR szRply, int nBufLen)
{
LPSTR szFtpRply;
int nPendingFtpCmd, i;
if (bDebug) {
wsprintf(achTempBuf,
"ProcessFtpRply() Qlen:%d Cmd[0]:%d [1]:%d [2]:%d [3]:%d, State:%d\n",
nQLen, astFtpCmd[0].nFtpCmd, astFtpCmd[1].nFtpCmd,
astFtpCmd[2].nFtpCmd, astFtpCmd[3].nFtpCmd, nAppState);
OutputDebugString (achTempBuf);
}
/* Skip continuation lines (denoted by a dash after reply code
* or with a blank reply code and no dash) */
szFtpRply = szRply;
while ((*(szFtpRply+3) == '-') ||
((*(szFtpRply)==' ')&&(*(szFtpRply+1)==' ')&&(*(szFtpRply+2)==' '))) {
/* find end of reply line */
for (i=0;*szFtpRply!=0x0a && *szFtpRply && i<nBufLen-3; szFtpRply++,i++);
szFtpRply++; /* go to beginning of next reply */
if (!(*szFtpRply)) /* quit if end of string */
return;
}
*szFtpCmd = 0; /* Disable old command string */
nPendingFtpCmd = astFtpCmd[0].nFtpCmd; /* Save last Ftp Cmd */
if ((*szFtpRply != '1') &&
(nPendingFtpCmd != LIST) &&
(nPendingFtpCmd != STOR) &&
(nPendingFtpCmd != RETR))
/* For any but preliminary reply, clear old command */
astFtpCmd[0].nFtpCmd = 0;
/* First digit in 3-digit Ftp reply code is the most significant */
switch (*szFtpRply) {
case ('1'): /* Positive preliminary reply */
break;
case ('2'): /* Positive completion reply */
switch(nPendingFtpCmd) {
case 0:
/* Check for "220 Service ready for new user" reply, and
* send user command to login if login message found */
if ((*(szFtpRply+1)=='2') && (*(szFtpRply+2)=='0'))
QueueFtpCmd(USER, szUser);
break;
case CWD:
case USER:
case PASS:
/* We're logged in! Get remote working directory */
QueueFtpCmd(PWD, 0);
break;
case PWD:
/* Display remote working directory */
SetDlgItemText (hWinMain, IDC_RPWD, &szFtpRply[4]);
break;
case TYPE:
case PORT:
/* Send next command (it's already queued) */
SendFtpCmd();
break;
case ABOR:
/* Close the data socket */
if (hDataSock != INVALID_SOCKET)
CloseFtpConn(&hDataSock, (PSTR)0, 0, hWinMain);
break;
case QUIT:
/* Close the control socket */
if (hCtrlSock != INVALID_SOCKET)
CloseFtpConn(&hCtrlSock, (PSTR)0, 0, hWinMain);
break;
default:
break; /* Nothing to do after most replies */
}
break;
case ('3'): /* Positive intermediate reply */
if (nPendingFtpCmd == USER)
QueueFtpCmd(PASS, szPWrd);
break;
case ('4'): /* Transient negative completion reply */
case ('5'): /* Permenant negative completion reply */
/* If Port failed, forget about queued commands */
if (nPendingFtpCmd != ABOR)
QueueFtpCmd(ABOR, 0);
break;
}
} /* end ProcessFtpRply() */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -