/* 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 += ""; Header += ""; Header += "\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 << "\"UP\""; ResponseSS << "\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 << "\"DIR\""; } else { SizeStrS = zfu.getFileSizeH(); if (ficico.length()) ImgStrSS << "\"FIC\""; } ResponseSS << "\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 = "
NomTailleModification (GMT)
" << ImgStrSS.str() << " " << NameStr << "" << SizeStrS << "" << SaveTimeStrS << "
" << ImgStrSS.str() << " " << zfu.getFileName() << "" << SizeStrS << "" << SaveTimeStrS << "
\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 << "\n

Erreur " << rr.getRetCode() << "

\n
\n"; ResponseBody << "

Il y a eu une erreur

\n

Le 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; } }