mirror of
https://github.com/Team-Resurgent/PrometheOS-Launcher.git
synced 2026-08-15 11:18:38 -07:00
1308 lines
37 KiB
C++
1308 lines
37 KiB
C++
#include "ftpServer.h"
|
|
#include "socketUtility.h"
|
|
#include "fileSystem.h"
|
|
#include "driveManager.h"
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include "stringUtility.h"
|
|
#include "utils.h"
|
|
|
|
#include <xtl.h>
|
|
|
|
#define SERVERID "PrometheOS FTP, welcome..."
|
|
|
|
namespace {
|
|
|
|
LONG activeConnections = 0;
|
|
|
|
int mMaxConnections;
|
|
int mCommandTimeout;
|
|
int mConnectTimeout;
|
|
|
|
bool mStopRequested;
|
|
|
|
uint64_t sListen;
|
|
sockaddr_in saiListen;
|
|
|
|
HANDLE mListenThreadHandle;
|
|
|
|
int incrementConnections()
|
|
{
|
|
return (int)_InterlockedIncrement(&activeConnections);
|
|
}
|
|
|
|
void decrementConnections()
|
|
{
|
|
_InterlockedDecrement(&activeConnections);
|
|
}
|
|
|
|
char* cleanVirtualPath(const char* virtualPath)
|
|
{
|
|
const char* in = virtualPath;
|
|
char* buf = new char[strlen(in) + 4];
|
|
buf[0] = '\0';
|
|
buf[1] = '\0';
|
|
buf[2] = '\0';
|
|
char* out = buf + 3;
|
|
do {
|
|
*out = *in;
|
|
if (*out == '\\') *out = '/'; // convert backslashes to forward slashes
|
|
if ((*out == '\0') || (*out == '/')) {
|
|
if (out[-1] == '.') { // output ends with "."
|
|
if (out[-2] == '\0') --out; // entire output is "."
|
|
else if (out[-2] == '/') { // output ends with "/."
|
|
if (out[-3] == '\0') --out; // entire output is "/."
|
|
else out -= 2;
|
|
}
|
|
else if (out[-2] == '.') { // output ends with ".."
|
|
if (out[-3] == '\0') out -= 2; // entire output is ".."
|
|
else if (out[-3] == '/') { // output ends with "/.."
|
|
if (out[-4] == '\0') out -= 2; // entire output is "/.."
|
|
else {
|
|
out -= 3;
|
|
while ((out[-1] != '\0') && (out[-1] != '/')) --out;
|
|
}
|
|
}
|
|
}
|
|
else ++in;
|
|
}
|
|
else {
|
|
++in;
|
|
if (out[-1] != '/') ++out;
|
|
}
|
|
}
|
|
else ++in, ++out;
|
|
} while (in[-1] != '\0');
|
|
|
|
char* result = strdup(buf + 3);
|
|
delete[] buf;
|
|
return result;
|
|
}
|
|
|
|
char* resolveRelative(const char* currentVirtual, const char* relativeVirtual)
|
|
{
|
|
if (relativeVirtual[0] != '/') {
|
|
char* newVirtual = stringUtility::formatString("%s\\%s", currentVirtual, relativeVirtual);
|
|
char *result = cleanVirtualPath(newVirtual);
|
|
free(newVirtual);
|
|
return result;
|
|
}
|
|
else {
|
|
char *result = cleanVirtualPath(relativeVirtual);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
pointerVector<fileSystem::FileInfoDetail*>* getDirectoryListing(const char* virtualPath)
|
|
{
|
|
if (stringUtility::equals(virtualPath, "/", false) == true)
|
|
{
|
|
pointerVector<fileSystem::FileInfoDetail*>* fileInfoDetails = new pointerVector<fileSystem::FileInfoDetail*>(true);
|
|
pointerVector<char*>* drives = driveManager::getMountedDrives();
|
|
|
|
for (size_t i = 0; i < drives->count(); i++)
|
|
{
|
|
fileSystem::FileInfoDetail* fileInfoDetail = new fileSystem::FileInfoDetail();
|
|
char* mountPoint = stringUtility::formatString("%s:", drives->get(i));
|
|
char* mountAlias = driveManager::convertMountPointToMountPointAlias(mountPoint);
|
|
mountAlias[strlen(mountAlias)-1] = '\0';
|
|
|
|
fileInfoDetail->isDirectory = true;
|
|
fileInfoDetail->isFile = false;
|
|
fileInfoDetail->isVirtual = true;
|
|
fileInfoDetail->path = mountAlias;
|
|
fileInfoDetail->accessTime.day = 1;
|
|
fileInfoDetail->accessTime.month = 1;
|
|
fileInfoDetail->accessTime.year = 2000;
|
|
fileInfoDetail->accessTime.minute = 0;
|
|
fileInfoDetail->accessTime.hour = 0;
|
|
fileInfoDetail->accessTime.second = 0;
|
|
fileInfoDetail->writeTime.day = 1;
|
|
fileInfoDetail->writeTime.month = 1;
|
|
fileInfoDetail->writeTime.year = 2000;
|
|
fileInfoDetail->writeTime.minute = 0;
|
|
fileInfoDetail->writeTime.hour = 0;
|
|
fileInfoDetail->writeTime.second = 0;
|
|
fileInfoDetails->add(fileInfoDetail);
|
|
|
|
free(mountPoint);
|
|
}
|
|
|
|
delete(drives);
|
|
return fileInfoDetails;
|
|
}
|
|
|
|
if (driveManager::ftpPathMounted(virtualPath) == false)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
char* ftpPath = driveManager::mapFtpPath(virtualPath);
|
|
|
|
pointerVector<fileSystem::FileInfoDetail*>* fileInfoDetails = fileSystem::fileGetFileInfoDetails(ftpPath);;
|
|
free(ftpPath);
|
|
|
|
if (fileInfoDetails == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
for (size_t i = 0; i < fileInfoDetails->count(); i++)
|
|
{
|
|
fileSystem::FileInfoDetail* fileInfoDetail = fileInfoDetails->get(i);
|
|
fileInfoDetail->path = fileSystem::getFileName(fileInfoDetail->path);
|
|
}
|
|
|
|
return fileInfoDetails;
|
|
}
|
|
}
|
|
|
|
bool WINAPI ftpServer::connectionThread(uint64_t sCmd)
|
|
{
|
|
uint64_t sData = 0;
|
|
uint64_t sPasv = 0;
|
|
char szPeerName[64];
|
|
char szOutput[1024];
|
|
char szCmd[512];
|
|
char* pszParam;
|
|
char* user = NULL;
|
|
char* currentVirtual = NULL;
|
|
char* rnfr = NULL;
|
|
uint32_t dw = 0;
|
|
uint32_t dwRestOffset = 0;
|
|
ReceiveStatus status = ReceiveStatus_OK;
|
|
bool isLoggedIn = false;
|
|
fileSystem::FileTime fileTime;
|
|
UINT_PTR i;
|
|
|
|
SOCKADDR_IN saiCmd;
|
|
memset(&saiCmd, 0, sizeof(SOCKADDR_IN));
|
|
SOCKADDR_IN saiCmdPeer;
|
|
memset(&saiCmdPeer, 0, sizeof(SOCKADDR_IN));
|
|
SOCKADDR_IN saiPasv;
|
|
memset(&saiPasv, 0, sizeof(SOCKADDR_IN));
|
|
SOCKADDR_IN saiData;
|
|
memset(&saiData, 0, sizeof(SOCKADDR_IN));
|
|
|
|
// Get peer address
|
|
dw=sizeof(SOCKADDR_IN);
|
|
getpeername((SOCKET)sCmd, (SOCKADDR *)&saiCmdPeer, (int *)&dw);
|
|
sprintf(szPeerName, "%u.%u.%u.%u",
|
|
saiCmdPeer.sin_addr.S_un.S_un_b.s_b1,
|
|
saiCmdPeer.sin_addr.S_un.S_un_b.s_b2,
|
|
saiCmdPeer.sin_addr.S_un.S_un_b.s_b3,
|
|
saiCmdPeer.sin_addr.S_un.S_un_b.s_b4);
|
|
|
|
// Send greeting
|
|
sprintf(szOutput, "220-%s\r\n220-You are connecting from %s:%u.\r\n220 Proceed with login.\r\n", SERVERID, szPeerName, ntohs(saiCmdPeer.sin_port));
|
|
socketSendString(sCmd, szOutput);
|
|
|
|
socketUtility::getSocketName(sCmd, &saiCmd);
|
|
|
|
// Command processing loop
|
|
while (mStopRequested == false) {
|
|
|
|
const int result = socketUtility::getReadStatus(sCmd);
|
|
if (result != 1)
|
|
{
|
|
Sleep(50);
|
|
continue;
|
|
}
|
|
|
|
status = socketReceiveString(sCmd, szCmd, 512, &dw);
|
|
|
|
if (status == ReceiveStatus_Network_Error) {
|
|
socketSendString(sCmd, "421 Network error.\r\n");
|
|
break;
|
|
} else if (status == ReceiveStatus_Timeout) {
|
|
socketSendString(sCmd, "421 Connection timed out.\r\n");
|
|
break;
|
|
} else if (status == ReceiveStatus_Invalid_Data) {
|
|
socketSendString(sCmd, "500 Malformed request.\r\n");
|
|
continue;
|
|
} else if (status == ReceiveStatus_Insufficient_Buffer) {
|
|
socketSendString(sCmd, "500 Command line too long.\r\n");
|
|
continue;
|
|
}
|
|
|
|
if (pszParam = strchr(szCmd, ' ')) {
|
|
*(pszParam++) = 0;
|
|
} else {
|
|
pszParam = szCmd + strlen(szCmd);
|
|
}
|
|
|
|
if (!stricmp(szCmd, "USER")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
continue;
|
|
} else if (isLoggedIn) {
|
|
socketSendString(sCmd, "503 Already logged in. Use REIN to change users.\r\n");
|
|
continue;
|
|
} else {
|
|
free(user);
|
|
user = strdup(pszParam);
|
|
if (stringUtility::equals(user, "xbox", false) == true)
|
|
{
|
|
sprintf(szOutput, "331 Need password for user \"%s\".\r\n", user);
|
|
socketSendString(sCmd, szOutput);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!stricmp(szCmd, "PASS")) {
|
|
if (user == NULL) {
|
|
socketSendString(sCmd, "503 Bad sequence of commands. Send USER first.\r\n");
|
|
} else if (isLoggedIn) {
|
|
socketSendString(sCmd, "503 Already logged in. Use REIN to change users.\r\n");
|
|
} else {
|
|
if (stringUtility::equals(user, "xbox", false) && stringUtility::equals(pszParam, "xbox", false)) {
|
|
if (incrementConnections() <= mMaxConnections) {
|
|
isLoggedIn = true;
|
|
free(currentVirtual);
|
|
currentVirtual = strdup("/");
|
|
sprintf(szOutput, "230 User \"%s\" logged in.\r\n", user);
|
|
socketSendString(sCmd, szOutput);
|
|
} else {
|
|
decrementConnections();
|
|
socketSendString(sCmd, "421 Your login was refused due to a server connection limit.\r\n");
|
|
break;
|
|
}
|
|
} else {
|
|
socketSendString(sCmd, "530 Incorrect password.\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "REIN")) {
|
|
if (isLoggedIn) {
|
|
isLoggedIn = false;
|
|
decrementConnections();
|
|
sprintf(szOutput, "220-User \"%s\" logged out.\r\n", user);
|
|
socketSendString(sCmd, szOutput);
|
|
free(user);
|
|
user = NULL;
|
|
}
|
|
socketSendString(sCmd, "220 REIN command successful.\r\n");
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "HELP")) {
|
|
socketSendString(sCmd, "214 For help, please cry to mom.\r\n");
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "FEAT")) {
|
|
socketSendString(sCmd, "211-Extensions supported:\r\n SIZE\r\n REST STREAM\r\n MDTM\r\n TVFS\r\n XCRC\r\n211 END\r\n");
|
|
}
|
|
|
|
//else if (!stricmp(szCmd, "SYST")) {
|
|
// sprintf(szOutput, "215 Windows_NT\r\n");
|
|
// socketSendString(sCmd, szOutput);
|
|
//}
|
|
|
|
else if (!stricmp(szCmd, "QUIT")) {
|
|
if (isLoggedIn) {
|
|
isLoggedIn = false;
|
|
decrementConnections();
|
|
sprintf(szOutput, "221-User \"%s\" logged out.\r\n", user);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
socketSendString(sCmd, "221 Goodbye!\r\n");
|
|
break;
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "NOOP")) {
|
|
socketSendString(sCmd, "200 NOOP command successful.\r\n");
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "PWD") || !stricmp(szCmd, "XPWD")) {
|
|
if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
sprintf(szOutput, "257 \"%s\" is current directory.\r\n", currentVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "CWD") || !stricmp(szCmd, "XCWD")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
bool isFolder = false;
|
|
if (stringUtility::equals(newVirtual, "/", false))
|
|
{
|
|
isFolder = true;
|
|
}
|
|
else
|
|
{
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
if (stringUtility::endsWith(ftpPath, ":", false))
|
|
{
|
|
isFolder = true;
|
|
}
|
|
else
|
|
{
|
|
fileSystem::directoryExists(ftpPath, isFolder);
|
|
}
|
|
free(ftpPath);
|
|
}
|
|
|
|
if (isFolder == true)
|
|
{
|
|
free(currentVirtual);
|
|
currentVirtual = strdup(newVirtual);
|
|
sprintf(szOutput, "250 \"%s\" is now current directory.\r\n", currentVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 %s failed. \"%s\": directory not found.\r\n", szCmd, newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "CDUP") || !stricmp(szCmd, "XCUP")) {
|
|
if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
|
|
char* tempCurrentVirtual = resolveRelative(currentVirtual, "..");
|
|
free(currentVirtual);
|
|
currentVirtual = tempCurrentVirtual;
|
|
|
|
sprintf(szOutput, "250 \"%s\" is now current directory.\r\n", currentVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "TYPE")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
socketSendString(sCmd, "200 TYPE command successful.\r\n");
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "REST")) {
|
|
if (!*pszParam || (!(dw = stringUtility::toInt(pszParam)) && (*pszParam != '0'))) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
dwRestOffset = dw;
|
|
sprintf(szOutput, "350 Ready to resume transfer at %u bytes.\r\n", dwRestOffset);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "PORT")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
memset(&saiData, 0, sizeof(SOCKADDR_IN));
|
|
saiData.sin_family = AF_INET;
|
|
|
|
unsigned short h1 = 0;
|
|
unsigned short h2 = 0;
|
|
unsigned short h3 = 0;
|
|
unsigned short h4 = 0;
|
|
unsigned short p1 = 0;
|
|
unsigned short p2 = 0;
|
|
|
|
dw = sscanf(pszParam, "%hu,%hu,%hu,%hu,%hu,%hu", &h1, &h2, &h3, &h4, &p1, &p2);
|
|
|
|
saiData.sin_addr.S_un.S_un_b.s_b1 = h1;
|
|
saiData.sin_addr.S_un.S_un_b.s_b2 = h2;
|
|
saiData.sin_addr.S_un.S_un_b.s_b3 = h3;
|
|
saiData.sin_addr.S_un.S_un_b.s_b4 = h4;
|
|
saiData.sin_port = (p2 << 8) + p1;
|
|
|
|
if (dw == 6) {
|
|
socketUtility::closeSocket(sPasv);
|
|
socketSendString(sCmd, "200 PORT command successful.\r\n");
|
|
} else {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
memset(&saiData, 0, sizeof(SOCKADDR_IN));
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "PASV")) {
|
|
if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
|
|
if (sPasv) {
|
|
socketUtility::closeSocket(sPasv);
|
|
}
|
|
|
|
static u_short portTest = 6000;
|
|
|
|
XNADDR addr;
|
|
XNetGetTitleXnAddr(&addr);
|
|
|
|
bool found = false;
|
|
while (!found) {
|
|
memset(&saiPasv, 0, sizeof(SOCKADDR_IN));
|
|
saiPasv.sin_family = AF_INET;
|
|
saiPasv.sin_addr.s_addr = INADDR_ANY;
|
|
saiPasv.sin_port = htons(portTest);
|
|
if (socketUtility::createSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, sPasv))
|
|
{
|
|
found = true;
|
|
}
|
|
else
|
|
{
|
|
utils::debugPrint("Error: Could not create socket on port %u, trying to find next available.\n", portTest);
|
|
}
|
|
portTest++;
|
|
if (portTest > 6999)
|
|
{
|
|
portTest = 6000;
|
|
}
|
|
}
|
|
|
|
socketUtility::bindSocket(sPasv, &saiPasv);
|
|
socketUtility::listenSocket(sPasv, 1);
|
|
socketUtility::getSocketName(sPasv, &saiPasv);
|
|
|
|
sprintf(szOutput, "227 Entering Passive Mode (%u,%u,%u,%u,%u,%u)\r\n",
|
|
addr.ina.S_un.S_un_b.s_b1,
|
|
addr.ina.S_un.S_un_b.s_b2,
|
|
addr.ina.S_un.S_un_b.s_b3,
|
|
addr.ina.S_un.S_un_b.s_b4,
|
|
((unsigned char *)&saiPasv.sin_port)[0],
|
|
((unsigned char *)&saiPasv.sin_port)[1]);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "LIST") || !stricmp(szCmd, "NLST"))
|
|
{
|
|
if (!isLoggedIn)
|
|
{
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
}
|
|
else
|
|
{
|
|
if (*pszParam == '-')
|
|
{
|
|
if (pszParam = strchr(pszParam, ' '))
|
|
{
|
|
pszParam++;
|
|
}
|
|
}
|
|
char* newVirtual = pszParam && *pszParam ? resolveRelative(currentVirtual, pszParam) : strdup(currentVirtual);
|
|
|
|
pointerVector<fileSystem::FileInfoDetail*>* fileInfoDetails = getDirectoryListing(newVirtual);
|
|
if (fileInfoDetails != NULL)
|
|
{
|
|
sData = establishDataConnection(&saiData, &sPasv);
|
|
if (sData)
|
|
{
|
|
sprintf(szOutput, "150 Opened %s mode data connection for \"%s\".\r\n", sPasv ? "passive" : "active", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
const char * months[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
|
|
for (size_t i = 0; i < fileInfoDetails->count(); i++)
|
|
{
|
|
char szLine[512];
|
|
fileSystem::FileInfoDetail* fileInfoDetail = fileInfoDetails->get(i);
|
|
if (stricmp(szCmd, "NLST") == 0)
|
|
{
|
|
strcpy(szLine, fileInfoDetail->path);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szLine, "%c--------- 1 ftp ftp %llu ", fileInfoDetail->isDirectory ? 'd' : '-', fileInfoDetail->size);
|
|
strcat(szLine, months[fileInfoDetail->writeTime.month - 1]);
|
|
sprintf(szLine + strlen(szLine), " %2i %.2i:%.2i ", fileInfoDetail->writeTime.day, fileInfoDetail->writeTime.hour, fileInfoDetail->writeTime.minute);
|
|
strcat(szLine, fileInfoDetail->path);
|
|
}
|
|
//if (fileInfoDetail->fileInfoDetail->isDirectory == true)
|
|
//{
|
|
// strcat(szLine, "/");
|
|
//}
|
|
strcat(szLine, "\r\n");
|
|
socketSendString(sData, szLine);
|
|
}
|
|
socketUtility::closeSocket(sData);
|
|
sprintf(szOutput, "226 %s command successful.\r\n", stricmp(szCmd, "NLST") ? "LIST" : "NLST");
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
socketSendString(sCmd, "425 Can't open data connection.\r\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Path not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(newVirtual);
|
|
delete(fileInfoDetails);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "STAT")) {
|
|
if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
if (*pszParam == '-')
|
|
{
|
|
if (pszParam = strchr(pszParam, ' '))
|
|
{
|
|
pszParam++;
|
|
}
|
|
}
|
|
|
|
char* newVirtual = pszParam && *pszParam ? resolveRelative(currentVirtual, pszParam) : strdup(currentVirtual);
|
|
|
|
pointerVector<fileSystem::FileInfoDetail*>* fileInfoDetails = getDirectoryListing(newVirtual);
|
|
if (fileInfoDetails != NULL)
|
|
{
|
|
sprintf(szOutput, "212-Sending directory listing of \"%s\".\r\n", newVirtual);
|
|
socketSendString(sCmd,szOutput);
|
|
const char * months[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
|
|
for (size_t i = 0; i < fileInfoDetails->count(); i++)
|
|
{
|
|
char szLine[512];
|
|
fileSystem::FileInfoDetail* fileInfoDetail = fileInfoDetails->get(i);
|
|
sprintf(szLine, "%c--------- 1 ftp ftp %llu ", fileInfoDetail->isDirectory ? 'd' : '-', fileInfoDetail->size);
|
|
strcat(szLine, months[fileInfoDetail->writeTime.month - 1]);
|
|
sprintf(szLine + strlen(szLine), " %2i %.2i:%.2i ", fileInfoDetail->writeTime.day, fileInfoDetail->writeTime.hour, fileInfoDetail->writeTime.minute);
|
|
strcat(szLine, fileInfoDetail->path);
|
|
if (fileInfoDetail->isDirectory == true)
|
|
{
|
|
strcat(szLine, "/");
|
|
}
|
|
strcat(szLine, "\r\n");
|
|
socketSendString(sCmd, szLine);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Path not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(newVirtual);
|
|
delete(fileInfoDetails);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "RETR")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
|
|
uint32_t fileHandle;
|
|
if (fileSystem::fileOpen(ftpPath, fileSystem::FileModeRead, fileHandle) == true)
|
|
{
|
|
if (dwRestOffset)
|
|
{
|
|
fileSystem::fileSeek(fileHandle, fileSystem::FileSeekModeStart, dwRestOffset);
|
|
dwRestOffset = 0;
|
|
}
|
|
sData = establishDataConnection(&saiData, &sPasv);
|
|
if (sData)
|
|
{
|
|
sprintf(szOutput, "150 Opened %s mode data connection for \"%s\".\r\n", sPasv ? "passive" : "active", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
if (sendSocketFile(sCmd, sData, fileHandle, &dw))
|
|
{
|
|
sprintf(szOutput, "226 \"%s\" transferred successfully.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
socketSendString(sCmd, "426 Connection closed; transfer aborted.\r\n");
|
|
if (dw)
|
|
{
|
|
socketSendString(sCmd, "226 ABOR command successful.\r\n");
|
|
}
|
|
}
|
|
Sleep(200);
|
|
socketUtility::closeSocket(sData);
|
|
}
|
|
|
|
else
|
|
{
|
|
socketSendString(sCmd, "425 Can't open data connection.\r\n");
|
|
}
|
|
fileSystem::fileClose(fileHandle);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Unable to open file.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "STOR") || !stricmp(szCmd, "APPE")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char *newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
|
|
uint32_t fileHandle;
|
|
if (fileSystem::fileOpen(ftpPath, fileSystem::FileModeWriteUpdate, fileHandle) == true)
|
|
{
|
|
if (stricmp(szCmd, "APPE") == 0)
|
|
{
|
|
fileSystem::fileSeek(fileHandle, fileSystem::FileSeekModeEnd, 0);
|
|
}
|
|
else
|
|
{
|
|
fileSystem::fileSeek(fileHandle, fileSystem::FileSeekModeStart, dwRestOffset);
|
|
fileSystem::fileTruncate(fileHandle, dwRestOffset);
|
|
}
|
|
dwRestOffset = 0;
|
|
sData = establishDataConnection(&saiData, &sPasv);
|
|
if (sData)
|
|
{
|
|
sprintf(szOutput, "150 Opened %s mode data connection for \"%s\".\r\n", sPasv ? "passive" : "active", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
if (receiveSocketFile(sCmd, sData, fileHandle))
|
|
{
|
|
sprintf(szOutput, "226 \"%s\" transferred successfully.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
socketSendString(sCmd, "426 Connection closed; transfer aborted.\r\n");
|
|
}
|
|
Sleep(200);
|
|
socketUtility::closeSocket(sData);
|
|
} else {
|
|
socketSendString(sCmd, "425 Can't open data connection.\r\n");
|
|
}
|
|
fileSystem::fileClose(fileHandle);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Unable to open file.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(newVirtual);
|
|
free(ftpPath);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "ABOR")) {
|
|
socketUtility::closeSocket(sPasv);
|
|
dwRestOffset = 0;
|
|
socketSendString(sCmd, "200 ABOR command successful.\r\n");
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "SIZE")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
|
|
uint32_t fileHandle;
|
|
if (fileSystem::fileOpen(ftpPath, fileSystem::FileModeRead, fileHandle) == true)
|
|
{
|
|
uint32_t fileSize = 0;
|
|
fileSystem::fileSize(fileHandle, fileSize);
|
|
sprintf(szOutput, "213 %llu\r\n", fileSize);
|
|
socketSendString(sCmd, szOutput);
|
|
fileSystem::fileClose(fileHandle);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": File not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(newVirtual);
|
|
free(ftpPath);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "MDTM")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
for (i = 0; i < 14; i++) {
|
|
if ((pszParam[i] < '0') || (pszParam[i] > '9')) {
|
|
break;
|
|
}
|
|
}
|
|
if ((i == 14) && (pszParam[14] == ' ')) {
|
|
strncpy(szOutput, pszParam, 4);
|
|
szOutput[4] = 0;
|
|
fileTime.year = (WORD)stringUtility::toInt(szOutput);
|
|
strncpy(szOutput, pszParam + 4, 2);
|
|
szOutput[2] = 0;
|
|
fileTime.month = (WORD)stringUtility::toInt(szOutput);
|
|
strncpy(szOutput, pszParam + 6, 2);
|
|
fileTime.day = (WORD)stringUtility::toInt(szOutput);
|
|
strncpy(szOutput, pszParam + 8, 2);
|
|
fileTime.hour = (WORD)stringUtility::toInt(szOutput);
|
|
strncpy(szOutput, pszParam + 10, 2);
|
|
fileTime.minute = (WORD)stringUtility::toInt(szOutput);
|
|
strncpy(szOutput, pszParam + 12, 2);
|
|
fileTime.second = (WORD)stringUtility::toInt(szOutput);
|
|
pszParam += 15;
|
|
dw = 1;
|
|
} else {
|
|
dw = 0;
|
|
}
|
|
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
if (dw == 1) {
|
|
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
if (fileSystem::setFileTime(ftpPath, fileTime) == true)
|
|
{
|
|
socketSendString(sCmd, "250 MDTM command successful.\r\n");
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": File not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(ftpPath);
|
|
|
|
} else {
|
|
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
fileSystem::FileInfoDetail* fileInfoDetail = fileSystem::fileGetFileInfoDetail(ftpPath);
|
|
if (fileInfoDetail != NULL)
|
|
{
|
|
sprintf(szOutput, "213 %04u%02u%02u%02u%02u%02u\r\n", fileInfoDetail->writeTime.year, fileInfoDetail->writeTime.month, fileInfoDetail->writeTime.day, fileInfoDetail->writeTime.hour, fileInfoDetail->writeTime.minute, fileInfoDetail->writeTime.second);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": File not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
delete(fileInfoDetail);
|
|
free(ftpPath);
|
|
}
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "DELE")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
if (fileSystem::fileDelete(ftpPath) == true)
|
|
{
|
|
sprintf(szOutput, "250 \"%s\" deleted successfully.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": File not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "RNFR")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
bool exists;
|
|
if ((fileSystem::fileExists(ftpPath, exists) == true && exists == true) || (fileSystem::directoryExists(ftpPath, exists) == true && exists == true))
|
|
{
|
|
free(rnfr);
|
|
rnfr = strdup(ftpPath);
|
|
sprintf(szOutput, "350 \"%s\": File/Directory exists; proceed with RNTO.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
} else {
|
|
sprintf(szOutput, "550 \"%s\": File/Directory not found.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "RNTO")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else if (rnfr == NULL) {
|
|
socketSendString(sCmd, "503 Bad sequence of commands. Send RNFR first.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
|
|
if (fileSystem::fileMove(rnfr, ftpPath) == true)
|
|
{
|
|
socketSendString(sCmd, "250 RNTO command successful.\r\n");
|
|
free(rnfr);
|
|
rnfr = NULL;
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "553 \"%s\": Unable to rename file.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(newVirtual);
|
|
free(ftpPath);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "MKD") || !stricmp(szCmd, "XMKD")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
if (fileSystem::directoryCreate(ftpPath) == true)
|
|
{
|
|
sprintf(szOutput, "250 \"%s\" created successfully.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Unable to create directory.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "RMD") || !stricmp(szCmd, "XRMD")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!isLoggedIn) {
|
|
socketSendString(sCmd, "530 Not logged in.\r\n");
|
|
} else {
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
if (fileSystem::directoryDelete(ftpPath, true) == true)
|
|
{
|
|
sprintf(szOutput, "250 \"%s\" removed successfully.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": Unable to remove directory.\r\n", newVirtual);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "OPTS")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "501 Syntax error in parameters or arguments.\r\n");
|
|
} else if (!stricmp(pszParam, "UTF8 On")) {
|
|
socketSendString(sCmd, "200 Always in UTF8 mode.\r\n");
|
|
} else {
|
|
socketSendString(sCmd, "501 Option not understood.\r\n");
|
|
}
|
|
}
|
|
|
|
else if (!stricmp(szCmd, "AVBL")) {
|
|
if (!*pszParam) {
|
|
socketSendString(sCmd, "550 Syntax error in parameters or arguments.\r\n");
|
|
} else {
|
|
|
|
char* newVirtual = resolveRelative(currentVirtual, pszParam);
|
|
char* ftpPath = driveManager::mapFtpPath(newVirtual);
|
|
|
|
uint64_t totalFree;
|
|
if (driveManager::getTotalFreeNumberOfBytes(ftpPath, totalFree) == true)
|
|
{
|
|
sprintf(szOutput, "213 %llu\r\n", totalFree);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
else
|
|
{
|
|
sprintf(szOutput, "550 \"%s\": not found.\r\n", pszParam);
|
|
socketSendString(sCmd, szOutput);
|
|
}
|
|
|
|
free(ftpPath);
|
|
free(newVirtual);
|
|
}
|
|
}
|
|
|
|
else {
|
|
sprintf(szOutput, "500 Syntax error, command \"%s\" unrecognized.\r\n",szCmd);
|
|
socketSendString(sCmd,szOutput);
|
|
}
|
|
|
|
}
|
|
|
|
socketUtility::closeSocket(sPasv);
|
|
socketUtility::closeSocket(sCmd);
|
|
|
|
if (isLoggedIn) {
|
|
decrementConnections();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WINAPI ftpServer::listenThread(LPVOID lParam)
|
|
{
|
|
uint64_t sIncoming = 0;
|
|
|
|
while (mStopRequested == false)
|
|
{
|
|
const int result = socketUtility::getReadStatus(sListen);
|
|
if (result == SOCKET_ERROR)
|
|
{
|
|
utils::debugPrint("Error: Socket status failed: %i\n", WSAGetLastError());
|
|
break;
|
|
}
|
|
|
|
if (result == 1 && socketUtility::acceptSocket(sListen, sIncoming))
|
|
{
|
|
HANDLE connectionThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)connectionThread, (void*)sIncoming, 0, NULL);
|
|
if (connectionThreadHandle != NULL) {
|
|
SetThreadPriority(connectionThreadHandle, 2);
|
|
CloseHandle(connectionThreadHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Sleep(50);
|
|
}
|
|
}
|
|
|
|
socketUtility::closeSocket(sListen);
|
|
socketUtility::closeSocket(sIncoming);
|
|
return false;
|
|
}
|
|
|
|
bool ftpServer::init()
|
|
{
|
|
mStopRequested = false;
|
|
|
|
mListenThreadHandle = NULL;
|
|
|
|
XSetFileCacheSize(4*1024*1024);
|
|
|
|
memset(&saiListen, 0, sizeof(SOCKADDR_IN));
|
|
saiListen.sin_family = AF_INET;
|
|
saiListen.sin_addr.S_un.S_addr = INADDR_ANY;
|
|
saiListen.sin_port = htons(21);
|
|
|
|
mMaxConnections = 20;
|
|
mCommandTimeout = 300;
|
|
mConnectTimeout = 60;
|
|
|
|
socketUtility::createSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, sListen);
|
|
|
|
if (!socketUtility::bindSocket(sListen, &saiListen)) {
|
|
socketUtility::closeSocket(sListen);
|
|
return false;
|
|
}
|
|
|
|
socketUtility::listenSocket(sListen, SOMAXCONN);
|
|
|
|
mListenThreadHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)listenThread, 0, 0, NULL);
|
|
if (mListenThreadHandle != NULL) {
|
|
SetThreadPriority(mListenThreadHandle, 2);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ftpServer::close()
|
|
{
|
|
mStopRequested = true;
|
|
WaitForSingleObject(mListenThreadHandle, INFINITE);
|
|
CloseHandle(mListenThreadHandle);
|
|
}
|
|
|
|
bool ftpServer::socketSendString(uint64_t s, const char *psz)
|
|
{
|
|
bool bSuccess = send((SOCKET)s, psz, strlen(psz), 0) != SOCKET_ERROR;
|
|
return bSuccess;
|
|
}
|
|
|
|
ftpServer::ReceiveStatus ftpServer::socketReceiveString(uint64_t s, char *psz, uint32_t dwMaxChars, uint32_t* pdwCharsReceived)
|
|
{
|
|
uint32_t dwChars = 0;
|
|
ReceiveStatus status, statusError;
|
|
char buf[2];
|
|
uint32_t dw;
|
|
|
|
for (;;) {
|
|
if (dwChars == dwMaxChars) {
|
|
statusError = ReceiveStatus_Insufficient_Buffer;
|
|
break;
|
|
}
|
|
|
|
status = socketReceiveLetter(s, psz, dwMaxChars - dwChars, &dw);
|
|
if (status == ReceiveStatus_OK) {
|
|
dwChars += dw;
|
|
if (*psz == '\r') *psz = 0;
|
|
else if (*psz == '\n') {
|
|
*psz = 0;
|
|
*pdwCharsReceived = dwChars;
|
|
return ReceiveStatus_OK;
|
|
}
|
|
psz += dw;
|
|
}
|
|
else if (status == ReceiveStatus_Invalid_Data || status == ReceiveStatus_Insufficient_Buffer) {
|
|
statusError = status;
|
|
break;
|
|
}
|
|
else {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// A non-critical error occurred, read until end of line
|
|
for (;;) {
|
|
status = socketReceiveLetter(s, buf, sizeof(buf) / sizeof(char), &dw);
|
|
if (status == ReceiveStatus_OK) {
|
|
if (*buf == '\n') {
|
|
return statusError;
|
|
}
|
|
}
|
|
else if (status == ReceiveStatus_Invalid_Data || status == ReceiveStatus_Insufficient_Buffer) {
|
|
// Go on...
|
|
}
|
|
else {
|
|
return status;
|
|
}
|
|
}
|
|
}
|
|
|
|
ftpServer::ReceiveStatus ftpServer::socketReceiveLetter(uint64_t s, char* pch, uint32_t dwMaxChars, uint32_t* pdwCharsReceived)
|
|
{
|
|
char buf[4];
|
|
uint32_t dw;
|
|
TIMEVAL tv;
|
|
fd_set fds;
|
|
|
|
tv.tv_sec = mCommandTimeout;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO(&fds);
|
|
FD_SET((SOCKET)s, &fds);
|
|
dw = select(0, &fds, 0, 0, &tv);
|
|
if (dw == SOCKET_ERROR || dw == 0) return ReceiveStatus_Timeout;
|
|
dw = recv((SOCKET)s, &buf[0], 1, 0);
|
|
if (dw == SOCKET_ERROR || dw == 0) return ReceiveStatus_Network_Error;
|
|
|
|
if (dwMaxChars == 0) {
|
|
return ReceiveStatus_Insufficient_Buffer;
|
|
}
|
|
|
|
pch[0] = buf[0];
|
|
|
|
*pdwCharsReceived = 1;
|
|
return ReceiveStatus_OK;
|
|
}
|
|
|
|
ftpServer::ReceiveStatus ftpServer::socketReceiveData(uint64_t s, char *psz, uint32_t dwBytesToRead, uint32_t* pdwBytesRead)
|
|
{
|
|
uint32_t dw;
|
|
TIMEVAL tv;
|
|
fd_set fds;
|
|
|
|
tv.tv_sec = mConnectTimeout;
|
|
tv.tv_usec = 0;
|
|
memset(&fds, 0, sizeof(fd_set));
|
|
FD_SET((SOCKET)s, &fds);
|
|
dw = select(0, &fds, 0, 0, &tv);
|
|
if (dw == SOCKET_ERROR || dw == 0) {
|
|
utils::debugPrint("Error: SocketReceiveData failed with Timeout.\n");
|
|
return ReceiveStatus_Timeout;
|
|
}
|
|
dw = recv((SOCKET)s, psz, dwBytesToRead, 0);
|
|
if (dw == SOCKET_ERROR) {
|
|
utils::debugPrint("Error: SocketReceiveData failed with Network Error.\n");
|
|
return ReceiveStatus_Network_Error;
|
|
}
|
|
*pdwBytesRead = dw;
|
|
return ReceiveStatus_OK;
|
|
}
|
|
|
|
uint64_t ftpServer::establishDataConnection(sockaddr_in *psaiData, uint64_t*psPasv)
|
|
{
|
|
uint64_t sData;
|
|
uint32_t dw;
|
|
TIMEVAL tv;
|
|
fd_set fds;
|
|
|
|
if (psPasv && *psPasv) {
|
|
tv.tv_sec = mConnectTimeout;
|
|
tv.tv_usec = 0;
|
|
memset(&fds, 0, sizeof(fd_set));
|
|
FD_SET((SOCKET)*psPasv,&fds);
|
|
dw = select(0,&fds,0,0,&tv);
|
|
if (dw && dw != SOCKET_ERROR) {
|
|
socketUtility::acceptSocket(*psPasv, psaiData, sData);
|
|
} else {
|
|
sData=0;
|
|
}
|
|
socketUtility::closeSocket(*psPasv);
|
|
return sData;
|
|
} else {
|
|
|
|
socketUtility::createSocket(AF_INET,SOCK_STREAM, IPPROTO_TCP, sData);
|
|
if (!socketUtility::connectSocket(sData, psaiData)) {
|
|
socketUtility::closeSocket(sData);
|
|
return INVALID_SOCKET;
|
|
} else {
|
|
return sData;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ftpServer::receiveSocketFile(uint64_t sCmd, uint64_t sData, uint32_t fileHandle)
|
|
{
|
|
uint32_t socketBufferSize = RECV_SOCKET_BUFFER_SIZE;
|
|
socketUtility::setSocketRecvSize(sData, socketBufferSize);
|
|
|
|
int driveSectorBufferSize = 128 * 1024;
|
|
int combinedBufferSize = socketBufferSize + driveSectorBufferSize;
|
|
char *combinedBuffer = (char*)malloc(combinedBufferSize);
|
|
|
|
bool fileComplete = false;
|
|
|
|
uint32_t totalBytesToWrite = 0;
|
|
uint32_t totalWritten = 0;
|
|
|
|
while (true)
|
|
{
|
|
|
|
while (!fileComplete && totalBytesToWrite < (uint32_t)driveSectorBufferSize)
|
|
{
|
|
uint32_t bytesRead = 0;
|
|
if (socketReceiveData(sData, combinedBuffer + totalBytesToWrite, socketBufferSize, &bytesRead) != ReceiveStatus_OK)
|
|
{
|
|
free(combinedBuffer);
|
|
return false;
|
|
}
|
|
totalBytesToWrite += bytesRead;
|
|
if (bytesRead == 0) {
|
|
fileComplete = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fileComplete && totalBytesToWrite == 0)
|
|
{
|
|
free(combinedBuffer);
|
|
return true;
|
|
}
|
|
|
|
uint32_t bytesWritten = 0;
|
|
uint32_t bytesToWrite = min((uint32_t)driveSectorBufferSize, totalBytesToWrite);
|
|
if (!fileSystem::fileWrite(fileHandle, combinedBuffer, bytesToWrite, bytesWritten))
|
|
{
|
|
utils::debugPrint("Error: WriteFile failed: %i", GetLastError());
|
|
free(combinedBuffer);
|
|
return false;
|
|
}
|
|
|
|
totalBytesToWrite -= bytesWritten;
|
|
totalWritten += bytesWritten;
|
|
memmove(combinedBuffer, combinedBuffer + bytesWritten, totalBytesToWrite);
|
|
|
|
}
|
|
}
|
|
|
|
bool ftpServer::sendSocketFile(uint64_t sCmd, uint64_t sData, uint32_t fileHandle, uint32_t* pdwAbortFlag)
|
|
{
|
|
uint32_t bufferSize = SEND_SOCKET_BUFFER_SIZE;
|
|
socketUtility::setSocketSendSize(sData, bufferSize);
|
|
char *szBuffer = (char*)malloc(bufferSize);
|
|
|
|
while (true)
|
|
{
|
|
uint32_t bytesRead;
|
|
if (fileSystem::fileRead(fileHandle, szBuffer, bufferSize, bytesRead) == false)
|
|
{
|
|
utils::debugPrint("Error: ReadFile failed: %i", GetLastError());
|
|
free(szBuffer);
|
|
return false;
|
|
}
|
|
|
|
if (bytesRead == 0)
|
|
{
|
|
free(szBuffer);
|
|
return true;
|
|
}
|
|
|
|
int bytesToSend = bytesRead;
|
|
int bufferOffset = 0;
|
|
|
|
while (bytesToSend > 0)
|
|
{
|
|
int sent = send((SOCKET)sData, szBuffer + bufferOffset, bytesToSend, 0);
|
|
int queueLength = 0;
|
|
socketUtility::getReadQueueLength(sCmd, queueLength);
|
|
if (queueLength > 0) {
|
|
char szCmd[512];
|
|
uint32_t bytes_received = 0;
|
|
if (socketReceiveString(sCmd, szCmd, 512, &bytes_received) == ReceiveStatus_OK) {
|
|
if (!stricmp(szCmd, "ABOR")) {
|
|
*pdwAbortFlag = 1;
|
|
free(szBuffer);
|
|
return false;
|
|
}
|
|
else {
|
|
socketSendString(sCmd, "500 Only command allowed at this time is ABOR.\r\n");
|
|
}
|
|
}
|
|
}
|
|
bytesToSend -= sent;
|
|
bufferOffset += (int)sent;
|
|
if (sent < 1)
|
|
{
|
|
free(szBuffer);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|