/*
ZazouMiniWebServer
Copyright (C) 2003-2008 Xavier Garreau
This file is part of ZazouMiniWebServer.
ZazouMiniWebServer 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 2 of the License, or
(at your option) any later version.
ZazouMiniWebServer 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 ZazouMiniWebServer; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
// ZMWS.cpp: implementation of the ZMWS class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ZOSAL.h"
#include
#include
#include
#include "ZMWS.h"
#include "ZMWSConfig.h"
#include "ZMWSFileUtils.h"
#include "ZMWSReqResp.h"
#include "ZMWSOptParser.h"
#include "ZMWSConfFileParser.h"
#include "ZMWSbase64.h"
#include "ZMWSResolver.h"
#include "ZMWSSAPI.h"
#ifdef WIN32
#include
#include
#include "ZMWSLogger.h"
#endif
#define WSVERSION MAKEWORD(2,2)
#define ZMWS_TIMEOUT 30 //(secondes)
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
volatile DWORD ZMWS::nbClients = 0;
ZMWS::ZMWS()
{
std::stringstream verSS;
verSS << VER_MAJOR << ".";
verSS << VER_MINOR << ".";
verSS << VER_MICRO;
serverVersion = verSS.str();
if (VER_MINOR%2)
verSS << " (unstable) ";
verSS << "\n";
print_dbg(("ZazouMiniWebServer "+verSS.str()).c_str());
std::string tmpS = "\n----------------------------------------\n";
tmpS += "Copyright (C) 2003-2008 Xavier Garreau \n";
tmpS += "ZazouMiniWebServer is free software.\n";
tmpS += "ZazouMiniWebServer comes with ABSOLUTELY NO WARRANTY.\n";
tmpS += "You are welcome to redistribute it under certain conditions,\n";
tmpS += "Read the LICENSE: http://www.fsf.org/licenses/gpl.txt\n";
tmpS += "----------------------------------------\n";
print_dbg (tmpS.c_str());
ZazouModSslHandle = NULL;
serverSocket = INVALID_SOCKET;
secureServerSocket = INVALID_SOCKET;
ServerStop = TRUE;
int err1 = 0, err2 = 0;
#ifdef WIN32
ZMWS_SERVERSTOP_MUTEX = CreateMutex(NULL, FALSE, "ZMWS_SERVERSTOP_MUTEX");
if (!ZMWS_SERVERSTOP_MUTEX) {
err1=-1;
}
ZMWS_NBCLIENTS_MUTEX = CreateMutex(NULL, FALSE, "ZMWS_NBCLIENTS_MUTEX");
if (!ZMWS_NBCLIENTS_MUTEX) {
err2=-1;
}
#else
err1 = pthread_mutex_init (&ZMWS_SERVERSTOP_MUTEX, NULL);
err2 = pthread_mutex_init (&ZMWS_NBCLIENTS_MUTEX, NULL);
#endif
if (err1)
print_dbg ("ERROR : Could not Create ZMWS_SERVERSTOP_MUTEX Mutex\n");
if (err2)
print_dbg ("ERROR : Could not Create ZMWS_NBCLIENTS_MUTEX Mutex\n");
#ifdef WIN32
// Considérons que ça marche à tous les coups :)
ZMWSSTOPMESSAGE = RegisterWindowMessage (LPCTSTR("ZazouMiniWebServerStop"));
// Initialisation winsocks
WSAStartup(WSVERSION, &wsaData);
#endif
}
ZMWS::~ZMWS()
{
BOOL l_ServerStop = FALSE;
DWORD result = WaitForSingleMutex (ZMWS_SERVERSTOP_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
l_ServerStop = ServerStop;
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
if (l_ServerStop != TRUE)
StopServer();
}
#ifdef WIN32
WSACleanup();
if (!ZMWS_NBCLIENTS_MUTEX)
CloseMutex (ZMWS_NBCLIENTS_MUTEX);
if (!ZMWS_SERVERSTOP_MUTEX)
CloseMutex (ZMWS_SERVERSTOP_MUTEX);
#else
CloseMutex (ZMWS_NBCLIENTS_MUTEX);
CloseMutex (ZMWS_SERVERSTOP_MUTEX);
#endif
}
void ZMWS::HideConsole()
{
// Le bloc qui suit cache la console...
// Truc adapté de http://mail.python.org/pipermail/python-list/2002-February/085621.html
// format a "unique" newWindowTitle
TCHAR newWindowTitle[256];
wsprintf(newWindowTitle,"%d/%d",
GetTickCount(),
GetCurrentProcessId());
// change current window title
SetConsoleTitle(newWindowTitle);
// ensure window title has been updated
Sleep(40);
// look for newWindowTitle
HWND hwndFound=FindWindow(NULL, newWindowTitle);
// If found, hide it
if ( hwndFound != NULL)
ShowWindow( hwndFound, SW_HIDE);
}
void __cdecl ZMWS::Run(LPVOID param)
{
ZMWS* obj = static_cast(param);
obj->MainThread();
_endthread();
}
void __cdecl ZMWS::QueueClient(LPVOID param) {
QUEUECLIENTPARAM* qcp = static_cast(param);
std::string tmpS;
DWORD local_nbclients;
DWORD result = WaitForSingleMutex (qcp->object->ZMWS_NBCLIENTS_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
local_nbclients = nbClients+1;
if (nbClients++ > qcp->object->serverConfig.GetMaxClients()) {
--nbClients;
ReleaseMutex(qcp->object->ZMWS_NBCLIENTS_MUTEX);
/* Fermeture de la connexion, brutal */
closesocket(qcp->clientSocket);
delete qcp;
_endthread();
} else {
ReleaseMutex(qcp->object->ZMWS_NBCLIENTS_MUTEX);
}
} else {
tmpS = "ERROR : QueueClient 1 : Could not acquire NBCLIENTS Mutex\n";
print_dbg (tmpS.c_str());
/* Fermeture de la connexion */
closesocket(qcp->clientSocket);
delete qcp;
_endthread();
}
qcp->modssl_data = NULL;
if (!qcp->object->serverConfig.GetBeQuiet()) {
/* Affichage du client */
std::stringstream tmpSS;
SOCKADDR_IN clientSockAddr;
int csaLen = sizeof(SOCKADDR_IN);
getpeername(qcp->clientSocket, (LPSOCKADDR)&clientSockAddr, &csaLen);
tmpSS << "Connection accepted (" << local_nbclients << '/' << qcp->object->serverConfig.GetMaxClients();
tmpSS << ") from ";
tmpSS << (int)clientSockAddr.sin_addr.S_un.S_un_b.s_b1 << ".";
tmpSS << (int)clientSockAddr.sin_addr.S_un.S_un_b.s_b2 << ".";
tmpSS << (int)clientSockAddr.sin_addr.S_un.S_un_b.s_b3 << ".";
tmpSS << (int)clientSockAddr.sin_addr.S_un.S_un_b.s_b4 << "\n";
tmpS = tmpSS.str();
print_dbg (tmpS.c_str());
}
qcp->object->HandleClient(qcp);
/* Fermeture de la connexion */
closesocket(qcp->clientSocket);
result = WaitForSingleMutex (qcp->object->ZMWS_NBCLIENTS_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
--nbClients;
ReleaseMutex(qcp->object->ZMWS_NBCLIENTS_MUTEX);
} else {
tmpS = "ERROR : QueueClient 2 : Could not acquire NBCLIENTS Mutex\n";
print_dbg (tmpS.c_str());
delete qcp;
_endthread();
}
if (!qcp->object->serverConfig.GetBeQuiet()) {
std::stringstream tmpSS2;
tmpSS2 << "Duree de la connexion: " << GetTickCount() - qcp->tinit << " msec\n";
tmpS = tmpSS2.str();
print_dbg(tmpS.c_str());
}
delete qcp;
_endthread();
}
// End of Internals
void ZMWS::BrowseNow() {
SOCKET s;
SOCKADDR_IN sockaddr;
int i = 0;
int csalen = sizeof(SOCKADDR);
unsigned short port, sport;
s = socket (AF_INET, SOCK_STREAM, 0);
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
port = serverConfig.GetPort();
if (port) {
sockaddr.sin_port = htons(port);
} else {
sport = serverConfig.GetHTTPSPort();
sockaddr.sin_port = htons(sport);
}
// On attend au maximum 20 secondes
while ( (connect(s, (LPSOCKADDR)&sockaddr, csalen) == SOCKET_ERROR) && (++i<200) ) {
Sleep(100);
}
closesocket(s);
std::stringstream URL;
if (port) {
URL << "http://127.0.0.1";
if (port!=80)
URL << ":" << port;
} else if (sport) {
URL << "https://127.0.0.1";
if (sport!=443)
URL << ":" << sport;
} else {
URL << "http://www.zmws.com/utils/badport.php";
}
if (serverConfig.GetBrowserCmd().length()) {
std::string cmd = "start ";
cmd += serverConfig.GetBrowserCmd();
std::string url = URL.str();
std::string::size_type pos;
while ((pos = cmd.find("%u")) != std::string::npos) {
cmd.replace(pos, 2, url);
}
system (cmd.c_str());
} else {
ShellExecute (NULL, NULL,
URL.str().c_str(),
NULL, NULL,
SW_SHOWNORMAL);
}
}
void ZMWS::WaitForShutDown() {
DWORD local_nbclients;
WaitForSingleThread(serverThread, INFINITE);
if (!serverConfig.GetDropClients()) {
do {
DWORD result = WaitForSingleMutex (this->ZMWS_NBCLIENTS_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
local_nbclients = nbClients;
ReleaseMutex(this->ZMWS_NBCLIENTS_MUTEX);
}
Sleep(100);
} while (local_nbclients);
}
print_dbg("Shutting down !\n");
}
BOOL ZMWS::CreateServerSockets () {
unsigned int IPaddr = INADDR_ANY;
SOCKADDR_IN serverSockAddr;
int rc;
std::string serverIP = serverConfig.GetBindAddr();
if (serverIP.length()) {
IPaddr = inet_addr(serverIP.c_str());
if (IPaddr == INADDR_NONE) {
print_dbg (("ERROR: Invalid bind option: " + serverIP + "\n").c_str());
return FALSE;
}
}
serverSockAddr.sin_family = AF_INET;
serverSockAddr.sin_addr.s_addr = IPaddr;
// HTTP
WORD port = serverConfig.GetPort();
if (port) {
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) return FALSE;
serverSockAddr.sin_port = htons(port);
if ((rc = bind(serverSocket, (LPSOCKADDR)&serverSockAddr, sizeof(SOCKADDR_IN))) == SOCKET_ERROR ) {
if (serverConfig.GetTry808xPorts()) {
port = 8080;
serverConfig.SetPort(port);
serverSockAddr.sin_port = htons(port);
while ((porth_addr_list;
while (*ip_ptr) {
std::stringstream ipSS;
if (serverIPs.length()) {
serverIPs += ", ";
}
iplistSS << "\n --> Adresse IP: ";
ipSS << (unsigned int)(*ip_ptr)[0] << "." << (unsigned int)(*ip_ptr)[1] << "." << (unsigned int)(*ip_ptr)[2] << "." << (unsigned int)(*ip_ptr)[3];
serverIPs += ipSS.str();
iplistSS << ipSS.str();
iplistSS << " (" << he->h_name << ")";
ip_ptr += 1;
}
iplistS = iplistSS.str();
}
}
// Renvoi d'une liste d'ip et noms de machine
return iplistS;
}
BOOL ZMWS::Configure(const char* config_file_path) {
BOOL OptsOK;
// Reset Config
serverConfig = ZMWSConfig();
// Command line args
ZMWSOptParser zmwsop;
OptsOK = zmwsop.Parse(0, NULL, serverConfig);
if (!OptsOK) {
print_dbg ("ERROR : Could not run server. Check your command line options ...\n");
return FALSE;
}
if (config_file_path) {
std::string cfp(config_file_path);
serverConfig.SetPathToConfigFile(cfp);
}
// Config file args
ZMWSConfFileParser zmwscfp;
OptsOK = zmwscfp.Parse(serverConfig, MIME_Manager);
if (!OptsOK) {
print_dbg ("ERROR : Could not run server. Check your configuration file ...\n");
return FALSE;
}
// Find and configure VHosts
serverConfig.ConfigureVHosts();
// Get local IPs list
CreateIPList();
return TRUE;
}
BOOL ZMWS::StartServer(int argc, char** argv)
{
BOOL OptsOK;
std::string tmpS;
std::string iplistS;
serverConfig = ZMWSConfig();
// Lecture des options de ligne de commande
ZMWSOptParser zmwsop;
OptsOK = zmwsop.Parse(argc, argv, serverConfig);
if (OptsOK) {
// Lecture des options du fichier de configuration
ZMWSConfFileParser zmwscfp;
OptsOK = zmwscfp.Parse(serverConfig, MIME_Manager);
if (!OptsOK) {
tmpS = "ERROR : Could not run server. Check your configuration file ...\n";
print_dbg (tmpS.c_str());
}
} else {
tmpS = "ERROR : Could not run server. Check your command line options ...\n";
print_dbg (tmpS.c_str());
}
// Configuration des valeurs par défaut des VHosts
serverConfig.ConfigureVHosts();
iplistS = CreateIPList();
if (serverConfig.GetHideConsole()) {
HideConsole();
}
// Set and create logs dir if needed !
if (serverConfig.GetWriteLogs())
serverConfig.SetLogsDir(serverConfig.GetLogsDir());
if (!CreateServerSockets()) {
return FALSE;
}
// zmws.pid
if (serverConfig.GetLogsDir().length()) {
std::ofstream pidfile;
std::stringstream pidfilePathStr;
pidfilePathStr << serverConfig.GetLogsDir() << "\\zmws_" << GetCurrentProcessId() << ".pid";
std::string pidfilePath = pidfilePathStr.str();
ZMWSFileUtils::normalizePath(pidfilePath);
pidfile.open (pidfilePath.c_str(), std::ios::out | std::ios::trunc);
if (pidfile.is_open()) {
pidfile << GetCurrentProcessId();
pidfile.close();
}
}
tmpS = "\n----------------------------------------";
std::stringstream tmpSS, tmpSS2;
tmpS += "\nServer Config:";
tmpS += "\n --> WebDir: ";
tmpS += serverConfig.GetDocumentRoot();
tmpS += "\n --> LogsDir: ";
if (serverConfig.GetWriteLogs())
tmpS += serverConfig.GetLogsDir();
else
tmpS+= "Logging disabled";
tmpS += "\n --> PhpPath: ";
std::string phpPath = serverConfig.GetPathToPHP();
if (phpPath.length())
tmpS += serverConfig.GetPathToPHP();
else
tmpS += "Not found: (use -phppath option)";
tmpS += "\n --> MaxClientConnections: ";
tmpSS2 << serverConfig.GetMaxClients();
tmpS += tmpSS2.str();
tmpS += "\n --> Indexing: ";
if (serverConfig.GetCanIndex())
tmpS += "Enabled";
else
tmpS += "Disabled";
tmpS += "\n --> On Requests: ";
if (serverConfig.GetBeQuiet())
tmpS += "Be quiet";
else
tmpS += "Be verbose";
tmpS += "\n --> Reverse DNS queries: ";
if (serverConfig.GetReverseDNS())
tmpS += "Enabled";
else
tmpS += "Disabled";
tmpS += "\n --> Can stop server: ";
if (serverConfig.GetCanStop())
tmpS += "Yes, browse fake URL /_stopServer.zmwsc";
else
tmpS += "Disabled";
tmpS += "\n --> Close Browser: ";
if (serverConfig.GetCanStop() && serverConfig.GetCloseBrowser())
tmpS += "Enabled";
else
tmpS += "Disabled";
tmpS += "\n --> Start Page: " + serverConfig.GetStartPages();
tmpS += "\n --> Allow from: ";
std::string af = serverConfig.GetAllowedFrom();
if (af.length())
tmpS += af;
else
tmpS += "All";
tmpS += "\n----------------------------------------";
tmpS += iplistS;
int port = serverConfig.GetPort();
tmpSS << "\n --> HTTP : ";
if (port)
tmpSS << "YES, port " << port;
else
tmpSS << "NO";
int sport = serverConfig.GetHTTPSPort();
tmpSS << "\n --> HTTPS : ";
if (sport)
tmpSS << "YES, port " << sport;
else
tmpSS << "NO";
tmpSS << "\n----------------------------------------\n";
tmpS += tmpSS.str();;
print_dbg (tmpS.c_str());
DWORD result = WaitForSingleMutex (ZMWS_SERVERSTOP_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
ServerStop = FALSE;
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
} else {
tmpS = "ERROR : StartServer : Could not acquire Server Mutex\n";
print_dbg (tmpS.c_str());
return FALSE;
}
if (OptsOK) {
serverThread = (HANDLE_THREAD)_beginthread(Run, 0, this);
if (serverConfig.GetBrowseNow()) {
print_dbg ("Launching browser ...\n");
BrowseNow();
}
} else {
tmpS = "ERROR : Could not run server. Check your options ...\n";
print_dbg (tmpS.c_str());
}
return OptsOK;
}
ZMWS& ZMWS::GetInstance()
{
static ZMWS theServer;
return theServer;
}
void ZMWS::GetConfig (CBaseZMWSConfig* zbc) {
serverConfig.GetCBaseConfig(zbc);
}
void ZMWS::FreeConfig (CBaseZMWSConfig* zbc) {
serverConfig.FreeCBaseConfig(zbc);
}
void ZMWS::StopServer(BOOL postMessage)
{
print_dbg("Exiting ...\n");
std::stringstream pidfilePathStr;
pidfilePathStr << serverConfig.GetLogsDir() << "\\zmws_" << GetCurrentProcessId() << ".pid";
std::string pidfilePath = pidfilePathStr.str();
ZMWSFileUtils::normalizePath (pidfilePath);
remove (pidfilePath.c_str());
SOCKET stopSocket;
SOCKADDR_IN stopSockAddr;
DWORD result = WaitForSingleMutex (ZMWS_SERVERSTOP_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
if (ServerStop) {
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
return;
} else {
ServerStop = TRUE;
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
}
} else {
std::string tmpS = "ERROR : StopServer : Could not acquire Server Mutex\n";
print_dbg (tmpS.c_str());
}
stopSocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
stopSockAddr.sin_family = AF_INET;
stopSockAddr.sin_port = htons(serverConfig.GetPort());
stopSockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
stopSockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
if (connect(stopSocket, (LPSOCKADDR)&stopSockAddr, sizeof(SOCKADDR)) != SOCKET_ERROR) {
closesocket(stopSocket);
}
stopSockAddr.sin_family = AF_INET;
stopSockAddr.sin_port = htons(serverConfig.GetHTTPSPort());
stopSockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
stopSockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
if (connect(stopSocket, (LPSOCKADDR)&stopSockAddr, sizeof(SOCKADDR)) != SOCKET_ERROR) {
closesocket(stopSocket);
}
if (serverSocket != INVALID_SOCKET) closesocket(serverSocket);
serverSocket = INVALID_SOCKET;
if (secureServerSocket != INVALID_SOCKET) closesocket(secureServerSocket);
secureServerSocket = INVALID_SOCKET;
WaitForSingleThread(serverThread, 30000);//INFINITE);
if (postMessage)
PostMessage (HWND_BROADCAST, ZMWSSTOPMESSAGE, 0, 0);
}
void ZMWS::ShutdownServer(unsigned short port)
{
print_dbg("Trying to stop a running instance of ZazouMiniWebServer ...\n");
SOCKET stopSocket;
SOCKADDR_IN stopSockAddr;
stopSocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
stopSockAddr.sin_family = AF_INET;
stopSockAddr.sin_port = htons(port);
stopSockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
stopSockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
stopSockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
if (connect(stopSocket, (LPSOCKADDR)&stopSockAddr, sizeof(SOCKADDR)) != SOCKET_ERROR) {
send(stopSocket, "GET /_stopServer.zmwsc\n", strlen("GET /_stopServer.zmwsc\n"), 0);
closesocket(stopSocket);
}
}
UINT ZMWS::MainThread()
{
SOCKET clientSocket;
SOCKET maxSocket = (serverSocket>secureServerSocket) ? serverSocket : secureServerSocket;
SOCKADDR_IN clientSockAddr;
int csaLen = sizeof(SOCKADDR);
BOOL ShouldStop = ((serverSocket == INVALID_SOCKET) && (secureServerSocket == INVALID_SOCKET));
while ( ShouldStop != TRUE ) {
DWORD result = WaitForSingleObject (ZMWS_SERVERSTOP_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
ShouldStop = ServerStop;
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
} else {
std::string tmpS = "ERROR : Mainthread : Could not acquire SERVERSTOP Mutex\n";
print_dbg (tmpS.c_str());
return FALSE;
}
/* Attente d'un client */
fd_set clientfds;
FD_ZERO(&clientfds);
if (serverSocket != INVALID_SOCKET)
FD_SET(serverSocket, &clientfds);
if (secureServerSocket != INVALID_SOCKET)
FD_SET(secureServerSocket, &clientfds);
if (select(maxSocket+1, &clientfds, NULL, NULL, NULL) <= 0) continue;
/* Acceptation si ok */
clientSocket = 0;
if (FD_ISSET(serverSocket, &clientfds)) {
if ((clientSocket = accept(serverSocket, (LPSOCKADDR)&clientSockAddr, &csaLen)) == INVALID_SOCKET) {
print_dbg("WARNING: accept_failed (http)\n");
continue;
}
} else if (FD_ISSET(secureServerSocket, &clientfds)) {
if ((clientSocket = accept(secureServerSocket, (LPSOCKADDR)&clientSockAddr, &csaLen)) == INVALID_SOCKET) {
print_dbg("WARNING: accept failed (https)\n");
continue;
}
} else {
continue;
}
QUEUECLIENTPARAM* qcp = NULL;
try {
qcp = new QUEUECLIENTPARAM;
} catch (std::bad_alloc) {
qcp = NULL;
}
if (!qcp) {
print_dbg("ERROR : Mainthread : Could not allocate a new qcp object\n");
if (clientSocket) {
closesocket (clientSocket);
}
continue;
}
qcp->clientSocket = clientSocket;
qcp->object = this;
qcp->tinit = GetTickCount();
_beginthread(QueueClient, 0, qcp);
}
if (serverSocket != INVALID_SOCKET)
closesocket (serverSocket);
if (secureServerSocket != INVALID_SOCKET)
closesocket (secureServerSocket);
return 0;
}
BOOL ZMWS::WaitForInputOnSocket (const SOCKET& clientSocket) const {
timeval tv;
tv.tv_sec = ZMWS_TIMEOUT;
tv.tv_usec = 0;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(clientSocket, &readfds);
// Si entrée, timeout ou erreur, on s'en va
// Si pas d'entrée retourne FALSE sinon TRUE
return (select(clientSocket+1, &readfds, NULL, NULL, &tv) > 0);
}
ZMWSReqResp* ZMWS::InitializeRR(const SOCKET& clientSocket)
{
ZMWSReqResp* RR;
try {
RR = new ZMWSReqResp;
} catch (std::bad_alloc) {
RR = NULL;
}
if (RR) {
RR->setSocket(&clientSocket);
RR->setReqMethod(UNKNOWN);
RR->setRetCode(200);
RR->cnx_policy = CNX_POLICY_CLOSE;
if (serverConfig.GetPBToken() != PBTOKEN_OFF) {
std::stringstream serverSW;
serverSW << "ZazouMiniWebServer";
if (serverConfig.GetPBToken() != PBTOKEN_NOVERSION) {
serverSW << " v" << VER_MAJOR << ".";
serverSW << VER_MINOR << ".";
serverSW << VER_MICRO;
}
RR->setRespVar("Server", serverSW.str());
}
RR->setHTTPVer(0.9);
struct sockaddr_in our_sa, client_sa;
int sa_len = sizeof(struct sockaddr_in);
getpeername (*(RR->getSocket()), (struct sockaddr *)&client_sa, &sa_len);
RR->setReqVar("REMOTE_ADDR", inet_ntoa(client_sa.sin_addr));
char* txtport = (char*)malloc(16);
if (txtport) {
RR->setReqVar("REMOTE_PORT", _ultoa(ntohs(client_sa.sin_port), txtport, 10));
free(txtport);
}
BOOL reverseDNS;
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(RR->RemovePort(RR->getReqVar("HOST")), vhc)) {
reverseDNS = vhc.reverseDNS;
} else {
reverseDNS = serverConfig.GetReverseDNS();
}
if (reverseDNS) {
std::string remote_host = ZMWSResolver::getHostName(client_sa.sin_addr);
if (remote_host.length())
RR->setReqVar("REMOTE_HOST", remote_host);
}
sa_len = sizeof(struct sockaddr_in);
getsockname (*(RR->getSocket()), (struct sockaddr *)&our_sa, &sa_len);
std::stringstream serverport;
serverport << ntohs(our_sa.sin_port);
RR->setReqVar("SERVER_PORT", serverport.str());
}
return RR;
}
void ZMWS::ParseRequestLine(std::string& RequestLine, ZMWSReqResp* RR) const
{
std::string::size_type pos = RequestLine.find(" ");
if ((pos!=std::string::npos) && (pos>0)) {
std::string ReqMethod = RequestLine.substr(0, pos);
std::transform (ReqMethod.begin(), ReqMethod.end(), ReqMethod.begin(), toupper);
if (ReqMethod=="GET") {
RR->setReqMethod(HTTP_GET);
RR->setReqVar("REQUEST_METHOD", "GET");
} else if (ReqMethod=="POST") {
RR->setReqMethod(HTTP_POST);
RR->setReqVar("REQUEST_METHOD", "POST");
} else if (ReqMethod=="HEAD") {
RR->setReqMethod(HTTP_HEAD);
RR->setReqVar("REQUEST_METHOD", "HEAD");
} else if (ReqMethod=="PUT") {
RR->setRetCode (501);
} else if (ReqMethod=="TRACE") {
RR->setRetCode (501);
} else if (ReqMethod=="DELETE") {
RR->setRetCode (501);
} else if (ReqMethod=="CONNECT") {
RR->setRetCode (501);
} else if (ReqMethod=="OPTIONS") {
RR->setRetCode (501);
} else {
RR->setRetCode (501);
}
} else {
RR->setRetCode(400);
RR->setErrorMessage("Invalid request: "+RequestLine);
}
RR->setRequestLine(RequestLine);
// Détermination de la version du protocole
std::transform (RequestLine.begin(),RequestLine.end(), RequestLine.begin(), toupper);
std::string::size_type Verpos = RequestLine.find("HTTP/");
double httpver = 0.9;
if (Verpos!=std::string::npos) {
Verpos += 5;
httpver = strtod(RequestLine.substr(Verpos).c_str(), NULL);
RR->setReqVar("SERVER_PROTOCOL", "HTTP/"+RequestLine.substr(Verpos));
if (httpver>=1.1) {
RR->cnx_policy = CNX_POLICY_KEEP;
}
} else {
RR->setReqVar("SERVER_PROTOCOL", "HTTP/0.9");
}
RR->setHTTPVer(httpver);
}
void ZMWS::ParseHeaderLine(std::string& HeaderLine, ZMWSReqResp* RR) const
{
std::string Key, Value;
std::string::size_type pos = HeaderLine.find(":");
if ((pos!=std::string::npos) && (pos>0)) {
Key = HeaderLine.substr(0, pos);
while (++possetReqVar(Key, Value);
}
} else {
RR->setRetCode(400);
RR->setErrorMessage("Unidentifiable header: '"+HeaderLine+"'");
}
}
ZMWSReqResp* ZMWS::GetRequestLines (ZMWSReqResp* RR)
{
BOOL foundEmptyLine = FALSE;
char buff[8192], c;
std::string Line = "";
std::string OldLine = "";
char LastWasEOL = FALSE;
int bytesRead = 0;
// print_dbg("In GetRequestLines\n");
do {
if (!WaitForInputOnSocket(*(RR->getSocket()))) {
delete RR;
RR = NULL;
break;
}
int bytesAvail = recv (*(RR->getSocket()), buff, 8192, MSG_PEEK);
if (bytesAvail <= 0) {
delete RR;
RR = NULL;
break;
}
while (!foundEmptyLine && bytesAvail--) {
if (RR->getIsSSL()) {
bytesRead = modssl_functions.fnRead (RR->GetModSSLData(), (LPBYTE)&c, 1);
} else {
bytesRead = recv (*(RR->getSocket()), &c, 1, 0);
}
if (bytesRead <= 0) {
delete RR;
RR = NULL;
break;
}
if ( (c!='\r') && (c!='\n') ) {
if (LastWasEOL) {
LastWasEOL = FALSE;
if ( (c==' ') || (c=='\t') ) {
// This is a multiline request continuation
// Add content to previous line
Line = OldLine;
Line += ' ';
continue;
} else {
// Previous line was a complete request line
if (OldLine.length()) {
ParseHeaderLine(OldLine, RR);
}
}
}
Line += c;
} else if (c=='\n') {
LastWasEOL = TRUE;
// Very useful for debugging purposes :)
// print_dbg((Line+"\n").c_str());
if (Line.length()) {
if (RR->getReqMethod() == UNKNOWN) {
// Parse the first request line
ParseRequestLine(Line, RR);
// For HTTP/0.9 break as soon as we've parsed the request
if (RR->getHTTPVer() < 1) {
// Fake an empty line to break off the 2 while loops
foundEmptyLine = TRUE;
}
} else {
// Cache the read line
OldLine = Line;
}
// Empty the consumed line
Line = "";
} else {
foundEmptyLine = TRUE;
// If we get here, we have to parse the last line
if (OldLine.length() && (RR->getReqMethod() != UNKNOWN)) {
ParseHeaderLine(OldLine, RR);
}
}
}
}
} while ( (!foundEmptyLine) && (bytesRead > 0) && (RR->getRetCode() < 300) );
if (RR) {
if (RR->getReqMethod() == UNKNOWN) {
RR->setRetCode(400);
RR->setErrorMessage("Empty request");
} else if (!RR->getReqVar("HOST").length() && (RR->getRetCode()==200) && (RR->getHTTPVer() >= 1.1)) {
RR->setRetCode(400);
RR->setErrorMessage("Missing 'Host:' header");
}
} else {
//print_dbg("GetRequestLines: No Request\n");
}
return RR;
}
std::string ZMWS::GetResponseLine(const HANDLE_FILE& h) const
{
char c = '\n';
DWORD cnt;
std::string Line = "";
do {
if (ReadFile (h, &c, 1, &cnt, NULL) <= 0) break;
if (!((c=='\r') || (c=='\n'))) Line += c;
} while (c!='\n');
return Line;
}
LPCTSTR ZMWS::SetupCgiEnvironment(ZMWSReqResp& rr) {
int total_size=0;
char* ce = NULL;
std::string new_var;
std::stringstream tmpSS;
char** env;
/* char* var;
env = GetEnvironmentStrings();
if (env != NULL) {
for (var = (char*)env; *var; var++)
{
new_var = var;
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
var += new_var.length();
}
FreeEnvironmentStrings((char*)env);
}
*/
env = _environ;
while (*env != NULL) {
new_var = *env;
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
++env;
}
if (rr.getAuthRealm().length()) {
new_var = "AUTH_TYPE=";
new_var += "Basic";
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
}
new_var = "DOCUMENT_ROOT=";
new_var += rr.getReqRootDir();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
new_var = "GATEWAY_INTERFACE=CGI/1.1";
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
if (rr.getIsSSL()) {
new_var = "HTTPS=on";
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
}
RRVARS ReqVars = rr.getReqVars();
RRVARS::iterator VarsIterator = ReqVars.begin();
do {
new_var = (*VarsIterator).first;
std::transform (new_var.begin(),new_var.end(), new_var.begin(), toupper);
std::replace(new_var.begin(), new_var.end(), '-','_');
if (new_var=="HOST") {
new_var = rr.RemovePort(new_var);
}
if ((new_var != "QUERY_STRING") &&
(new_var != "REQUEST_METHOD") &&
(new_var != "CONTENT_LENGTH") &&
(new_var != "CONTENT_TYPE") &&
(new_var != "PATH_INFO") &&
(new_var != "PATH_TRANSLATED") &&
(new_var != "REMOTE_ADDR") &&
(new_var != "REMOTE_HOST") &&
(new_var != "REMOTE_PORT") &&
(new_var != "REMOTE_USER") &&
(new_var != "SERVER_PROTOCOL") &&
(new_var != "SERVER_PORT")
)
{
new_var = "HTTP_" + new_var;
}
new_var += "=" + (*VarsIterator).second;
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
} while (++VarsIterator != ReqVars.end());
new_var = "PHP_SELF=";
new_var += rr.getURL();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
std::stringstream redirectStatus;
redirectStatus << rr.getRetCode();
new_var = "REDIRECT_STATUS=" + redirectStatus.str();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
new_var = "REQUEST_URI=";
new_var += rr.getURL();
std::string QS = rr.getReqVar("QUERY_STRING");
if (QS.length()) {
new_var += "?"+QS;
}
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
new_var = "SCRIPT_NAME=";
std::string path_info = rr.getReqVar("PATH_INFO");
std::string script_name = rr.getURL().substr(0, rr.getURL().length()-path_info.length());
if (script_name[script_name.length()-1]=='/') {
ZMWSFileUtils zfu;
if (zfu.getFile(rr.getFilePath())) {
script_name += zfu.getFileName();
}
}
new_var += script_name;
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
new_var = "SCRIPT_FILENAME=";
new_var += rr.getFilePath();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
new_var = "PATH_TRANSLATED=";
new_var += rr.getFilePath();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
if (rr.RemovePort(rr.getReqVar("HOST")).length()) {
new_var = "SERVER_NAME=";
new_var += rr.RemovePort(rr.getReqVar("HOST"));
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
}
std::stringstream serverSW;
serverSW << "ZazouMiniWebServer/";
serverSW << VER_MAJOR << ".";
serverSW << VER_MINOR << ".";
serverSW << VER_MICRO;
new_var = "SERVER_SOFTWARE="+serverSW.str();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
if (rr.getRetCode()>=400) {
new_var = "ZMWS_HTTP_MSG=";
new_var += HTTP_Utils.getReasonPhrase(rr.getRetCode());
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
if (rr.getErrorMessage().length()) {
new_var = "ZMWS_ERR_MSG="+rr.getErrorMessage();
ce = (LPSTR)realloc(ce, total_size + new_var.length()+1);
strcpy (ce+total_size, new_var.c_str());
total_size += new_var.length()+1;
}
}
// Bloc d'environnement terminé par un 0
ce = (LPSTR)realloc(ce, total_size+1);
ce[total_size]=0;
total_size += 1;
return ce;
}
void ZMWS::Redirect(ZMWSReqResp& rr, const std::string& newUrl)
{
rr.setRespVar("Location", newUrl.c_str());
rr.setErrorMessage("This page has moved to "+newUrl+"\n");
DefaultErrorMessage(rr);
}
void ZMWS::CloseBrowser(ZMWSReqResp& rr) {
std::stringstream Response;
rr.setReqVar("CONNECTION", "CLOSE");
rr.setRespVar("Content-Type", "text/html");
Response << "\n\n";
Response << "\n";
Response << "\n\n";
SendHeaders(rr);
SendChunk(rr, (void*)Response.str().c_str(), Response.str().length());
SendChunk(rr, NULL, 0);
}
void ZMWS::AddRespVars(ZMWSReqResp& rr, std::stringstream& Response)
{
// Ajout de la Date
SYSTEMTIME st;
GetSystemTime(&st);
rr.setRespVar ("Date", rr.ComputeDate(st));
RRVARS RespVars = rr.getRespVars();
RRVARS::iterator VarsIterator = RespVars.begin();
do {
Response << (*VarsIterator).first << ": " << (*VarsIterator).second << "\r\n";
} while (++VarsIterator != RespVars.end());
}
BOOL ZMWS::SendHeaders(ZMWSReqResp& rr, BOOL EndHeaders) {
// Pas d'entête en HTTP 0.9
if (rr.getHTTPVer()<1.0) return TRUE;
std::stringstream Response;
// En tête, numéro de version
std::string httpVer("1.1");
if (rr.getHTTPVer()<1.1)
httpVer = "1.0";
Response << "HTTP/" << httpVer << " " << rr.getRetCode() << " " << HTTP_Utils.getReasonPhrase(rr.getRetCode()) << "\r\n";
// En tête, encodage chunked pour les clients HTTP/1.1
// Si le document :
// * est du texte
// * qu'on a du contenu
// * qu'il n'est pas caché (réparation du bug Safari)
// sinon, on utilise le Content-Length
if ((rr.getHTTPVer() >= 1.1) &&
( (rr.getRespVar("Content-Type").find("text/")!=std::string::npos) ||
(!(rr.getRespVar("Content-Length").length())))) {
if (rr.getRetCode()!=304) {
// Pour Safari, ne pas dire comment on aurait envoyé le contenu
// s'il ne l'avait pas eu en cache ...
rr.setRespVar("Transfer-Encoding", "Chunked");
}
rr.eraseRespVar("Content-Length");
}
// CharSet par défaut si aucun spécifié //
if ( (rr.getRespVar("Content-Type").find("text/")!=std::string::npos) &&
(rr.getRespVar("Content-Type").find("charset=")==std::string::npos) ) {
std::string new_cs;
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc)) {
new_cs = vhc.defCharSet;
if (!new_cs.length())
new_cs = serverConfig.GetDefaultCharSet();
} else {
new_cs = serverConfig.GetDefaultCharSet();
}
if (new_cs.length()) {
new_cs = rr.getRespVar("Content-Type") + "; charset=" + new_cs;
rr.setRespVar("Content-Type", new_cs);
}
}
std::string connectionMAJ = rr.getReqVar("CONNECTION");
std::transform (connectionMAJ.begin(),connectionMAJ.end(), connectionMAJ.begin(), toupper);
if (connectionMAJ=="CLOSE") {
// Fermeture de connexion demandée par le client
rr.cnx_policy = CNX_POLICY_CLOSE;
} else if (connectionMAJ=="KEEP-ALIVE") {
// Compatibilité connexions persistantes HTTP/1.0
if ((rr.getHTTPVer() >= 1.1) || (rr.getRespVar("Content-Length").length())) {
rr.setRespVar("Connection", "Keep-Alive");
rr.cnx_policy = CNX_POLICY_KEEP;
} else {
rr.cnx_policy = CNX_POLICY_CLOSE;
}
}
// Si fermeture de connexion, envoi de l'entête correspondant
if (rr.cnx_policy == CNX_POLICY_CLOSE) {
rr.setRespVar("Connection", "Close");
}
// Ajout d'un message si erreur
AddRespVars(rr, Response);
// Est ce tout pour les headers ? (dans le cas de php, c'est lui qui finit !)
if (EndHeaders) {
Response << "\r\n";
}
// On envoie les entêtes
SendChunk(rr, (void*)Response.str().c_str(), Response.str().length(), /* is_header */ TRUE);
return TRUE;
}
BOOL ZMWS::FindAuthFile(ZMWSReqResp& rr) {
// look for an auth file, sets AuthFilePath and return TRUE if found
ZMWSFileUtils zfu;
std::string rootDir = rr.getAliasedRootDir().length() ? rr.getAliasedRootDir() : rr.getReqRootDir();
std::string tmpPath = rr.getFilePath();
if (rr.getIsDirectory()) tmpPath += '\\';
int next_path_pos = tmpPath.find_last_of("/\\");
while (next_path_pos != std::string::npos) {
tmpPath = tmpPath.substr(0, next_path_pos);
if (zfu.getFile(tmpPath+"/_accesspath.zmwsc")) {
std::ifstream ifs((tmpPath+"/_accesspath.zmwsc").c_str());
std::string accesspath;
std::string authRealm;
char tmp_char;
while ((tmp_char = ifs.get()) != -1) {
if (tmp_char != '\n') {
authRealm += tmp_char;
} else {
break;
}
}
if (authRealm.length()) {
rr.setAuthRealm(authRealm);
} else {
rr.setRetCode(500);
rr.setErrorMessage("Erreur dans la procédure d'authentification");
return TRUE;
}
while ((tmp_char = ifs.get()) != -1) {
if (tmp_char != '\n') {
accesspath += tmp_char;
} else {
break;
}
}
if (accesspath.length()>2) {
ZMWSFileUtils::normalizePath(accesspath);
if (accesspath[1]!=':') {
if (accesspath[0]=='\\') {
accesspath = rootDir + accesspath;
} else {
accesspath = tmpPath + '\\' + accesspath;
}
}
rr.setAuthFilePath(accesspath);
return TRUE;
} else {
rr.setRetCode(500);
rr.setErrorMessage("Erreur dans la procédure d'authentification.");
return TRUE;
}
}
if (zfu.getFile(tmpPath+"/_access.zmwsc")) {
std::ifstream ifs((tmpPath+"/_access.zmwsc").c_str());
std::string authRealm;
char tmp_char;
while ((tmp_char = ifs.get()) != -1) {
if (tmp_char != '\n') {
authRealm += tmp_char;
} else {
break;
}
}
if (authRealm.length()) {
rr.setAuthRealm(authRealm);
rr.setAuthFilePath(tmpPath+"/_access.zmwsc");
return TRUE;
} else {
rr.setRetCode(500);
rr.setErrorMessage("Erreur dans la procédure d'authentification");
return TRUE;
}
}
next_path_pos = tmpPath.find_last_of("/\\", tmpPath.length()-2);
if (tmpPath.length() <= rootDir.length())
break;
}
return FALSE;
}
BOOL ZMWS::AuthenticateUser(ZMWSReqResp& rr) {
// authenticate user,
std::ifstream ifs(rr.getAuthFilePath().c_str());
if (!ifs) {
rr.setRetCode(500);
rr.setErrorMessage("Erreur dans la procédure d'authentification.\nImpossible d'ouvrir le fichier.");
return TRUE;
}
char tmp_char;
// Supprimé pour le cas des _accesspath.zmwsc
// ifs.ignore(rr.getAuthRealm().length()+1);
// Remplacé par
while (((tmp_char = ifs.get()) != -1) && (tmp_char != '\n'));
while (!ifs.eof()) {
std::string chalenge_response;
while ((tmp_char = ifs.get()) != -1) {
if (tmp_char != '\n') {
chalenge_response += tmp_char;
} else {
break;
}
}
std::string credentials = rr.getReqVar("AUTHORIZATION");
std::string b64_userpass;
std::string::size_type b64_userpass_pos;
b64_userpass_pos = credentials.find_last_of(' ');
b64_userpass = credentials.substr(b64_userpass_pos+1);
if (base64::encode(chalenge_response) == b64_userpass) {
std::string::size_type username_end = chalenge_response.find(':');
if (username_end != std::string::npos) {
rr.setReqVar("REMOTE_USER", chalenge_response.substr(0, username_end));
}
return TRUE;
}
}
return FALSE;
}
void ZMWS::SendAuthChallenge(ZMWSReqResp& rr) {
rr.setRespVar("WWW-Authenticate", "Basic realm=\""+rr.getAuthRealm()+"\"");
}
BOOL ZMWS::LocalFileIsMoreRecentThanClientCache(ZMWSReqResp& rr) {
BOOL result = TRUE;
std::string clientVersionDate = rr.getReqVar("IF_MODIFIED_SINCE");
std::istringstream clientVersionDateSS;
clientVersionDateSS.str(clientVersionDate);
std::string tmp, SortTime;
// day of week
clientVersionDateSS >> tmp;
//Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
//Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
//Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
std::string fullDate, day, month, year, fullTime, hour, minute, second;
switch (tmp.length()) {
case 4: // RFC822/1123 !
if (!(clientVersionDateSS >> day)) return result;
if (!(clientVersionDateSS >> month)) return result;
if (!(clientVersionDateSS >> year)) return result;
if (!(clientVersionDateSS >> fullTime)) return result;
break;
case 3: // asctime C format !
if (!(clientVersionDateSS >> month)) return result;
if (!(clientVersionDateSS >> day)) return result;
if (day.length()<2) day = "0" + day;
if (!(clientVersionDateSS >> fullTime)) return result;
if (!(clientVersionDateSS >> year)) return result;
break;
default: // RFC850/1036 !
if (!(clientVersionDateSS >> fullDate)) return result;
if (!(clientVersionDateSS >> fullTime)) return result;
if (fullDate.length()!=9) return result;
day = fullDate.substr(0, 2);
month = fullDate.substr(3, 3);
year = fullDate.substr(7, 2);
if (year.length()!=2) return result;
if (year[0]>='8')
year = "19"+year;
else
year = "20"+year;
break;
}
std::transform (month.begin(),month.end(), month.begin(), toupper);
if (!month.compare("JAN")) month="01";
if (!month.compare("FEB")) month="02";
if (!month.compare("MAR")) month="03";
if (!month.compare("APR")) month="04";
if (!month.compare("MAY")) month="05";
if (!month.compare("JUN")) month="06";
if (!month.compare("JUL")) month="07";
if (!month.compare("AUG")) month="08";
if (!month.compare("SEP")) month="09";
if (!month.compare("OCT")) month="10";
if (!month.compare("NOV")) month="11";
if (!month.compare("DEC")) month="12";
if (month.length()!=2) return result;
if (fullTime.length()!=8) return result;
hour = fullTime.substr(0, 2);
minute = fullTime.substr(3, 2);
second = fullTime.substr(6, 2);
SortTime = year + month + day + hour + minute + second;
ZMWSFileUtils zfu;
zfu.getFile(rr.getFilePath());
result = (zfu.getWriteDateSort() > SortTime);
return result;
}
int ZMWS::StartPHP(ZMWSReqResp& rr) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE_FILE hChildStdoutRd0, hChildStdoutRd, hChildStdoutWr;
if (!CreatePipe(&hChildStdoutRd0, &hChildStdoutWr, &sa, 0)) {
print_dbg("ERREUR: CreatePipe1");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
if (!DuplicateHandle(
GetCurrentProcess(), hChildStdoutRd0,
GetCurrentProcess(), &hChildStdoutRd,
0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
print_dbg("ERREUR: DuplicateHandle1");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
HANDLE_FILE hChildStdinRd, hChildStdinWr0, hChildStdinWr;
if (!CreatePipe(&hChildStdinRd, &hChildStdinWr0, &sa, 0)) {
print_dbg("ERREUR: CreatePipe2");
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
if (!DuplicateHandle(
GetCurrentProcess(), hChildStdinWr0,
GetCurrentProcess(), &hChildStdinWr,
0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
print_dbg("ERREUR: DuplicateHandle2");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = hChildStdinRd;
si.hStdOutput = hChildStdoutWr;
si.wShowWindow = SW_HIDE;
LPVOID MODULE_ENVIRONMENT = (LPVOID)SetupCgiEnvironment(rr);
BOOL retVal = ::CreateProcess(NULL,
(char*)(serverConfig.GetPathToPHP().c_str()),
NULL,
NULL,
TRUE,
0,
MODULE_ENVIRONMENT,
NULL,
&si,
&pi);
free (MODULE_ENVIRONMENT);
if (!retVal) {
print_dbg("ERREUR: Je n'ai pas lancé le module\n");
CloseFile(hChildStdinRd);
CloseFile(hChildStdinWr);
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
CloseThread(pi.hThread);
CloseProcess(pi.hProcess);
CloseFile(hChildStdinRd);
CloseFile(hChildStdoutWr);
rr.setHandle(&hChildStdoutRd);
DWORD ContentLength = 0;
std::string tmp = rr.getReqVar("CONTENT_LENGTH");
if (tmp.length()) ContentLength = strtoul(tmp.c_str(), NULL, 10);
char buff[8192];
DWORD nbOfCharRead, nbOfCharWritten, nbOfCharWritten_total = 0;
BOOL ret;
if (ContentLength) {
do {
if (rr.getIsSSL()) {
nbOfCharRead = modssl_functions.fnRead (rr.GetModSSLData(), (LPBYTE)buff, 8192);
} else {
// 14.05.2006: Déplacé ici pour ne pas faire le test en https
if (!WaitForInputOnSocket(*(rr.getSocket()))) break;
nbOfCharRead = recv(*(rr.getSocket()), buff, 8192, 0);
}
if ( nbOfCharRead > 0 ) {
DWORD delta = 0;
do {
ret = WriteFile(hChildStdinWr,
buff + delta,
nbOfCharRead - delta,
&nbOfCharWritten, NULL);
if (!ret) {
DWORD err = GetLastError();
print_dbg ("Erreur lors du passage des variables POST !\n");
}
delta+=nbOfCharWritten;
nbOfCharWritten_total += nbOfCharWritten;
} while (ret && (delta < nbOfCharRead));
}
} while ( ret && nbOfCharRead && ( nbOfCharWritten_total < ContentLength ) );
}
CloseFile(hChildStdinWr);
// Récupération d'un code HTTP différent de 200
std::string respL, respL_MAJ;
std::stringstream respLs;
do {
respL = GetResponseLine(*(rr.getHandle()));
respL_MAJ = respL;
std::transform (respL_MAJ.begin(),respL_MAJ.end(), respL_MAJ.begin(), toupper);
if (respL_MAJ.substr(0, 7) == "STATUS:") {
int deb = 7;
while (deb= 100 && rr.getRetCode() < 200) || // Information
(rr.getRetCode() == 204) || // No Content
(rr.getRetCode() == 304)) // Not Modified
))
{
// Envoi du reste
SendFile(rr);
// Fermeture
SendChunk(rr, NULL, 0);
}
CloseFile(hChildStdoutRd);
return rr.getRetCode();
}
int ZMWS::StartZMWSSAPIHandler(ZMWSReqResp& rr) {
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE_FILE hChildStdoutRd, hChildStdoutWr;
if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &sa, 0)) {
print_dbg("ERREUR: CreatePipe1");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
HANDLE_FILE hChildStdinRd, hChildStdinWr;
if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &sa, 0)) {
print_dbg("ERREUR: CreatePipe2");
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
// Lancement du handler
ZMWSHandler handler = rr.getHandler();
LPVOID MODULE_ENVIRONMENT = (LPVOID)SetupCgiEnvironment(rr);
ZMWSSAPI_DATA* zmwssapi_args = (ZMWSSAPI_DATA*)malloc(sizeof(ZMWSSAPI_DATA));
if (!zmwssapi_args) {
print_dbg("ERREUR: Create zmwssapi_args");
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
CloseFile(hChildStdinWr);
CloseFile(hChildStdinRd);
rr.setRetCode(500);
rr.setErrorMessage("No more memory ...");
DefaultErrorMessage (rr);
return rr.getRetCode();
}
zmwssapi_args->hIN = hChildStdinRd;
zmwssapi_args->hOUT = hChildStdoutWr;
zmwssapi_args->ENV = MODULE_ENVIRONMENT;
HANDLE_THREAD zsapi_handler = ZMWSSAPI::Run (handler.handlerPath.c_str(), zmwssapi_args);
// Lecture des retours
rr.setHandle(&hChildStdoutRd);
DWORD ContentLength = 0;
std::string tmp = rr.getReqVar("CONTENT_LENGTH");
if (tmp.length()) ContentLength = strtoul(tmp.c_str(), NULL, 10);
char buff[8192];
DWORD nbOfCharRead, nbOfCharWritten, nbOfCharWritten_total = 0;
BOOL ret;
if (ContentLength) {
do {
if (rr.getIsSSL()) {
nbOfCharRead = modssl_functions.fnRead (rr.GetModSSLData(), (LPBYTE)buff, 8192);
} else {
// 14.05.2006: Déplacé ici pour ne pas faire le test en https
if (!WaitForInputOnSocket(*(rr.getSocket()))) break;
nbOfCharRead = recv(*(rr.getSocket()), buff, 8192, 0);
}
if ( nbOfCharRead > 0 ) {
DWORD delta = 0;
do {
ret = WriteFile(hChildStdinWr,
buff + delta,
nbOfCharRead - delta,
&nbOfCharWritten, NULL);
if (!ret) {
DWORD err = GetLastError();
print_dbg ("Erreur lors du passage des variables POST !\n");
}
delta+=nbOfCharWritten;
nbOfCharWritten_total += nbOfCharWritten;
} while (ret && (delta < nbOfCharRead));
}
} while ( ret && nbOfCharRead && ( nbOfCharWritten_total < ContentLength ) );
}
// Récupération d'un code HTTP différent de 200
std::string respL, respL_MAJ;
std::stringstream respLs;
do {
respL = GetResponseLine(*(rr.getHandle()));
respL_MAJ = respL;
std::transform (respL_MAJ.begin(),respL_MAJ.end(), respL_MAJ.begin(), toupper);
if (respL_MAJ.substr(0, 7) == "STATUS:") {
int deb = 7;
while (deb= 100 && rr.getRetCode() < 200) || // Information
(rr.getRetCode() == 204) || // No Content
(rr.getRetCode() == 304)) // Not Modified
))
{
// Envoi du reste
SendFile(rr);
// Fermeture
SendChunk(rr, NULL, 0);
}
// Fermeture des handles
CloseFile(hChildStdinWr);
CloseFile(hChildStdoutRd);
// ZMWSSAPI Cleanup
return rr.getRetCode();
}
int ZMWS::StartHandler(ZMWSReqResp& rr) {
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
HANDLE_FILE hChildStdoutRd0, hChildStdoutRd, hChildStdoutWr;
if (!CreatePipe(&hChildStdoutRd0, &hChildStdoutWr, &sa, 0)) {
print_dbg("ERREUR: CreatePipe1");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
if (!DuplicateHandle(
GetCurrentProcess(), hChildStdoutRd0,
GetCurrentProcess(), &hChildStdoutRd,
0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
print_dbg("ERREUR: DuplicateHandle1");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
HANDLE_FILE hChildStdinRd, hChildStdinWr0, hChildStdinWr;
if (!CreatePipe(&hChildStdinRd, &hChildStdinWr0, &sa, 0)) {
print_dbg("ERREUR: CreatePipe2");
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
if (!DuplicateHandle(
GetCurrentProcess(), hChildStdinWr0,
GetCurrentProcess(), &hChildStdinWr,
0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
print_dbg("ERREUR: DuplicateHandle2");
rr.setRetCode(500);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
memset(&si, 0, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdInput = hChildStdinRd;
si.hStdOutput = hChildStdoutWr;
si.wShowWindow = SW_HIDE;
ZMWSHandler handler = rr.getHandler();
std::string commandLine = handler.handlerPath;
if (handler.handlerType == handler.DEFAULT) {
if (commandLine.length()) {
commandLine += " \""+rr.getFilePath()+"\"";
} else {
commandLine = "\""+rr.getFilePath()+"\"";
}
}
LPVOID MODULE_ENVIRONMENT = (LPVOID)SetupCgiEnvironment(rr);
BOOL retVal = ::CreateProcess(NULL,
(char*)((commandLine).c_str()),
NULL,
NULL,
TRUE,
0,
MODULE_ENVIRONMENT,
NULL,
&si,
&pi);
free (MODULE_ENVIRONMENT);
if (!retVal) {
print_dbg(("ERREUR: "+commandLine+"\n").c_str());
CloseFile(hChildStdinRd);
CloseFile(hChildStdinWr);
CloseFile(hChildStdoutWr);
CloseFile(hChildStdoutRd);
rr.setRetCode(500);
rr.setErrorMessage("Le handler n'a pas pu être exécuté\n");
DefaultErrorMessage (rr);
return rr.getRetCode();
}
// WaitForInputIdle(pi.hProcess, INFINITE);
CloseThread(pi.hThread);
CloseProcess(pi.hProcess);
CloseFile(hChildStdinRd);
CloseFile(hChildStdoutWr);
rr.setHandle(&hChildStdoutRd);
DWORD ContentLength = 0;
std::string tmp = rr.getReqVar("CONTENT_LENGTH");
if (tmp.length()) ContentLength = strtoul(tmp.c_str(), NULL, 10);
char buff[8192];
DWORD nbOfCharRead, nbOfCharWritten, nbOfCharWritten_total = 0;
BOOL ret;
if (ContentLength) {
do {
if (rr.getIsSSL()) {
nbOfCharRead = modssl_functions.fnRead (rr.GetModSSLData(), (LPBYTE)buff, 8192);
} else {
// 14.05.2006: Déplacé ici pour ne pas faire le test en https
if (!WaitForInputOnSocket(*(rr.getSocket()))) break;
nbOfCharRead = recv(*(rr.getSocket()), buff, 8192, 0);
}
if ( nbOfCharRead > 0 ) {
DWORD delta = 0;
do {
ret = WriteFile(hChildStdinWr,
buff + delta,
nbOfCharRead - delta,
&nbOfCharWritten, NULL);
if (!ret) {
DWORD err = GetLastError();
print_dbg ("Erreur lors du passage des variables POST !\n");
}
delta+=nbOfCharWritten;
nbOfCharWritten_total += nbOfCharWritten;
} while (ret && (delta < nbOfCharRead));
}
} while ( ret && nbOfCharRead && ( nbOfCharWritten_total < ContentLength ) );
}
CloseFile(hChildStdinWr);
// Récupération d'un code HTTP différent de 200
std::string respL, respL_MAJ;
std::stringstream respLs;
do {
respL = GetResponseLine(*(rr.getHandle()));
respL_MAJ = respL;
std::transform (respL_MAJ.begin(),respL_MAJ.end(), respL_MAJ.begin(), toupper);
if (respL_MAJ.substr(0, 7) == "STATUS:") {
int deb = 7;
while (deb= 100 && rr.getRetCode() < 200) || // Information
(rr.getRetCode() == 204) || // No Content
(rr.getRetCode() == 304)) // Not Modified
))
{
// Envoi du reste
SendFile(rr);
// Fermeture
SendChunk(rr, NULL, 0);
}
CloseFile(hChildStdoutRd);
return rr.getRetCode();
}
int ZMWS::SendRSSDirListing(ZMWSReqResp& rr) {
std::string tmpPath;
ZMWSFileUtils zfu;
std::string urlPrefix = rr.getIsSSL() ? "https://" : "http://";
if (rr.getReqVar("HOST").length()) {
urlPrefix += rr.getReqVar("HOST"); // Contient déjà le port
} else {
rr.setRetCode (400);
rr.setErrorMessage ("Missing 'Host' header, RSS listing is only available to HTTP/1.1 clients");
return rr.getRetCode();
}
tmpPath = rr.getFilePath() + "/*.*";
if (zfu.getFile(tmpPath)) {
rr.setRespVar("Content-Type", "application/xml");
rr.setRetCode(200);
SendHeaders(rr);
if (rr.getReqMethod() == HTTP_HEAD)
return rr.getRetCode();
std::string thisDirS = rr.getURL();
std::string thisDirLink = thisDirS;
std::string Header = "\n";
ZMWSFileUtils::normalizeUrl(thisDirLink);
std::ifstream here_headers ((rr.getFilePath() + "/_rssIndex.zmwsc").c_str());
if (here_headers) {
std::string header_string;
while (getline(here_headers, header_string)) {
Header += header_string + "\n";
}
here_headers.close();
}
Header += "\n";
Header += "\n";
Header += ""+thisDirS+" sur "+rr.getReqVar("HOST")+"\n";
Header += "Listing des fichiers de "+thisDirS+" sur "+rr.getReqVar("HOST")+"\n";
Header += ""+urlPrefix+thisDirLink+"\n";
SendChunk(rr, (void*)Header.c_str(), Header.length());
BOOL IndexContinue = TRUE;
do {
std::string NameStr = zfu.getFileName();
std::string NameLink = NameStr;
std::stringstream ResponseSS;
std::string SaveTimeStrS;
ZMWSFileUtils::normalizeUrl(NameLink);
if (NameStr == ".") continue;
if (NameStr == "..") continue;
if (NameStr.find(".zmwsc")!=std::string::npos) continue;
SaveTimeStrS = zfu.getWriteDateRFC1123();
ResponseSS << "- \n" ;
ResponseSS << "" << NameStr << "\n";
ResponseSS << "" << SaveTimeStrS << "\n";
ResponseSS << "" << urlPrefix << thisDirLink << NameLink << "?" << zfu.getWriteDateSort() << "\n";
ResponseSS << "
\n" ;
SendChunk(rr, (void*)ResponseSS.str().c_str(), ResponseSS.str().length());
} while (zfu.getNextFile());
std::string Footer = "\n";
Footer += "\n";
SendChunk(rr, (void*)Footer.c_str(), Footer.length());
SendChunk(rr, NULL, 0);
} else {
rr.setRetCode(404);
}
return rr.getRetCode();
}
int ZMWS::SendXMLDirListing(ZMWSReqResp& rr) {
std::string tmpPath;
ZMWSFileUtils zfu;
tmpPath = rr.getFilePath() + "/*.*";
if (zfu.getFile(tmpPath)) {
rr.setRespVar("Content-Type", "application/xml");
rr.setRetCode(200);
SendHeaders(rr);
if (rr.getReqMethod() == HTTP_HEAD)
return rr.getRetCode();
std::string thisDirS = rr.getURL();
std::string Header = "\n";
std::ifstream here_headers ((rr.getFilePath() + "/_xmlIndex.zmwsc").c_str());
if (here_headers) {
std::string header_string;
while (getline(here_headers, header_string)) {
Header += header_string + "\n";
}
here_headers.close();
}
Header += "\n";
Header += ""+thisDirS+"\n";
Header += "\n";
SendChunk(rr, (void*)Header.c_str(), Header.length());
BOOL IndexContinue = TRUE;
do {
std::string NameStr = zfu.getFileName();
std::stringstream ResponseSS;
std::string SaveTimeStrS;
if (NameStr == ".") continue;
if (NameStr == "..") continue;
if (NameStr.find(".zmwsc")!=std::string::npos) continue;
ZMWSFileUtils::normalizeUrl(NameStr);
SaveTimeStrS = zfu.getWriteDate();
if ( zfu.isDir() ) {
ResponseSS << "\n" ;
ResponseSS << "" << NameStr << "\n";
ResponseSS << "" << zfu.getFileName() << "\n";
ResponseSS << "" << SaveTimeStrS << "\n";
ResponseSS << "\n" ;
} else {
ResponseSS << "\n" ;
ResponseSS << "" << NameStr << "\n";
ResponseSS << "" << zfu.getFileName() << "\n";
ResponseSS << "" << SaveTimeStrS << "\n";
ResponseSS << "" << zfu.getFileSizeH() << "\n";
ResponseSS << "" << zfu.getFileSize() << "\n";
ResponseSS << "\n" ;
}
SendChunk(rr, (void*)ResponseSS.str().c_str(), ResponseSS.str().length());
} while (zfu.getNextFile());
std::string Footer = "\n";
if (serverConfig.GetPBToken() != PBTOKEN_OFF) {
Footer += "";
Footer += rr.getRespVar("Server");
Footer += "\n";
}
Footer += "\n";
SendChunk(rr, (void*)Footer.c_str(), Footer.length());
SendChunk(rr, NULL, 0);
} else {
rr.setRetCode(404);
}
return rr.getRetCode();
}
int ZMWS::SendDirListing(ZMWSReqResp& rr) {
std::string tmpPath;
typedef std::pair DWSTRPair;
typedef std::pair STRSTRPair;
typedef std::multimap::reverse_iterator DWSTRMapRIterator;
typedef std::multimap::reverse_iterator STRSTRMapRIterator;
typedef std::multimap::iterator STRSTRMapIterator;
std::multimap StringSortedMapD;
std::multimap DWORDSortedMapF;
std::multimap StringSortedMapF;
BOOL canIndex;
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc)) {
canIndex = vhc.canIndex;
} else {
canIndex = serverConfig.GetCanIndex();
}
if (!canIndex) {
if (!ZMWSFileUtils::fileExists(rr.getFilePath() + "/_canIndex.zmwsc")) {
rr.setRetCode(403);
rr.setErrorMessage("Directory listing is disabled ...");
return rr.getRetCode();
}
}
if (ZMWSFileUtils::fileExists(rr.getFilePath() + "/_rssIndex.zmwsc")) {
SendRSSDirListing (rr);
return rr.getRetCode();
}
if (ZMWSFileUtils::fileExists(rr.getFilePath() + "/_xmlIndex.zmwsc")) {
SendXMLDirListing (rr);
return rr.getRetCode();
}
std::string dirico, ficico, upico;
ZMWSFileUtils zfu;
tmpPath = rr.getReqRootDir()+"/icons/dir.*";
ZMWSFileUtils::normalizePath(tmpPath);
if (zfu.getFile(tmpPath)) {
dirico = "/icons/";
dirico += zfu.getFileName();
}
tmpPath = rr.getReqRootDir()+"/icons/fic.*";
ZMWSFileUtils::normalizePath(tmpPath);
if (zfu.getFile(tmpPath)) {
ficico = "/icons/";
ficico += zfu.getFileName();
}
tmpPath = rr.getReqRootDir()+"/icons/up.*";
ZMWSFileUtils::normalizePath(tmpPath);
if (zfu.getFile(tmpPath)) {
upico = "/icons/";
upico += zfu.getFileName();
}
tmpPath = rr.getFilePath() + "/*.*";
ZMWSFileUtils::normalizePath(tmpPath);
if (zfu.getFile(tmpPath)) {
rr.setRespVar("Content-Type", "text/html; charset=iso-8859-1");
rr.setRetCode(200);
SendHeaders(rr);
if (rr.getReqMethod() == HTTP_HEAD)
return rr.getRetCode();
std::string thisDirS = rr.getURL();
std::string Header = "\n";
Header += "\n";
Header += "\n";
Header += "Index : "+thisDirS+"\n";
Header += "\n";
Header += "\n";
Header += "\n";
Header += "Index de ";
Header += thisDirS;
Header += "
\n
\n\n";
Header += "| Nom | ";
Header += "Taille | ";
Header += "Modification (GMT) |
\n";
SendChunk(rr, (void*)Header.c_str(), Header.length());
BOOL IndexContinue = TRUE;
do {
std::string NameStr = zfu.getFileName();
if (NameStr == ".") continue;
if (NameStr.find(".zmwsc")!=std::string::npos) continue;
std::stringstream ImgStrSS;
std::stringstream ResponseSS;
std::string SizeStrS;
std::string SaveTimeStrS;
if (NameStr == "..") {
NameStr = "Remonter d'un niveau";
if (upico.length()) ImgStrSS << "
";
ResponseSS << "| " << ImgStrSS.str() << " " << NameStr << " | " << SizeStrS
<< " | " << SaveTimeStrS << " |
\n";
SendChunk(rr, (void*)ResponseSS.str().c_str(), ResponseSS.str().length());
continue;
}
ZMWSFileUtils::normalizeUrl(NameStr);
SaveTimeStrS = zfu.getWriteDate();
if ( zfu.isDir() ) {
SizeStrS = "DIR";
if (dirico.length()) ImgStrSS << "
";
} else {
SizeStrS = zfu.getFileSizeH();
if (ficico.length()) ImgStrSS << "
";
}
ResponseSS << "| " << ImgStrSS.str() << " " << zfu.getFileName() << " | " << SizeStrS
<< " | " << SaveTimeStrS << " |
\n";
if (rr.getReqVar("QUERY_STRING").find("S=S")!=std::string::npos) {
if (zfu.isDir())
StringSortedMapD.insert(STRSTRPair(zfu.getFileName(), ResponseSS.str()));
else
DWORDSortedMapF.insert(DWSTRPair(zfu.getFileSizeN(), ResponseSS.str()));
} else if (rr.getReqVar("QUERY_STRING").find("S=D")!=std::string::npos) {
if (zfu.isDir())
StringSortedMapD.insert(STRSTRPair(zfu.getWriteDateSort(), ResponseSS.str()));
else
StringSortedMapF.insert(STRSTRPair(zfu.getWriteDateSort(), ResponseSS.str()));
} else {
if (zfu.isDir())
StringSortedMapD.insert(STRSTRPair(zfu.getFileName(), ResponseSS.str()));
else
StringSortedMapF.insert(STRSTRPair(zfu.getFileName(), ResponseSS.str()));
}
} while (zfu.getNextFile());
if (rr.getReqVar("QUERY_STRING").find("S=S")!=std::string::npos) {
STRSTRMapIterator itS;
DWSTRMapRIterator ritD;
for (itS = StringSortedMapD.begin(); itS != StringSortedMapD.end(); ++itS) {
SendChunk(rr, (void*)itS->second.c_str(), itS->second.length());
}
for (ritD = DWORDSortedMapF.rbegin(); ritD != DWORDSortedMapF.rend(); ++ritD) {
SendChunk(rr, (void*)ritD->second.c_str(), ritD->second.length());
}
} else if (rr.getReqVar("QUERY_STRING").find("S=D")!=std::string::npos) {
STRSTRMapRIterator rit;
for (rit = StringSortedMapD.rbegin(); rit != StringSortedMapD.rend(); ++rit) {
SendChunk(rr, (void*)rit->second.c_str(), rit->second.length());
}
for (rit = StringSortedMapF.rbegin(); rit != StringSortedMapF.rend(); ++rit) {
SendChunk(rr, (void*)rit->second.c_str(), rit->second.length());
}
} else {
STRSTRMapIterator it;
for (it = StringSortedMapD.begin(); it != StringSortedMapD.end(); ++it) {
SendChunk(rr, (void*)it->second.c_str(), it->second.length());
}
for (it = StringSortedMapF.begin(); it != StringSortedMapF.end(); ++it) {
SendChunk(rr, (void*)it->second.c_str(), it->second.length());
}
}
std::string Footer = "
\n
\n";
if (serverConfig.GetPBToken() != PBTOKEN_OFF) {
Footer += "Powered by ";
Footer += rr.getRespVar("Server");
Footer += "
\n";
}
Footer += "\n\n";
SendChunk(rr, (void*)Footer.c_str(), Footer.length());
SendChunk(rr, NULL, 0);
} else {
rr.setRetCode(404);
}
return rr.getRetCode();
}
int ZMWS::GetStartPage(ZMWSReqResp& rr, ZMWSFileUtils& indexZFU) {
std::string startPages = serverConfig.GetStartPages();
std::string::size_type pos1 = 0, pos2 = 0;
std::string pattern;
std::string::size_type pattern_length;
do {
// On cherche le motif
pos2 = startPages.find(';', pos1);
if ((pos2 != std::string::npos)) {
pattern_length = pos2-pos1;
} else {
pattern_length = startPages.length()-pos1;
}
pattern = startPages.substr(pos1, pattern_length);
// On avance pos1
pos1 += pattern_length + 1;
// On cherche un fichier correpondant
if (!indexZFU.getFile(rr.getFilePath() + '\\' + pattern)) {
continue;
}
// Tant que c'est un répertoire ça ne nous intéresse pas
while (indexZFU.isDir()) {
if (!indexZFU.getNextFile()) break;
}
// Si c'est un fichier, on a gagné //
} while ((!indexZFU.isFile()) && (pos2 != std::string::npos));
return rr.getRetCode();
}
BOOL ZMWS::FindWithLangNegotiation (ZMWSReqResp& rr, std::string FilePath) {
// On tente les .lang
ZMWSFileUtils zfu;
langs_map_type langs = rr.getLangsMap();
langs_map_type::reverse_iterator lang_it;
for (lang_it = langs.rbegin() ; lang_it != langs.rend() ; ++lang_it) {
if (lang_it->first == 0) continue;
std::string searchPath = FilePath + "." + lang_it->second;
std::string::size_type extensionPos, typeExtensionPos, lastSlashPos;
std::string extension;
if (zfu.getFile(searchPath) && zfu.isFile()) {
extensionPos = zfu.getFileName().rfind('.');
if (extensionPos != std::string::npos) {
extension = zfu.getFileName().substr(extensionPos+1);
} else {
extension ="";
}
typeExtensionPos = zfu.getFileName().rfind('.', extensionPos-1);
if (typeExtensionPos != std::string::npos) {
extension = zfu.getFileName().substr(typeExtensionPos+1, extensionPos-typeExtensionPos-1);
} else {
extension ="";
}
lastSlashPos = FilePath.find_last_of("/\\");
rr.setFilePath(FilePath.substr(0, lastSlashPos+1)+zfu.getFileName());
rr.setFileExtension (extension);
return TRUE;
}
}
return FALSE;
}
BOOL ZMWS::FindWithMultiview (ZMWSReqResp& rr, std::string FilePath) {
/* if (FindWithLangNegotiation(rr, FilePath+".*")) {
return TRUE;
}
*/
// Si on n'a rien trouvé encore, on essaie un .*
ZMWSFileUtils zfu;
std::string searchPath = FilePath + ".*";
std::string::size_type extensionPos;
std::string extension;
if (zfu.getFile(searchPath) && zfu.isFile()) {
extensionPos = zfu.getFileName().rfind('.');
if (extensionPos != std::string::npos) {
extension = zfu.getFileName().substr(extensionPos+1);
} else {
extension ="";
}
rr.setFilePath(FilePath+"."+extension);
rr.setFileExtension (extension);
return TRUE;
}
return FALSE;
}
int ZMWS::FindLocalFile (ZMWSReqResp& rr, std::string FilePath) {
ZMWSFileUtils zfu;
int next_path_pos = FilePath.find_last_of("/\\");
std::string OrigFilePath = FilePath;
std::string rootDir = rr.getAliasedRootDir().length() ?
rr.getAliasedRootDir() : rr.getReqRootDir();
// Add support for multiviews here
while (next_path_pos != std::string::npos) {
if (FindWithMultiview(rr, FilePath)) {
std::string path_info = OrigFilePath.substr(FilePath.length());
rr.setReqVar("PATH_INFO", path_info);
rr.setRetCode(200);
return rr.getRetCode();
}
FilePath = FilePath.substr(0, next_path_pos);
if (zfu.getFile(FilePath) && zfu.isFile()) {
std::string path_info = OrigFilePath.substr(FilePath.length());
rr.setReqVar("PATH_INFO", path_info);
// Non, php s'en sert pour savoir de quel fichier on parle
// rr.setReqVar("PATH_TRANSLATED", rr.getReqRootDir()+path_info);
rr.setFilePath(FilePath);
rr.setRetCode(200);
return rr.getRetCode();
}
next_path_pos = FilePath.find_last_of("/\\", FilePath.length()-2);
if (FilePath.length() <= rootDir.length()) {
rr.setRetCode(404);
break;
}
}
return rr.getRetCode();
}
int ZMWS::GetLocalFile(ZMWSReqResp& rr) {
// disallow ".." sequences in the urls
if (rr.getURL().find("..")!=std::string::npos) {
rr.setRetCode(403);
return rr.getRetCode();
}
if (rr.getURL().find(".zmwsc")!=std::string::npos) {
// zmwsc dirs and files do NOT exist !
rr.setRetCode(403);
return rr.getRetCode();
}
// Get the VHostConfig if any
ZMWSVHostConfig vhc;
serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc);
// Alias rules
std::string FilePath;
std::string AliasedReqRootDir;
std::string UrlToFile = serverConfig.GetAliasedDir(rr.getURL(), AliasedReqRootDir, vhc.domain);
if (UrlToFile.length()) {
ZMWSFileUtils::normalizePath(AliasedReqRootDir);
rr.setAliasedRootDir(AliasedReqRootDir);
FilePath = UrlToFile;
} else {
FilePath = rr.getReqRootDir() + rr.getURL();
}
rr.setFilePath(FilePath);
std::string rootDir = AliasedReqRootDir.length() ? AliasedReqRootDir : rr.getReqRootDir();
ZMWSFileUtils zfu;
ZMWSFileUtils indexZFU;
// Directory index ou redirection pour / manquant à la fin
if (rr.getFilePath() == rootDir) {
// Permet de passer outre le fait que findFirstFile sur des racines de lecteurs
// ne fonctionne pas comme avec les autres chemins
if ( rr.getURL().at(rr.getURL().length()-1) != '/') {
rr.setURL(rr.getURL()+'/');
rr.setRetCode(301);
Redirect(rr, rr.getURL());
} else {
GetStartPage(rr, indexZFU);
if (indexZFU.isFile()) {
rr.setFilePath(rr.getFilePath() + '\\' + indexZFU.getFileName());
// rr.setURL(rr.getURL()+indexZFU.getFileName());
} else {
rr.setIsDirectory();
}
}
} else if (zfu.getFile(rr.getFilePath()) && zfu.isDir()) {
if ( rr.getURL().at(rr.getURL().length()-1) != '/') {
rr.setURL(rr.getURL()+'/');
rr.setRetCode(301);
Redirect(rr, rr.getURL());
} else {
GetStartPage(rr, indexZFU);
if (indexZFU.isFile()) {
rr.setFilePath(rr.getFilePath() + '\\' + indexZFU.getFileName());
// rr.setURL(rr.getURL()+indexZFU.getFileName());
} else {
rr.setIsDirectory();
}
}
} else {
// Regular file
if (!ZMWSFileUtils::fileExists(rr.getFilePath())) {
rr.setRetCode(404);
// Try to find a path info (with multiviews)
FindLocalFile(rr, FilePath);
}/* else {
FindWithLangNegotiation(rr, FilePath);
}*/
}
return rr.getRetCode();
}
void ZMWS::DefaultErrorMessage ( ZMWSReqResp& rr ) {
HTTP_RETCODE rc = rr.getRetCode();
if ((rc >= 100 && rc < 200) || // Information
(rc == 204) || // No Content
(rc == 304)) { // Not Modified
// No Message Body Here !
SendHeaders(rr);
} else {
rr.setRespVar("Content-Type", "text/html");
std::stringstream ResponseBody;
ResponseBody << "\n";
ResponseBody << "\n";
ResponseBody << "\n";
ResponseBody << "Erreur :" << rr.getRetCode() << "\n";
// ResponseBody << "\n";
ResponseBody << "\n";
ResponseBody << "\nErreur " << rr.getRetCode() << "
\n
\n";
ResponseBody << "Il y a eu une erreur
\nLe code est ";
ResponseBody << rr.getRetCode() << ".
\n";
if (rr.getErrorMessage().length())
ResponseBody << "
\nMessage Serveur:
" << rr.getErrorMessage() << "
\n";
std::string Footer = "
\n";
if (serverConfig.GetPBToken() != PBTOKEN_OFF) {
Footer += "Powered by ";
Footer += rr.getRespVar("Server");
Footer += "
\n";
}
Footer += "\n\n";
ResponseBody << Footer;
std::stringstream taille;
taille << ResponseBody.str().length();
rr.setRespVar("Content-Length", taille.str());
SendHeaders(rr);
SendChunk(rr, (void*)ResponseBody.str().c_str(), ResponseBody.str().length());
SendChunk(rr, NULL, 0);
}
}
int ZMWS::Reply( ZMWSReqResp& rr ) {
// vhost check :-)
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc)) {
// map url to local path
rr.setReqRootDir(vhc.documentRoot);
} else {
// map url to local path
rr.setReqRootDir(serverConfig.GetDocumentRoot());
}
// Allow from
if (!CheckClientIPOK(rr)) {
rr.cnx_policy = CNX_POLICY_CLOSE;
} else {
// Arrêt du serveur ?
// Uniquement depuis la machine locale
if ((serverConfig.GetCanStop()) &&
(rr.getURL()=="/_stopServer.zmwsc") &&
(rr.getReqVar("REMOTE_ADDR")=="127.0.0.1")) {
rr.cnx_policy = CNX_POLICY_CLOSE;
if (serverConfig.GetCloseBrowser()) CloseBrowser(rr);
StopServer(TRUE); // TRUE == Post a Quit Message
return rr.getRetCode();
}
// Limitation de traffic ?
if (CheckSentBytesOK(rr)) {
// Lien URI --- Fichier Local
if (rr.getRetCode() == 200) {
int retcode = GetLocalFile(rr);
if (retcode != 200) {
// les redirections sont prises en charge avant ...
// le cache est pris en charge ensuite ...
if (retcode<400) return retcode;
}
}
// Authentification
// Après avoir cherché le fichier local
if (FindAuthFile(rr)) {
if (rr.getRetCode()!=500) {
if (!rr.getReqVar("AUTHORIZATION").length() || !AuthenticateUser(rr)) {
rr.setRetCode(401);
SendAuthChallenge(rr);
}
}
}
// C'est un indexage de répertoires ?
if (rr.getIsDirectory() && (rr.getRetCode() == 200)) {
rr.setRetCode(SendDirListing(rr));
if (rr.getRetCode() == 200) {
return rr.getRetCode();
}
}
}
}
// Erreurs
if (rr.getRetCode() >= 400) {
// On cherche une page d'erreur personnalisée
ZMWSFileUtils zfu;
std::stringstream pageName;
std::string pathprefix;
// On la cherche dans l'arborescence du VHost
pathprefix = rr.getReqRootDir();
pageName << pathprefix << "/_errorpages.zmwsc/" << rr.getRetCode() << ".*";
zfu.getFile(pageName.str());
if (!zfu.fileExists()) {
std::stringstream pageName;
pageName << pathprefix << "/_errorpages.zmwsc/default.*";
zfu.getFile(pageName.str());
}
// Si on n'a rien, on recherche dans l'arborescence principale
if (!zfu.fileExists()) {
pathprefix = serverConfig.GetDocumentRoot();
std::stringstream pageName;
pageName << pathprefix << "/_errorpages.zmwsc/" << rr.getRetCode() << ".*";
zfu.getFile(pageName.str());
}
if (!zfu.fileExists()) {
std::stringstream pageName;
pageName << pathprefix << "/_errorpages.zmwsc/default.*";
zfu.getFile(pageName.str());
}
// Si on a trouvé une page d'erreur on l'utilise !
if (zfu.fileExists()) {
std::string errPagePath(pathprefix);
errPagePath += "/_errorpages.zmwsc/";
errPagePath += zfu.getFileName();
rr.setFilePath(errPagePath);
} else {
// Body par défaut des erreurs
DefaultErrorMessage (rr);
return rr.getRetCode();
}
}
HANDLE_FILE FileHandle;
if ((FileHandle = CreateFile(rr.getFilePath().c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE) {
rr.setRetCode(404); // Limite 500 là ...
rr.setErrorMessage("Could not open: "+rr.getFilePath());
DefaultErrorMessage (rr);
} else {
ZMWSVHostConfig vhc;
serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc);
// Lancements des modules s'il y a besoin
ZMWSFileUtils zfu;
if (zfu.getFile(rr.getFilePath())) {
std::string extension = rr.getFileExtension();
if (!extension.length()) {
std::string::size_type extensionPos = zfu.getFileName().rfind('.');
if (extensionPos != std::string::npos) {
rr.setFileExtension (zfu.getFileName().substr(extensionPos+1));
extension = rr.getFileExtension();
}
}
if ( extension.length() ) {
ZMWSPerUrlConfig puc;
if (serverConfig.GetPerUrlConfig(rr.getURL(), puc)) {
ZMWSHandler handler = puc.GetHandler(extension);
if (handler.handlerType != handler.NONE) {
CloseFile(FileHandle);
rr.setHandler(handler);
StartHandler(rr);
return rr.getRetCode();
}
}
ZMWSHandler handler = serverConfig.GetHandler(extension, vhc.domain);
switch (handler.handlerType) {
case handler.DEFAULT:
case handler.TRUECGI:
CloseFile(FileHandle);
rr.setHandler(handler);
StartHandler(rr);
return rr.getRetCode();
case handler.ZMWSSAPI:
CloseFile(FileHandle);
rr.setHandler(handler);
StartZMWSSAPIHandler(rr);
return rr.getRetCode();
default:
break;
}
if (serverConfig.GetPathToPHP().length() && extension.substr(0,3) == "php") {
CloseFile(FileHandle);
StartPHP(rr);
return rr.getRetCode();
}
}
// Vérification du champ if Modified Since
// On n'envoie pas un fichier que le client a en cache !
if (rr.getReqVar("IF_MODIFIED_SINCE").length()) {
if (!LocalFileIsMoreRecentThanClientCache(rr)) {
CloseFile(FileHandle);
rr.setRetCode(304);
DefaultErrorMessage (rr);
return rr.getRetCode();
}
}
// Traitement normal pour les fichiers statiques
// On accepte les ranges et on spécifie la date de modification
rr.setRespVar("Accept-Ranges", "bytes");
rr.setRespVar("Last-Modified", zfu.getWriteDateRFC1123());
rr.setRespVar("Content-Type", MIME_Manager.getMimeType(extension));
rr.setRespVar("Content-Length", zfu.getFileSize());
// si range dans rr alors :
// - retcode à 206
// - entêtes Content-Range correspondantes
// - mise à jour du Content-Length pour avoir le nombre d'octets envoyés
// TODO: pour plusieurs plages, envoyer en Content-Type: multipart/byteranges
ZMWSRange rangeReq = rr.popRangeReq();
if (rangeReq.range_spec_type != ZMWSRange::INVALID_RANGE_SPEC) {
unsigned long fbyte, lbyte;
switch (rangeReq.range_spec_type) {
case ZMWSRange::BYTE_RANGE_SPEC:
fbyte = rangeReq.begin;
lbyte = rangeReq.end;
break;
case ZMWSRange::PREFIX_BYTE_RANGE_SPEC:
fbyte = rangeReq.begin;
lbyte = zfu.getFileSizeN()-1;
break;
case ZMWSRange::SUFFIX_BYTE_RANGE_SPEC:
fbyte = zfu.getFileSizeN()- 1 - rangeReq.end;
lbyte = zfu.getFileSizeN()- 1;
break;
}
if (lbyte >= zfu.getFileSizeN()){
rr.setRetCode(416);
rr.setRespVar("Content-Range", "bytes */" + zfu.getFileSize());
} else if (lbyte >= fbyte) {
rr.setRetCode(206);
std::stringstream content_range, content_length;
content_range << "bytes " << fbyte << "-" << lbyte;
content_range << '/' << zfu.getFileSize();
content_length << lbyte - fbyte + 1;
rr.setRespVar("Content-Range", content_range.str());
rr.setRespVar("Content-Length", content_length.str());
rr.pushContentRangeResp(ZMWSContentRange(fbyte, lbyte, zfu.getFileSizeN()));
}
}
}
SendHeaders(rr);
rr.setHandle(&FileHandle);
if (rr.getReqMethod()!=HTTP_HEAD) {
SendFile (rr);
}
CloseFile(FileHandle);
}
return rr.getRetCode();
}
int ZMWS::SendFile ( ZMWSReqResp& rr ) {
char buffer[4096];
DWORD NbBytes, BytesRead, RemainingBytes = 0;
BOOL RetCode, Ranged = FALSE;
ZMWSContentRange content_range;
if (rr.getRetCode()==206) {
content_range = rr.popContentRangeResp();
if (content_range.isValid()) {
SetFilePointer(*(rr.getHandle()), content_range.begin, 0, FILE_BEGIN);
RemainingBytes = content_range.end - content_range.begin + 1;
Ranged = TRUE;
}
}
do {
NbBytes = 4096;
if (Ranged && (RemainingBytes < 4096)) {
NbBytes = RemainingBytes;
}
if (RetCode = ReadFile(*(rr.getHandle()), buffer, NbBytes, &BytesRead, NULL)) {
if (SendChunk(rr, buffer, BytesRead) == SOCKET_ERROR) {
// Supprimé pour le cas des Handlers ...
// TODO: Voir s'il faut distinguer fichier statique/handler
// RetCode = 0;
// On finit quand même la lecture ...
// TODO: Arranger ça
// Normalement avec le ménage fait dans les CloseHandle
// ça doit être bon maintenant, même sans lire la fin de
// ce que génère le Handler
// A VERIFIER !
//
// J'ai supprimé la lecture du reste, sinon, c'est sans fin
// while (ReadFile(*(rr.getHandle()), buffer, NbBytes, &BytesRead, NULL) && BytesRead);
// Remplacé par :
break;
}
}
if (Ranged) {
RemainingBytes -= BytesRead;
if (!RemainingBytes) break;
}
} while ( RetCode && BytesRead );
return rr.getRetCode();
}
int ZMWS::SendChunk(ZMWSReqResp& rr, void* chunk, const DWORD& chunksize, const BOOL is_header) {
DWORD nBytes;
DWORD TotBytesSent = 0;
DWORD BytesSent = 0;
BOOL RetCode = rr.getRetCode();
BOOL chunked = (rr.getRespVar("Transfer-Encoding")=="Chunked");
if (!chunk) {
if (chunked) {
std::string endchunk = "0\r\n\r\n";
if (rr.getIsSSL()) {
if (BytesSent = modssl_functions.fnWrite(rr.GetModSSLData(), (LPBYTE)endchunk.c_str(), endchunk.length()) <= 0) {
RetCode = SOCKET_ERROR;
} else {
// print_dbg((const char *)endchunk.c_str());
rr.addSentBytes (BytesSent);
}
} else {
if (BytesSent = send(*(rr.getSocket()), (const char *)endchunk.c_str(), endchunk.length(), 0) == SOCKET_ERROR) {
RetCode = SOCKET_ERROR;
} else {
// print_dbg((const char *)endchunk.c_str());
rr.addSentBytes (BytesSent);
}
}
}
return RetCode;
}
char buffer[8192];
do {
memset(buffer, 0, 8192);
nBytes = (chunksize - TotBytesSent > 4096) ? 4096 : chunksize - TotBytesSent;
if (!chunked || is_header) {
if (rr.getIsSSL()) {
if (BytesSent = modssl_functions.fnWrite(rr.GetModSSLData(), (LPBYTE)chunk + TotBytesSent, nBytes) <= 0) {
RetCode = SOCKET_ERROR;
break;
} else {
// print_dbg((const char *)endchunk.c_str());
rr.addSentBytes (BytesSent);
}
} else {
if ((BytesSent = send(*(rr.getSocket()), (const char *)chunk + TotBytesSent, nBytes, 0)) == SOCKET_ERROR) {
RetCode = SOCKET_ERROR;
break;
} else {
// print_dbg((const char *)chunk);
rr.addSentBytes (BytesSent);
}
}
} else {
std::stringstream chunkheader;
chunkheader.setf(std::ios::hex, std::ios::basefield);
chunkheader << nBytes << "\r\n";
std::stringstream chunkfooter;
chunkfooter << "\r\n";
int bufferpos = 0;
memcpy (buffer + bufferpos, chunkheader.str().c_str(), chunkheader.str().length());
bufferpos += chunkheader.str().length();
memcpy (buffer + bufferpos, (char*)chunk + TotBytesSent, nBytes);
bufferpos += nBytes;
memcpy (buffer + bufferpos, chunkfooter.str().c_str(), chunkfooter.str().length());
bufferpos += chunkfooter.str().length();
if (rr.getIsSSL()) {
if (BytesSent = modssl_functions.fnWrite(rr.GetModSSLData(), (LPBYTE)buffer, bufferpos) <= 0) {
RetCode = SOCKET_ERROR;
break;
} else {
// print_dbg((const char *)endchunk.c_str());
rr.addSentBytes (BytesSent);
}
} else {
if ((BytesSent = send(*(rr.getSocket()), (const char *)buffer, bufferpos, 0)) == SOCKET_ERROR) {
RetCode = SOCKET_ERROR;
break;
} else {
// print_dbg((const char *)buffer);
rr.addSentBytes (BytesSent);
}
}
}
TotBytesSent += nBytes;
} while ( TotBytesSent < chunksize );
return RetCode;
}
int ZMWS::LogRequest(ZMWSReqResp& rr) {
if (!serverConfig.GetWriteLogs()) return 1;
std::string FilePath;
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc)) {
FilePath = vhc.logsDir+"/access_log_"+vhc.domain;
} else {
FilePath = serverConfig.GetLogsDir()+"/access_log";
}
std::stringstream LogLine;
if (rr.getReqVar("REMOTE_HOST").length())
LogLine << rr.getReqVar("REMOTE_HOST");
else
LogLine << rr.getReqVar("REMOTE_ADDR");
LogLine << " - - [";
SYSTEMTIME lt, st;
GetSystemTime(&st);
GetLocalTime(<);
if (lt.wDay<10) LogLine << '0';
LogLine << lt.wDay << '/';
switch (lt.wMonth) {
case 1:
LogLine << "Jan/";
break;
case 2:
LogLine << "Feb/";
break;
case 3:
LogLine << "Mar/";
break;
case 4:
LogLine << "Apr/";
break;
case 5:
LogLine << "May/";
break;
case 6:
LogLine << "Jun/";
break;
case 7:
LogLine << "Jul/";
break;
case 8:
LogLine << "Aug/";
break;
case 9:
LogLine << "Sep/";
break;
case 10:
LogLine << "Oct/";
break;
case 11:
LogLine << "Nov/";
break;
case 12:
LogLine << "Dec/";
break;
}
if (lt.wYear<10) LogLine << "0";
LogLine << lt.wYear << ":";
if (lt.wHour<10) LogLine << "0";
LogLine << lt.wHour << ":";
if (lt.wMinute<10) LogLine << "0";
LogLine << lt.wMinute << ":";
if (lt.wSecond<10) LogLine << "0";
LogLine << lt.wSecond << " ";
int deltaH, deltaM;
deltaH = lt.wHour - st.wHour;
deltaM = lt.wMinute - st.wMinute;
if (deltaM < 0) {
deltaM+=60;
deltaH-=1;
}
if (deltaH>12) deltaH = 24 - deltaH;
if (deltaH<-12) deltaH = 24 + deltaH;
if (deltaH<0) {
LogLine << "-";
deltaH -= 2*deltaH;
} else {
LogLine << "+";
}
if (deltaH<10) LogLine << "0";
LogLine << deltaH;
if (deltaM<10) LogLine << "0";
LogLine << deltaM << "] ";
LogLine << "\"" << rr.getRequestLine() << "\" ";
LogLine << rr.getRetCode() << " " << rr.getSentBytes_LO() << " ";
std::string tmp = rr.getReqVar("REFERER");
if (!tmp.length()) tmp = "-";
LogLine << "\"" << tmp << "\" ";
tmp = rr.getReqVar("USER_AGENT");
if (!tmp.length()) tmp = "-";
LogLine << "\"" << tmp << "\"" << std::endl;
#ifdef WIN32
ZMWSLogger::LogIt(FilePath, LogLine.str());
#else
std::ofstream ofs;
do {
ofs.open(FilePath.c_str(), std::ios::app);
} while (!ofs.is_open());
ofs << LogLine.str();
ofs.close();
#endif
return 0;
}
int ZMWS::LogSentBytes(ZMWSReqResp& rr) {
if (!serverConfig.GetWriteLogs())
return 1;
ZMWSVHostConfig vhc;
if (!serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc))
return 1;
if (!vhc.maxKBytesDTime) return 1;
std::string FilePath = vhc.logsDir+"/bytes_log_"+vhc.domain;
DWORD BytesWritten;
DWORD BytesRead1, BytesRead2;
DWORD SentBytes;
HANDLE_FILE FileHandle;
while ((FileHandle = CreateFile(FilePath.c_str(),
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE) {
Sleep(1);
}
time_t tpsNow, tpsLast;
time (&tpsNow);
if (! ReadFile(FileHandle, &tpsLast, 4, &BytesRead1, NULL)) {
print_dbg("Bytes Log Error (R)\n");
}
if (! ReadFile(FileHandle, &SentBytes, 4, &BytesRead2, NULL)) {
print_dbg("Bytes Log Error (R)\n");
}
if ((tpsNow > (tpsLast + vhc.maxKBytesDTime)) || !BytesRead1 || !BytesRead2) {
SetFilePointer(FileHandle, (LONG)0, NULL, FILE_BEGIN);
if (! WriteFile(FileHandle, &tpsNow, 4, &BytesWritten, NULL)) {
print_dbg("Bytes Log Error (W)\n");
}
SentBytes = rr.getSentBytes_LO()/1024;
} else {
SentBytes += rr.getSentBytes_LO()/1024;
}
SetFilePointer(FileHandle, 4, NULL, FILE_BEGIN);
if (! WriteFile(FileHandle, &SentBytes, 4, &BytesWritten, NULL)) {
print_dbg("Bytes Log Error (W)\n");
}
CloseFile(FileHandle);
return 0;
}
BOOL ZMWS::CheckClientIPOK(ZMWSReqResp& rr) {
std::string allowedFrom;
ZMWSVHostConfig vhc;
if (serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc)) {
allowedFrom = vhc.allowFrom;
if (!allowedFrom.length()) {
allowedFrom = serverConfig.GetAllowedFrom();
} else if ("all" == allowedFrom) {
allowedFrom = "";
}
} else {
allowedFrom = serverConfig.GetAllowedFrom();
}
if (0 == allowedFrom.length()) return rr.getRetCode();
std::string clientIP = rr.getReqVar("REMOTE_ADDR");
std::string::size_type pos1 = 0, pos2 = 0;
std::string pattern;
std::string::size_type pattern_length;
std::string::size_type client_length = clientIP.length();
BOOL allow = FALSE;
do {
// On cherche le motif
pos2 = allowedFrom.find(';', pos1);
if ((pos2 != std::string::npos)) {
pattern_length = pos2-pos1;
} else {
pattern_length = allowedFrom.length()-pos1;
}
// On regarde si le pattern correspond à l'IP cliente
// Si le dernier n'est pas un point
// On a une IP complète donc on matche sur la longueur de l'ip du client
// Si non, on matche sur la longueur du pattern
if ('.' != allowedFrom.at(pos1+pattern_length-1)) {
if (0 == allowedFrom.compare(pos1, client_length, clientIP, 0, client_length)) {
allow = TRUE;
}
} else {
if (0 == allowedFrom.compare(pos1, pattern_length, clientIP, 0, pattern_length)) {
allow = TRUE;
}
}
// On avance pos1
pos1 += pattern_length + 1;
} while ((!allow) && (pos2 != std::string::npos));
if (!allow) {
rr.setRetCode(403);
rr.setErrorMessage(clientIP+" is not allowed on this server.");
}
return allow;
}
int ZMWS::CheckSentBytesOK(ZMWSReqResp& rr) {
if (!serverConfig.GetWriteLogs())
return 1;
ZMWSVHostConfig vhc;
if (!serverConfig.GetVHostConfig(rr.RemovePort(rr.getReqVar("HOST")), vhc))
return 1;
if (!vhc.maxKBytesDTime) return 1;
if (!vhc.maxKBytes) {
rr.setRetCode(413);
rr.setErrorMessage("This website is temporarily disabled
\nPlease, try again later
\n");
return 0;
}
std::string FilePath = vhc.logsDir+"/bytes_log_"+vhc.domain;
// Si le fichier n'existe pas
// On considère que c'est bon ...
if (!ZMWSFileUtils::fileExists(FilePath)) return 1;
DWORD BytesRead;
DWORD SentBytes;
HANDLE_FILE FileHandle;
while ((FileHandle = CreateFile(FilePath.c_str(),
GENERIC_READ,
0,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE) {
Sleep(1);
}
time_t tpsNow, tpsLast;
time (&tpsNow);
if (! ReadFile(FileHandle, &tpsLast, 4, &BytesRead, NULL)) {
print_dbg("Bytes Log Error (R)\n");
}
if (!BytesRead) {
CloseFile(FileHandle);
return 1;
}
if (! ReadFile(FileHandle, &SentBytes, 4, &BytesRead, NULL)) {
print_dbg("Bytes Log Error (R)\n");
}
CloseFile(FileHandle);
if (!BytesRead) {
return 1;
}
if (tpsNow > (tpsLast + vhc.maxKBytesDTime)) return 1;
if (SentBytes >= vhc.maxKBytes) {
rr.setRetCode(413);
std::string tmpS;
tmpS = "This website is temporarily disabled because it has reached its traffic limit.
\n";
tmpS += "Please, try again later
\n";
rr.setErrorMessage(tmpS);
return 0;
}
return 1;
}
int ZMWS::HandleRequest(ZMWSReqResp* RR, const SOCKET& clientSocket)
{
std::string Current;
if (!serverConfig.GetBeQuiet()) {
std::string tmpS = RR->getRequestLine();
tmpS += "\n";
if (RR->getReqVar("REFERER").length()) {
tmpS += " Referer: " + RR->getReqVar("REFERER");
tmpS += "\n";
}
print_dbg(tmpS.c_str());
}
DWORD SizeSent = 0;
Current = RR->getRequestLine();
std::string::size_type firstSpace = Current.find(" ");
if ((firstSpace==std::string::npos)) {
RR->setRetCode(400);
RR->setErrorMessage("Invalid request: "+Current);
} else {
std::string::size_type secondSpace = Current.find(' ', ++firstSpace);
if ((secondSpace==std::string::npos))
secondSpace = Current.length();
std::string::size_type QSpos = Current.find("?");
if (QSpos!=std::string::npos) {
if (secondSpace==Current.length())
RR->setReqVar("QUERY_STRING", Current.substr(QSpos+1));
else
RR->setReqVar("QUERY_STRING", Current.substr(QSpos+1, secondSpace-1-QSpos));
}
if ((QSpos>firstSpace) && (QSpossetURL (Current.substr(firstSpace, QSpos-firstSpace));
} else {
RR->setURL (Current.substr(firstSpace, secondSpace-firstSpace));
}
}
// Prise en charge du codage %xy ...
// TODO: ajouter la prise en charge utf-8 ? (je ne suis pas pour)
std::string::size_type ind = 0;
while ((ind = RR->getURL().find("%", ind)) < std::string::npos) {
char c = (char)strtoul(RR->getURL().substr(ind+1, 2).c_str(), NULL, 16);
if (c) {
RR->getURL().replace(ind, 3, std::string(1, c));
}
++ind;
}
// Fetch preferred languages from the Accept-Language header
std::string langs = RR->getReqVar("ACCEPT_LANGUAGE");
std::string::iterator new_end = std::remove(langs.begin(), langs.end(), ' ');
langs.erase(new_end, langs.end());
std::string::size_type lang = 0, nextlang, qual;
std::string alang;
std::string aqual_str;
double aqual;
char* dummy;
do {
nextlang = langs.find(",", lang);
qual = langs.find(";", lang);
if (nextlang == std::string::npos) {
if (qual == std::string::npos) {
alang = langs.substr(lang);
aqual_str = "1";
} else {
alang = langs.substr(lang, qual-lang);
aqual_str = langs.substr(qual+1+2, -1-2); // +2 et -2 pour q=
}
} else {
if (qual == std::string::npos) {
alang = langs.substr(lang, nextlang-lang);
aqual_str = "1";
} else {
if (qual < nextlang) {
alang = langs.substr(lang, qual-lang);
aqual_str = langs.substr(qual+1+2, nextlang-qual-1-2); // +2 et -2 pour q=
} else {
alang = langs.substr(lang, nextlang-lang);
aqual_str = "1";
}
}
}
aqual = strtod(aqual_str.c_str(), &dummy);
RR->addLang(alang, aqual);
lang = nextlang+1;
} while (nextlang != std::string::npos);
switch (RR->getReqMethod()) {
case HTTP_GET:
case HTTP_POST:
case HTTP_HEAD:
Reply(*RR);
break;
default:
Reply(*RR);
break;
}
LogRequest(*RR);
LogSentBytes(*RR);
int rc = RR->cnx_policy;
if (!serverConfig.GetBeQuiet()) {
std::stringstream tmpSS2;
tmpSS2 << "Code de retour: " << RR->getRetCode() << "\n";
print_dbg(tmpSS2.str().c_str());
}
delete RR;
return rc;
}
void ZMWS::HandleClient(QUEUECLIENTPARAM* qcp) {
std::string tmpS;
BOOL ShouldStop;
BOOL isSSL = FALSE;
int rc;
ZMWSReqResp* RR = NULL;
struct sockaddr_in sa;
int sa_len = sizeof(struct sockaddr_in);
isSSL = FALSE;
if (SOCKET_ERROR != getsockname (qcp->clientSocket, (struct sockaddr *)&sa, &sa_len)) {
short sport = ntohs(sa.sin_port);
if (sport==serverConfig.GetHTTPSPort()) {
isSSL = TRUE;
qcp->modssl_data = modssl_functions.fnAccept(qcp->clientSocket);
}
}
do {
ShouldStop = FALSE;
rc = CNX_POLICY_KEEP;
/* Lecture de la requête */
ZMWSReqResp* RR = this->InitializeRR(qcp->clientSocket);
if (!RR) {
print_dbg("WARNING : HandleClient : Failed to allocate a new ZMWSReqResp\n");
rc = CNX_POLICY_CLOSE;
} else {
RR->SetIsSSL(isSSL);
RR->SetModSSLData(qcp->modssl_data);
RR = GetRequestLines(RR);
if (!RR) {
// Could not read request lines
// Time out or connexion closed
rc = CNX_POLICY_CLOSE;
}
}
DWORD result = WaitForSingleMutex (ZMWS_SERVERSTOP_MUTEX, 5000);
if (result == WAIT_OBJECT_0) {
ShouldStop = ServerStop;
ReleaseMutex(ZMWS_SERVERSTOP_MUTEX);
} else {
tmpS = "ERROR : HandleClient : Could not acquire Server Mutex\n";
print_dbg (tmpS.c_str());
}
if (!ShouldStop) {
/* Traitement de la requête */
if (RR) rc = HandleRequest(RR, qcp->clientSocket);
}
} while(rc == CNX_POLICY_KEEP);
if (isSSL && qcp->modssl_data) {
modssl_functions.fnClose(qcp->modssl_data);
qcp->modssl_data = NULL;
}
}