MegaGlest/source/shared_lib/sources/feathery_ftp/ftpCmds.c
SoftCoder 3111b733e2 - bugfix for chrono class calcs causing group camera recall to fail (And numerous other problems)
- added better tracking of mutexes by assigning them location info
2013-12-24 22:27:44 -08:00

1070 lines
30 KiB
C

/* FEATHERY FTP-Server
* Copyright (C) 2005-2010 Andreas Martin (andreas.martin@linuxmail.org)
* <https://sourceforge.net/projects/feathery>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
DECLARATIVE SPECIFICATIONS
5.1. MINIMUM IMPLEMENTATION
In order to make FTP workable without needless error messages, the
following minimum implementation is required for all servers:
TYPE - ASCII Non-print
MODE - Stream
STRUCTURE - File, Record
COMMANDS - USER, QUIT, PORT,
TYPE, MODE, STRU,
for the default values
RETR, STOR,
NOOP.
The default values for transfer parameters are:
TYPE - ASCII Non-print
MODE - Stream
STRU - File
All hosts must accept the above as the standard defaults.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "ftpTypes.h"
#include "ftpConfig.h"
#include "ftp.h"
#include "ftpIfc.h"
#include "ftpMessages.h"
LOCAL uint8_t scratchBuf[LEN_SCRATCHBUF];
int ftpSendMsg(msgmode_E mode, int sessionId, int ret, const char* msg)
{
int sentlen = 0;
int len = (int)strlen(msg);
char buf[6];
if(mode == MSG_QUOTE)
{
snprintf((char*)buf, 6,"%03d \"", ret);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, buf, 5);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, msg, len);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, "\"\r\n", 3);
}
else
{
snprintf((char*)buf, 6,"%03d ", ret);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, buf, 4);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, msg, len);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, "\r\n", 2);
}
if(VERBOSE_MODE_ENABLED) printf("%02d <-- %s%s\n", sessionId, buf, msg);
return sentlen;
}
int ftpExecTransmission(int sessionId)
{
int finished = FALSE;
size_t len;
ftpSession_S *pSession = ftpGetSession(sessionId);
transmission_S *pTrans = &pSession->activeTrans;
int rxLen;
pSession->timeLastCmd = ftpGetUnixTime();
switch(pTrans->op)
{
case OP_RETR:
len = ftpReadFile(scratchBuf, 1, LEN_SCRATCHBUF, pTrans->fsHandle);
if(len > 0)
{
pTrans->fileSize -= (uint32_t)len;
if(ftpSend(pTrans->dataSocket, scratchBuf, (int)len))
{
ftpSendMsg(MSG_NORMAL, sessionId, 426, ftpMsg000);
finished = TRUE;
}
}
else
{
if(VERBOSE_MODE_ENABLED) printf("ERROR in ftpExecTransmission ftpReadFile returned = %d for sessionId = %d\n",(int)len,sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg001);
finished = TRUE;
}
if(pTrans->fileSize == 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg002);
finished = TRUE;
}
break;
case OP_STOR:
rxLen = 0;
do
{
len = ftpReceive(pTrans->dataSocket, &scratchBuf[rxLen], LEN_SCRATCHBUF - rxLen);
if(len < 1) {
int errorNumber = getLastSocketError();
const char *errText = getLastSocketErrorText(&errorNumber);
if(VERBOSE_MODE_ENABLED) printf("ftpExecTransmission ERROR ON RECEIVE for socket = %d, data len = %d, error = %d [%s]\n",pTrans->dataSocket,(LEN_SCRATCHBUF - rxLen),errorNumber,errText);
break;
}
rxLen += (int)len;
} while(rxLen < LEN_SCRATCHBUF);
if(rxLen > 0)
{
size_t res = ftpWriteFile(scratchBuf, 1, rxLen, pTrans->fsHandle);
if(res != (size_t)rxLen)
{
if(VERBOSE_MODE_ENABLED) printf("ERROR in ftpExecTransmission ftpWriteFile returned = %d for sessionId = %d\n",(int)res,sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg001);
finished = TRUE;
}
}
if(len < 1)
{
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg003);
finished = TRUE;
}
break;
case OP_LIST:
break;
default:
return -1;
}
if(finished)
{
ftpCloseTransmission(sessionId);
return 1;
}
else
{
return 0;
}
}
LOCAL int ftpCmdUser(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 331, ftpMsg004);
ftpGetSession(sessionId)->userId = ftpFindAccount(args);
return 0;
}
LOCAL int ftpCmdPass(int sessionId, const char* args, int len)
{
if(!ftpCheckPassword(ftpGetSession(sessionId)->userId, args))
{
ftpAuthSession(sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 230, ftpMsg005);
}
else
{
ftpGetSession(sessionId)->userId = 0;
ftpSendMsg(MSG_NORMAL, sessionId, 530, ftpMsg006);
}
return 0;
}
LOCAL int ftpCmdSyst(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 215, "UNIX Type: L8");
return 0;
}
LOCAL int ftpCmdPort(int sessionId, const char* args, int len)
{
//char clientIp[16]="";
uint16_t clientPort=0;
int commaCnt = 0;
int n;
char* p;
for(n = 0; args[n] != '\0'; n++)
{
if(commaCnt <= 3) // Ip-Adresse
{
if(args[n] == ',')
{
commaCnt++;
// if(commaCnt < 4)
// clientIp[n] = '.';
// else
// clientIp[n] = '\0';
}
// else
// clientIp[n] = args[n];
}
else // Port-Nummer
{
p = (char*)&args[n];
clientPort = (uint16_t)(strtoul(p, &p, 0) << 8);
p++;
clientPort |= strtoul(p, &p, 0);
break;
}
}
if(ftpGetSession(sessionId)->passiveDataSocket >= 0)
{
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPort about to Close socket = %d for sessionId = %d\n",ftpGetSession(sessionId)->passiveDataSocket,sessionId);
ftpUntrackSocket(ftpGetSession(sessionId)->passiveDataSocket);
ftpCloseSocket(&ftpGetSession(sessionId)->passiveDataSocket);
ftpGetSession(sessionId)->passiveDataSocket = -1;
}
//ftpGetSession(sessionId)->passiveDataSocket = -1;
ftpGetSession(sessionId)->remoteDataPort = clientPort;
ftpGetSession(sessionId)->passive = FALSE;
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg007);
return 0;
}
LOCAL int ftpCmdNoop(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg008);
return 0;
}
LOCAL int ftpCmdQuit(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 221, ftpMsg009);
return -1;
}
LOCAL int ftpCmdAbor(int sessionId, const char* args, int len)
{
ftpCloseTransmission(sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg040);
return 0;
}
#define ALL 0x80
#define LIST 1
#define NLST 2
#define MLST 4
#define MLSD 8
LOCAL int sendListing(socket_t dataSocket, int sessionId, const char* path, int format)
{
void *dir;
const char monName[12][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
dir = ftpOpenDir(path);
if(dir)
{
const char* dirEntry = NULL;
int len = 0;
int err = 0;
ftpTime_S currTime = {0};
ftpPathInfo_S fileInfo;
int haveAnySuccessfulFiles = 0;
ftpGetLocalTime(&currTime);
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg010);
if(VERBOSE_MODE_ENABLED) printf("In sendListing about to read dir contents [%s] for sessionId = %d, dataSocket = %d\n", path,sessionId,dataSocket);
haveAnySuccessfulFiles = 0;
while((dirEntry = ftpReadDir(dir)) != NULL)
{
const char * realPath = ftpGetRealPath(sessionId, dirEntry, FALSE);
int statResult = ftpStat(realPath, &fileInfo);
if(VERBOSE_MODE_ENABLED) printf("ftpGetRealPath() returned [%s] stat() = %d\n", realPath, statResult);
if(statResult == 0)
{
if((format & ALL) == 0)
{
if(dirEntry[0] == '.')
continue;
}
if(format & LIST)
{
switch(fileInfo.type)
{
default:
case TYPE_FILE:
scratchBuf[0] = '-';
break;
case TYPE_DIR:
scratchBuf[0] = 'd';
break;
case TYPE_LINK:
scratchBuf[0] = 'l';
break;
}
if(currTime.year == fileInfo.mTime.year)
{
len = snprintf((char*)&scratchBuf[1], LEN_SCRATCHBUF-1,"rwxrwxrwx %4u %-8s %-8s %8u %s %02d %02d:%02d %s\r\n",
fileInfo.links,
fileInfo.user,
fileInfo.group,
fileInfo.size,
monName[fileInfo.mTime.month - 1],
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
dirEntry);
}
else
{
len = snprintf((char*)&scratchBuf[1], LEN_SCRATCHBUF-1,"rwxrwxrwx %4u %-8s %-8s %8u %s %02d %5d %s\r\n",
fileInfo.links,
fileInfo.user,
fileInfo.group,
fileInfo.size,
monName[fileInfo.mTime.month - 1],
fileInfo.mTime.day,
fileInfo.mTime.year,
dirEntry);
}
ftpSend(dataSocket, scratchBuf, len + 1);
haveAnySuccessfulFiles = 1;
}
else if(format & NLST)
{
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,"%s\r\n", dirEntry);
ftpSend(dataSocket, scratchBuf, len);
haveAnySuccessfulFiles = 1;
}
else if(format & MLSD)
{
if(!strcmp("..", dirEntry))
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,"Type=pdir");
else
{
switch(fileInfo.type)
{
default:
case TYPE_FILE:
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,"Type=file");
break;
case TYPE_DIR:
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,"Type=dir");
break;
case TYPE_LINK:
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,"Type=OS.unix=slink");
break;
}
}
ftpSend(dataSocket, scratchBuf, len);
len = snprintf((char*)scratchBuf, LEN_SCRATCHBUF,";Size=%u;Modify=%04d%02d%02d%02d%02d%02d;Perm=r; %s\r\n",
fileInfo.size,
fileInfo.mTime.year,
fileInfo.mTime.month,
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
fileInfo.mTime.second,
dirEntry);
ftpSend(dataSocket, scratchBuf, len);
haveAnySuccessfulFiles = 1;
}
}
else
{
err = 1;
//break;
}
}
ftpCloseDir(dir);
if(err && haveAnySuccessfulFiles == 0)
{
if(VERBOSE_MODE_ENABLED) printf("ERROR in sendListing err = %d, path = [%s] for sessionId = %d, dataSocket = %d\n",err,path,sessionId,dataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg039);
}
else
{
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg013);
}
}
else
{
if(VERBOSE_MODE_ENABLED) printf("ERROR opendir [%s] returned errno: %#x for sessionId = %d, dataSocket = %d\n", path,errno,sessionId,dataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg038);
}
return 0;
}
LOCAL int ftpCmdList(int sessionId, const char* args, int len)
{
const char* realPath;
socket_t s;
//#### Funktioniert nicht wenn Pfad übergeben wird
/* if(args[0] != '\0')
{
if(args[0] != '-')
realPath = ftpGetRealPath(sessionId, args);
}
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if(ftpGetSession(sessionId)->passive == FALSE)
{
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
}
else
{
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, LIST);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdList about to Close socket = %d for sessionId = %d\n",s,sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
LOCAL int ftpCmdNlst(int sessionId, const char* args, int len)
{
//#### -List kann auch Argumente haben
const char* realPath;
socket_t s;
/* if(pfad übergeben)
realPath = ftpGetRealPath(sessionId, args);
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if(ftpGetSession(sessionId)->passive == FALSE)
{
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
}
else
{
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, NLST);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdNlst about to Close socket = %d for sessionId = %d\n",s,sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
LOCAL int ftpCmdRetr(int sessionId, const char* args, int len)
{
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
socket_t s;
void *fp;
int statResult = 0;
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr args [%s] realPath [%s]\n", args, realPath);
statResult = ftpStat(realPath, &fileInfo);
if(VERBOSE_MODE_ENABLED) printf("stat() = %d fileInfo.type = %d\n", statResult,fileInfo.type);
if(statResult || (fileInfo.type != TYPE_FILE)) // file accessible?
{
if(VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr [file not available] args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
if(ftpIsClientAllowedToGetFile != NULL) {
if(ftpIsClientAllowedToGetFile(ftpGetSession(sessionId)->remoteIp,ftpFindAccountById(ftpGetSession(sessionId)->userId),realPath) != 1) {
if(VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr FILE DISALLOWED By MGserver [file not available] args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
}
if(ftpGetSession(sessionId)->passive == FALSE)
{
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
}
else
{
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr about accept passive data connection, args [%s] realPath [%s]\n", args, realPath);
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if(s < 0)
{
if(VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr failed to accept data connection, args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg014);
fp = ftpOpenFile(realPath, "rb");
if(fp)
{
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr opened realPath [%s] [%p] for sessionId = %d for socket = %d\n",realPath,fp,sessionId,s);
ftpOpenTransmission(sessionId, OP_RETR, fp, s, fileInfo.size);
ftpExecTransmission(sessionId);
}
else
{
if(VERBOSE_MODE_ENABLED) printf("ERROR in ftpCmdRetr could not open realPath [%s] for sessionId = %d for socket = %d\n",realPath,sessionId,s);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg015);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr about to Close socket = %d for sessionId = %d\n",s,sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
}
return 0;
}
LOCAL int ftpCmdStor(int sessionId, const char* args, int len)
{
socket_t s;
void* fp;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if(ftpGetSession(sessionId)->passive == FALSE)
{
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
}
else
{
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg016);
fp = ftpOpenFile(realPath, "wb");
if(fp)
{
ftpOpenTransmission(sessionId, OP_STOR, fp, s, 0);
ftpExecTransmission(sessionId);
}
else
{
if(VERBOSE_MODE_ENABLED) printf("ERROR in ftpCmdStor could not open realPath [%s]\n",realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg015);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdStor about to Close socket = %d for sessionId = %d\n",s,sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
}
return 0;
}
LOCAL int ftpCmdDele(int sessionId, const char* args, int len)
{
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if(ftpRemoveFile(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 450, ftpMsg018);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg019);
return 0;
}
LOCAL int ftpCmdMkd(int sessionId, const char* args, int len)
{
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if(ftpMakeDir(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg020);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg021);
return 0;
}
LOCAL int ftpCmdRmd(int sessionId, const char* args, int len)
{
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if(ftpRemoveDir(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg022);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg023);
return 0;
}
LOCAL int ftpCmdPwd(int sessionId, const char* args, int len)
{
if(ftpGetSession(sessionId)->workingDir[0])
ftpSendMsg(MSG_QUOTE, sessionId, 257, ftpGetSession(sessionId)->workingDir);
else
ftpSendMsg(MSG_QUOTE, sessionId, 257, "/");
return 0;
}
LOCAL int ftpCmdCwd(int sessionId, const char* args, int len)
{
if(ftpChangeDir(sessionId, args) == 0)
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg024);
else
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg025);
return 0;
}
LOCAL int ftpCmdType(int sessionId, const char* args, int len)
{
switch(args[0])
{
case 'I':
case 'i':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg026);
ftpGetSession(sessionId)->binary = TRUE;
break;
case 'A':
case 'a':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg027);
ftpGetSession(sessionId)->binary = FALSE;
break;
default:
ftpSendMsg(MSG_NORMAL, sessionId, 504, ftpMsg028);
break;
}
return 0;
}
LOCAL int ftpCmdPasv(int sessionId, const char* args, int len)
{
uint16_t port;
uint32_t ip;
char str[50];
socket_t s;
uint32_t remoteFTPServerIp;
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, ftpGetSession(sessionId)->passiveDataSocket = %d\n", sessionId, ftpGetSession(sessionId)->passiveDataSocket);
if(ftpGetSession(sessionId)->passiveDataSocket <= 0)
{
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv about to Close socket = %d for sessionId = %d\n",ftpGetSession(sessionId)->passiveDataSocket,sessionId);
//ftpUntrackSocket(ftpGetSession(sessionId)->passiveDataSocket);
//ftpCloseSocket(&ftpGetSession(sessionId)->passiveDataSocket);
//}
//ftpGetSession(sessionId)->passiveDataSocket = -1;
s = ftpEstablishDataConnection(TRUE, &ip, &port,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
ftpGetSession(sessionId)->passiveDataSocket = s;
ftpGetSession(sessionId)->passiveIp = ip;
ftpGetSession(sessionId)->passivePort = port;
}
else
{
s = ftpGetSession(sessionId)->passiveDataSocket;
ip = ftpGetSession(sessionId)->passiveIp;
port = ftpGetSession(sessionId)->passivePort;
}
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, client IP = %u, remote IP = %u, port = %d, ftpAddUPNPPortForward = %p, ftpRemoveUPNPPortForward = %p using listener socket = %d\n",
// sessionId, ftpGetSession(sessionId)->remoteIp, ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp), port,ftpAddUPNPPortForward,ftpRemoveUPNPPortForward,s);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, client IP = %u, remote IP = %u, port = %d, using listener socket = %d\n",
sessionId, ftpGetSession(sessionId)->remoteIp, ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp), port,s);
if(ftpAddUPNPPortForward != NULL && ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp) != 0)
{
ftpGetSession(sessionId)->remoteFTPServerPassivePort = port;
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, adding UPNP port forward\n", sessionId);
//ftpAddUPNPPortForward(port, port);
remoteFTPServerIp = ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp);
snprintf(str, 50,"%s (%d,%d,%d,%d,%d,%d)",
ftpMsg029,
(remoteFTPServerIp >> 24) & 0xFF,
(remoteFTPServerIp >> 16) & 0xFF,
(remoteFTPServerIp >> 8) & 0xFF,
remoteFTPServerIp & 0xFF,
(port >> 8) & 0xFF,
port & 0xFF);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, str [%s]\n", sessionId, str);
}
else
{
snprintf(str, 50,"%s (%d,%d,%d,%d,%d,%d)",
ftpMsg029,
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF,
(port >> 8) & 0xFF,
port & 0xFF);
}
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, ftpGetSession(sessionId)->passiveDataSocket = %d SENDING 227 to client\n", sessionId, ftpGetSession(sessionId)->passiveDataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 227, str);
ftpGetSession(sessionId)->passive = TRUE;
return 0;
}
LOCAL int ftpCmdCdup(int sessionId, const char* args, int len)
{
return ftpCmdCwd(sessionId, "..", 2);
}
LOCAL int ftpCmdStru(int sessionId, const char* args, int len)
{
switch(args[0])
{
case 'F':
case 'f':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg030);
break;
default:
ftpSendMsg(MSG_NORMAL, sessionId, 504, ftpMsg031);
break;
}
return 0;
}
#if RFC3659
LOCAL int ftpCmdSize(int sessionId, const char* args, int len)
{
int ret;
char str[12];
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
ret = ftpStat(realPath, &fileInfo);
if(ret)
{
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
snprintf(str, 12,"%d", fileInfo.size);
ftpSendMsg(MSG_NORMAL, sessionId, 213, str);
return 0;
}
LOCAL int ftpCmdMdtm(int sessionId, const char* args, int len)
{
int ret;
char str[15];
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
ret = ftpStat(realPath, &fileInfo);
if(ret)
{
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
snprintf(str, 15,"%04d%02d%02d%02d%02d%02d", fileInfo.mTime.year,
fileInfo.mTime.month,
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
fileInfo.mTime.second);
ftpSendMsg(MSG_NORMAL, sessionId, 213, str);
return 0;
}
LOCAL int ftpCmdMlst(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 550, "MLST command not implemented");
return 0;
}
/*LOCAL int ftpCmdMlsd(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 550, "MLSD command not implemented");
return 0;
}*/
LOCAL int ftpCmdMlsd(int sessionId, const char* args, int len)
{
const char* realPath;
socket_t s;
//#### Funktioniert nicht wenn Pfad übergeben wird
/* if(args[0] != '\0')
{
if(args[0] != '-')
realPath = ftpGetRealPath(sessionId, args);
}
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if(ftpGetSession(sessionId)->passive == FALSE)
{
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort,sessionId);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
}
else
{
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if(s < 0)
{
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, MLSD);
if(VERBOSE_MODE_ENABLED) printf("In ftpCmdMlsd about to Close socket = %d for sessionId = %d\n",s,sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
#endif
typedef struct
{
char cmdToken[5]; ///< command-string
int tokLen; ///< len of cmdToken
int neededRights; ///< required access rights for executing the command
int needLogin; ///< if TRUE, command needs successful authentication via USER and PASS
int reportFeat; ///< if TRUE, command is reported via FEAT
int duringTransfer; ///< if TRUE, command can be executed during a file transfer
int (*handler)(int, const char*, int); ///< handler function
}ftpCommand_S;
static const ftpCommand_S cmds[] = {
{"USER", 4, 0, FALSE, FALSE, FALSE, ftpCmdUser},
{"PASS", 4, 0, FALSE, FALSE, FALSE, ftpCmdPass},
{"SYST", 4, 0, TRUE, FALSE, FALSE, ftpCmdSyst},
{"PORT", 4, 0, TRUE, FALSE, FALSE, ftpCmdPort},
{"NOOP", 4, 0, TRUE, FALSE, FALSE, ftpCmdNoop},
{"QUIT", 4, 0, FALSE, FALSE, TRUE, ftpCmdQuit},
{"ABOR", 4, 0, FALSE, FALSE, TRUE, ftpCmdAbor},
{"LIST", 4, FTP_ACC_LS, TRUE, FALSE, FALSE, ftpCmdList},
{"NLST", 4, FTP_ACC_LS, TRUE, FALSE, FALSE, ftpCmdNlst},
{"PWD", 3, FTP_ACC_RD, TRUE, FALSE, FALSE, ftpCmdPwd},
{"XPWD", 4, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdPwd},
{"TYPE", 4, 0, TRUE, FALSE, FALSE, ftpCmdType},
{"PASV", 4, 0, TRUE, FALSE, FALSE, ftpCmdPasv},
{"CWD", 3, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdCwd},
{"CDUP", 4, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdCdup},
{"STRU", 4, 0, TRUE, FALSE, FALSE, ftpCmdStru},
{"RETR", 4, FTP_ACC_RD, TRUE, FALSE, FALSE, ftpCmdRetr},
{"STOR", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdStor},
{"DELE", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdDele},
{"MKD" , 3, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdMkd},
{"XMKD", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdMkd},
{"RMD" , 3, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdRmd},
{"XRMD", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdRmd},
#if RFC3659
{"SIZE", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdSize},
{"MDTM", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdMdtm},
{"MLST", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdMlst},
{"MLSD", 4, FTP_ACC_LS, TRUE, TRUE, FALSE, ftpCmdMlsd}
#endif
};
int execFtpCmd(int sessionId, const char* cmd, int cmdlen)
{
int n;
int ret = 0;
ftpSession_S *pSession = ftpGetSession(sessionId);
//if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd ARRAY_SIZE(cmds) = %lu for sessionId = %d\n",ARRAY_SIZE(cmds),sessionId);
if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd cmd [%s] for sessionId = %d\n",cmd,sessionId);
for(n = 0; n < (int)ARRAY_SIZE(cmds); n++)
{
if(!strncmp(cmds[n].cmdToken, cmd, cmds[n].tokLen))
{
int i = cmds[n].tokLen;
if(cmds[n].needLogin)
{
if((pSession->userId == 0) || (pSession->authenticated == FALSE))
{
if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd User NOT loggedin for sessionId = %d\n",sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 530, ftpMsg033);
return 0;
}
if(ftpCheckAccRights(pSession->userId, cmds[n].neededRights))
{
if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd User has no ACCESS for sessionId = %d\n",sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg034);
return 0;
}
}
if((pSession->activeTrans.op != OP_NOP)) // transfer in progress?
{
if(cmds[n].duringTransfer == FALSE) // command during transfer allowed?
{
if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd got command during transfer, discarding for sessionId = %d\n",sessionId);
return 0; // no => silently discard command
}
}
while(cmd[i] != '\0')
{
if((cmd[i] != ' ') && (cmd[i] != '\t'))
break;
i++;
}
if(VERBOSE_MODE_ENABLED) printf("About to execute cmds[n].cmdToken [%s] command [%s] for sessionId = %d\n",cmds[n].cmdToken,&cmd[i],sessionId);
ret = cmds[n].handler(sessionId, &cmd[i], (int)strlen(&cmd[i])); // execute command
if(VERBOSE_MODE_ENABLED) printf("Executed cmds[n].cmdToken [%s] command [%s] ret = %d for sessionId = %d\n",cmds[n].cmdToken,&cmd[i],ret,sessionId);
pSession->timeLastCmd = ftpGetUnixTime();
return ret;
}
else
{
//if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd SKIPPED COMMAND cmds[n].cmdToken = [%s] n = %d for sessionId = %d\n",cmds[n].cmdToken,n,sessionId);
}
}
if(VERBOSE_MODE_ENABLED) printf("ERROR UNKNOWN COMMAND cmd [%s] for sessionId = %d\n",cmd,sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 500, cmd); // reject unknown commands
pSession->timeLastCmd = ftpGetUnixTime();
return ret;
}
void ftpParseCmd(int sessionId)
{
ftpSession_S *pSession;
int len;
socket_t ctrlSocket;
pSession = ftpGetSession(sessionId);
len = pSession->rxBufWriteIdx;
ctrlSocket = pSession->ctrlSocket;
if((pSession->rxBuf[len - 1] == '\n') &&
(pSession->rxBuf[len - 2] == '\r') ) // command correctly terminated?
{
int c = 0;
pSession->rxBuf[len - 2] = '\0';
pSession->rxBufWriteIdx = 0;
for(c = 0; c < len; c++) // convert command token to uppercase
{
if(isspace(pSession->rxBuf[c]))
break;
pSession->rxBuf[c] = toupper(pSession->rxBuf[c]);
}
if(VERBOSE_MODE_ENABLED) printf("%02d --> %s for socket: %d\n", sessionId, pSession->rxBuf,ctrlSocket);
if(execFtpCmd(sessionId, pSession->rxBuf, len - 2) == -1)
{
if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd command triggered close for socket: %d!\n",ctrlSocket);
ftpUntrackSocket(ctrlSocket);
ftpCloseSession(sessionId);
}
}
else
{
if(VERBOSE_MODE_ENABLED) printf("ERROR In execFtpCmd problem with parsing string [%s] for socket: %d!\n",pSession->rxBuf,ctrlSocket);
}
if(pSession->rxBufWriteIdx >= LEN_RXBUF) // overflow of receive buffer?
{
pSession->rxBufWriteIdx = 0;
ftpSendMsg(MSG_NORMAL, sessionId, 500, ftpMsg035);
if(VERBOSE_MODE_ENABLED) printf("ERROR: Receive buffer overflow. Received data discarded.\n");
}
}