📄 ftp-server-code.txt
字号:
BOOL UnixPathToDosPath(const char unixFullPath[MAX_PATH], char dosFullPath[MAX_PATH])
{
if( unixFullPath[0]=='/' && isalpha(unixFullPath[1]) && unixFullPath[2] == '/' )
{
sprintf(dosFullPath, "%c:\\%s", unixFullPath[1], unixFullPath+3);
for(int i = strlen(dosFullPath)-1; i>=0; i--)
if( dosFullPath[i] == '/' ) dosFullPath[i] = '\\';
return TRUE;
}
else
return FALSE;
}
BOOL DosPathToUnixPath(const char dosFullPath[MAX_PATH], char unixFullPath[MAX_PATH])
{
if( isalpha(dosFullPath[0]) && memcmp(dosFullPath+1, ":\\", 2)==0 )
{
sprintf(unixFullPath, "/%c/%s", dosFullPath[0], dosFullPath+3);
for(int i = strlen(unixFullPath)-1; i>=0; i--)
if( unixFullPath[i] == '\\' ) unixFullPath[i] = '/';
return TRUE;
}
else
return FALSE;
}
BOOL GetFullUnixPath(const char unixPath[MAX_PATH], const char unixWorkDir[MAX_PATH],
char unixFullPath[MAX_PATH])
{
//get full path that may be redundant, saying include . or ..
if( unixPath[0] == '/' ) //it's full now
{
strcpy(unixFullPath, unixPath);
}
else if( strcmp(unixWorkDir, "/")==0 ) //unixWorkDir is root dir
{
sprintf(unixFullPath, "/%s", unixPath);
}
else
{
sprintf(unixFullPath, "%s/%s", unixWorkDir, unixPath);
}
if( strcmp(unixFullPath, "") == 0 )
strcpy(unixFullPath, "/");
//get pure full path, get rid of . or ..
char unixPureFullPath[MAX_PATH];
strcpy(unixPureFullPath, "");
char *p1, *p2;
for(p1=unixFullPath, p2=unixFullPath+1;;p2++)
{
if( *p2 == 0 || *p2 == '/' )
{
char unit[MAX_PATH];
memcpy(unit, p1, p2-p1);
unit[p2-p1] = 0;
p1 = p2;
if( strcmp(unit, "/.") == 0 )
{
//keep directory not changed
if( *p2 == 0 )
break;
else
continue;
}
else if( strcmp(unit, "/..") == 0 )
{
//come back to parent directory
//look for the last '/', and set it to 0
for(int i=strlen(unixPureFullPath)-1; i>=0 && unixPureFullPath[i]!='/'; i--)NULL;
if( i>=0 )
unixPureFullPath[i] = 0;
}
else //add a subdir to unixPureFullPath
{
strcat(unixPureFullPath, unit);
}
}
if( *p2 == 0 )
break;
}
if( strcmp(unixPureFullPath, "") == 0 )
strcpy(unixPureFullPath, "/");
strcpy(unixFullPath, unixPureFullPath);
return TRUE;
}
BOOL GetParentUnixPath(const char unixFullPath[MAX_PATH], char parentPath[MAX_PATH])
{
if( strcmp(unixFullPath, "/") == 0 )
strcpy(parentPath, "/");
else
{
//look for the last '/'
for(int i=strlen(unixFullPath)-1; i>=0 && unixFullPath[i] != '/'; i--)NULL;
if( i<=0 )
strcpy(parentPath, "/");
else
{
memcpy(parentPath, unixFullPath, i);
parentPath[i] = 0;
}
}
return TRUE;
}
enum FTP_TRANSLATE_TYPE {TYPE_RETR, TYPE_STOR, TYPE_APPE};
struct FTP_CONTROL_BLOCK
{
//input
SOCKET sockCtrl;
SOCKET sockData;
char filename[MAX_PATH];
enum FTP_TRANSLATE_TYPE translateType;
DWORD dwStartPos;
BOOL bDeleteFileAfterCompletion;
BOOL bAbort;
//output
BOOL bThreadExited;
};
DWORD WINAPI FtpDataThreadProc(LPVOID lpParam)
{
char replyStr[FTP_MAX_LINE_BUFFER];
BYTE buffer[FTP_TRANSFER_FILE_BUFFER_LENGTH];
char szError[MAX_SOCKET_ERROR_STRING];
//init
FTP_CONTROL_BLOCK* pBlock = (FTP_CONTROL_BLOCK*)lpParam;
pBlock->bThreadExited = FALSE;
BOOL bSocketError = FALSE;
BOOL bFileError = FALSE;
BOOL bComplete = FALSE;
//send 150 reply string
sprintf(replyStr, "150 openning data connection\r\n");
BlockingSendToSocket(pBlock->sockCtrl, replyStr, strlen(replyStr), &pBlock->bAbort, szError);
//open file to receive or send
FILE* fp = NULL;
if( pBlock->translateType == TYPE_RETR )
{
fp = fopen(pBlock->filename, "rb");
if( fseek(fp, pBlock->dwStartPos, SEEK_SET) != 0 )
{
fclose(fp);
fp = NULL;
}
}
else if( pBlock->translateType == TYPE_STOR )
{
if( ExistFile(pBlock->filename) )
{
fp = fopen(pBlock->filename, "r+b");
if( fseek(fp, pBlock->dwStartPos, SEEK_SET) !=0 ) //seek failed
{
fclose(fp);
fp = NULL;
}
}
else
{
if( pBlock->dwStartPos != 0 ) //cannot write to it from place that isn't begin (0)
bFileError = TRUE;
else
fp = fopen(pBlock->filename, "wb");
}
}
else if( pBlock->translateType == TYPE_APPE )
fp = fopen(pBlock->filename, "ab");
if( fp )
{
DWORD dwCount;
while( !pBlock->bAbort && !bSocketError && !bFileError && !bComplete )
{
if( pBlock->translateType == TYPE_RETR )
{
dwCount = fread(buffer, 1, sizeof(buffer), fp);
if( !BlockingSendToSocket(pBlock->sockData, (char*)buffer, dwCount, &pBlock->bAbort, szError) )
{
if( !pBlock->bAbort )
bSocketError = TRUE;
}
if( dwCount == 0 )
bComplete = TRUE;
}
else
{
FD_SET fds;
timeval timeout = {1,0};
FD_ZERO(&fds);
FD_SET(pBlock->sockData, &fds);
int nReady = select(pBlock->sockData+1, &fds, NULL, NULL, &timeout);
if( nReady == SOCKET_ERROR )
{
debug_printf("FtpDataThreadProc: select %d\n", WSAGetLastError() );
bSocketError = TRUE;
continue;
}
else if( nReady == 0 )
continue;
int nRead = recv(pBlock->sockData, (char*)buffer, sizeof(buffer), 0);
if( nRead < 0 )
{
debug_printf("CFtpServer::Run(): recv %d\n", WSAGetLastError() );
bSocketError = TRUE;
}
else if( nRead == 0 )
{
bComplete = TRUE;
}
else
{
int nWrite = fwrite(buffer, 1, nRead, fp);
if( nWrite != nRead )
bFileError = TRUE;
}
}
}
fclose(fp);
}
else
bFileError = TRUE;
//delete file if neccesary
if( pBlock->bDeleteFileAfterCompletion )
DeleteFile(pBlock->filename);
//create reply code string
if( bComplete )
sprintf(replyStr, "226 transfer complete\r\n");
if( bFileError )
sprintf(replyStr, "450 access to %s failed\r\n", pBlock->filename);
if( bSocketError )
sprintf(replyStr, "420 network error %s\r\n", szError);
if( pBlock->bAbort )
sprintf(replyStr, "226 ABOR command successful\r\n");
//send reply code
BOOL bShouldExit = FALSE;
BlockingSendToSocket(pBlock->sockCtrl, replyStr, strlen(replyStr), &bShouldExit, szError);
debug_printf("%s", replyStr);
//close data socket
if( pBlock->translateType == TYPE_RETR )
{
shutdown(pBlock->sockData, SD_SEND);
//wait for the peer to close
while(recv(pBlock->sockData, (char*)buffer, sizeof(buffer), 0)>0)NULL;
closesocket(pBlock->sockData);
}
else
{
shutdown(pBlock->sockData, SD_SEND);
closesocket(pBlock->sockData);
}
pBlock->sockData = INVALID_SOCKET;
pBlock->dwStartPos = 0;
//set exit flag
pBlock->bThreadExited = TRUE;
return 0;
}
BOOL GetListRootResult(BOOL bNameList, FILE*fp)
{
char rootPath[4] = "A:\\";
char name[2] = "A";
char item[MAX_PATH+100];
for(int i=0; i<26; i++)
{
rootPath[0] = 'A' + i;
if( GetDriveType(rootPath) != DRIVE_NO_ROOT_DIR ) //this driver exists
{
name[0] = 'A' + i;
if( bNameList )
{
sprintf(item, "%s\r\n", name);
fwrite(item, 1, strlen(item), fp);
}
else
{
sprintf(item, "drwxrwxrwx 1 owner group 0 Jul 11 20:40 %s\r\n", name);
fwrite(item, 1, strlen(item), fp);
}
}
}
return TRUE;
}
BOOL GetListResult(const char szListCommand[MAX_PATH], const char unixWorkDir[MAX_PATH],
FILE*fp)
{
if( memicmp(szListCommand, "LIST", 4) != 0 && memicmp(szListCommand, "NLST", 4) != 0 )
return FALSE;
BOOL bNameList = FALSE;
if( memicmp(szListCommand, "NLST", 4) == 0 )
bNameList = TRUE;
//extract out unixFullPath
int i;
char unixFullPath[MAX_PATH];
for(i=4; ' ' == szListCommand[i]; i++)NULL; //skip white space
if( szListCommand[i]==0 ) //no parameter
strcpy(unixFullPath, unixWorkDir);
else //has parameter
GetFullUnixPath(szListCommand+i, unixWorkDir, unixFullPath);
//root dir is treated specially
if( strcmp(unixFullPath, "/") == 0 )
{
return GetListRootResult(bNameList, fp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -