diff --git a/data/etc/licenses/LibHTTPd b/data/etc/licenses/LibHTTPd new file mode 100644 index 0000000000..ee6d68091d --- /dev/null +++ b/data/etc/licenses/LibHTTPd @@ -0,0 +1,25 @@ +/* +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ diff --git a/src/apps/aboutsystem/AboutSystem.cpp b/src/apps/aboutsystem/AboutSystem.cpp index 1c8797810b..0a2f7e5bf3 100644 --- a/src/apps/aboutsystem/AboutSystem.cpp +++ b/src/apps/aboutsystem/AboutSystem.cpp @@ -565,7 +565,7 @@ AboutView::AboutView(const BRect &rect) _GetLicensesPath(licensesPath); BPath mitPath(licensesPath); - mitPath.Append("MIT license"); + mitPath.Append("MIT"); font.SetSize(be_bold_font->Size() + 4); font.SetFace(B_BOLD_FACE); @@ -808,6 +808,13 @@ AboutView::AboutView(const BRect &rect) // "http://www.opensound.com"); // BSD license + // libhttpd copyright (used as Poorman backend) + AddCopyrightEntry("libhttpd", + "Copyright " B_UTF8_COPYRIGHT "1995,1998,1999,2000,2001 by " + "Jef Poskanzer. All rights reserved.", + Licenses("LibHTTPd", NULL), + "http://www.acme.com/software/thttpd/"); + #ifdef __INTEL__ // Udis86 copyrights AddCopyrightEntry("Udis86", @@ -1018,7 +1025,7 @@ AboutView::AddCopyrightEntry(const char *name, const char *text, for (int32 i = 0; i < licenses.CountLicenses(); i++) { const char* license = licenses.LicenseAt(i); BString licensePath(licensesPath.Path()); - licensePath += license; + licensePath << '/' << license; if (i > 0) fCreditsView->Insert(", "); diff --git a/src/apps/poorman/Jamfile b/src/apps/poorman/Jamfile index 217022e5b4..44ca8e6547 100644 --- a/src/apps/poorman/Jamfile +++ b/src/apps/poorman/Jamfile @@ -1,19 +1,53 @@ SubDir HAIKU_TOP src apps poorman ; +SubDirCcFlags [ FDefines HAVE__PROGNAME=1 HAVE_FCNTL_H=1 HAVE_GRP_H=1 HAVE_MEMORY_H=1 HAVE_POLL_H=1 HAVE_SYS_POLL_H=1 TIME_WITH_SYS_TIME=1 HAVE_DIRENT_H=1 HAVE_STRERROR=1 HAVE_WAITPID=1 HAVE_VSNPRINTF=1 HAVE_SETSID=1 HAVE_SIGSET=1 HAVE_ATOLL=1 HAVE_UNISTD_H=1 HAVE_GETPAGESIZE=1 HAVE_MMAP=1 HAVE_SELECT=1 HAVE_POLL=1 HAVE_TM_GMTOFF=1 HAVE_SOCKLENT=1 HAVE_INT64T=1 ] ; + +SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps poorman libhttpd ] ; + +rule THTTPMakeHeader header : text +{ + header = [ FGristFiles $(header) ] ; + text = [ FGristFiles $(text) ] ; + + SEARCH on $(text) = $(SEARCH_SOURCE) ; + MakeLocateArch $(header) ; + + Depends $(header) : $(text) ; + LocalClean clean : $(header) ; + + THTTPMakeHeader1 $(header) : $(text) ; +} + +actions THTTPMakeHeader1 +{ + rm -f $(1) + sed < $(2) > $(1) \ + -e 's/#.*//' -e 's/[ ]*$//' -e '/^$/d' \ + -e 's/[ ][ ]*/", 0, "/' -e 's/^/{ "/' -e 's/$/", 0 },/' +} + +THTTPMakeHeader mime_encodings.h : mime_encodings.txt ; +THTTPMakeHeader mime_types.h : mime_types.txt ; + AddResources PoorMan : PoorMan.rdef ; Application PoorMan : PoorMan.cpp - PoorManMainView.cpp PoorManWindow.cpp PoorManView.cpp PoorManAdvancedView.cpp StatusSlider.cpp + PoorManServer.cpp PoorManSiteView.cpp + PoorManLogger.cpp PoorManLoggingView.cpp PoorManPreferencesWindow.cpp PoorManApplication.cpp constants.cpp - : be tracker $(TARGET_LIBSTDC++) + + # libhttpd + libhttpd.c + match.c + tdate_parse.c + + : be network tracker $(TARGET_LIBSTDC++) ; - - diff --git a/src/apps/poorman/PoorMan.cpp b/src/apps/poorman/PoorMan.cpp index 48a8b1ba83..6e7ce3262d 100644 --- a/src/apps/poorman/PoorMan.cpp +++ b/src/apps/poorman/PoorMan.cpp @@ -18,4 +18,4 @@ main() //printf("be_app: %p", be_app); return 0; -} \ No newline at end of file +} diff --git a/src/apps/poorman/PoorManApplication.cpp b/src/apps/poorman/PoorManApplication.cpp index ab46c70b0b..688e118f9e 100644 --- a/src/apps/poorman/PoorManApplication.cpp +++ b/src/apps/poorman/PoorManApplication.cpp @@ -5,30 +5,35 @@ * Version: 0.1 */ +#include #include +#include #include "constants.h" -#include "PoorManView.h" #include "PoorManApplication.h" - +#include "PoorManWindow.h" PoorManApplication::PoorManApplication() : BApplication(STR_APP_SIG), - status(false), hits(0) + mainWindow(NULL) { - // ------------------------------------------- - // Main Window - //PoorManView * mainView; - BRect mainRect; - - mainRect.Set(30.0f, 30.0f, 355.0f, 185.0f); + BRect mainRect(82.0f, 30.0f, 400.0f, 350.0f); mainWindow = new PoorManWindow(mainRect); + mainWindow->Hide(); + mainWindow->Show(); - //mainRect.OffsetTo(B_ORIGIN); - //mainView = new PoorManView(mainRect, STR_APP_NAME); - //mainView->SetViewColor(216,216,216,255); + BDirectory webDir; + if(mainWindow->ReadSettings() != B_OK){ + if(webDir.SetTo(STR_DEFAULT_WEB_DIRECTORY) != B_OK) + mainWindow->DefaultSettings(); + } else { + if(webDir.SetTo(mainWindow->WebDir()) != B_OK) + mainWindow->DefaultSettings(); + } - mainWindow->Show(); + mainWindow->StartServer(); + mainWindow->SetDirLabel(mainWindow->WebDir()); + mainWindow->Show(); } void diff --git a/src/apps/poorman/PoorManApplication.h b/src/apps/poorman/PoorManApplication.h index 71f92f9f81..497d19aef6 100644 --- a/src/apps/poorman/PoorManApplication.h +++ b/src/apps/poorman/PoorManApplication.h @@ -11,8 +11,8 @@ #include -#include "PoorManWindow.h" -#include "PoorManPreferencesWindow.h" +class PoorManWindow; +//#include "PoorManPreferencesWindow.h" class PoorManApplication: public BApplication @@ -23,13 +23,13 @@ public: PoorManWindow * GetPoorManWindow() { return mainWindow; } private: PoorManWindow * mainWindow; - PoorManPreferencesWindow * prefWindow; + //PoorManPreferencesWindow * prefWindow; // -------------------------------------------- // settings variables - bool status; - char directory[512]; - uint32 hits; + //bool status; + //char directory[512]; + //uint32 hits; }; diff --git a/src/apps/poorman/PoorManLogger.cpp b/src/apps/poorman/PoorManLogger.cpp new file mode 100644 index 0000000000..9d47475bac --- /dev/null +++ b/src/apps/poorman/PoorManLogger.cpp @@ -0,0 +1,48 @@ +/* + * Copyright 2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Author(s): + * Ma Jie, china.majie at gmail + */ + +#include "PoorManLogger.h" + +#include +#include + +#include +#include +#include + +#include "PoorManApplication.h" +#include "PoorManWindow.h" + +void +poorman_log(const char* msg, bool needTimeHeader, + in_addr_t addr, rgb_color color) +{ + time_t now = time(NULL); + + PoorManWindow* window = static_cast(be_app)->GetPoorManWindow(); + + if(!window->LogConsoleFlag() && !window->LogFileFlag()) + return; + + BMessenger messenger(window); + BMessage message(MSG_LOG); + + if(message.AddString("cstring", msg) != B_OK) + return; + if(needTimeHeader){ + if(message.AddData("time_t", B_TIME_TYPE, &now, sizeof(time_t)) != B_OK) + return; + } + if(addr != INADDR_NONE) + message.AddData("in_addr_t", B_ANY_TYPE, &addr, sizeof(in_addr_t)); + + if(color != BLACK) + message.AddData("rgb_color", B_RGB_COLOR_TYPE, &color, sizeof(rgb_color)); + + messenger.SendMessage(&message, (BHandler*)NULL, 1000000); +} diff --git a/src/apps/poorman/PoorManLogger.h b/src/apps/poorman/PoorManLogger.h new file mode 100644 index 0000000000..961d5e6e61 --- /dev/null +++ b/src/apps/poorman/PoorManLogger.h @@ -0,0 +1,33 @@ +/* + * Copyright 2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Author(s): + * Ma Jie, china.majie at gmail + */ +#ifndef POOR_MAN_LOG_H +#define POOR_MAN_LOG_H + +#include + +#include "constants.h" //for rgb_color BLACK + +#ifdef __cplusplus + extern "C" + void poorman_log( + const char* msg, + bool needTimeHeader = true, + in_addr_t addr = INADDR_NONE, + rgb_color color = BLACK + ); +#else //c version is for libhttpd + void poorman_log( + const char* msg, + bool needTimeHeader, + in_addr_t addr, + rgb_color color + ); +#endif + + +#endif // POOR_MAN_LOG_H diff --git a/src/apps/poorman/PoorManMainView.cpp b/src/apps/poorman/PoorManMainView.cpp deleted file mode 100644 index 13529b1fe3..0000000000 --- a/src/apps/poorman/PoorManMainView.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* PoorManMainView.cpp - * - * Philip Harrison - * Started: 5/13/2004 - * Version: 0.1 - */ - - -#include "PoorManMainView.h" - -PoorManMainView::PoorManMainView(BRect rect, char *name) - : BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW) -{ -} - -void -PoorManMainView::AttachedToWindow() -{ - SetPenSize(1); -} - -void -PoorManMainView::Draw(BRect updateRect) -{ - BRect tempRect; - tempRect = updateRect; - - StrokeRect(tempRect, B_SOLID_HIGH); -} diff --git a/src/apps/poorman/PoorManMainView.h b/src/apps/poorman/PoorManMainView.h deleted file mode 100644 index 2270a1d9ed..0000000000 --- a/src/apps/poorman/PoorManMainView.h +++ /dev/null @@ -1,21 +0,0 @@ -/* PoorManMainView.h - * - * Philip Harrison - * Started: 5/13/2004 - * Version: 0.1 - */ - -#ifndef POOR_MAN_MAIN_VIEW_H -#define POOR_MAN_MAIN_VIEW_H - -#include - -class PoorManMainView: public BView -{ -public: - PoorManMainView(BRect, char *name); -virtual void AttachedToWindow(); -virtual void Draw(BRect updateRect); -}; - -#endif diff --git a/src/apps/poorman/PoorManPreferencesWindow.cpp b/src/apps/poorman/PoorManPreferencesWindow.cpp index 98eafc9af2..17fedb96ce 100644 --- a/src/apps/poorman/PoorManPreferencesWindow.cpp +++ b/src/apps/poorman/PoorManPreferencesWindow.cpp @@ -8,13 +8,13 @@ #include #include #include -#include +#include #include "constants.h" #include "PoorManWindow.h" #include "PoorManApplication.h" #include "PoorManPreferencesWindow.h" - +#include "PoorManServer.h" PoorManPreferencesWindow::PoorManPreferencesWindow(BRect frame, char * name) : BWindow(frame, name, B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_NOT_RESIZABLE), @@ -102,7 +102,7 @@ PoorManPreferencesWindow::PoorManPreferencesWindow(BRect frame, char * name) BMessenger messenger(this); BMessage message(MSG_FILE_PANEL_SELECT_WEB_DIR); webDirFilePanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL, - B_DIRECTORY_NODE, false, &message); + B_DIRECTORY_NODE, false, &message, NULL, true); webDirFilePanel->SetPanelDirectory(new BDirectory("/boot/home/public_html")); webDirFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, "Select"); @@ -120,39 +120,44 @@ PoorManPreferencesWindow::PoorManPreferencesWindow(BRect frame, char * name) } - PoorManPreferencesWindow::~PoorManPreferencesWindow() { delete logFilePanel; delete webDirFilePanel; } - void PoorManPreferencesWindow::MessageReceived(BMessage* message) { switch (message->what) { case MSG_PREF_BTN_DONE: PoorManWindow * win; + PoorManServer* server; win = ((PoorManApplication *)be_app)->GetPoorManWindow(); + server = win->GetServer(); - std::cout << "Pref Window: sendDir CheckBox: " << siteView->SendDirValue() << std::endl; + PRINT(("Pref Window: sendDir CheckBox: %d\n", siteView->SendDirValue())); + server->SetListDir(siteView->SendDirValue()); win->SetDirListFlag(siteView->SendDirValue()); - std::cout << "Pref Window: indexFileName TextControl: " << siteView->IndexFileName() << std::endl; - win->SetIndexFileName(siteView->IndexFileName()); - std::cout << "Pref Window: webDir: " << siteView->WebDir() << std::endl; - win->SetWebDir(siteView->WebDir()); - win->SetDirLabel(siteView->WebDir()); + PRINT(("Pref Window: indexFileName TextControl: %s\n", siteView->IndexFileName())); + if(server->SetIndexName(siteView->IndexFileName()) == B_OK) + win->SetIndexFileName(siteView->IndexFileName()); + PRINT(("Pref Window: webDir: %s\n", siteView->WebDir())); + if(server->SetWebDir(siteView->WebDir()) == B_OK){ + win->SetWebDir(siteView->WebDir()); + win->SetDirLabel(siteView->WebDir()); + } - std::cout << "Pref Window: logConsole CheckBox: " << loggingView->LogConsoleValue() << std::endl; + PRINT(("Pref Window: logConsole CheckBox: %d\n", loggingView->LogConsoleValue())); win->SetLogConsoleFlag(loggingView->LogConsoleValue()); - std::cout << "Pref Window: logFile CheckBox: " << loggingView->LogFileValue() << std::endl; + PRINT(("Pref Window: logFile CheckBox: %d\n", loggingView->LogFileValue())); win->SetLogFileFlag(loggingView->LogFileValue()); - std::cout << "Pref Window: logFileName: " << loggingView->LogFileName() << std::endl; + PRINT(("Pref Window: logFileName: %s\n", loggingView->LogFileName())); win->SetLogPath(loggingView->LogFileName()); - std::cout << "Pref Window: MaxConnections Slider: " << advancedView->MaxSimultaneousConnections() << std::endl; - win->SetMaxConnections(advancedView->MaxSimultaneousConnections()); + PRINT(("Pref Window: MaxConnections Slider: %ld\n", advancedView->MaxSimultaneousConnections())); + server->SetMaxConns(advancedView->MaxSimultaneousConnections()); + win->SetMaxConnections((int16)advancedView->MaxSimultaneousConnections()); if (Lock()) @@ -169,7 +174,7 @@ PoorManPreferencesWindow::MessageReceived(BMessage* message) break; case MSG_FILE_PANEL_SELECT_WEB_DIR: // handle the open BMessage from the Select Web Directory File Panel - std::cout << "Select Web Directory:\n" << std::endl; + PRINT(("Select Web Directory:\n")); SelectWebDir(message); break; case MSG_PREF_LOG_BTN_CREATE_FILE: @@ -178,12 +183,12 @@ PoorManPreferencesWindow::MessageReceived(BMessage* message) break; case MSG_FILE_PANEL_CREATE_LOG_FILE: // handle the save BMessage from the Create Log File Panel - std::cout << "Create Log File:\n" << std::endl; + PRINT(("Create Log File:\n")); CreateLogFile(message); break; case MSG_PREF_ADV_SLD_MAX_CONNECTION: max_connections = advancedView->MaxSimultaneousConnections(); - std::cout << "Max Connections: " << max_connections << std::endl; + PRINT(("Max Connections: %ld\n", max_connections)); break; default: @@ -212,8 +217,20 @@ PoorManPreferencesWindow::SelectWebDir(BMessage * message) // ;//return err; entry.GetPath(&path); - std::cout << "DIR: " << path.Path() << std::endl; + PRINT(("DIR: %s\n", path.Path())); siteView->SetWebDir(path.Path()); + + bool temp; + if(message->FindBool("Default Dialog", &temp) == B_OK){ + PoorManWindow* win = ((PoorManApplication *)be_app)->GetPoorManWindow(); + if(win->GetServer()->SetWebDir(siteView->WebDir()) == B_OK){ + win->SetWebDir(siteView->WebDir()); + win->SetDirLabel(siteView->WebDir()); + } + win->StartServer(); + if(Lock()) + Quit(); + } } void @@ -236,7 +253,7 @@ PoorManPreferencesWindow::CreateLogFile(BMessage * message) // ;//return err; entry.GetPath(&path); path.Append(name); - std::cout << "Log File: " << path.Path() << std::endl; + PRINT(("Log File: %s\n", path.Path())); if (err == B_OK) { @@ -246,3 +263,15 @@ PoorManPreferencesWindow::CreateLogFile(BMessage * message) // mark the checkbox } + +/*A special version for "the default dialog", don't use it in MessageReceived()*/ +void +PoorManPreferencesWindow::ShowWebDirFilePanel() +{ + BMessage message(MSG_FILE_PANEL_SELECT_WEB_DIR); + message.AddBool("Default Dialog", true); + + webDirFilePanel->SetMessage(&message); + if(!webDirFilePanel->IsShowing()) + webDirFilePanel->Show(); +} diff --git a/src/apps/poorman/PoorManPreferencesWindow.h b/src/apps/poorman/PoorManPreferencesWindow.h index aca230567a..2aaace87ef 100644 --- a/src/apps/poorman/PoorManPreferencesWindow.h +++ b/src/apps/poorman/PoorManPreferencesWindow.h @@ -72,7 +72,7 @@ public: virtual void MessageReceived(BMessage * message); - void ShowWebDirFilePanel() { if (!webDirFilePanel->IsShowing()) webDirFilePanel->Show(); } + void ShowWebDirFilePanel(); void SelectWebDir(BMessage * message); void CreateLogFile(BMessage * message); }; diff --git a/src/apps/poorman/PoorManServer.cpp b/src/apps/poorman/PoorManServer.cpp new file mode 100644 index 0000000000..f014eb0f42 --- /dev/null +++ b/src/apps/poorman/PoorManServer.cpp @@ -0,0 +1,424 @@ +/* + * Copyright 2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Author(s): + * Ma Jie, china.majie at gmail + */ + +#include "PoorManServer.h" + +#include +#include +#include +#include //for struct timeval +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "PoorManApplication.h" +#include "PoorManLogger.h" +#include "PoorManWindow.h" +#include "libhttpd/libhttpd.h" + +PoorManServer::PoorManServer(const char* webDir, + int32 maxConns, bool listDir,const char* idxName) + :fIsRunning(false), + fMaxConns(maxConns), + fIndexName(new char[strlen(idxName)+1]), + fCurConns(0) +{ + fHttpdServer = httpd_initialize( + (char*)0,//hostname + (httpd_sockaddr*)0,//sa4P + (httpd_sockaddr*)0,//sa6P + (unsigned short)80,//port + (char*)0,//cgi pattern + 0,//cgi_limit + "iso-8859-1",//charset + "",//p3p + -1,//max_age + const_cast(webDir),//cwd + 1,//no_log + (FILE*)0,//logfp + 0,//no_symlink_check + 0,//vhost + 0,//global_passwd + (char*)0,//url_pattern + (char*)0,//local_pattern + 0//no_empty_referers + ); + + strcpy(fIndexName, idxName); + + size_t cwdLen = strlen(fHttpdServer->cwd); + if(fHttpdServer->cwd[cwdLen-1] == '/'){ + fHttpdServer->cwd[cwdLen-1] = '\0'; + } + + fHttpdServer->do_list_dir = (listDir?1:0); + fHttpdServer->index_name = fIndexName; + + pthread_rwlock_init(&fWebDirLock, NULL); + pthread_rwlock_init(&fIndexNameLock, NULL); +} + +PoorManServer::~PoorManServer() +{ + Stop(); + httpd_terminate(fHttpdServer); + delete[] fIndexName; + pthread_rwlock_destroy(&fWebDirLock); + pthread_rwlock_destroy(&fIndexNameLock); +} + +status_t PoorManServer::Run() +{ + if(chdir(fHttpdServer->cwd) == -1){ + poorman_log("no web directory, can't start up.\n", false, INADDR_NONE, RED); + return B_ERROR; + } + + httpd_sockaddr sa4; + memset(&sa4,0,sizeof(httpd_sockaddr)); + sa4.sa_in.sin_family = AF_INET; + sa4.sa_in.sin_port = htons(80); + sa4.sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + fHttpdServer->listen4_fd = httpd_initialize_listen_socket(&sa4); + if(fHttpdServer->listen4_fd == -1) + return B_ERROR; + + fListenerTid = spawn_thread( + PoorManServer::_Listener, + "www listener", + B_NORMAL_PRIORITY, + static_cast(this) + ); + if(fListenerTid < B_OK){ + poorman_log("can't create listener thread.\n", false, INADDR_NONE, RED); + return B_ERROR; + } + fIsRunning = true; + if(resume_thread(fListenerTid) != B_OK){ + fIsRunning = false; + return B_ERROR; + } + + //our server is up and running + return B_OK; +} + +status_t PoorManServer::Stop() +{ + if(fIsRunning){ + fIsRunning = false; + httpd_unlisten(fHttpdServer); + } + return B_OK; +} + +/*The Web Dir is not changed if an error occured. + */ +status_t PoorManServer::SetWebDir(const char* webDir) +{ + if(chdir(webDir) == -1){ + //log it + return B_ERROR; + } + + char* tmp = strdup(webDir); + if(tmp == NULL) + return B_ERROR; + + if(pthread_rwlock_wrlock(&fWebDirLock) == 0){ + free(fHttpdServer->cwd); + fHttpdServer->cwd = tmp; + if(tmp[strlen(tmp)-1] == '/'){ + tmp[strlen(tmp)-1] = '\0'; + } + pthread_rwlock_unlock(&fWebDirLock); + } else { + free(tmp); + return B_ERROR; + } + + return B_OK; +} + +status_t PoorManServer::SetMaxConns(int32 count) +{ + fMaxConns = count; + return B_OK; +} + +status_t PoorManServer::SetListDir(bool listDir) +{ + fHttpdServer->do_list_dir = (listDir?1:0); + return B_OK; +} + +status_t PoorManServer::SetIndexName(const char* idxName) +{ + size_t length = strlen(idxName); + if(length > B_PATH_NAME_LENGTH+1) + return B_ERROR; + + char* tmp = new char[length+1]; + if(tmp == NULL) + return B_ERROR; + + strcpy(tmp, idxName); + if(pthread_rwlock_wrlock(&fIndexNameLock) == 0){ + delete[] fIndexName; + fIndexName = tmp; + fHttpdServer->index_name = fIndexName; + pthread_rwlock_unlock(&fIndexNameLock); + } else { + delete[] tmp; + return B_ERROR; + } + + return B_OK; +} + +int32 PoorManServer::_Listener(void* data) +{ + PRINT(("The listener thread is working.\n")); + int retval; + thread_id tid; + httpd_conn* hc; + PoorManServer* s = static_cast(data); + + while(s->fIsRunning){ + hc = new httpd_conn; + hc->initialized = 0; + PRINT(("calling httpd_get_conn()\n")); + retval = //accept(), blocked here + httpd_get_conn(s->fHttpdServer, s->fHttpdServer->listen4_fd, hc); + switch (retval){ + case GC_OK: + break; + case GC_FAIL: + httpd_destroy_conn(hc); + delete hc; + s->fIsRunning = false; + return -1; + case GC_NO_MORE: //should not happen, since we have a blocking socket + httpd_destroy_conn(hc); + continue; + break; + default: //shouldn't happen + continue; + break; + } + + if(s->fCurConns > s->fMaxConns){ + httpd_send_err(hc, 503, + httpd_err503title, "", httpd_err503form, ""); + httpd_write_response(hc); + continue; + } + + tid = spawn_thread( + PoorManServer::_Worker, + "www connection", + B_NORMAL_PRIORITY, + static_cast(s) + ); + if(tid < B_OK){ + continue; + } + /*We don't check the return code here. + *As we can't kill a thread that doesn't receive the + *httpd_conn, we simply let it die itself. + */ + send_data(tid, 512, &hc, sizeof(httpd_conn*)); + atomic_add(&s->fCurConns, 1); + resume_thread(tid); + }//while + return 0; +} + +int32 PoorManServer::_Worker(void* data) +{ + static const struct timeval kTimeVal = {60, 0}; + PoorManServer* s = static_cast(data); + httpd_conn* hc; + int retval; + + if(has_data(find_thread(NULL))){ + thread_id sender; + if(receive_data(&sender, &hc, sizeof(httpd_conn*)) != 512) + goto cleanup; + } else { + goto cleanup; + } + + PRINT(("A worker thread starts to work.\n")); + + setsockopt(hc->conn_fd, SOL_SOCKET, SO_RCVTIMEO, &kTimeVal, sizeof(struct timeval)); + retval = recv( + hc->conn_fd, + &(hc->read_buf[hc->read_idx]), + hc->read_size - hc->read_idx, + 0 + ); + if(retval < 0) + goto cleanup; + + hc->read_idx += retval; + switch(httpd_got_request(hc)){ + case GR_GOT_REQUEST: + break; + case GR_BAD_REQUEST: + httpd_send_err(hc,400,httpd_err400title,"",httpd_err400form,""); + httpd_write_response(hc);//fall through + case GR_NO_REQUEST: //fall through + default: //won't happen + goto cleanup; + break; + } + + if(httpd_parse_request(hc) < 0){ + httpd_write_response(hc); + goto cleanup; + } + + retval = httpd_start_request(hc,(struct timeval*)0); + if(retval < 0){ + httpd_write_response(hc); + goto cleanup; + } + + /*true means the connection is already handled + *by the directory index generator in httpd_start_request(). + */ + if(hc->file_address == (char*) 0){ + static_cast(be_app)->GetPoorManWindow()->SetHits( + static_cast(be_app)->GetPoorManWindow()->GetHits()+1 + ); + hc->conn_fd = -1; + goto cleanup; + } + + switch(hc->method){ + case METHOD_GET: + s->_HandleGet(hc); + break; + case METHOD_HEAD: + s->_HandleHead(hc); + break; + case METHOD_POST: + s->_HandlePost(hc); + break; + default: + break; + } + +cleanup: ; + httpd_close_conn(hc,(struct timeval*)0); + httpd_destroy_conn(hc); + + delete hc; + atomic_add(&s->fCurConns, -1); + return 0; +} + +status_t PoorManServer::_HandleGet(httpd_conn* hc) +{ + PRINT(("HandleGet() called\n")); + + off_t length; + ssize_t bytesRead; + uint8* buf; + BString log; + + BFile file(hc->expnfilename, B_READ_ONLY); + if(file.InitCheck() != B_OK) + return B_ERROR; + + buf = new uint8[POOR_MAN_BUF_SIZE]; + if(buf == NULL) + return B_ERROR; + + if(hc->got_range == 1) + length = hc->last_byte_index + 1 - hc->first_byte_index; + else + length = hc->sb.st_size; + + static_cast(be_app)->GetPoorManWindow()->SetHits( + static_cast(be_app)->GetPoorManWindow()->GetHits()+1 + ); + + log.SetTo("Sending file: "); + if(pthread_rwlock_rdlock(&fWebDirLock) == 0){ + log << hc->hs->cwd; + pthread_rwlock_unlock(&fWebDirLock); + } + log << '/' << hc->expnfilename << '\n'; + poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr); + + //send mime headers + if(send(hc->conn_fd,hc->response,hc->responselen,0) < 0){ + delete [] buf; + return B_ERROR; + } + + file.Seek(hc->first_byte_index, SEEK_SET); + while(true){ + bytesRead = file.Read(buf, POOR_MAN_BUF_SIZE); + if(bytesRead == 0) + break; + else if(bytesRead < 0){ + delete [] buf; + return B_ERROR; + } + if(send(hc->conn_fd, (void*)buf, bytesRead, 0) < 0){ + log.SetTo("Error sending file: "); + if(pthread_rwlock_rdlock(&fWebDirLock) == 0){ + log << hc->hs->cwd; + pthread_rwlock_unlock(&fWebDirLock); + } + log << '/' << hc->expnfilename << '\n'; + poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr, RED); + delete [] buf; + return B_ERROR; + } + } + + delete [] buf; + return B_OK; +} + +status_t PoorManServer::_HandleHead(httpd_conn* hc) +{ + int retval = send(hc->conn_fd,hc->response,hc->responselen,0); + if(retval == -1) + return B_ERROR; + return B_OK; +} + +status_t PoorManServer::_HandlePost(httpd_conn* hc) +{ + //not implemented + return B_OK; +} + +pthread_rwlock_t* get_web_dir_lock() +{ + return static_cast(be_app)-> + GetPoorManWindow()->GetServer()->GetWebDirLock(); +} + +pthread_rwlock_t* get_index_name_lock() +{ + return static_cast(be_app)-> + GetPoorManWindow()->GetServer()->GetIndexNameLock(); +} diff --git a/src/apps/poorman/PoorManServer.h b/src/apps/poorman/PoorManServer.h new file mode 100644 index 0000000000..9a5d0b2a0a --- /dev/null +++ b/src/apps/poorman/PoorManServer.h @@ -0,0 +1,76 @@ +/* + * Copyright 2009 Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Author(s): + * Ma Jie, china.majie at gmail + */ +#ifndef POOR_MAN_SERVER_H +#define POOR_MAN_SERVER_H + +#include + +#include +#include + +#include "libhttpd/libhttpd.h" + +#define POOR_MAN_BUF_SIZE 1048576ll + +#ifdef __cplusplus +class PoorManServer{ +public: + PoorManServer(const char* webDir, int32 maxConns, + bool listDir, const char* idxName); + virtual ~PoorManServer(); + + status_t Run(); + status_t Stop(); + + bool IsRunning()const{return fIsRunning;} + + status_t SetWebDir(const char* webDir); + status_t SetMaxConns(int32 count); + status_t SetListDir(bool listDir); + status_t SetIndexName(const char* idxName); + + pthread_rwlock_t* GetWebDirLock(){return &fWebDirLock;} + pthread_rwlock_t* GetIndexNameLock(){return &fIndexNameLock;} +private: + bool fIsRunning; + int32 fMaxConns;//Max Thread Count + char* fIndexName;//Index File Name + + thread_id fListenerTid; + int32 fCurConns; + httpd_server* fHttpdServer; + + pthread_rwlock_t fWebDirLock; + pthread_rwlock_t fIndexNameLock; + + PoorManServer(){} + PoorManServer(PoorManServer& s){} + PoorManServer& operator=(PoorManServer& s){return *this;} + + //two thread functions. + static int32 _Listener(void* data); + static int32 _Worker(void* data); + + status_t _HandleGet(httpd_conn* hc); + status_t _HandleHead(httpd_conn* hc); + status_t _HandlePost(httpd_conn* hc); +}; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +pthread_rwlock_t* get_web_dir_lock(); +pthread_rwlock_t* get_index_name_lock(); + +#ifdef __cplusplus +} +#endif + +#endif //POOR_MAN_SERVER_H diff --git a/src/apps/poorman/PoorManWindow.cpp b/src/apps/poorman/PoorManWindow.cpp index 6938b495de..8c581be21e 100644 --- a/src/apps/poorman/PoorManWindow.cpp +++ b/src/apps/poorman/PoorManWindow.cpp @@ -5,28 +5,54 @@ * Version: 0.1 */ -#include "PoorManApplication.h" -#include "PoorManPreferencesWindow.h" #include "PoorManWindow.h" -#include "constants.h" +#include +#include +#include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include - - -//#include +#include "PoorManApplication.h" +#include "PoorManPreferencesWindow.h" +#include "PoorManView.h" +#include "PoorManServer.h" +#include "PoorManLogger.h" +#include "constants.h" PoorManWindow::PoorManWindow(BRect frame) : BWindow(frame, STR_APP_NAME, B_TITLED_WINDOW, 0), - status(false), hits(0), last_width(325), last_height(155) -{ - DefaultSettings(); + status(false), hits(0), prefWindow(NULL), fLogFile(NULL), fServer(NULL) +{ + //preferences init + web_directory.SetTo(STR_DEFAULT_WEB_DIRECTORY); + index_file_name.SetTo("index.html"); + dir_list_flag = false; + log_console_flag = true; + log_file_flag = false; + log_path.SetTo(""); + max_connections = (int16)32; + + is_zoomed = true; + last_width = 318.0f; + last_height = 320.0f; + this->frame = frame; + setwindow_frame.Set(112.0f, 60.0f, 492.0f, 340.0f); // PoorMan Window SetSizeLimits(318, 1600, 53, 1200); @@ -45,10 +71,10 @@ PoorManWindow::PoorManWindow(BRect frame) mainView->SetFont(be_bold_font); mainView->SetFontSize(12); AddChild(mainView); - + // BBox tests BRect br; - br = frame; + br = mainView->Bounds(); br.top = 1.0; BBox * bb = new BBox(br, "Background", B_FOLLOW_ALL_SIDES, @@ -67,7 +93,7 @@ PoorManWindow::PoorManWindow(BRect frame) statusRect.left += 5; statusRect.top += 3; statusRect.bottom = statusRect.top + 15; - statusRect.right = statusRect.left + 75; // make the width wide enough for the string to display + statusRect.right = statusRect.left + 100; // make the width wide enough for the string to display statusView = new BStringView(statusRect, "Status View", "Status: Stopped"); bb->AddChild(statusView); @@ -80,13 +106,13 @@ PoorManWindow::PoorManWindow(BRect frame) dirRect.left = statusRect.left; dirRect.right -= 5; - dirView = new BStringView(dirRect, "Dir View", "Directory: /boot/home/", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); + dirView = new BStringView(dirRect, "Dir View", "Directory: (none)", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); bb->AddChild(dirView); // Hits String BRect hitsRect; hitsRect = bb->Bounds(); - hitsRect.InsetBy(5, 5); + hitsRect.InsetBy(5.0f, 5.0f); hitsRect.top = statusRect.top; hitsRect.bottom = statusRect.bottom; hitsRect.left = statusRect.right + 20; @@ -99,34 +125,37 @@ PoorManWindow::PoorManWindow(BRect frame) // Logging View // logRect - BRect logRect(5.0, 36.0, 306.0, 131.0); + BRect logRect = bb->Bounds();//(5.0, 36.0, 306.0, 131.0); + logRect.InsetBy(5, 5); + logRect.top = 36.0f; + logRect.right -= B_V_SCROLL_BAR_WIDTH; // textRect BRect textRect; //(1.0, 1.0, 175.0, 75.0); textRect = logRect; - textRect.InsetBy(1.0, 1.0); textRect.top = 0.0; - textRect.left = 0.0; + textRect.left = 2.0; + textRect.right = logRect.right - logRect.left - 2.0; + textRect.bottom = logRect.bottom - logRect.top; + + fLogViewFont = new BFont(be_plain_font); + fLogViewFont->SetSize(11.0); loggingView = new BTextView(logRect, STR_TXT_VIEW, textRect, - B_FOLLOW_ALL_SIDES, B_WILL_DRAW ); + fLogViewFont, NULL, B_FOLLOW_ALL_SIDES, B_WILL_DRAW ); loggingView->MakeEditable(false); // user cannot change the text loggingView->MakeSelectable(true); loggingView->SetViewColor(WHITE); - - loggingView->Insert("This is PoorMan.\n"); // text_run_array - //loggingView->MakeFocus(true); - //AddChild(loggingView); - - + loggingView->SetStylable(true); + // create the scroll view scrollView = new BScrollView("Scroll View", loggingView, B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS, // Make sure articles on border do not occur when resizing false, true); bb->AddChild(scrollView); - + loggingView->MakeFocus(true); // ----------------------------------------------------------------- @@ -177,9 +206,19 @@ PoorManWindow::PoorManWindow(BRect frame) AddChild(FileMenuBar); + + pthread_rwlock_init(&fLogFileLock, NULL); } -void +PoorManWindow::~PoorManWindow() +{ + delete fServer; + delete fLogViewFont; + delete fLogFile; + pthread_rwlock_destroy(&fLogFileLock); +} + +void PoorManWindow::MessageReceived(BMessage* message) { switch (message->what) { @@ -198,33 +237,20 @@ PoorManWindow::MessageReceived(BMessage* message) SaveConsole(message, true); break; case MSG_MENU_EDIT_PREF: - prefWindow = new PoorManPreferencesWindow(BRect(30.0f, 30.0f, 410.0f, 310.0f), STR_WIN_NAME_PREF); + prefWindow = new PoorManPreferencesWindow( + setwindow_frame, + STR_WIN_NAME_PREF + ); break; case MSG_MENU_CTRL_RUN: - BMenuItem * RunServer; - RunServer = ControlsMenu->FindItem(STR_MNU_CTRL_RUN_SERVER); - if (RunServer) - { - //status = httpd->Run(); - /* For Testing - if (status) - status = false; - else - status = true; - */ - - UpdateStatusLabel(status); - if (status) - { - RunServer->SetMarked(true); - } else { - RunServer->SetMarked(false); - } - } + if(status) + StopServer(); + else + StartServer(); break; case MSG_MENU_CTRL_CLEAR_HIT: SetHits(0); - UpdateHitsLabel(); + //UpdateHitsLabel(); break; case MSG_MENU_CTRL_CLEAR_CONSOLE: loggingView->SelectAll(); @@ -235,12 +261,95 @@ PoorManWindow::MessageReceived(BMessage* message) f = fopen(log_path.String(), "w"); fclose(f); break; + case MSG_LOG: + { + if(!log_console_flag && !log_file_flag) + break; + + time_t time; + in_addr_t address; + rgb_color color; + const void* pointer; + ssize_t size; + const char* msg; + BString line; + + if(message->FindString("cstring", &msg) != B_OK) + break; + if(message->FindData("time_t", B_TIME_TYPE, &pointer, &size) != B_OK) + time = -1; + else + time = *static_cast(pointer); + if(message->FindData("in_addr_t", B_ANY_TYPE, &pointer, &size) != B_OK) + address = INADDR_NONE; + else + address = *static_cast(pointer); + if(message->FindData("rgb_color", B_RGB_COLOR_TYPE, &pointer, &size) != B_OK) + color = BLACK; + else + color = *static_cast(pointer); + + if(time != -1){ + char timeString[26]; + if(ctime_r(&time, timeString) != NULL){ + timeString[24] = '\0'; + line << '[' << timeString << "]: "; + } + } + + if(address != INADDR_NONE){ + char addr[INET_ADDRSTRLEN]; + struct in_addr sin_addr; + sin_addr.s_addr = address; + if(inet_ntop(AF_INET, &sin_addr, addr, sizeof(addr)) != NULL){ + addr[strlen(addr)] = '\0'; + line << '(' << addr << ") "; + } + } + + line << msg; + + text_run run; + text_run_array runs; + + run.offset = 0; + run.font = *fLogViewFont; + run.color = color; + + runs.count = 1; + runs.runs[0] = run; + + if(Lock()){ + if(log_console_flag){ + loggingView->Insert(loggingView->TextLength(), + line.String(), line.Length(), &runs); + loggingView->ScrollToOffset(loggingView->TextLength()); + } + + if(log_file_flag){ + if(pthread_rwlock_rdlock(&fLogFileLock) == 0){ + fLogFile->Write(line.String(), line.Length()); + pthread_rwlock_unlock(&fLogFileLock); + } + } + + Unlock(); + } + + break; + } default: BWindow::MessageReceived(message); break; } } +void +PoorManWindow::FrameMoved(BPoint origin) +{ + frame.left = origin.x; + frame.top = origin.y; +} void PoorManWindow::FrameResized(float width, float height) @@ -250,35 +359,64 @@ PoorManWindow::FrameResized(float width, float height) last_width = width; last_height = height; } - printf("(%f, %f)\n", width, height); } -bool +bool PoorManWindow::QuitRequested() { + if(status){ + time_t now = time(NULL); + char line[] = "[Thu Jan 1 00:00:00 1970]: Shutting down.\n"; + + ctime_r(&now, line+1); + line[25] = ']'; + line[26] = ' '; + + if(log_console_flag){ + loggingView->Insert(loggingView->TextLength(), + line, strlen(line)); + loggingView->ScrollToOffset(loggingView->TextLength()); + } + + if(log_file_flag){ + if(pthread_rwlock_rdlock(&fLogFileLock) == 0){ + fLogFile->Write(line, strlen(line)); + pthread_rwlock_unlock(&fLogFileLock); + } + } + + fServer->Stop(); + status = false; + UpdateStatusLabelAndMenuItem(); + } + SaveSettings(); - be_app->PostMessage(B_QUIT_REQUESTED); - return (true); + be_app_messenger.SendMessage(B_QUIT_REQUESTED); + return true; } -void +void PoorManWindow::Zoom(BPoint origin, float width, float height) { - printf("Zoom: Is Zoomed before: %d (%f, %f)\n", is_zoomed, width, height); if (is_zoomed) { // Change to the Minimal size is_zoomed = false; ResizeTo(318, 53); - printf("ResizedTo(318, 53)\n"); } else { // Change to the Zoomed size is_zoomed = true; ResizeTo(last_width, last_height); - printf("ResizedTo(%f, %f)\n", last_width, last_height); } } +void +PoorManWindow::SetHits(uint32 num) +{ + hits = num; + UpdateHitsLabel(); +} + // Private: Methods ------------------------------------------ BMenu * @@ -368,11 +506,7 @@ void PoorManWindow::SetDirLabel(const char * name) { BString dirPath("Directory: "); - int32 length = dirPath.Length() + 1; - dirPath.LockBuffer(length); dirPath.Append(name); - - dirPath.UnlockBuffer(length + strlen(name)); if (Lock()) { @@ -382,15 +516,15 @@ PoorManWindow::SetDirLabel(const char * name) } void -PoorManWindow::UpdateStatusLabel(bool set) +PoorManWindow::UpdateStatusLabelAndMenuItem() { if (Lock()) { - if (set) + if (status) statusView->SetText("Status: Running"); else statusView->SetText("Status: Stopped"); - + ControlsMenu->FindItem(STR_MNU_CTRL_RUN_SERVER)->SetMarked(status); Unlock(); } } @@ -417,13 +551,13 @@ PoorManWindow::SaveConsole(BMessage * message, bool selection) status_t err = B_OK; FILE *f; - if (err = message->FindRef("directory", &ref) != B_OK) + if ((err = message->FindRef("directory", &ref)) != B_OK) return err; - if (err = message->FindString("name", &name) != B_OK) + if ((err = message->FindString("name", &name)) != B_OK) return err; - if (err = entry.SetTo(&ref) != B_OK) + if ((err = entry.SetTo(&ref)) != B_OK) return err; entry.GetPath(&path); @@ -433,7 +567,7 @@ PoorManWindow::SaveConsole(BMessage * message, bool selection) return B_ERROR; if (!selection) - { + { // write the data to the file err = fwrite(loggingView->Text(), 1, loggingView->TextLength(), f); } else { @@ -455,88 +589,208 @@ PoorManWindow::SaveConsole(BMessage * message, bool selection) } -void +void PoorManWindow::DefaultSettings() { - // Site Tab - SetIndexFileName("index.html"); - SetDirListFlag(false); - // Logging Tab - SetLogConsoleFlag(true); - SetLogFileFlag(false); - // Advanced Tab - SetMaxConnections(32); + BAlert* serverAlert = new BAlert("Error Server", STR_ERR_CANT_START, "OK"); + BAlert* dirAlert = new BAlert("Error Dir", STR_ERR_WEB_DIR, + "Cancel", "Select", "Default", B_WIDTH_AS_USUAL, B_OFFSET_SPACING); + dirAlert->SetShortcut(0, B_ESCAPE); + int32 buttonIndex = dirAlert->Go(); - BDirectory webDir; - if (!webDir.Contains("/boot/home/public_html", B_DIRECTORY_NODE)) - { - BAlert * serverAlert = new BAlert("Error Server", STR_ERR_CANT_START, "OK"); - - BAlert * dirAlert = new BAlert("Error Dir", STR_ERR_WEB_DIR, - "Cancel", "Select", "Default", B_WIDTH_AS_USUAL, B_OFFSET_SPACING); - dirAlert->SetShortcut(0, B_ESCAPE); - int32 buttonIndex = dirAlert->Go(); - - - - // process dirAlert - switch (buttonIndex) - { - case 0: - serverAlert->Go(); - QuitRequested(); - break; - case 1: - serverAlert->Go(); - prefWindow = new PoorManPreferencesWindow(BRect(30.0f, 30.0f, 410.0f, 310.0f), STR_WIN_NAME_PREF); - prefWindow->ShowWebDirFilePanel(); - break; - case 2: - - break; + switch (buttonIndex){ + case 0: + serverAlert->Go(); + SetWebDir(""); + break; + case 1: + prefWindow = new PoorManPreferencesWindow( + setwindow_frame, + STR_WIN_NAME_PREF + ); + serverAlert->Go(); + prefWindow->ShowWebDirFilePanel(); + break; + case 2: + if(create_directory(STR_DEFAULT_WEB_DIRECTORY, 0755) != B_OK){ + serverAlert->Go(); + SetWebDir(""); + break; } - } else { - printf("BDirectory contains: /boot/home/public_html\n"); - SetWebDir("/boot/home/public_html"); + BAlert* dirCreatedAlert = + new BAlert("Dir Created",STR_DIR_CREATED,"OK"); + dirCreatedAlert->Go(); + SetWebDir(STR_DEFAULT_WEB_DIRECTORY); + break; } - } -status_t +status_t PoorManWindow::ReadSettings() { + BPath p; + BFile f; + BMessage m; + + if(find_directory(B_USER_SETTINGS_DIRECTORY, &p) != B_OK) + return B_ERROR; + p.Append(STR_SETTINGS_FILE_NAME); + + f.SetTo(p.Path(), B_READ_ONLY); + if(f.InitCheck()!=B_OK) + return B_ERROR; + + if(m.Unflatten(&f)!=B_OK) + return B_ERROR; + + if(MSG_PREF_FILE != m.what) + return B_ERROR; + + //site tab + if(m.FindString("web_directory", &web_directory) != B_OK) + web_directory.SetTo(STR_DEFAULT_WEB_DIRECTORY); + if(m.FindString("index_file_name", &index_file_name) != B_OK) + index_file_name.SetTo("index.html"); + if(m.FindBool("dir_list_flag", &dir_list_flag) != B_OK) + dir_list_flag = false; + + //logging tab + if(m.FindBool("log_console_flag", &log_console_flag) != B_OK) + log_console_flag = true; + if(m.FindBool("log_file_flag", &log_file_flag) != B_OK) + log_file_flag = false; + if(m.FindString("log_path", &log_path) != B_OK) + log_path.SetTo(""); + + //advance tab + if(m.FindInt16("max_connections", &max_connections) != B_OK) + max_connections = (int16)32; + + //windows' position and size + if(m.FindRect("frame", &frame) != B_OK) + frame.Set(82.0f, 30.0f, 400.0f, 350.0f); + if(m.FindRect("setwindow_frame", &setwindow_frame) != B_OK) + setwindow_frame.Set(112.0f, 60.0f, 492.0f, 340.0f); + if(m.FindBool("is_zoomed", &is_zoomed) != B_OK) + is_zoomed = true; + if(m.FindFloat("last_width", &last_width) != B_OK) + last_width = 318.0f; + if(m.FindFloat("last_height", &last_height) != B_OK) + last_height = 320.0f; + + is_zoomed?ResizeTo(last_width, last_height):ResizeTo(318, 53); + MoveTo(frame.left, frame.top); + + fLogFile = new BFile(log_path.String(), B_CREATE_FILE|B_WRITE_ONLY|B_OPEN_AT_END); + if(fLogFile->InitCheck() != B_OK){ + log_file_flag = false; + //log it to console, "log to file unavailable." + return B_OK; + } + + SetDirLabel(web_directory.String()); + + return B_OK; } -status_t +status_t PoorManWindow::SaveSettings() { - FILE * f; - size_t s; + BPath p; + BFile f; + BMessage m(MSG_PREF_FILE); + + //site tab + m.AddString("web_directory", web_directory); + m.AddString("index_file_name", index_file_name); + m.AddBool("dir_list_flag", dir_list_flag); - f = fopen(STR_SETTINGS_NEW_FILE_PATH, "wb"); - if (f) - { - // Need to rewrite - /* - //fprintf(f, "%s", web_directory.String()); - s = fwrite(web_directory.String(), sizeof(char), web_directory.Length() + 1, f); - - fprintf(f, "%s", index_file_name.String()); - fwrite(dir_list_flag, sizeof(bool), 1, f); - //fprintf(f, "%uc", dir_list_flag); - - // logging tab - fwrite(log_file_flag, sizeof(bool), 1, f); - //fprintf(f, "%uc", log_console_flag); - fprintf(f, "%uc", log_file_flag); - fprintf(f, "%s", log_path.String()); - // advanced tab - fprintf(f, "%ld", max_connections); + //logging tab + m.AddBool("log_console_flag", log_console_flag); + m.AddBool("log_file_flag", log_file_flag); + m.AddString("log_path", log_path); - fprintf(f, "%uc", is_zoomed); - fprintf(f, "%f", last_width); - fprintf(f, "%f", last_height); - */ - } - fclose(f); + //advance tab + m.AddInt16("max_connections", max_connections); + + //windows' position and size + m.AddRect("frame", frame); + m.AddRect("setwindow_frame", setwindow_frame); + m.AddBool("is_zoomed", is_zoomed); + m.AddFloat("last_width", last_width); + m.AddFloat("last_height", last_height); + + if(find_directory(B_USER_SETTINGS_DIRECTORY, &p) != B_OK) + return B_ERROR; + p.Append(STR_SETTINGS_FILE_NAME); + + f.SetTo(p.Path(), B_WRITE_ONLY| B_ERASE_FILE| B_CREATE_FILE); + if(f.InitCheck()!=B_OK) + return B_ERROR; + + if(m.Flatten(&f)!=B_OK) + return B_ERROR; + + return B_OK; +} + +status_t +PoorManWindow::StartServer() +{ + if(NULL == fServer) + fServer = new PoorManServer( + web_directory.String(), + max_connections, + dir_list_flag, + index_file_name.String() + ); + + poorman_log("Starting up... "); + if(fServer->Run() != B_OK){ + return B_ERROR; + } + + status = true; + UpdateStatusLabelAndMenuItem(); + poorman_log("done.\n", false, INADDR_NONE, GREEN); + + return B_OK; +} + +status_t +PoorManWindow::StopServer() +{ + if(NULL == fServer) + return B_ERROR; + + poorman_log("Shutting down.\n"); + fServer->Stop(); + status = false; + UpdateStatusLabelAndMenuItem(); + return B_OK; +} + +void +PoorManWindow::SetLogPath(const char* str) +{ + if(log_path == str) + return; + + BFile* temp = new BFile(str, B_CREATE_FILE|B_WRITE_ONLY|B_OPEN_AT_END); + + if(temp->InitCheck() != B_OK){ + delete temp; + return; + } + + if(pthread_rwlock_wrlock(&fLogFileLock) == 0){ + delete fLogFile; + fLogFile = temp; + pthread_rwlock_unlock(&fLogFileLock); + } else { + delete temp; + return; + } + + log_path.SetTo(str); } diff --git a/src/apps/poorman/PoorManWindow.h b/src/apps/poorman/PoorManWindow.h index dcde3b076f..bdbca27bcf 100644 --- a/src/apps/poorman/PoorManWindow.h +++ b/src/apps/poorman/PoorManWindow.h @@ -8,29 +8,36 @@ #ifndef POOR_MAN_WINDOW_H #define POOR_MAN_WINDOW_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include -#include "PoorManView.h" -#include "PoorManPreferencesWindow.h" +#include +#include +#include + +class BPoint; +class BFilePanel; +class BMessage; +class BMenuBar; +class BMenu; +class BTextView; +class BStringView; +class BScrollView; +class BRect; +class BFile; +class BFont; + +class PoorManView; +class PoorManPreferencesWindow; +class PoorManServer; class PoorManWindow: public BWindow { public: PoorManWindow(BRect frame); +virtual ~PoorManWindow(); virtual void MessageReceived(BMessage * message); +virtual void FrameMoved(BPoint origin); virtual void FrameResized(float width, float height); virtual bool QuitRequested(); virtual void Zoom(BPoint origin, float width, float height); @@ -38,11 +45,18 @@ virtual void Zoom(BPoint origin, float width, float height); // ------------------------------------------- // Public PoorMan Window Methods void SetDirLabel(const char * name); - void SetHits(uint32 num) { hits = num;} + void SetHits(uint32 num); uint32 GetHits() { return hits; } status_t SaveConsole(BMessage * message, bool); - status_t SaveSettings(); + status_t SaveSettings(); + status_t ReadSettings(); + void DefaultSettings(); + + status_t StartServer(); + status_t StopServer(); + + PoorManServer* GetServer()const{return fServer;} // ------------------------------------------- // Preferences and Settings // Site Tab @@ -58,19 +72,16 @@ virtual void Zoom(BPoint origin, float width, float height); bool LogFileFlag() { return log_file_flag; } void SetLogFileFlag(bool flag) { log_file_flag = flag; } const char * LogPath() { return log_path.String(); } - void SetLogPath(const char * str) { log_path.SetTo(str); } + void SetLogPath(const char * str); // Advanced Tab - int32 MaxConnections() { return max_connections; } - void SetMaxConnections(int32 num) { max_connections = num; } + int16 MaxConnections() { return max_connections; } + void SetMaxConnections(int16 num) { max_connections = num; } private: // ------------------------------------------- // PoorMan Window Methods - status_t ReadSettings(); - void DefaultSettings(); - - void UpdateStatusLabel(bool); + void UpdateStatusLabelAndMenuItem(); void UpdateHitsLabel(); private: @@ -121,8 +132,9 @@ private: bool log_console_flag; bool log_file_flag; BString log_path; + // advanced tab - int32 max_connections; + int16 max_connections; bool is_zoomed; float last_width; @@ -130,11 +142,16 @@ private: BRect frame; BRect setwindow_frame; - // File Panels BFilePanel * saveConsoleFilePanel; BFilePanel * saveConsoleSelectionFilePanel; + BFile* fLogFile; + BFont* fLogViewFont; + + PoorManServer* fServer; + + pthread_rwlock_t fLogFileLock; }; #endif diff --git a/src/apps/poorman/StatusSlider.cpp b/src/apps/poorman/StatusSlider.cpp index 13ce45f092..a0725e174a 100644 --- a/src/apps/poorman/StatusSlider.cpp +++ b/src/apps/poorman/StatusSlider.cpp @@ -38,4 +38,4 @@ StatusSlider::UpdateText() const sprintf(temp, "%ld %s", Value(), StatusPrefix); return temp; -} \ No newline at end of file +} diff --git a/src/apps/poorman/StatusSlider.h b/src/apps/poorman/StatusSlider.h index 1e7ab348eb..ae02fc73c0 100644 --- a/src/apps/poorman/StatusSlider.h +++ b/src/apps/poorman/StatusSlider.h @@ -8,7 +8,7 @@ #ifndef STATUS_SLIDER #define STATUS_SLIDER -#define BEOS_R5_COMPATIBLE +//#define BEOS_R5_COMPATIBLE #include diff --git a/src/apps/poorman/constants.cpp b/src/apps/poorman/constants.cpp index ed4f5e101b..0a84622f25 100644 --- a/src/apps/poorman/constants.cpp +++ b/src/apps/poorman/constants.cpp @@ -16,6 +16,10 @@ extern const char* STR_ERR_WEB_DIR "Do you wish to select a folder to publish on the Web?"; extern const char* STR_ERR_CANT_START = "Can't Start the Server"; +extern const char* STR_DIR_CREATED + = "A default Web directory has been created for you " + "at \"/boot/home/public_html.\"\nMake sure an HTML " + "document named \"index.html\" is in that directory."; extern const char* STR_MNU_FILE = "File"; @@ -58,11 +62,12 @@ extern const char* STR_TXT_VIEW char* STR_WIN_NAME_PREF = "PoorMan Settings"; -extern const char* STR_SETTINGS_FILE_PATH - = "/boot/home/config/settings/PoorMan Settings"; -extern const char* STR_SETTINGS_NEW_FILE_PATH - = "/boot/home/config/settings/OBOS PoorMan Settings"; - +extern const char* STR_SETTINGS_FILE_NAME + = "PoorMan Settings"; +extern const char* STR_SETTINGS_NEW_FILE_NAME + = "OBOS PoorMan Settings"; +extern const char* STR_DEFAULT_WEB_DIRECTORY + = "/boot/home/public_html"; extern const char* STR_TAB_SITE = "Site"; extern const char* STR_BBX_LOCATION @@ -123,12 +128,14 @@ extern const char* STR_ABOUT_BUTTON = "OK"; extern const char* STR_ABOUT_DESC = "Poor Man's Web Server\n" - "Copyright © 2004 Haiku\n" + "Copyright " B_UTF8_COPYRIGHT " 2004-2009 Haiku\n" "All rights reserved.\n\n" - "Written by: Philip Harrison"; + "Written by: Philip Harrison, Ma Jie"; // -------------------------------- extern const rgb_color WHITE = { 255, 255, 255, 255 }; extern const rgb_color GRAY = { 184, 184, 184, 255 }; extern const rgb_color BACKGROUND_COLOR = { 216, 216, 216, 255 }; extern const rgb_color BLACK = { 0, 0, 0, 255 }; +extern const rgb_color RED = { 255, 0, 0, 255}; +extern const rgb_color GREEN = { 0, 255, 0, 255}; diff --git a/src/apps/poorman/constants.h b/src/apps/poorman/constants.h index 797570b077..4e12b9c975 100644 --- a/src/apps/poorman/constants.h +++ b/src/apps/poorman/constants.h @@ -32,12 +32,18 @@ const int32 MSG_FILE_PANEL_CREATE_LOG_FILE = 'oCLF'; // Advanced Tab const int32 MSG_PREF_ADV_SLD_MAX_CONNECTION = 'PASM'; +// BMessage Preferences File +const uint32 MSG_PREF_FILE = 'pref'; + +//BMessage for Logger +const int32 MSG_LOG = 'logg'; // ------------------------------------------------------ // PoorMan Window extern const char* STR_APP_SIG; extern char* STR_APP_NAME; extern const char* STR_ERR_WEB_DIR; // Alert Box extern const char* STR_ERR_CANT_START; +extern const char* STR_DIR_CREATED; extern const char* STR_MNU_FILE; extern const char* STR_MNU_FILE_SAVE_AS; @@ -61,9 +67,9 @@ extern const char* STR_TXT_VIEW; // ------------------------------------------------------ // Preferences Window extern char* STR_WIN_NAME_PREF; -extern const char* STR_SETTINGS_FILE_PATH; -extern const char* STR_SETTINGS_NEW_FILE_PATH; - +extern const char* STR_SETTINGS_FILE_NAME; +extern const char* STR_SETTINGS_NEW_FILE_NAME; +extern const char* STR_DEFAULT_WEB_DIRECTORY; extern const char* STR_TAB_SITE; extern const char* STR_BBX_LOCATION; @@ -104,4 +110,6 @@ extern const rgb_color WHITE; extern const rgb_color GRAY; extern const rgb_color BACKGROUND_COLOR; extern const rgb_color BLACK; +extern const rgb_color RED; +extern const rgb_color GREEN; #endif diff --git a/src/apps/poorman/libhttpd/README b/src/apps/poorman/libhttpd/README new file mode 100644 index 0000000000..1769f8cdb6 --- /dev/null +++ b/src/apps/poorman/libhttpd/README @@ -0,0 +1,5 @@ +This is libhttpd from thttpd v2.25b, which is written by Jef Poskanzer. +This version of libhttpd is modified to fit into the multithreaded PoorMan application. + +For more information about thttpd, please visit: +http://www.acme.com/software/thttpd/ diff --git a/src/apps/poorman/libhttpd/config.h b/src/apps/poorman/libhttpd/config.h new file mode 100644 index 0000000000..d5e0c7ac41 --- /dev/null +++ b/src/apps/poorman/libhttpd/config.h @@ -0,0 +1,398 @@ +/* config.h - configuration defines for thttpd and libhttpd +** +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + + +/* The following configuration settings are sorted in order of decreasing +** likelihood that you'd want to change them - most likely first, least +** likely last. +** +** In case you're not familiar with the convention, "#ifdef notdef" +** is a Berkeleyism used to indicate temporarily disabled code. +** The idea here is that you re-enable it by just moving it outside +** of the ifdef. +*/ + +/* CONFIGURE: CGI programs must match this pattern to get executed. It's +** a simple shell-style wildcard pattern, with * meaning any string not +** containing a slash, ** meaning any string at all, and ? meaning any +** single character; or multiple such patterns separated by |. The +** patterns get checked against the filename part of the incoming URL. +** +** Restricting CGI programs to a single directory lets the site administrator +** review them for security holes, and is strongly recommended. If there +** are individual users that you trust, you can enable their directories too. +** +** You can also specify a CGI pattern on the command line, with the -c flag. +** Such a pattern overrides this compiled-in default. +** +** If no CGI pattern is specified, neither here nor on the command line, +** then CGI programs cannot be run at all. If you want to disable CGI +** as a security measure that's how you do it, just don't define any +** pattern here and don't run with the -c flag. +*/ +#ifdef notdef +/* Some sample patterns. Allow programs only in one central directory: */ +#define CGI_PATTERN "/cgi-bin/*" +/* Allow programs in a central directory, or anywhere in a trusted +** user's tree: */ +#define CGI_PATTERN "/cgi-bin/*|/jef/**" +/* Allow any program ending with a .cgi: */ +#define CGI_PATTERN "**.cgi" +/* When virtual hosting, enable the central directory on every host: */ +#define CGI_PATTERN "/*/cgi-bin/*" +#endif + +/* CONFIGURE: How many seconds to allow CGI programs to run before killing +** them. This is in case someone writes a CGI program that goes into an +** infinite loop, or does a massive database lookup that would take hours, +** or whatever. If you don't want any limit, comment this out, but that's +** probably a really bad idea. +*/ +//#define CGI_TIMELIMIT 30 + +/* CONFIGURE: Maximum number of simultaneous CGI programs allowed. +** If this many are already running, then attempts to run more will +** return an HTTP 503 error. If this is not defined then there's +** no limit (and you'd better have a lot of memory). This can also be +** set in the runtime config file. +*/ +#ifdef notdef +#define CGI_LIMIT 50 +#endif + +/* CONFIGURE: How many seconds to allow for reading the initial request +** on a new connection. +*/ +#define IDLE_READ_TIMELIMIT 60 + +/* CONFIGURE: How many seconds before an idle connection gets closed. +*/ +#define IDLE_SEND_TIMELIMIT 300 + +/* CONFIGURE: The syslog facility to use. Using this you can set up your +** syslog.conf so that all thttpd messages go into a separate file. Note +** that even if you use the -l command line flag to send logging to a +** file, errors still get sent via syslog. +*/ +#define LOG_FACILITY LOG_DAEMON + +/* CONFIGURE: Tilde mapping. Many URLs use ~username to indicate a +** user's home directory. thttpd provides two options for mapping +** this construct to an actual filename. +** +** 1) Map ~username to /username. This is the recommended choice. +** Each user gets a subdirectory in the main chrootable web tree, and +** the tilde construct points there. The prefix could be something +** like "users", or it could be empty. See also the makeweb program +** for letting users create their own web subdirectories. +** +** 2) Map ~username to /. The postfix would be +** the name of a subdirectory off of the user's actual home dir, something +** like "public_html". This is what Apache and other servers do. The problem +** is, you can't do this and chroot() at the same time, so it's inherently +** a security hole. This is strongly dis-recommended, but it's here because +** some people really want it. Use at your own risk. +** +** You can also leave both options undefined, and thttpd will not do +** anything special about tildes. Enabling both options is an error. +*/ +//#ifdef notdef +//#define TILDE_MAP_1 "users" +//#define TILDE_MAP_2 "public_html" +//#endif + +/* CONFIGURE: The file to use for authentication. If this is defined then +** thttpd checks for this file in the local directory before every fetch. +** If the file exists then authentication is done, otherwise the fetch +** proceeds as usual. +** +** If you undefine this then thttpd will not implement authentication +** at all and will not check for auth files, which saves a bit of CPU time. +*/ +//#define AUTH_FILE ".htpasswd" + +/* CONFIGURE: The default character set name to use with text MIME types. +** This gets substituted into the MIME types where they have a "%s". +** +** You can override this in the config file with the "charset" setting, +** or on the command like with the -T flag. +*/ +#define DEFAULT_CHARSET "iso-8859-1" + + +/* Most people won't want to change anything below here. */ + +/* CONFIGURE: This controls the SERVER_NAME environment variable that gets +** passed to CGI programs. By default thttpd does a gethostname(), which +** gives the host's canonical name. If you want to always use some other name +** you can define it here. +** +** Alternately, if you want to run the same thttpd binary on multiple +** machines, and want to build in alternate names for some or all of +** them, you can define a list of canonical name to altername name +** mappings. thttpd seatches the list and when it finds a match on +** the canonical name, that alternate name gets used. If no match +** is found, the canonical name gets used. +** +** If both SERVER_NAME and SERVER_NAME_LIST are defined here, thttpd searches +** the list as above, and if no match is found then SERVER_NAME gets used. +** +** In any case, if thttpd is started with the -h flag, that name always +** gets used. +*/ +#ifdef notdef +#define SERVER_NAME "your.hostname.here" +#define SERVER_NAME_LIST \ + "canonical.name.here/alternate.name.here", \ + "canonical.name.two/alternate.name.two" +#endif + +/* CONFIGURE: Undefine this if you want thttpd to hide its specific version +** when returning into to browsers. Instead it'll just say "thttpd" with +** no version. +*/ +//#define SHOW_SERVER_VERSION + +/* CONFIGURE: Define this if you want to always chroot(), without having +** to give the -r command line flag. Some people like this as a security +** measure, to prevent inadvertant exposure by accidentally running without -r. +** You can still disable it at runtime with the -nor flag. +*/ +#ifdef notdef +#define ALWAYS_CHROOT +#endif + +/* CONFIGURE: Define this if you want to always do virtual hosting, without +** having to give the -v command line flag. You can still disable it at +** runtime with the -nov flag. +*/ +#ifdef notdef +#define ALWAYS_VHOST +#endif + +/* CONFIGURE: If you're using the vhost feature and you have a LOT of +** virtual hostnames (like, hundreds or thousands), you will want to +** enable this feature. It avoids a problem with most Unix filesystems, +** where if there are a whole lot of items in a directory then name lookup +** becomes very slow. This feature makes thttpd use subdirectories +** based on the first characters of each hostname. You can set it to use +** from one to three characters. If the hostname starts with "www.", that +** part is skipped over. Dots are also skipped over, and if the name isn't +** long enough then "_"s are used. Here are some examples of how hostnames +** would get turned into directory paths, for each different setting: +** 1: www.acme.com -> a/www.acme.com +** 1: foobar.acme.com -> f/foobar.acme.com +** 2: www.acme.com -> a/c/www.acme.com +** 2: foobar.acme.com -> f/o/foobar.acme.com +** 3: www.acme.com -> a/c/m/www.acme.com +** 3: foobar.acme.com -> f/o/o/foobar.acme.com +** 3: m.tv -> m/t/v/m.tv +** 4: m.tv -> m/t/v/_/m.tv +** Note that if you compile this setting in but then forget to set up +** the corresponding subdirectories, the only error indication you'll +** get is a "404 Not Found" when you try to visit a site. So be careful. +*/ +#ifdef notdef +#define VHOST_DIRLEVELS 1 +#define VHOST_DIRLEVELS 2 +#define VHOST_DIRLEVELS 3 +#endif + +/* CONFIGURE: Define this if you want to always use a global passwd file, +** without having to give the -P command line flag. You can still disable +** it at runtime with the -noP flag. +*/ +#ifdef notdef +#define ALWAYS_GLOBAL_PASSWD +#endif + +/* CONFIGURE: When started as root, the default username to switch to after +** initializing. If this user (or the one specified by the -u flag) does +** not exist, the program will refuse to run. +*/ +#define DEFAULT_USER "nobody" + +/* CONFIGURE: When started as root, the program can automatically chdir() +** to the home directory of the user specified by -u or DEFAULT_USER. +** An explicit -d still overrides this. +*/ +#ifdef notdef +#define USE_USER_DIR +#endif + +/* CONFIGURE: If this is defined, some of the built-in error pages will +** have more explicit information about exactly what the problem is. +** Some sysadmins don't like this, for security reasons. +*/ +#define EXPLICIT_ERROR_PAGES + +/* CONFIGURE: Subdirectory for custom error pages. The error filenames are +** $WEBDIR/$ERR_DIR/err%d.html - if virtual hosting is enabled then +** $WEBDIR/hostname/$ERR_DIR/err%d.html is searched first. This allows +** different custom error pages for each virtual hosting web server. If +** no custom page for a given error can be found, the built-in error page +** is generated. If ERR_DIR is not defined at all, only the built-in error +** pages will be generated. +*/ +#define ERR_DIR "errors" + +/* CONFIGURE: Define this if you want a standard HTML tail containing +** $SERVER_SOFTWARE and $SERVER_ADDRESS to be appended to the custom error +** pages. (It is always appended to the built-in error pages.) +*/ +#define ERR_APPEND_SERVER_INFO + +/* CONFIGURE: nice(2) value to use for CGI programs. If this is undefined, +** CGI programs run at normal priority. +*/ +//#define CGI_NICE 10 + +/* CONFIGURE: $PATH to use for CGI programs. +*/ +#define CGI_PATH "/usr/local/bin:/usr/ucb:/bin:/usr/bin" + +/* CONFIGURE: If defined, $LD_LIBRARY_PATH to use for CGI programs. +*/ +#ifdef notdef +#define CGI_LD_LIBRARY_PATH "/usr/local/lib:/usr/lib" +#endif + +/* CONFIGURE: How often to run the occasional cleanup job. +*/ +#define OCCASIONAL_TIME 120 + +/* CONFIGURE: Seconds between stats syslogs. If this is undefined then +** no stats are accumulated and no stats syslogs are done. +*/ +#define STATS_TIME 3600 + +/* CONFIGURE: The mmap cache tries to keep the total number of mapped +** files below this number, so you don't run out of kernel file descriptors. +** If you have reconfigured your kernel to have more descriptors, you can +** raise this and thttpd will keep more maps cached. However it's not +** a hard limit, thttpd will go over it if you really are accessing +** a whole lot of files. +*/ +#define DESIRED_MAX_MAPPED_FILES 1000 + +/* CONFIGURE: The mmap cache also tries to keep the total mapped bytes +** below this number, so you don't run out of address space. Again +** it's not a hard limit, thttpd will go over it if you really are +** accessing a bunch of large files. +*/ +#define DESIRED_MAX_MAPPED_BYTES 1000000000 + +/* CONFIGURE: Minimum and maximum intervals between child-process reaping, +** in seconds. +*/ +#define MIN_REAP_TIME 30 +#define MAX_REAP_TIME 900 + + +/* You almost certainly don't want to change anything below here. */ + +/* CONFIGURE: When throttling CGI programs, we don't know how many bytes +** they send back to the client because it would be inefficient to +** interpose a counter. CGI programs are much more expensive than +** regular files to serve, so we set an arbitrary and high byte count +** that gets applied to all CGI programs for throttling purposes. +*/ +#define CGI_BYTECOUNT 25000 + +/* CONFIGURE: The default port to listen on. 80 is the standard HTTP port. +*/ +#define DEFAULT_PORT 80 + +/* CONFIGURE: A list of index filenames to check. The files are searched +** for in this order. +*/ +//#define INDEX_NAMES "index.html", "index.htm", "index.xhtml", "index.xht", "Default.htm", "index.cgi" + +/* CONFIGURE: If this is defined then thttpd will automatically generate +** index pages for directories that don't have an explicit index file. +** If you want to disable this behavior site-wide, perhaps for security +** reasons, just undefine this. Note that you can disable indexing of +** individual directories by merely doing a "chmod 711" on them - the +** standard Unix file permission to allow file access but disable "ls". +*/ +#define GENERATE_INDEXES + +/* CONFIGURE: Whether to log unknown request headers. Most sites will not +** want to log them, which will save them a bit of CPU time. +*/ +//#ifdef notdef +//#define LOG_UNKNOWN_HEADERS +//#endif + +/* CONFIGURE: Whether to fflush() the log file after each request. If +** this is turned off there's a slight savings in CPU cycles. +*/ +#define FLUSH_LOG_EVERY_TIME + +/* CONFIGURE: Time between updates of the throttle table's rolling averages. */ +#define THROTTLE_TIME 2 + +/* CONFIGURE: The listen() backlog queue length. The 1024 doesn't actually +** get used, the kernel uses its maximum allowed value. This is a config +** parameter only in case there's some OS where asking for too high a queue +** length causes an error. Note that on many systems the maximum length is +** way too small - see http://www.acme.com/software/thttpd/notes.html +*/ +#define LISTEN_BACKLOG 1024 + +/* CONFIGURE: Maximum number of throttle patterns that any single URL can +** be included in. This has nothing to do with the number of throttle +** patterns that you can define, which is unlimited. +*/ +#define MAXTHROTTLENUMS 10 + +/* CONFIGURE: Number of file descriptors to reserve for uses other than +** connections. Currently this is 10, representing one for the listen fd, +** one for dup()ing at connection startup time, one for reading the file, +** one for syslog, and possibly one for the regular log file, which is +** five, plus a factor of two for who knows what. +*/ +#define SPARE_FDS 10 + +/* CONFIGURE: How many milliseconds to leave a connection open while doing a +** lingering close. +*/ +#define LINGER_TIME 500 + +/* CONFIGURE: Maximum number of symbolic links to follow before +** assuming there's a loop. +*/ +#define MAX_LINKS 32 + +/* CONFIGURE: You don't even want to know. +*/ +#define MIN_WOULDBLOCK_DELAY 100L + +#endif /* _CONFIG_H_ */ diff --git a/src/apps/poorman/libhttpd/libhttpd.c b/src/apps/poorman/libhttpd/libhttpd.c new file mode 100644 index 0000000000..1c4523910b --- /dev/null +++ b/src/apps/poorman/libhttpd/libhttpd.c @@ -0,0 +1,3670 @@ +/* libhttpd.c - HTTP protocol library +** +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include "config.h" +#include "version.h" + +#ifdef SHOW_SERVER_VERSION +#define EXPOSED_SERVER_SOFTWARE SERVER_SOFTWARE +#else /* SHOW_SERVER_VERSION */ +#define EXPOSED_SERVER_SOFTWARE "Haiku/PoorMan" +#endif /* SHOW_SERVER_VERSION */ + +#include +#include +#include + +#include +#include +#include +#include +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include + +#ifdef HAVE_OSRELDATE_H +#include +#endif /* HAVE_OSRELDATE_H */ + +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +extern char* crypt( const char* key, const char* setting ); + +#include "libhttpd.h" +//#include "mmc.h" +//#include "timers.h" +#include "match.h" +#include "tdate_parse.h" +#include "../PoorManServer.h" +#include "../PoorManLogger.h" + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#ifndef HAVE_INT64T +typedef long long int64_t; +#endif + +#ifndef HAVE_SOCKLENT +typedef int socklen_t; +#endif + +#ifdef __CYGWIN__ +#define timezone _timezone +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + + +#ifdef O_NONBLOCK +#define O_NDELAY O_NONBLOCK +#else +#error no NONBLOCK +#endif + + +/* Forwards. */ +static void check_options( void ); +static void free_httpd_server( httpd_server* hs ); +//static int initialize_listen_socket( httpd_sockaddr* saP ); +static void add_response( httpd_conn* hc, char* str ); +static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod ); +static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ); +static void send_response_tail( httpd_conn* hc ); +static void defang( char* str, char* dfstr, int dfsize ); +#ifdef ERR_DIR +static int send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename ); +#endif /* ERR_DIR */ +#ifdef AUTH_FILE +static void send_authenticate( httpd_conn* hc, char* realm ); +static int b64_decode( const char* str, unsigned char* space, int size ); +static int auth_check( httpd_conn* hc, char* dirname ); +static int auth_check2( httpd_conn* hc, char* dirname ); +#endif /* AUTH_FILE */ +static void send_dirredirect( httpd_conn* hc ); +static int hexit( char c ); +static void strdecode( char* to, char* from ); +#ifdef GENERATE_INDEXES +static void strencode( char* to, int tosize, char* from ); +#endif /* GENERATE_INDEXES */ +#ifdef TILDE_MAP_1 +static int tilde_map_1( httpd_conn* hc ); +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 +static int tilde_map_2( httpd_conn* hc ); +#endif /* TILDE_MAP_2 */ +static int vhost_map( httpd_conn* hc ); +static char* expand_symlinks( char* path, char** freethis, char** restP, int no_symlink_check, int tildemapped ); +static char* bufgets( httpd_conn* hc ); +static void de_dotdot( char* file ); +static void init_mime( void ); +static void figure_mime( httpd_conn* hc ); +#ifdef CGI_TIMELIMIT +static void cgi_kill2( ClientData client_data, struct timeval* nowP ); +static void cgi_kill( ClientData client_data, struct timeval* nowP ); +#endif /* CGI_TIMELIMIT */ +#ifdef GENERATE_INDEXES +static int ls( httpd_conn* hc ); +#endif /* GENERATE_INDEXES */ +//static char* build_env( char* fmt, char* arg ); +#ifdef SERVER_NAME_LIST +static char* hostname_map( char* hostname ); +#endif /* SERVER_NAME_LIST */ +static int really_start_request( httpd_conn* hc, struct timeval* nowP ); +static void make_log_entry( httpd_conn* hc, struct timeval* nowP ); +static int check_referer( httpd_conn* hc ); +static int really_check_referer( httpd_conn* hc ); +static int sockaddr_check( httpd_sockaddr* saP ); +static size_t sockaddr_len( httpd_sockaddr* saP ); +static int my_snprintf( char* str, size_t size, const char* format, ... ); +#ifndef HAVE_ATOLL +static long long atoll( const char* str ); +#endif /* HAVE_ATOLL */ + + +/* This global keeps track of whether we are in the main process or a +** sub-process. The reason is that httpd_write_response() can get called +** in either context; when it is called from the main process it must use +** non-blocking I/O to avoid stalling the server, but when it is called +** from a sub-process it wants to use blocking I/O so that the whole +** response definitely gets written. So, it checks this variable. A bit +** of a hack but it seems to do the right thing. +*/ +//static int sub_process = 0; + +static void +check_options( void ) + { +#if defined(TILDE_MAP_1) && defined(TILDE_MAP_2) +// syslog( LOG_CRIT, "both TILDE_MAP_1 and TILDE_MAP_2 are defined" ); + exit( 1 ); +#endif /* both */ + } + + +static void +free_httpd_server( httpd_server* hs ) + { + if ( hs->binding_hostname != (char*) 0 ) + free( (void*) hs->binding_hostname ); + if ( hs->cwd != (char*) 0 ) + free( (void*) hs->cwd ); + if ( hs->cgi_pattern != (char*) 0 ) + free( (void*) hs->cgi_pattern ); + if ( hs->charset != (char*) 0 ) + free( (void*) hs->charset ); + if ( hs->p3p != (char*) 0 ) + free( (void*) hs->p3p ); + if ( hs->url_pattern != (char*) 0 ) + free( (void*) hs->url_pattern ); + if ( hs->local_pattern != (char*) 0 ) + free( (void*) hs->local_pattern ); + free( (void*) hs ); + } + + +httpd_server* +httpd_initialize( + char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, + unsigned short port, char* cgi_pattern, int cgi_limit, char* charset, + char* p3p, int max_age, char* cwd, int no_log, FILE* logfp, + int no_symlink_check, int vhost, int global_passwd, char* url_pattern, + char* local_pattern, int no_empty_referers ) + { + httpd_server* hs; + static char ghnbuf[256]; + char* cp; + + check_options(); + + hs = NEW( httpd_server, 1 ); + if ( hs == (httpd_server*) 0 ) + { +// syslog( LOG_CRIT, "out of memory allocating an httpd_server" ); + return (httpd_server*) 0; + } + + if ( hostname != (char*) 0 ) + { + hs->binding_hostname = strdup( hostname ); + if ( hs->binding_hostname == (char*) 0 ) + { +// syslog( LOG_CRIT, "out of memory copying hostname" ); + return (httpd_server*) 0; + } + hs->server_hostname = hs->binding_hostname; + } + else + { + hs->binding_hostname = (char*) 0; + hs->server_hostname = (char*) 0; + if ( gethostname( ghnbuf, sizeof(ghnbuf) ) < 0 ) + ghnbuf[0] = '\0'; +#ifdef SERVER_NAME_LIST + if ( ghnbuf[0] != '\0' ) + hs->server_hostname = hostname_map( ghnbuf ); +#endif /* SERVER_NAME_LIST */ + if ( hs->server_hostname == (char*) 0 ) + { +#ifdef SERVER_NAME + hs->server_hostname = SERVER_NAME; +#else /* SERVER_NAME */ + if ( ghnbuf[0] != '\0' ) + hs->server_hostname = ghnbuf; +#endif /* SERVER_NAME */ + } + } + + hs->port = port; + if ( cgi_pattern == (char*) 0 ) + hs->cgi_pattern = (char*) 0; + else + { + /* Nuke any leading slashes. */ + if ( cgi_pattern[0] == '/' ) + ++cgi_pattern; + hs->cgi_pattern = strdup( cgi_pattern ); + if ( hs->cgi_pattern == (char*) 0 ) + { +// syslog( LOG_CRIT, "out of memory copying cgi_pattern" ); + return (httpd_server*) 0; + } + /* Nuke any leading slashes in the cgi pattern. */ + while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 ) + (void) strcpy( cp + 1, cp + 2 ); + } + hs->cgi_limit = cgi_limit; + hs->cgi_count = 0; + hs->charset = strdup( charset ); + hs->p3p = strdup( p3p ); + hs->max_age = max_age; + hs->cwd = strdup( cwd ); + if ( hs->cwd == (char*) 0 ) + { +// syslog( LOG_CRIT, "out of memory copying cwd" ); + return (httpd_server*) 0; + } + if ( url_pattern == (char*) 0 ) + hs->url_pattern = (char*) 0; + else + { + hs->url_pattern = strdup( url_pattern ); + if ( hs->url_pattern == (char*) 0 ) + { +// syslog( LOG_CRIT, "out of memory copying url_pattern" ); + return (httpd_server*) 0; + } + } + if ( local_pattern == (char*) 0 ) + hs->local_pattern = (char*) 0; + else + { + hs->local_pattern = strdup( local_pattern ); + if ( hs->local_pattern == (char*) 0 ) + { +// syslog( LOG_CRIT, "out of memory copying local_pattern" ); + return (httpd_server*) 0; + } + } + hs->no_log = no_log; + hs->logfp = (FILE*) 0; + httpd_set_logfp( hs, logfp ); + hs->no_symlink_check = no_symlink_check; + hs->vhost = vhost; + hs->global_passwd = global_passwd; + hs->no_empty_referers = no_empty_referers; + + /* Initialize listen sockets. Try v6 first because of a Linux peculiarity; + ** like some other systems, it has magical v6 sockets that also listen for + ** v4, but in Linux if you bind a v4 socket first then the v6 bind fails. + */ + /*if ( sa6P == (httpd_sockaddr*) 0 ) + hs->listen6_fd = -1; + else + hs->listen6_fd = initialize_listen_socket( sa6P ); + if ( sa4P == (httpd_sockaddr*) 0 ) + hs->listen4_fd = -1; + else + hs->listen4_fd = initialize_listen_socket( sa4P );*/ + /* If we didn't get any valid sockets, fail. */ + /*if ( hs->listen4_fd == -1 && hs->listen6_fd == -1 ) + { + free_httpd_server( hs ); + return (httpd_server*) 0; + }*/ + + init_mime(); + + /* Done initializing. */ +// if ( hs->binding_hostname == (char*) 0 ) +// syslog( +// LOG_NOTICE, "%.80s starting on port %d", SERVER_SOFTWARE, +// (int) hs->port ); +// else +// syslog( +// LOG_NOTICE, "%.80s starting on %.80s, port %d", SERVER_SOFTWARE, +// httpd_ntoa( hs->listen4_fd != -1 ? sa4P : sa6P ), +// (int) hs->port ); + return hs; + } + + +int +httpd_initialize_listen_socket( httpd_sockaddr* saP ) + { + int listen_fd; + int on/*, flags*/; + + /* Check sockaddr. */ + if ( ! sockaddr_check( saP ) ) + { +// syslog( LOG_CRIT, "unknown sockaddr family on listen socket" ); + return -1; + } + + /* Create socket. */ + listen_fd = socket( saP->sa.sa_family, SOCK_STREAM, 0 ); + if ( listen_fd < 0 ) + { +// syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) ); + poorman_log("can't create socket.\n", false, INADDR_NONE, RED); + return -1; + } + (void) fcntl( listen_fd, F_SETFD, 1 ); + + /* Allow reuse of local addresses. */ + on = 1; + if ( setsockopt( + listen_fd, SOL_SOCKET, SO_REUSEADDR, (char*) &on, + sizeof(on) ) < 0 ) + /*syslog( LOG_CRIT, "setsockopt SO_REUSEADDR - %m" )*/; + + /* Bind to it. */ + if ( bind( listen_fd, &saP->sa, sockaddr_len( saP ) ) < 0 ) + { +// syslog( +// LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) ); + poorman_log("can't bind to socket.\n", false, INADDR_NONE, RED); + (void) close( listen_fd ); + return -1; + } + + /* Start a listen going. */ + if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 ) + { +// syslog( LOG_CRIT, "listen - %m" ); + poorman_log("can't listen to socket.\n", false, INADDR_NONE, RED); + (void) close( listen_fd ); + return -1; + } + + /* Use accept filtering, if available. */ +#ifdef SO_ACCEPTFILTER + { +#if ( __FreeBSD_version >= 411000 ) +#define ACCEPT_FILTER_NAME "httpready" +#else +#define ACCEPT_FILTER_NAME "dataready" +#endif + struct accept_filter_arg af; + (void) bzero( &af, sizeof(af) ); + (void) strcpy( af.af_name, ACCEPT_FILTER_NAME ); + (void) setsockopt( + listen_fd, SOL_SOCKET, SO_ACCEPTFILTER, (char*) &af, sizeof(af) ); + } +#endif /* SO_ACCEPTFILTER */ + + return listen_fd; + } + + +void +httpd_set_logfp( httpd_server* hs, FILE* logfp ) + { + if ( hs->logfp != (FILE*) 0 ) + (void) fclose( hs->logfp ); + hs->logfp = logfp; + } + + +void +httpd_terminate( httpd_server* hs ) + { + httpd_unlisten( hs ); + if ( hs->logfp != (FILE*) 0 ) + (void) fclose( hs->logfp ); + free_httpd_server( hs ); + } + + +void +httpd_unlisten( httpd_server* hs ) + { + if ( hs->listen4_fd != -1 ) + { + (void) close( hs->listen4_fd ); + hs->listen4_fd = -1; + } + if ( hs->listen6_fd != -1 ) + { + (void) close( hs->listen6_fd ); + hs->listen6_fd = -1; + } + } + + +/* Conditional macro to allow two alternate forms for use in the built-in +** error pages. If EXPLICIT_ERROR_PAGES is defined, the second and more +** explicit error form is used; otherwise, the first and more generic +** form is used. +*/ +#ifdef EXPLICIT_ERROR_PAGES +#define ERROR_FORM(a,b) b +#else /* EXPLICIT_ERROR_PAGES */ +#define ERROR_FORM(a,b) a +#endif /* EXPLICIT_ERROR_PAGES */ + + +static char* ok200title = "OK"; +static char* ok206title = "Partial Content"; + +static char* err302title = "Found"; +static char* err302form = "The actual URL is '%.80s'.\n"; + +static char* err304title = "Not Modified"; + +char* httpd_err400title = "Bad Request"; +char* httpd_err400form = + "Your request has bad syntax or is inherently impossible to satisfy.\n"; + +#ifdef AUTH_FILE +static char* err401title = "Unauthorized"; +static char* err401form = + "Authorization required for the URL '%.80s'.\n"; +#endif /* AUTH_FILE */ + +static char* err403title = "Forbidden"; +#ifndef EXPLICIT_ERROR_PAGES +static char* err403form = + "You do not have permission to get URL '%.80s' from this server.\n"; +#endif /* !EXPLICIT_ERROR_PAGES */ + +static char* err404title = "Not Found"; +static char* err404form = + "The requested URL '%.80s' was not found on this server.\n"; + +char* httpd_err408title = "Request Timeout"; +char* httpd_err408form = + "No request appeared within a reasonable time period.\n"; + +static char* err500title = "Internal Error"; +static char* err500form = + "There was an unusual problem serving the requested URL '%.80s'.\n"; + +static char* err501title = "Not Implemented"; +static char* err501form = + "The requested method '%.80s' is not implemented by this server.\n"; + +char* httpd_err503title = "Service Temporarily Overloaded"; +char* httpd_err503form = + "The requested URL '%.80s' is temporarily overloaded. Please try again later.\n"; + + +/* Append a string to the buffer waiting to be sent as response. */ +static void +add_response( httpd_conn* hc, char* str ) + { + size_t len; + + len = strlen( str ); + httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len ); + (void) memmove( &(hc->response[hc->responselen]), str, len ); + hc->responselen += len; + } + +/* Send the buffered response. */ +void +httpd_write_response( httpd_conn* hc ) + { + /* If we are in a sub-process, turn off no-delay mode. */ + /*if ( sub_process ) + httpd_clear_ndelay( hc->conn_fd );*/ + /* Send the response, if necessary. */ + if ( hc->responselen > 0 ) + { + (void) httpd_write_fully( hc->conn_fd, hc->response, hc->responselen ); + hc->responselen = 0; + } + } + + +/* Set no-delay / non-blocking mode on a socket. */ +void +httpd_set_ndelay( int fd ) + { + int flags, newflags; + + flags = fcntl( fd, F_GETFL, 0 ); + if ( flags != -1 ) + { + newflags = flags | (int) O_NDELAY; + if ( newflags != flags ) + (void) fcntl( fd, F_SETFL, newflags ); + } + } + + +/* Clear no-delay / non-blocking mode on a socket. */ +void +httpd_clear_ndelay( int fd ) + { + int flags, newflags; + + flags = fcntl( fd, F_GETFL, 0 ); + if ( flags != -1 ) + { + newflags = flags & ~ (int) O_NDELAY; + if ( newflags != flags ) + (void) fcntl( fd, F_SETFL, newflags ); + } + } + + +static void +send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, off_t length, time_t mod ) + { + time_t now, expires; + const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; + char nowbuf[100]; + char modbuf[100]; + char expbuf[100]; + char fixed_type[500]; + char buf[1000]; + int partial_content; + int s100; + + hc->status = status; + hc->bytes_to_send = length; + if ( hc->mime_flag ) + { + if ( status == 200 && hc->got_range && + ( hc->last_byte_index >= hc->first_byte_index ) && + ( ( hc->last_byte_index != length - 1 ) || + ( hc->first_byte_index != 0 ) ) && + ( hc->range_if == (time_t) -1 || + hc->range_if == hc->sb.st_mtime ) ) + { + partial_content = 1; + hc->status = status = 206; + title = ok206title; + } + else + { + partial_content = 0; + hc->got_range = 0; + } + + now = time( (time_t*) 0 ); + if ( mod == (time_t) 0 ) + mod = now; + (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) ); + (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) ); + (void) my_snprintf( + fixed_type, sizeof(fixed_type), type, hc->hs->charset ); + (void) my_snprintf( buf, sizeof(buf), + "%.20s %d %s\015\012Server: %s\015\012Content-Type: %s\015\012Date: %s\015\012Last-Modified: %s\015\012Accept-Ranges: bytes\015\012Connection: close\015\012", + hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, + nowbuf, modbuf ); + add_response( hc, buf ); + s100 = status / 100; + if ( s100 != 2 && s100 != 3 ) + { + (void) my_snprintf( buf, sizeof(buf), + "Cache-Control: no-cache,no-store\015\012" ); + add_response( hc, buf ); + } + if ( encodings[0] != '\0' ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Encoding: %s\015\012", encodings ); + add_response( hc, buf ); + } + if ( partial_content ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Range: bytes %lld-%lld/%lld\015\012Content-Length: %lld\015\012", + (int64_t) hc->first_byte_index, (int64_t) hc->last_byte_index, + (int64_t) length, + (int64_t) ( hc->last_byte_index - hc->first_byte_index + 1 ) ); + add_response( hc, buf ); + } + else if ( length >= 0 ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Length: %lld\015\012", (int64_t) length ); + add_response( hc, buf ); + } + if ( hc->hs->p3p[0] != '\0' ) + { + (void) my_snprintf( buf, sizeof(buf), "P3P: %s\015\012", hc->hs->p3p ); + add_response( hc, buf ); + } + if ( hc->hs->max_age >= 0 ) + { + expires = now + hc->hs->max_age; + (void) strftime( + expbuf, sizeof(expbuf), rfc1123fmt, gmtime( &expires ) ); + (void) my_snprintf( buf, sizeof(buf), + "Cache-Control: max-age=%d\015\012Expires: %s\015\012", + hc->hs->max_age, expbuf ); + add_response( hc, buf ); + } + if ( extraheads[0] != '\0' ) + add_response( hc, extraheads ); + add_response( hc, "\015\012" ); + } + } + + +static int str_alloc_count = 0; +static size_t str_alloc_size = 0; + +void +httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size ) + { + if ( *maxsizeP == 0 ) + { + *maxsizeP = MAX( 200, size + 100 ); + *strP = NEW( char, *maxsizeP + 1 ); + ++str_alloc_count; + str_alloc_size += *maxsizeP; + } + else if ( size > *maxsizeP ) + { + str_alloc_size -= *maxsizeP; + *maxsizeP = MAX( *maxsizeP * 2, size * 5 / 4 ); + *strP = RENEW( *strP, char, *maxsizeP + 1 ); + str_alloc_size += *maxsizeP; + } + else + return; + if ( *strP == (char*) 0 ) + { +// syslog( +// LOG_ERR, "out of memory reallocating a string to %d bytes", +// *maxsizeP ); + exit( 1 ); + } + } + + +static void +send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ) + { + char defanged_arg[1000], buf[2000]; + + send_mime( + hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1, + (time_t) 0 ); + (void) my_snprintf( buf, sizeof(buf), "\ +\n\ +%d %s\n\ +\n\ +

%d %s

\n", + status, title, status, title ); + add_response( hc, buf ); + defang( arg, defanged_arg, sizeof(defanged_arg) ); + (void) my_snprintf( buf, sizeof(buf), form, defanged_arg ); + add_response( hc, buf ); + if ( match( "**MSIE**", hc->useragent ) ) + { + int n; + add_response( hc, "\n" ); + } + send_response_tail( hc ); + } + + +static void +send_response_tail( httpd_conn* hc ) + { + char buf[1000]; + + (void) my_snprintf( buf, sizeof(buf), "\ +
\n\ +
%s
\n\ +\n\ +\n", + SERVER_ADDRESS, EXPOSED_SERVER_SOFTWARE ); + add_response( hc, buf ); + } + + +static void +defang( char* str, char* dfstr, int dfsize ) + { + char* cp1; + char* cp2; + + for ( cp1 = str, cp2 = dfstr; + *cp1 != '\0' && cp2 - dfstr < dfsize - 5; + ++cp1, ++cp2 ) + { + switch ( *cp1 ) + { + case '<': + *cp2++ = '&'; + *cp2++ = 'l'; + *cp2++ = 't'; + *cp2 = ';'; + break; + case '>': + *cp2++ = '&'; + *cp2++ = 'g'; + *cp2++ = 't'; + *cp2 = ';'; + break; + default: + *cp2 = *cp1; + break; + } + } + *cp2 = '\0'; + } + + +void +httpd_send_err( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ) + { +#ifdef ERR_DIR + + char filename[1000]; + + /* Try virtual host error page. */ + if ( hc->hs->vhost && hc->hostdir[0] != '\0' ) + { + (void) my_snprintf( filename, sizeof(filename), + "%s/%s/err%d.html", hc->hostdir, ERR_DIR, status ); + if ( send_err_file( hc, status, title, extraheads, filename ) ) + return; + } + + /* Try server-wide error page. */ + (void) my_snprintf( filename, sizeof(filename), + "%s/err%d.html", ERR_DIR, status ); + if ( send_err_file( hc, status, title, extraheads, filename ) ) + return; + + /* Fall back on built-in error page. */ + send_response( hc, status, title, extraheads, form, arg ); + +#else /* ERR_DIR */ + + send_response( hc, status, title, extraheads, form, arg ); + +#endif /* ERR_DIR */ + } + + +#ifdef ERR_DIR +static int +send_err_file( httpd_conn* hc, int status, char* title, char* extraheads, char* filename ) + { + FILE* fp; + char buf[1000]; + size_t r; + + fp = fopen( filename, "r" ); + if ( fp == (FILE*) 0 ) + return 0; + send_mime( + hc, status, title, "", extraheads, "text/html; charset=%s", (off_t) -1, + (time_t) 0 ); + for (;;) + { + r = fread( buf, 1, sizeof(buf) - 1, fp ); + if ( r == 0 ) + break; + buf[r] = '\0'; + add_response( hc, buf ); + } + (void) fclose( fp ); + +#ifdef ERR_APPEND_SERVER_INFO + send_response_tail( hc ); +#endif /* ERR_APPEND_SERVER_INFO */ + + return 1; + } +#endif /* ERR_DIR */ + + +#ifdef AUTH_FILE + +static void +send_authenticate( httpd_conn* hc, char* realm ) + { + static char* header; + static size_t maxheader = 0; + static char headstr[] = "WWW-Authenticate: Basic realm=\""; + + httpd_realloc_str( + &header, &maxheader, sizeof(headstr) + strlen( realm ) + 3 ); + (void) my_snprintf( header, maxheader, "%s%s\"\015\012", headstr, realm ); + httpd_send_err( hc, 401, err401title, header, err401form, hc->encodedurl ); + /* If the request was a POST then there might still be data to be read, + ** so we need to do a lingering close. + */ + if ( hc->method == METHOD_POST ) + hc->should_linger = 1; + } + + +/* Base-64 decoding. This represents binary data as printable ASCII +** characters. Three 8-bit binary bytes are turned into four 6-bit +** values, like so: +** +** [11111111] [22222222] [33333333] +** +** [111111] [112222] [222233] [333333] +** +** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". +*/ + +static int b64_decode_table[256] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ + 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ + 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ + -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ + 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ + }; + +/* Do base-64 decoding on a string. Ignore any non-base64 bytes. +** Return the actual number of bytes generated. The decoded size will +** be at most 3/4 the size of the encoded, and may be smaller if there +** are padding characters (blanks, newlines). +*/ +static int +b64_decode( const char* str, unsigned char* space, int size ) + { + const char* cp; + int space_idx, phase; + int d, prev_d = 0; + unsigned char c; + + space_idx = 0; + phase = 0; + for ( cp = str; *cp != '\0'; ++cp ) + { + d = b64_decode_table[(int) *cp]; + if ( d != -1 ) + { + switch ( phase ) + { + case 0: + ++phase; + break; + case 1: + c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); + if ( space_idx < size ) + space[space_idx++] = c; + ++phase; + break; + case 2: + c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); + if ( space_idx < size ) + space[space_idx++] = c; + ++phase; + break; + case 3: + c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); + if ( space_idx < size ) + space[space_idx++] = c; + phase = 0; + break; + } + prev_d = d; + } + } + return space_idx; + } + + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int +auth_check( httpd_conn* hc, char* dirname ) + { + if ( hc->hs->global_passwd ) + { + char* topdir; + if ( hc->hs->vhost && hc->hostdir[0] != '\0' ) + topdir = hc->hostdir; + else + topdir = "."; + switch ( auth_check2( hc, topdir ) ) + { + case -1: + return -1; + case 1: + return 1; + } + } + return auth_check2( hc, dirname ); + } + + +/* Returns -1 == unauthorized, 0 == no auth file, 1 = authorized. */ +static int +auth_check2( httpd_conn* hc, char* dirname ) + { + static char* authpath; + static size_t maxauthpath = 0; + struct stat sb; + char authinfo[500]; + char* authpass; + char* colon; + int l; + FILE* fp; + char line[500]; + char* cryp; + static char* prevauthpath; + static size_t maxprevauthpath = 0; + static time_t prevmtime; + static char* prevuser; + static size_t maxprevuser = 0; + static char* prevcryp; + static size_t maxprevcryp = 0; + + /* Construct auth filename. */ + httpd_realloc_str( + &authpath, &maxauthpath, strlen( dirname ) + 1 + sizeof(AUTH_FILE) ); + (void) my_snprintf( authpath, maxauthpath, "%s/%s", dirname, AUTH_FILE ); + + /* Does this directory have an auth file? */ + if ( stat( authpath, &sb ) < 0 ) + /* Nope, let the request go through. */ + return 0; + + /* Does this request contain basic authorization info? */ + if ( hc->authorization[0] == '\0' || + strncmp( hc->authorization, "Basic ", 6 ) != 0 ) + { + /* Nope, return a 401 Unauthorized. */ + send_authenticate( hc, dirname ); + return -1; + } + + /* Decode it. */ + l = b64_decode( + &(hc->authorization[6]), (unsigned char*) authinfo, + sizeof(authinfo) - 1 ); + authinfo[l] = '\0'; + /* Split into user and password. */ + authpass = strchr( authinfo, ':' ); + if ( authpass == (char*) 0 ) + { + /* No colon? Bogus auth info. */ + send_authenticate( hc, dirname ); + return -1; + } + *authpass++ = '\0'; + /* If there are more fields, cut them off. */ + colon = strchr( authpass, ':' ); + if ( colon != (char*) 0 ) + *colon = '\0'; + + /* See if we have a cached entry and can use it. */ + if ( maxprevauthpath != 0 && + strcmp( authpath, prevauthpath ) == 0 && + sb.st_mtime == prevmtime && + strcmp( authinfo, prevuser ) == 0 ) + { + /* Yes. Check against the cached encrypted password. */ + if ( strcmp( crypt( authpass, prevcryp ), prevcryp ) == 0 ) + { + /* Ok! */ + httpd_realloc_str( + &hc->remoteuser, &hc->maxremoteuser, strlen( authinfo ) ); + (void) strcpy( hc->remoteuser, authinfo ); + return 1; + } + else + { + /* No. */ + send_authenticate( hc, dirname ); + return -1; + } + } + + /* Open the password file. */ + fp = fopen( authpath, "r" ); + if ( fp == (FILE*) 0 ) + { + /* The file exists but we can't open it? Disallow access. */ +// syslog( +// LOG_ERR, "%.80s auth file %.80s could not be opened - %m", +// httpd_ntoa( &hc->client_addr ), authpath ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is protected by an authentication file, but the authentication file cannot be opened.\n" ), + hc->encodedurl ); + return -1; + } + + /* Read it. */ + while ( fgets( line, sizeof(line), fp ) != (char*) 0 ) + { + /* Nuke newline. */ + l = strlen( line ); + if ( line[l - 1] == '\n' ) + line[l - 1] = '\0'; + /* Split into user and encrypted password. */ + cryp = strchr( line, ':' ); + if ( cryp == (char*) 0 ) + continue; + *cryp++ = '\0'; + /* Is this the right user? */ + if ( strcmp( line, authinfo ) == 0 ) + { + /* Yes. */ + (void) fclose( fp ); + /* So is the password right? */ + if ( strcmp( crypt( authpass, cryp ), cryp ) == 0 ) + { + /* Ok! */ + httpd_realloc_str( + &hc->remoteuser, &hc->maxremoteuser, strlen( line ) ); + (void) strcpy( hc->remoteuser, line ); + /* And cache this user's info for next time. */ + httpd_realloc_str( + &prevauthpath, &maxprevauthpath, strlen( authpath ) ); + (void) strcpy( prevauthpath, authpath ); + prevmtime = sb.st_mtime; + httpd_realloc_str( + &prevuser, &maxprevuser, strlen( authinfo ) ); + (void) strcpy( prevuser, authinfo ); + httpd_realloc_str( &prevcryp, &maxprevcryp, strlen( cryp ) ); + (void) strcpy( prevcryp, cryp ); + return 1; + } + else + { + /* No. */ + send_authenticate( hc, dirname ); + return -1; + } + } + } + + /* Didn't find that user. Access denied. */ + (void) fclose( fp ); + send_authenticate( hc, dirname ); + return -1; + } + +#endif /* AUTH_FILE */ + + +static void +send_dirredirect( httpd_conn* hc ) + { + /*static*/char* location; + /*static*/char* header; + /*static*/size_t maxlocation = 0, maxheader = 0; + static char headstr[] = "Location: "; + + if ( hc->query[0] != '\0') + { + char* cp = strchr( hc->encodedurl, '?' ); + if ( cp != (char*) 0 ) /* should always find it */ + *cp = '\0'; + httpd_realloc_str( + &location, &maxlocation, + strlen( hc->encodedurl ) + 2 + strlen( hc->query ) ); + (void) my_snprintf( location, maxlocation, + "%s/?%s", hc->encodedurl, hc->query ); + } + else + { + httpd_realloc_str( + &location, &maxlocation, strlen( hc->encodedurl ) + 1 ); + (void) my_snprintf( location, maxlocation, + "%s/", hc->encodedurl ); + } + httpd_realloc_str( + &header, &maxheader, sizeof(headstr) + strlen( location ) ); + (void) my_snprintf( header, maxheader, + "%s%s\015\012", headstr, location ); + send_response( hc, 302, err302title, header, err302form, location ); + free(header); + free(location); + } + + +char* +httpd_method_str( int method ) + { + switch ( method ) + { + case METHOD_GET: return "GET"; + case METHOD_HEAD: return "HEAD"; + case METHOD_POST: return "POST"; + default: return "UNKNOWN"; + } + } + + +static int +hexit( char c ) + { + if ( c >= '0' && c <= '9' ) + return c - '0'; + if ( c >= 'a' && c <= 'f' ) + return c - 'a' + 10; + if ( c >= 'A' && c <= 'F' ) + return c - 'A' + 10; + return 0; /* shouldn't happen, we're guarded by isxdigit() */ + } + + +/* Copies and decodes a string. It's ok for from and to to be the +** same string. +*/ +static void +strdecode( char* to, char* from ) + { + for ( ; *from != '\0'; ++to, ++from ) + { + if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) ) + { + *to = hexit( from[1] ) * 16 + hexit( from[2] ); + from += 2; + } + else + *to = *from; + } + *to = '\0'; + } + + +#ifdef GENERATE_INDEXES +/* Copies and encodes a string. */ +static void +strencode( char* to, int tosize, char* from ) + { + int tolen; + + for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from ) + { + if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 ) + { + *to = *from; + ++to; + ++tolen; + } + else + { + (void) sprintf( to, "%%%02x", (int) *from & 0xff ); + to += 3; + tolen += 3; + } + } + *to = '\0'; + } +#endif /* GENERATE_INDEXES */ + + +#ifdef TILDE_MAP_1 +/* Map a ~username/whatever URL into /username. */ +static int +tilde_map_1( httpd_conn* hc ) + { + static char* temp; + static size_t maxtemp = 0; + int len; + static char* prefix = TILDE_MAP_1; + + len = strlen( hc->expnfilename ) - 1; + httpd_realloc_str( &temp, &maxtemp, len ); + (void) strcpy( temp, &hc->expnfilename[1] ); + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, strlen( prefix ) + 1 + len ); + (void) strcpy( hc->expnfilename, prefix ); + if ( prefix[0] != '\0' ) + (void) strcat( hc->expnfilename, "/" ); + (void) strcat( hc->expnfilename, temp ); + return 1; + } +#endif /* TILDE_MAP_1 */ + +#ifdef TILDE_MAP_2 +/* Map a ~username/whatever URL into /. */ +static int +tilde_map_2( httpd_conn* hc ) + { + static char* temp; + static size_t maxtemp = 0; + static char* postfix = TILDE_MAP_2; + char* cp; + struct passwd* pw; + char* alt; + char* rest; + + /* Get the username. */ + httpd_realloc_str( &temp, &maxtemp, strlen( hc->expnfilename ) - 1 ); + (void) strcpy( temp, &hc->expnfilename[1] ); + cp = strchr( temp, '/' ); + if ( cp != (char*) 0 ) + *cp++ = '\0'; + else + cp = ""; + + /* Get the passwd entry. */ + pw = getpwnam( temp ); + if ( pw == (struct passwd*) 0 ) + return 0; + + /* Set up altdir. */ + httpd_realloc_str( + &hc->altdir, &hc->maxaltdir, + strlen( pw->pw_dir ) + 1 + strlen( postfix ) ); + (void) strcpy( hc->altdir, pw->pw_dir ); + if ( postfix[0] != '\0' ) + { + (void) strcat( hc->altdir, "/" ); + (void) strcat( hc->altdir, postfix ); + } + alt = expand_symlinks( hc->altdir, &rest, 0, 1 ); + if ( rest[0] != '\0' ) + return 0; + httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) ); + (void) strcpy( hc->altdir, alt ); + + /* And the filename becomes altdir plus the post-~ part of the original. */ + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, + strlen( hc->altdir ) + 1 + strlen( cp ) ); + (void) my_snprintf( hc->expnfilename, hc->maxexpnfilename, + "%s/%s", hc->altdir, cp ); + + /* For this type of tilde mapping, we want to defeat vhost mapping. */ + hc->tildemapped = 1; + + return 1; + } +#endif /* TILDE_MAP_2 */ + + +/* Virtual host mapping. */ +static int +vhost_map( httpd_conn* hc ) + { + httpd_sockaddr sa; + socklen_t sz; + static char* tempfilename; + static size_t maxtempfilename = 0; + char* cp1; + int len; +#ifdef VHOST_DIRLEVELS + int i; + char* cp2; +#endif /* VHOST_DIRLEVELS */ + + /* Figure out the virtual hostname. */ + if ( hc->reqhost[0] != '\0' ) + hc->hostname = hc->reqhost; + else if ( hc->hdrhost[0] != '\0' ) + hc->hostname = hc->hdrhost; + else + { + sz = sizeof(sa); + if ( getsockname( hc->conn_fd, &sa.sa, &sz ) < 0 ) + { +// syslog( LOG_ERR, "getsockname - %m" ); + return 0; + } + hc->hostname = httpd_ntoa( &sa ); + } + /* Pound it to lower case. */ + for ( cp1 = hc->hostname; *cp1 != '\0'; ++cp1 ) + if ( isupper( *cp1 ) ) + *cp1 = tolower( *cp1 ); + + if ( hc->tildemapped ) + return 1; + + /* Figure out the host directory. */ +#ifdef VHOST_DIRLEVELS + httpd_realloc_str( + &hc->hostdir, &hc->maxhostdir, + strlen( hc->hostname ) + 2 * VHOST_DIRLEVELS ); + if ( strncmp( hc->hostname, "www.", 4 ) == 0 ) + cp1 = &hc->hostname[4]; + else + cp1 = hc->hostname; + for ( cp2 = hc->hostdir, i = 0; i < VHOST_DIRLEVELS; ++i ) + { + /* Skip dots in the hostname. If we don't, then we get vhost + ** directories in higher level of filestructure if dot gets + ** involved into path construction. It's `while' used here instead + ** of `if' for it's possible to have a hostname formed with two + ** dots at the end of it. + */ + while ( *cp1 == '.' ) + ++cp1; + /* Copy a character from the hostname, or '_' if we ran out. */ + if ( *cp1 != '\0' ) + *cp2++ = *cp1++; + else + *cp2++ = '_'; + /* Copy a slash. */ + *cp2++ = '/'; + } + (void) strcpy( cp2, hc->hostname ); +#else /* VHOST_DIRLEVELS */ + httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, strlen( hc->hostname ) ); + (void) strcpy( hc->hostdir, hc->hostname ); +#endif /* VHOST_DIRLEVELS */ + + /* Prepend hostdir to the filename. */ + len = strlen( hc->expnfilename ); + httpd_realloc_str( &tempfilename, &maxtempfilename, len ); + (void) strcpy( tempfilename, hc->expnfilename ); + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, + strlen( hc->hostdir ) + 1 + len ); + (void) strcpy( hc->expnfilename, hc->hostdir ); + (void) strcat( hc->expnfilename, "/" ); + (void) strcat( hc->expnfilename, tempfilename ); + return 1; + } + + +/* Expands all symlinks in the given filename, eliding ..'s and leading /'s. +** Returns the expanded path (pointer to static string), or (char*) 0 on +** errors. Also returns, in the string pointed to by restP, any trailing +** parts of the path that don't exist. +** +** This is a fairly nice little routine. It handles any size filenames +** without excessive mallocs. +** +** PoorMan: This routine is modified for multithreaded envirenment. Static +** pointers are changed. When you done with the pointer returned and restP, +** free() the pointer and *freethis. If there is something wrong within this +** routine, NULL is returned, the malloc()ed is free()ed for you and *freethis +** is set to NULL. +*/ +static char* +expand_symlinks( char* path, char** freethis, char** restP, int no_symlink_check, int tildemapped ) + { + char* checked; + char* rest; + char link[5000]; + size_t maxchecked = 0, maxrest = 0; + size_t checkedlen , restlen, prevcheckedlen, prevrestlen; + ssize_t linklen; + int nlinks, i; + char* r; + char* cp1; + char* cp2; + + if ( no_symlink_check ) + { + /* If we are chrooted, we can actually skip the symlink-expansion, + ** since it's impossible to get out of the tree. However, we still + ** need to do the pathinfo check, and the existing symlink expansion + ** code is a pretty reasonable way to do this. So, what we do is + ** a single stat() of the whole filename - if it exists, then we + ** return it as is with nothing in restP. If it doesn't exist, we + ** fall through to the existing code. + ** + ** One side-effect of this is that users can't symlink to central + ** approved CGIs any more. The workaround is to use the central + ** URL for the CGI instead of a local symlinked one. + */ + struct stat sb; + if ( stat( path, &sb ) != -1 ) + { + checkedlen = strlen( path ); + httpd_realloc_str( &checked, &maxchecked, checkedlen ); + (void) strcpy( checked, path ); + /* Trim trailing slashes. */ + while ( checked[checkedlen - 1] == '/' ) + { + checked[checkedlen - 1] = '\0'; + --checkedlen; + } + httpd_realloc_str( &rest, &maxrest, 0 ); + rest[0] = '\0'; + *restP = rest; + *freethis = rest; + return checked; + } + } + + /* Start out with nothing in checked and the whole filename in rest. */ + httpd_realloc_str( &checked, &maxchecked, 1 ); + checked[0] = '\0'; + checkedlen = 0; + restlen = strlen( path ); + httpd_realloc_str( &rest, &maxrest, restlen ); + (void) strcpy( rest, path ); + if ( rest[restlen - 1] == '/' ) + rest[--restlen] = '\0'; /* trim trailing slash */ + if ( ! tildemapped ) + /* Remove any leading slashes. */ + while ( rest[0] == '/' ) + { + (void) strcpy( rest, &(rest[1]) ); + --restlen; + } + r = rest; + nlinks = 0; + + /* While there are still components to check... */ + while ( restlen > 0 ) + { + /* Save current checkedlen in case we get a symlink. Save current + ** restlen in case we get a non-existant component. + */ + prevcheckedlen = checkedlen; + prevrestlen = restlen; + + /* Grab one component from r and transfer it to checked. */ + cp1 = strchr( r, '/' ); + if ( cp1 != (char*) 0 ) + { + i = cp1 - r; + if ( i == 0 ) + { + /* Special case for absolute paths. */ + httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 ); + (void) strncpy( &checked[checkedlen], r, 1 ); + checkedlen += 1; + } + else if ( strncmp( r, "..", MAX( i, 2 ) ) == 0 ) + { + /* Ignore ..'s that go above the start of the path. */ + if ( checkedlen != 0 ) + { + cp2 = strrchr( checked, '/' ); + if ( cp2 == (char*) 0 ) + checkedlen = 0; + else if ( cp2 == checked ) + checkedlen = 1; + else + checkedlen = cp2 - checked; + } + } + else + { + httpd_realloc_str( &checked, &maxchecked, checkedlen + 1 + i ); + if ( checkedlen > 0 && checked[checkedlen-1] != '/' ) + checked[checkedlen++] = '/'; + (void) strncpy( &checked[checkedlen], r, i ); + checkedlen += i; + } + checked[checkedlen] = '\0'; + r += i + 1; + restlen -= i + 1; + } + else + { + /* No slashes remaining, r is all one component. */ + if ( strcmp( r, ".." ) == 0 ) + { + /* Ignore ..'s that go above the start of the path. */ + if ( checkedlen != 0 ) + { + cp2 = strrchr( checked, '/' ); + if ( cp2 == (char*) 0 ) + checkedlen = 0; + else if ( cp2 == checked ) + checkedlen = 1; + else + checkedlen = cp2 - checked; + checked[checkedlen] = '\0'; + } + } + else + { + httpd_realloc_str( + &checked, &maxchecked, checkedlen + 1 + restlen ); + if ( checkedlen > 0 && checked[checkedlen-1] != '/' ) + checked[checkedlen++] = '/'; + (void) strcpy( &checked[checkedlen], r ); + checkedlen += restlen; + } + r += restlen; + restlen = 0; + } + + /* Try reading the current filename as a symlink */ + if ( checked[0] == '\0' ) + continue; + linklen = readlink( checked, link, sizeof(link) - 1 ); + if ( linklen == -1 ) + { + if ( errno == EINVAL ) + continue; /* not a symlink */ + if ( errno == EACCES || errno == ENOENT || errno == ENOTDIR ) + { + /* That last component was bogus. Restore and return. */ + *restP = r - ( prevrestlen - restlen ); + if ( prevcheckedlen == 0 ) + (void) strcpy( checked, "." ); + else + checked[prevcheckedlen] = '\0'; + *freethis = rest; + return checked; + } +// syslog( LOG_ERR, "readlink %.80s - %m", checked ); + free(checked); + free(rest); + *freethis = 0; + return (char*) 0; + } + ++nlinks; + if ( nlinks > MAX_LINKS ) + { +// syslog( LOG_ERR, "too many symlinks in %.80s", path ); + free(checked); + free(rest); + *freethis = 0; + return (char*) 0; + } + link[linklen] = '\0'; + if ( link[linklen - 1] == '/' ) + link[--linklen] = '\0'; /* trim trailing slash */ + + /* Insert the link contents in front of the rest of the filename. */ + if ( restlen != 0 ) + { + (void) strcpy( rest, r ); + httpd_realloc_str( &rest, &maxrest, restlen + linklen + 1 ); + for ( i = restlen; i >= 0; --i ) + rest[i + linklen + 1] = rest[i]; + (void) strcpy( rest, link ); + rest[linklen] = '/'; + restlen += linklen + 1; + r = rest; + } + else + { + /* There's nothing left in the filename, so the link contents + ** becomes the rest. + */ + httpd_realloc_str( &rest, &maxrest, linklen ); + (void) strcpy( rest, link ); + restlen = linklen; + r = rest; + } + + if ( rest[0] == '/' ) + { + /* There must have been an absolute symlink - zero out checked. */ + checked[0] = '\0'; + checkedlen = 0; + } + else + { + /* Re-check this component. */ + checkedlen = prevcheckedlen; + checked[checkedlen] = '\0'; + } + } + + /* Ok. */ + *restP = r; + if ( checked[0] == '\0' ) + (void) strcpy( checked, "." ); + *freethis = rest; + return checked; + } + + +int +httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc ) + { + httpd_sockaddr sa; + socklen_t sz; + //int retval; + + if ( ! hc->initialized ) + { + hc->read_size = 0; + httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 ); + hc->maxdecodedurl = + hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = + hc->maxpathinfo = hc->maxquery = hc->maxaccept = + hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = + hc->maxremoteuser = hc->maxresponse = 0; +#ifdef TILDE_MAP_2 + hc->maxaltdir = 0; +#endif /* TILDE_MAP_2 */ + httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 ); + httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 ); + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 ); + httpd_realloc_str( &hc->encodings, &hc->maxencodings, 0 ); + httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, 0 ); + httpd_realloc_str( &hc->query, &hc->maxquery, 0 ); + httpd_realloc_str( &hc->accept, &hc->maxaccept, 0 ); + httpd_realloc_str( &hc->accepte, &hc->maxaccepte, 0 ); + httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 ); + httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 ); + httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 ); + httpd_realloc_str( &hc->response, &hc->maxresponse, 0 ); +#ifdef TILDE_MAP_2 + httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 ); +#endif /* TILDE_MAP_2 */ + hc->initialized = 1; + } + + /* Accept the new connection. */ + sz = sizeof(sa); + hc->conn_fd = accept( listen_fd, &sa.sa, &sz ); + if ( hc->conn_fd < 0 ) + { + if ( errno == EWOULDBLOCK ) + return GC_NO_MORE; +// syslog( LOG_ERR, "accept - %m" ); + return GC_FAIL; + } + if ( ! sockaddr_check( &sa ) ) + { +// syslog( LOG_ERR, "unknown sockaddr family" ); + close( hc->conn_fd ); + hc->conn_fd = -1; + return GC_FAIL; + } + (void) fcntl( hc->conn_fd, F_SETFD, 1 ); + hc->hs = hs; + (void) memset( &hc->client_addr, 0, sizeof(hc->client_addr) ); + (void) memmove( &hc->client_addr, &sa, sockaddr_len( &sa ) ); + hc->read_idx = 0; + hc->checked_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->response[0] = '\0'; +#ifdef TILDE_MAP_2 + hc->altdir[0] = '\0'; +#endif /* TILDE_MAP_2 */ + hc->responselen = 0; + hc->if_modified_since = (time_t) -1; + hc->range_if = (time_t) -1; + hc->contentlength = -1; + hc->type = ""; + hc->hostname = (char*) 0; + hc->mime_flag = 1; + hc->one_one = 0; + hc->got_range = 0; + hc->tildemapped = 0; + hc->first_byte_index = 0; + hc->last_byte_index = -1; + hc->keep_alive = 0; + hc->should_linger = 0; + hc->file_address = (char*) 0; + return GC_OK; + } + + +/* Checks hc->read_buf to see whether a complete request has been read so far; +** either the first line has two words (an HTTP/0.9 request), or the first +** line has three words and there's a blank line present. +** +** hc->read_idx is how much has been read in; hc->checked_idx is how much we +** have checked so far; and hc->checked_state is the current state of the +** finite state machine. +*/ +int +httpd_got_request( httpd_conn* hc ) + { + char c; + + for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) + { + c = hc->read_buf[hc->checked_idx]; + switch ( hc->checked_state ) + { + case CHST_FIRSTWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_FIRSTWS; + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_FIRSTWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_SECONDWORD; + break; + } + break; + case CHST_SECONDWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_SECONDWS; + break; + case '\012': case '\015': + /* The first line has only two words - an HTTP/0.9 request. */ + return GR_GOT_REQUEST; + } + break; + case CHST_SECONDWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': case '\015': + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + default: + hc->checked_state = CHST_THIRDWORD; + break; + } + break; + case CHST_THIRDWORD: + switch ( c ) + { + case ' ': case '\t': + hc->checked_state = CHST_THIRDWS; + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_THIRDWS: + switch ( c ) + { + case ' ': case '\t': + break; + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_BOGUS; + return GR_BAD_REQUEST; + } + break; + case CHST_LINE: + switch ( c ) + { + case '\012': + hc->checked_state = CHST_LF; + break; + case '\015': + hc->checked_state = CHST_CR; + break; + } + break; + case CHST_LF: + switch ( c ) + { + case '\012': + /* Two newlines in a row - a blank line - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CR: + switch ( c ) + { + case '\012': + hc->checked_state = CHST_CRLF; + break; + case '\015': + /* Two returns in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLF: + switch ( c ) + { + case '\012': + /* Two newlines in a row - end of request. */ + return GR_GOT_REQUEST; + case '\015': + hc->checked_state = CHST_CRLFCR; + break; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_CRLFCR: + switch ( c ) + { + case '\012': case '\015': + /* Two CRLFs or two CRs in a row - end of request. */ + return GR_GOT_REQUEST; + default: + hc->checked_state = CHST_LINE; + break; + } + break; + case CHST_BOGUS: + return GR_BAD_REQUEST; + } + } + return GR_NO_REQUEST; + } + + +int +httpd_parse_request( httpd_conn* hc ) + { + char* buf; + char* method_str; + char* url; + char* protocol; + char* reqhost; + char* eol; + char* cp; + char* pi; + char* freethis; + + hc->checked_idx = 0; /* reset */ + method_str = bufgets( hc ); + url = strpbrk( method_str, " \t\012\015" ); + if ( url == (char*) 0 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + *url++ = '\0'; + url += strspn( url, " \t\012\015" ); + protocol = strpbrk( url, " \t\012\015" ); + if ( protocol == (char*) 0 ) + { + protocol = "HTTP/0.9"; + hc->mime_flag = 0; + } + else + { + *protocol++ = '\0'; + protocol += strspn( protocol, " \t\012\015" ); + if ( *protocol != '\0' ) + { + eol = strpbrk( protocol, " \t\012\015" ); + if ( eol != (char*) 0 ) + *eol = '\0'; + if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 ) + hc->one_one = 1; + } + } + hc->protocol = protocol; + + /* Check for HTTP/1.1 absolute URL. */ + if ( strncasecmp( url, "http://", 7 ) == 0 ) + { + if ( ! hc->one_one ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + reqhost = url + 7; + url = strchr( reqhost, '/' ); + if ( url == (char*) 0 ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + *url = '\0'; + if ( strchr( reqhost, '/' ) != (char*) 0 || reqhost[0] == '.' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, strlen( reqhost ) ); + (void) strcpy( hc->reqhost, reqhost ); + *url = '/'; + } + + if ( *url != '/' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 ) + hc->method = METHOD_GET; + else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 ) + hc->method = METHOD_HEAD; + /*else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 ) + hc->method = METHOD_POST;*/ + else + { + httpd_send_err( hc, 501, err501title, "", err501form, method_str ); + return -1; + } + + hc->encodedurl = url; + httpd_realloc_str( + &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) ); + strdecode( hc->decodedurl, hc->encodedurl ); + + httpd_realloc_str( + &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) ); + (void) strcpy( hc->origfilename, &hc->decodedurl[1] ); + /* Special case for top-level URL. */ + if ( hc->origfilename[0] == '\0' ) + (void) strcpy( hc->origfilename, "." ); + + /* Extract query string from encoded URL. */ + cp = strchr( hc->encodedurl, '?' ); + if ( cp != (char*) 0 ) + { + ++cp; + httpd_realloc_str( &hc->query, &hc->maxquery, strlen( cp ) ); + (void) strcpy( hc->query, cp ); + /* Remove query from (decoded) origfilename. */ + cp = strchr( hc->origfilename, '?' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + } + + de_dotdot( hc->origfilename ); + if ( hc->origfilename[0] == '/' || + ( hc->origfilename[0] == '.' && hc->origfilename[1] == '.' && + ( hc->origfilename[2] == '\0' || hc->origfilename[2] == '/' ) ) ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + if ( hc->mime_flag ) + { + /* Read the MIME headers. */ + while ( ( buf = bufgets( hc ) ) != (char*) 0 ) + { + if ( buf[0] == '\0' ) + break; + if ( strncasecmp( buf, "Referer:", 8 ) == 0 ) + { + cp = &buf[8]; + cp += strspn( cp, " \t" ); + hc->referer = cp; + } + else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 ) + { + cp = &buf[11]; + cp += strspn( cp, " \t" ); + hc->useragent = cp; + } + else if ( strncasecmp( buf, "Host:", 5 ) == 0 ) + { + cp = &buf[5]; + cp += strspn( cp, " \t" ); + hc->hdrhost = cp; + cp = strchr( hc->hdrhost, ':' ); + if ( cp != (char*) 0 ) + *cp = '\0'; + if ( strchr( hc->hdrhost, '/' ) != (char*) 0 || hc->hdrhost[0] == '.' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + } + else if ( strncasecmp( buf, "Accept:", 7 ) == 0 ) + { + cp = &buf[7]; + cp += strspn( cp, " \t" ); + if ( hc->accept[0] != '\0' ) + { + if ( strlen( hc->accept ) > 5000 ) + { +// syslog( +// LOG_ERR, "%.80s way too much Accept: data", +// httpd_ntoa( &hc->client_addr ) ); + continue; + } + httpd_realloc_str( + &hc->accept, &hc->maxaccept, + strlen( hc->accept ) + 2 + strlen( cp ) ); + (void) strcat( hc->accept, ", " ); + } + else + httpd_realloc_str( + &hc->accept, &hc->maxaccept, strlen( cp ) ); + (void) strcat( hc->accept, cp ); + } + else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 ) + { + cp = &buf[16]; + cp += strspn( cp, " \t" ); + if ( hc->accepte[0] != '\0' ) + { + if ( strlen( hc->accepte ) > 5000 ) + { +// syslog( +// LOG_ERR, "%.80s way too much Accept-Encoding: data", +// httpd_ntoa( &hc->client_addr ) ); + continue; + } + httpd_realloc_str( + &hc->accepte, &hc->maxaccepte, + strlen( hc->accepte ) + 2 + strlen( cp ) ); + (void) strcat( hc->accepte, ", " ); + } + else + httpd_realloc_str( + &hc->accepte, &hc->maxaccepte, strlen( cp ) ); + (void) strcpy( hc->accepte, cp ); + } + else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 ) + { + cp = &buf[16]; + cp += strspn( cp, " \t" ); + hc->acceptl = cp; + } + else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 ) + { + cp = &buf[18]; + hc->if_modified_since = tdate_parse( cp ); + if ( hc->if_modified_since == (time_t) -1 ) + /*syslog( LOG_DEBUG, "unparsable time: %.80s", cp )*/; + } + else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 ) + { + cp = &buf[7]; + cp += strspn( cp, " \t" ); + hc->cookie = cp; + } + else if ( strncasecmp( buf, "Range:", 6 ) == 0 ) + { + /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ + if ( strchr( buf, ',' ) == (char*) 0 ) + { + char* cp_dash; + cp = strpbrk( buf, "=" ); + if ( cp != (char*) 0 ) + { + cp_dash = strchr( cp + 1, '-' ); + if ( cp_dash != (char*) 0 && cp_dash != cp + 1 ) + { + *cp_dash = '\0'; + hc->got_range = 1; + hc->first_byte_index = atoll( cp + 1 ); + if ( hc->first_byte_index < 0 ) + hc->first_byte_index = 0; + if ( isdigit( (int) cp_dash[1] ) ) + { + hc->last_byte_index = atoll( cp_dash + 1 ); + if ( hc->last_byte_index < 0 ) + hc->last_byte_index = -1; + } + } + } + } + } + else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 || + strncasecmp( buf, "If-Range:", 9 ) == 0 ) + { + cp = &buf[9]; + hc->range_if = tdate_parse( cp ); + if ( hc->range_if == (time_t) -1 ) + /*syslog( LOG_DEBUG, "unparsable time: %.80s", cp )*/; + } + else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 ) + { + cp = &buf[13]; + cp += strspn( cp, " \t" ); + hc->contenttype = cp; + } + else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 ) + { + cp = &buf[15]; + hc->contentlength = atol( cp ); + } + else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 ) + { + cp = &buf[14]; + cp += strspn( cp, " \t" ); + hc->authorization = cp; + } + else if ( strncasecmp( buf, "Connection:", 11 ) == 0 ) + { + cp = &buf[11]; + cp += strspn( cp, " \t" ); + if ( strcasecmp( cp, "keep-alive" ) == 0 ) + hc->keep_alive = 1; + } +#ifdef LOG_UNKNOWN_HEADERS + else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 || + strncasecmp( buf, "Accept-Language:", 16 ) == 0 || + strncasecmp( buf, "Agent:", 6 ) == 0 || + strncasecmp( buf, "Cache-Control:", 14 ) == 0 || + strncasecmp( buf, "Cache-Info:", 11 ) == 0 || + strncasecmp( buf, "Charge-To:", 10 ) == 0 || + strncasecmp( buf, "Client-IP:", 10 ) == 0 || + strncasecmp( buf, "Date:", 5 ) == 0 || + strncasecmp( buf, "Extension:", 10 ) == 0 || + strncasecmp( buf, "Forwarded:", 10 ) == 0 || + strncasecmp( buf, "From:", 5 ) == 0 || + strncasecmp( buf, "HTTP-Version:", 13 ) == 0 || + strncasecmp( buf, "Max-Forwards:", 13 ) == 0 || + strncasecmp( buf, "Message-Id:", 11 ) == 0 || + strncasecmp( buf, "MIME-Version:", 13 ) == 0 || + strncasecmp( buf, "Negotiate:", 10 ) == 0 || + strncasecmp( buf, "Pragma:", 7 ) == 0 || + strncasecmp( buf, "Proxy-Agent:", 12 ) == 0 || + strncasecmp( buf, "Proxy-Connection:", 17 ) == 0 || + strncasecmp( buf, "Security-Scheme:", 16 ) == 0 || + strncasecmp( buf, "Session-Id:", 11 ) == 0 || + strncasecmp( buf, "UA-Color:", 9 ) == 0 || + strncasecmp( buf, "UA-CPU:", 7 ) == 0 || + strncasecmp( buf, "UA-Disp:", 8 ) == 0 || + strncasecmp( buf, "UA-OS:", 6 ) == 0 || + strncasecmp( buf, "UA-Pixels:", 10 ) == 0 || + strncasecmp( buf, "User:", 5 ) == 0 || + strncasecmp( buf, "Via:", 4 ) == 0 || + strncasecmp( buf, "X-", 2 ) == 0 ) + ; /* ignore */ + else + syslog( LOG_DEBUG, "unknown request header: %.80s", buf ); +#endif /* LOG_UNKNOWN_HEADERS */ + } + } + + if ( hc->one_one ) + { + /* Check that HTTP/1.1 requests specify a host, as required. */ + if ( hc->reqhost[0] == '\0' && hc->hdrhost[0] == '\0' ) + { + httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); + return -1; + } + + /* If the client wants to do keep-alives, it might also be doing + ** pipelining. There's no way for us to tell. Since we don't + ** implement keep-alives yet, if we close such a connection there + ** might be unread pipelined requests waiting. So, we have to + ** do a lingering close. + */ + if ( hc->keep_alive ) + hc->should_linger = 1; + } + + /* Ok, the request has been parsed. Now we resolve stuff that + ** may require the entire request. + */ + + /* Copy original filename to expanded filename. */ + httpd_realloc_str( + &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) ); + (void) strcpy( hc->expnfilename, hc->origfilename ); + + /* Tilde mapping. */ + if ( hc->expnfilename[0] == '~' ) + { +#ifdef TILDE_MAP_1 + if ( ! tilde_map_1( hc ) ) + { + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } +#endif /* TILDE_MAP_1 */ +#ifdef TILDE_MAP_2 + if ( ! tilde_map_2( hc ) ) + { + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } +#endif /* TILDE_MAP_2 */ + } + + /* Virtual host mapping. */ + if ( hc->hs->vhost ) + if ( ! vhost_map( hc ) ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + + /* Expand all symbolic links in the filename. This also gives us + ** any trailing non-existing components, for pathinfo. + */ + cp = expand_symlinks( hc->expnfilename, &freethis, &pi, hc->hs->no_symlink_check, hc->tildemapped ); + if ( cp == (char*) 0 ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) ); + (void) strcpy( hc->expnfilename, cp ); + free(cp); + httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) ); + (void) strcpy( hc->pathinfo, pi ); + free(freethis); + + /* Remove pathinfo stuff from the original filename too. */ + if ( hc->pathinfo[0] != '\0' ) + { + int i; + i = strlen( hc->origfilename ) - strlen( hc->pathinfo ); + if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 ) + hc->origfilename[i - 1] = '\0'; + } + + /* If the expanded filename is an absolute path, check that it's still + ** within the current directory or the alternate directory. + */ + if ( hc->expnfilename[0] == '/' ) + { + if(pthread_rwlock_rdlock(get_web_dir_lock()) == 0){ + if ( strncmp( + hc->expnfilename, hc->hs->cwd, strlen( hc->hs->cwd ) ) == 0 ) + { + /* Elide the current directory. */ + (void) strcpy( + hc->expnfilename, &hc->expnfilename[strlen( hc->hs->cwd )] ); + } +#ifdef TILDE_MAP_2 + else if ( hc->altdir[0] != '\0' && + ( strncmp( + hc->expnfilename, hc->altdir, + strlen( hc->altdir ) ) == 0 && + ( hc->expnfilename[strlen( hc->altdir )] == '\0' || + hc->expnfilename[strlen( hc->altdir )] == '/' ) ) ) + {} +#endif /* TILDE_MAP_2 */ + else + { +// syslog( +// LOG_NOTICE, "%.80s URL \"%.80s\" goes outside the web tree", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file outside the permitted web server directory tree.\n" ), + hc->encodedurl ); + return -1; + } + pthread_rwlock_unlock(get_web_dir_lock()); + } + else{ + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + } + + return 0; + } + + +static char* +bufgets( httpd_conn* hc ) + { + int i; + char c; + + for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) + { + c = hc->read_buf[hc->checked_idx]; + if ( c == '\012' || c == '\015' ) + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + if ( c == '\015' && hc->checked_idx < hc->read_idx && + hc->read_buf[hc->checked_idx] == '\012' ) + { + hc->read_buf[hc->checked_idx] = '\0'; + ++hc->checked_idx; + } + return &(hc->read_buf[i]); + } + } + return (char*) 0; + } + + +static void +de_dotdot( char* file ) + { + char* cp; + char* cp2; + int l; + + /* Collapse any multiple / sequences. */ + while ( ( cp = strstr( file, "//") ) != (char*) 0 ) + { + for ( cp2 = cp + 2; *cp2 == '/'; ++cp2 ) + continue; + (void) strcpy( cp + 1, cp2 ); + } + + /* Remove leading ./ and any /./ sequences. */ + while ( strncmp( file, "./", 2 ) == 0 ) + (void) strcpy( file, file + 2 ); + while ( ( cp = strstr( file, "/./") ) != (char*) 0 ) + (void) strcpy( cp, cp + 2 ); + + /* Alternate between removing leading ../ and removing xxx/../ */ + for (;;) + { + while ( strncmp( file, "../", 3 ) == 0 ) + (void) strcpy( file, file + 3 ); + cp = strstr( file, "/../" ); + if ( cp == (char*) 0 ) + break; + for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) + continue; + (void) strcpy( cp2 + 1, cp + 4 ); + } + + /* Also elide any xxx/.. at the end. */ + while ( ( l = strlen( file ) ) > 3 && + strcmp( ( cp = file + l - 3 ), "/.." ) == 0 ) + { + for ( cp2 = cp - 1; cp2 >= file && *cp2 != '/'; --cp2 ) + continue; + if ( cp2 < file ) + break; + *cp2 = '\0'; + } + } + + +void +httpd_close_conn( httpd_conn* hc, struct timeval* nowP ) + { + make_log_entry( hc, nowP ); + + if ( hc->file_address != (char*) 0 ) + { + //mmc_unmap( hc->file_address, &(hc->sb), nowP ); + hc->file_address = (char*) 0; + } + if ( hc->conn_fd >= 0 ) + { + (void) close( hc->conn_fd ); + hc->conn_fd = -1; + } + } + +void +httpd_destroy_conn( httpd_conn* hc ) + { + if ( hc->initialized ) + { + free( (void*) hc->read_buf ); + free( (void*) hc->decodedurl ); + free( (void*) hc->origfilename ); + free( (void*) hc->expnfilename ); + free( (void*) hc->encodings ); + free( (void*) hc->pathinfo ); + free( (void*) hc->query ); + free( (void*) hc->accept ); + free( (void*) hc->accepte ); + free( (void*) hc->reqhost ); + free( (void*) hc->hostdir ); + free( (void*) hc->remoteuser ); + free( (void*) hc->response ); +#ifdef TILDE_MAP_2 + free( (void*) hc->altdir ); +#endif /* TILDE_MAP_2 */ + hc->initialized = 0; + } + } + + +struct mime_entry { + char* ext; + size_t ext_len; + char* val; + size_t val_len; + }; +static struct mime_entry enc_tab[] = { +#include "mime_encodings.h" + }; +static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); +static struct mime_entry typ_tab[] = { +#include "mime_types.h" + }; +static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); + + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int +ext_compare( a, b ) + struct mime_entry* a; + struct mime_entry* b; + { + return strcmp( a->ext, b->ext ); + } + + +static void +init_mime( void ) + { + int i; + + /* Sort the tables so we can do binary search. */ + qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); + qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); + + /* Fill in the lengths. */ + for ( i = 0; i < n_enc_tab; ++i ) + { + enc_tab[i].ext_len = strlen( enc_tab[i].ext ); + enc_tab[i].val_len = strlen( enc_tab[i].val ); + } + for ( i = 0; i < n_typ_tab; ++i ) + { + typ_tab[i].ext_len = strlen( typ_tab[i].ext ); + typ_tab[i].val_len = strlen( typ_tab[i].val ); + } + + } + + +/* Figure out MIME encodings and type based on the filename. Multiple +** encodings are separated by commas, and are listed in the order in +** which they were applied to the file. +*/ +static void +figure_mime( httpd_conn* hc ) + { + char* prev_dot; + char* dot; + char* ext; + int me_indexes[100]; + unsigned int n_me_indexes; + size_t ext_len, encodings_len; + int i, top, bot, mid; + int r; + char* default_type = "text/plain"; + + /* Peel off encoding extensions until there aren't any more. */ + n_me_indexes = 0; + for ( prev_dot = &hc->expnfilename[strlen(hc->expnfilename)]; ; prev_dot = dot ) + { + for ( dot = prev_dot - 1; dot >= hc->expnfilename && *dot != '.'; --dot ) + ; + if ( dot < hc->expnfilename ) + { + /* No dot found. No more encoding extensions, and no type + ** extension either. + */ + hc->type = default_type; + goto done; + } + ext = dot + 1; + ext_len = prev_dot - ext; + /* Search the encodings table. Linear search is fine here, there + ** are only a few entries. + */ + for ( i = 0; i < n_enc_tab; ++i ) + { + if ( ext_len == enc_tab[i].ext_len && strncasecmp( ext, enc_tab[i].ext, ext_len ) == 0 ) + { + if ( n_me_indexes < sizeof(me_indexes)/sizeof(*me_indexes) ) + { + me_indexes[n_me_indexes] = i; + ++n_me_indexes; + } + goto next; + } + } + /* No encoding extension found. Break and look for a type extension. */ + break; + + next: ; + } + + /* Binary search for a matching type extension. */ + top = n_typ_tab - 1; + bot = 0; + while ( top >= bot ) + { + mid = ( top + bot ) / 2; + r = strncasecmp( ext, typ_tab[mid].ext, ext_len ); + if ( r < 0 ) + top = mid - 1; + else if ( r > 0 ) + bot = mid + 1; + else + if ( ext_len < typ_tab[mid].ext_len ) + top = mid - 1; + else if ( ext_len > typ_tab[mid].ext_len ) + bot = mid + 1; + else + { + hc->type = typ_tab[mid].val; + goto done; + } + } + hc->type = default_type; + + done: + + /* The last thing we do is actually generate the mime-encoding header. */ + hc->encodings[0] = '\0'; + encodings_len = 0; + for ( i = n_me_indexes - 1; i >= 0; --i ) + { + httpd_realloc_str( + &hc->encodings, &hc->maxencodings, + encodings_len + enc_tab[me_indexes[i]].val_len + 1 ); + if ( hc->encodings[0] != '\0' ) + { + (void) strcpy( &hc->encodings[encodings_len], "," ); + ++encodings_len; + } + (void) strcpy( &hc->encodings[encodings_len], enc_tab[me_indexes[i]].val ); + encodings_len += enc_tab[me_indexes[i]].val_len; + } + + } + +#ifdef GENERATE_INDEXES + +/* qsort comparison routine - declared old-style on purpose, for portability. */ +static int +name_compare( a, b ) + char** a; + char** b; + { + return strcmp( *a, *b ); + } + + +static int +ls( httpd_conn* hc ) + { + DIR* dirp; + struct dirent* de; + struct dirent* dep; + int namlen; + /*static*/int maxnames = 0; + int nnames; + /*static*/char* names = NULL; + /*static*/char** nameptrs = NULL; + /*static*/char* name; + /*static*/size_t maxname = 0; + /*static*/char* rname; + /*static*/size_t maxrname = 0; + /*static*/char* encrname; + /*static*/size_t maxencrname = 0; + FILE* fp; + int i/*, r*/; + struct stat sb; + struct stat lsb; + //char modestr[20]; + char* linkprefix; + char link[MAXPATHLEN+1]; + int linklen; + char* fileclass; + time_t now; + char timestr[26]; + //ClientData client_data; + + de = dep = malloc(sizeof(struct dirent)+B_FILE_NAME_LENGTH+1); + if(de == 0) + { + httpd_send_err( hc, 501, err501title, "", err501form, hc->encodedurl ); + return -1; + } + + dirp = opendir( hc->expnfilename ); + if ( dirp == (DIR*) 0 ) + { + char logString[27+B_PATH_NAME_LENGTH+1]; + sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); + poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); +// syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename ); + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } + + if ( hc->method == METHOD_HEAD ) + { + closedir( dirp ); + send_mime( + hc, 200, ok200title, "", "", "text/html; charset=%s", (off_t) -1, + hc->sb.st_mtime ); + } + else if ( hc->method == METHOD_GET ) + { + { + char logString[B_FILE_NAME_LENGTH+B_PATH_NAME_LENGTH+51]; + if(pthread_rwlock_rdlock(get_web_dir_lock()) == 0){ + sprintf( + logString, + "Directory %s/%s/ has no ", + hc->hs->cwd, + hc->expnfilename + ); + pthread_rwlock_unlock(get_web_dir_lock()); + } + else + strcpy(logString, "A web directory has no "); + + if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){ + strcat(logString, hc->hs->index_name); + pthread_rwlock_unlock(get_index_name_lock()); + } + else + strcat(logString, "index file"); + + strcat(logString, ". Sending directory listing.\n"); + poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, BLACK); + } + + send_mime( + hc, 200, ok200title, "", "", "text/html; charset=%s", + (off_t) -1, hc->sb.st_mtime ); + httpd_write_response( hc ); + + /* Open a stdio stream so that we can use fprintf, which is more + ** efficient than a bunch of separate write()s. We don't have + ** to worry about double closes or file descriptor leaks cause + ** we're in a subprocess. + */ + fp = fdopen( hc->conn_fd, "w" ); + if ( fp == (FILE*) 0 ) + { +// syslog( LOG_ERR, "fdopen - %m" ); + httpd_send_err( + hc, 500, err500title, "", err500form, hc->encodedurl ); + httpd_write_response( hc ); + closedir( dirp ); + return -1; + } + + (void) fprintf( fp, "\ +\n\ +Index of %.80s\n\ +\n\ +

Index of %.80s

\n\ +
\n\
+     bytes  last-changed  name\n\
+
", + hc->decodedurl, hc->decodedurl ); + + /* Read in names. */ + nnames = 0; + while ( readdir_r( dirp, de, &dep ) == 0 && dep != NULL) /* dirent or direct */ + { + if ( nnames >= maxnames ) + { + if ( maxnames == 0 ) + { + maxnames = 100; + names = NEW( char, maxnames * ( MAXPATHLEN + 1 ) ); + nameptrs = NEW( char*, maxnames ); + } + else + { + maxnames *= 2; + names = RENEW( names, char, maxnames * ( MAXPATHLEN + 1 ) ); + nameptrs = RENEW( nameptrs, char*, maxnames ); + } + if ( names == (char*) 0 || nameptrs == (char**) 0 ) + { +// syslog( LOG_ERR, "out of memory reallocating directory names" ); + closedir( dirp ); + return -1; + } + for ( i = 0; i < maxnames; ++i ) + nameptrs[i] = &names[i * ( MAXPATHLEN + 1 )]; + } + namlen = NAMLEN(de); + (void) strncpy( nameptrs[nnames], de->d_name, namlen ); + nameptrs[nnames][namlen] = '\0'; + ++nnames; + }//while loop + closedir( dirp ); + free(de); + + /* Sort the names. */ + qsort( nameptrs, nnames, sizeof(*nameptrs), name_compare ); + + /* Generate output. */ + for ( i = 0; i < nnames; ++i ) + { + httpd_realloc_str( + &name, &maxname, + strlen( hc->expnfilename ) + 1 + strlen( nameptrs[i] ) ); + httpd_realloc_str( + &rname, &maxrname, + strlen( hc->origfilename ) + 1 + strlen( nameptrs[i] ) ); + if ( hc->expnfilename[0] == '\0' || + strcmp( hc->expnfilename, "." ) == 0 ) + { + (void) strcpy( name, nameptrs[i] ); + (void) strcpy( rname, nameptrs[i] ); + } + else + { + (void) my_snprintf( name, maxname, + "%s/%s", hc->expnfilename, nameptrs[i] ); + if ( strcmp( hc->origfilename, "." ) == 0 ) + (void) my_snprintf( rname, maxrname, + "%s", nameptrs[i] ); + else + (void) my_snprintf( rname, maxrname, + "%s%s", hc->origfilename, nameptrs[i] ); + } + httpd_realloc_str( + &encrname, &maxencrname, 3 * strlen( rname ) + 1 ); + strencode( encrname, maxencrname, rname ); + + if ( stat( name, &sb ) < 0 || lstat( name, &lsb ) < 0 ) + continue; + + linkprefix = ""; + link[0] = '\0'; + /* Break down mode word. First the file type. */ + switch ( lsb.st_mode & S_IFMT ) + { + /*case S_IFIFO: modestr[0] = 'p'; break; + case S_IFCHR: modestr[0] = 'c'; break; + case S_IFDIR: modestr[0] = 'd'; break; + case S_IFBLK: modestr[0] = 'b'; break; + case S_IFREG: modestr[0] = '-'; break; + case S_IFSOCK: modestr[0] = 's'; break;*/ + case S_IFLNK: //modestr[0] = 'l'; + linklen = readlink( name, link, sizeof(link) - 1 ); + if ( linklen != -1 ) + { + link[linklen] = '\0'; + linkprefix = " -> "; + } + break; + //default: modestr[0] = '?'; break; + } + /* Now the world permissions. Owner and group permissions + ** are not of interest to web clients. + */ + /*modestr[1] = ( lsb.st_mode & S_IROTH ) ? 'r' : '-'; + modestr[2] = ( lsb.st_mode & S_IWOTH ) ? 'w' : '-'; + modestr[3] = ( lsb.st_mode & S_IXOTH ) ? 'x' : '-'; + modestr[4] = '\0';*/ + + /* We also leave out the owner and group name, they are + ** also not of interest to web clients. Plus if we're + ** running under chroot(), they would require a copy + ** of /etc/passwd and /etc/group, which we want to avoid. + */ + + /* Get time string. */ + now = time( (time_t*) 0 ); + ctime_r( &lsb.st_mtime, timestr ); + timestr[ 0] = timestr[ 4]; + timestr[ 1] = timestr[ 5]; + timestr[ 2] = timestr[ 6]; + timestr[ 3] = ' '; + timestr[ 4] = timestr[ 8]; + timestr[ 5] = timestr[ 9]; + timestr[ 6] = ' '; + if ( now - lsb.st_mtime > 60*60*24*182 ) /* 1/2 year */ + { + timestr[ 7] = ' '; + timestr[ 8] = timestr[20]; + timestr[ 9] = timestr[21]; + timestr[10] = timestr[22]; + timestr[11] = timestr[23]; + } + else + { + timestr[ 7] = timestr[11]; + timestr[ 8] = timestr[12]; + timestr[ 9] = ':'; + timestr[10] = timestr[14]; + timestr[11] = timestr[15]; + } + timestr[12] = '\0'; + + /* The ls -F file class. */ + switch ( sb.st_mode & S_IFMT ) + { + case S_IFDIR: fileclass = "/"; break; + //case S_IFSOCK: fileclass = "="; break; + //case S_IFLNK: fileclass = "@"; break; + default: + fileclass = "";//( sb.st_mode & S_IXOTH ) ? "*" : ""; + break; + } + + /* And print. */ + (void) fprintf( fp, + "%10lld %s %.80s%s%s%s\n", + (int64_t) lsb.st_size, + timestr, encrname, S_ISDIR(sb.st_mode) ? "/" : "", + nameptrs[i], linkprefix, link, fileclass ); + }//for loop + + (void) fprintf( fp, "
\n\n" ); + (void) fclose( fp ); + + hc->status = 200; + hc->bytes_sent = CGI_BYTECOUNT; + hc->should_linger = 0; + + free(names); + free(nameptrs); + free(name); + free(rname); + free(encrname); + }//else if ( hc->method == METHOD_GET ) + else + { + closedir( dirp ); + httpd_send_err( + hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); + return -1; + } + + return 0; + } + +#endif /* GENERATE_INDEXES */ + + +//static char* +//build_env( char* fmt, char* arg ) +// { +// char* cp; +// size_t size; +// static char* buf; +// static size_t maxbuf = 0; +// +// size = strlen( fmt ) + strlen( arg ); +// if ( size > maxbuf ) +// httpd_realloc_str( &buf, &maxbuf, size ); +// (void) my_snprintf( buf, maxbuf, fmt, arg ); +// cp = strdup( buf ); +// if ( cp == (char*) 0 ) +// { +// syslog( LOG_ERR, "out of memory copying environment variable" ); +// exit( 1 ); +// } +// return cp; +// } + + +#ifdef SERVER_NAME_LIST +static char* +hostname_map( char* hostname ) + { + int len, n; + static char* list[] = { SERVER_NAME_LIST }; + + len = strlen( hostname ); + for ( n = sizeof(list) / sizeof(*list) - 1; n >= 0; --n ) + if ( strncasecmp( hostname, list[n], len ) == 0 ) + if ( list[n][len] == '/' ) /* check in case of a substring match */ + return &list[n][len + 1]; + return (char*) 0; + } +#endif /* SERVER_NAME_LIST */ + + +static int +really_start_request( httpd_conn* hc, struct timeval* nowP ) + { + /*static*/ char* indexname; + /*static*/ size_t maxindexname = 0; + //static const char* index_names[]; + //int i; +#ifdef AUTH_FILE + static char* dirname; + static size_t maxdirname = 0; +#endif /* AUTH_FILE */ + size_t expnlen, indxlen; + char* cp; + char* pi; + char* freethis; + + expnlen = strlen( hc->expnfilename ); + + if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD /*&& + hc->method != METHOD_POST*/ ) + { + httpd_send_err( + hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) ); + return -1; + } + + /* Stat the file. */ + if ( stat( hc->expnfilename, &hc->sb ) < 0 ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + + /* Is it world-readable or world-executable? We check explicitly instead + ** of just trying to open it, so that no one ever gets surprised by + ** a file that's not set world-readable and yet somehow is + ** readable by the HTTP server and therefore the *whole* world. + */ + if ( ! ( hc->sb.st_mode & ( S_IROTH /*| S_IXOTH*/ ) ) ) + { +// syslog( +// LOG_INFO, +// "%.80s URL \"%.80s\" resolves to a non world-readable file", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ), + hc->encodedurl ); + return -1; + } + + /* Is it a directory? */ + if ( S_ISDIR(hc->sb.st_mode) ) + { + /* If there's pathinfo, it's just a non-existent file. */ + if ( hc->pathinfo[0] != '\0' ) + { + char logString[27+B_PATH_NAME_LENGTH+1]; + sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); + poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); + httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); + return -1; + } + + /* Special handling for directory URLs that don't end in a slash. + ** We send back an explicit redirect with the slash, because + ** otherwise many clients can't build relative URLs properly. + */ + if ( strcmp( hc->origfilename, "" ) != 0 && + strcmp( hc->origfilename, "." ) != 0 && + hc->origfilename[strlen( hc->origfilename ) - 1] != '/' ) + { + send_dirredirect( hc ); + return -1; + } + + /* Check for an index file. */ + if(pthread_rwlock_rdlock(get_index_name_lock()) == 0){ + httpd_realloc_str( + &indexname, &maxindexname, + expnlen + 1 + strlen( /*index_names[i]*/hc->hs->index_name ) ); + (void) strcpy( indexname, hc->expnfilename ); + indxlen = strlen( indexname ); + if ( indxlen == 0 || indexname[indxlen - 1] != '/' ) + (void) strcat( indexname, "/" ); + if ( strcmp( indexname, "./" ) == 0 ) + indexname[0] = '\0'; + (void) strcat( indexname, /*index_names[i]*/hc->hs->index_name ); + pthread_rwlock_unlock(get_index_name_lock()); + } + else{ + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + if ( stat( indexname, &hc->sb ) >= 0 ) + goto got_one; + +free(indexname); + /* Nope, no index file, so it's an actual directory request. */ +#ifdef GENERATE_INDEXES +if(hc->hs->do_list_dir){ + /* Directories must be readable for indexing. */ + if ( ! ( hc->sb.st_mode & S_IROTH ) ) + { +// syslog( +// LOG_INFO, +// "%.80s URL \"%.80s\" tried to index a directory with indexing disabled", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ), + hc->encodedurl ); + return -1; + } +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + if ( auth_check( hc, hc->expnfilename ) == -1 ) + return -1; +#endif /* AUTH_FILE */ + /* Referer check. */ + if ( ! check_referer( hc ) ) + return -1; + /* Ok, generate an index. */ + return ls( hc ); +//#else /* GENERATE_INDEXES */ +} else { +// syslog( +// LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 404, err404title, "", + err404form, + hc->encodedurl ); + return -1; +} +#endif /* GENERATE_INDEXES */ + + got_one: ; + /* Got an index file. Expand symlinks again. More pathinfo means + ** something went wrong. + */ + cp = expand_symlinks( indexname, &freethis, &pi, hc->hs->no_symlink_check, hc->tildemapped ); + if ( cp == (char*) 0 || pi[0] != '\0' ) + { + httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); + return -1; + } + free(indexname); + free(freethis); + expnlen = strlen( cp ); + httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen ); + (void) strcpy( hc->expnfilename, cp ); + free(cp); + + /* Now, is the index version world-readable or world-executable? */ + if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) ) + { +// syslog( +// LOG_INFO, +// "%.80s URL \"%.80s\" resolves to a non-world-readable index file", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ), + hc->encodedurl ); + return -1; + } + }/* Is it a directory? */ + +#ifdef AUTH_FILE + /* Check authorization for this directory. */ + httpd_realloc_str( &dirname, &maxdirname, expnlen ); + (void) strcpy( dirname, hc->expnfilename ); + cp = strrchr( dirname, '/' ); + if ( cp == (char*) 0 ) + (void) strcpy( dirname, "." ); + else + *cp = '\0'; + if ( auth_check( hc, dirname ) == -1 ) + return -1; + + /* Check if the filename is the AUTH_FILE itself - that's verboten. */ + if ( expnlen == sizeof(AUTH_FILE) - 1 ) + { + if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 ) + { +// syslog( +// LOG_NOTICE, +// "%.80s URL \"%.80s\" tried to retrieve an auth file", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } + } + else if ( expnlen >= sizeof(AUTH_FILE) && + strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 && + hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' ) + { +// syslog( +// LOG_NOTICE, +// "%.80s URL \"%.80s\" tried to retrieve an auth file", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ), + hc->encodedurl ); + return -1; + } +#endif /* AUTH_FILE */ + + /* Referer check. */ + if ( ! check_referer( hc ) ) + return -1; + + /* Is it world-executable and in the CGI area? */ + /*if ( hc->hs->cgi_pattern != (char*) 0 && + ( hc->sb.st_mode & S_IXOTH ) && + match( hc->hs->cgi_pattern, hc->expnfilename ) ) + return cgi( hc );*/ + + /* It's not CGI. If it's executable or there's pathinfo, someone's + ** trying to either serve or run a non-CGI file as CGI. Either case + ** is prohibited. + */ + /*if ( hc->sb.st_mode & S_IXOTH ) + { + syslog( + LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI", + httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ), + hc->encodedurl ); + return -1; + }*/ + if ( hc->pathinfo[0] != '\0' ) + { +// syslog( +// LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI", +// httpd_ntoa( &hc->client_addr ), hc->encodedurl ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ), + hc->encodedurl ); + return -1; + } + + /* Fill in last_byte_index, if necessary. */ + if ( hc->got_range && + ( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) ) + hc->last_byte_index = hc->sb.st_size - 1; + + figure_mime( hc ); + + if ( hc->method == METHOD_HEAD ) + { + send_mime( + hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, + hc->sb.st_mtime ); + } + else if ( hc->if_modified_since != (time_t) -1 && + hc->if_modified_since >= hc->sb.st_mtime ) + { + send_mime( + hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1, + hc->sb.st_mtime ); + } + else + { + hc->file_address = (char*)1; + send_mime( + hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, + hc->sb.st_mtime ); + } + + return 0; + } + + +int +httpd_start_request( httpd_conn* hc, struct timeval* nowP ) + { + int r; + + /* Really start the request. */ + r = really_start_request( hc, nowP ); + + /* And return the status. */ + return r; + } + + +static void +make_log_entry( httpd_conn* hc, struct timeval* nowP ) + { + char* ru; + char url[305]; + char bytes[40]; + + if ( hc->hs->no_log ) + return; + + /* This is straight CERN Combined Log Format - the only tweak + ** being that if we're using syslog() we leave out the date, because + ** syslogd puts it in. The included syslogtocern script turns the + ** results into true CERN format. + */ + + /* Format remote user. */ + if ( hc->remoteuser[0] != '\0' ) + ru = hc->remoteuser; + else + ru = "-"; + /* If we're vhosting, prepend the hostname to the url. This is + ** a little weird, perhaps writing separate log files for + ** each vhost would make more sense. + */ + if ( hc->hs->vhost && ! hc->tildemapped ) + (void) my_snprintf( url, sizeof(url), + "/%.100s%.200s", + hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname, + hc->encodedurl ); + else + (void) my_snprintf( url, sizeof(url), + "%.200s", hc->encodedurl ); + /* Format the bytes. */ + if ( hc->bytes_sent >= 0 ) + (void) my_snprintf( + bytes, sizeof(bytes), "%lld", (int64_t) hc->bytes_sent ); + else + (void) strcpy( bytes, "-" ); + + /* Logfile or syslog? */ + if ( hc->hs->logfp != (FILE*) 0 ) + { + time_t now; + struct tm* t; + const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S"; + char date_nozone[100]; + int zone; + char sign; + char date[100]; + + /* Get the current time, if necessary. */ + if ( nowP != (struct timeval*) 0 ) + now = nowP->tv_sec; + else + now = time( (time_t*) 0 ); + /* Format the time, forcing a numeric timezone (some log analyzers + ** are stoooopid about this). + */ + t = localtime( &now ); + (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t ); +#ifdef HAVE_TM_GMTOFF + zone = t->tm_gmtoff / 60L; +#else + zone = -timezone / 60L; + /* Probably have to add something about daylight time here. */ +#endif + if ( zone >= 0 ) + sign = '+'; + else + { + sign = '-'; + zone = -zone; + } + zone = ( zone / 60 ) * 100 + zone % 60; + (void) my_snprintf( date, sizeof(date), + "%s %c%04d", date_nozone, sign, zone ); + /* And write the log entry. */ + (void) fprintf( hc->hs->logfp, + "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.200s\"\n", + httpd_ntoa( &hc->client_addr ), ru, date, + httpd_method_str( hc->method ), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent ); +#ifdef FLUSH_LOG_EVERY_TIME + (void) fflush( hc->hs->logfp ); +#endif + } + else + /*syslog( LOG_INFO, + "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.200s\"", + httpd_ntoa( &hc->client_addr ), ru, + httpd_method_str( hc->method ), url, hc->protocol, + hc->status, bytes, hc->referer, hc->useragent )*/; + } + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int +check_referer( httpd_conn* hc ) + { + int r; + char* cp; + + /* Are we doing referer checking at all? */ + if ( hc->hs->url_pattern == (char*) 0 ) + return 1; + + r = really_check_referer( hc ); + + if ( ! r ) + { + if ( hc->hs->vhost && hc->hostname != (char*) 0 ) + cp = hc->hostname; + else + cp = hc->hs->server_hostname; + if ( cp == (char*) 0 ) + cp = ""; +// syslog( +// LOG_INFO, "%.80s non-local referer \"%.80s%.80s\" \"%.80s\"", +// httpd_ntoa( &hc->client_addr ), cp, hc->encodedurl, hc->referer ); + httpd_send_err( + hc, 403, err403title, "", + ERROR_FORM( err403form, "You must supply a local referer to get URL '%.80s' from this server.\n" ), + hc->encodedurl ); + } + return r; + } + + +/* Returns 1 if ok to serve the url, 0 if not. */ +static int +really_check_referer( httpd_conn* hc ) + { + httpd_server* hs; + char* cp1; + char* cp2; + char* cp3; + /*static*/char* refhost = (char*) 0; + /*static*/size_t refhost_size = 0; + char *lp; + + hs = hc->hs; + + /* Check for an empty referer. */ + if ( hc->referer == (char*) 0 || hc->referer[0] == '\0' || + ( cp1 = strstr( hc->referer, "//" ) ) == (char*) 0 ) + { + /* Disallow if we require a referer and the url matches. */ + if ( hs->no_empty_referers && match( hs->url_pattern, hc->origfilename ) ) + return 0; + /* Otherwise ok. */ + return 1; + } + + /* Extract referer host. */ + cp1 += 2; + for ( cp2 = cp1; *cp2 != '/' && *cp2 != ':' && *cp2 != '\0'; ++cp2 ) + continue; + httpd_realloc_str( &refhost, &refhost_size, cp2 - cp1 ); + for ( cp3 = refhost; cp1 < cp2; ++cp1, ++cp3 ) + if ( isupper(*cp1) ) + *cp3 = tolower(*cp1); + else + *cp3 = *cp1; + *cp3 = '\0'; + + /* Local pattern? */ + if ( hs->local_pattern != (char*) 0 ) + lp = hs->local_pattern; + else + { + /* No local pattern. What's our hostname? */ + if ( ! hs->vhost ) + { + /* Not vhosting, use the server name. */ + lp = hs->server_hostname; + if ( lp == (char*) 0 ) + { + /* Couldn't figure out local hostname - give up. */ + free(refhost); + return 1; + } + } + else + { + /* We are vhosting, use the hostname on this connection. */ + lp = hc->hostname; + if ( lp == (char*) 0 ) + { + /* Oops, no hostname. Maybe it's an old browser that + ** doesn't send a Host: header. We could figure out + ** the default hostname for this IP address, but it's + ** not worth it for the few requests like this. + */ + free(refhost); + return 1; + } + } + } + + /* If the referer host doesn't match the local host pattern, and + ** the filename does match the url pattern, it's an illegal reference. + */ + if ( ! match( lp, refhost ) && match( hs->url_pattern, hc->origfilename ) ) + { + free(refhost); + return 0; + } + /* Otherwise ok. */ + free(refhost); + return 1; + } + + +char* +httpd_ntoa( httpd_sockaddr* saP ) + { +#ifdef USE_IPV6 + static char str[200]; + + if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 ) + { + str[0] = '?'; + str[1] = '\0'; + } + else if ( IN6_IS_ADDR_V4MAPPED( &saP->sa_in6.sin6_addr ) && strncmp( str, "::ffff:", 7 ) == 0 ) + /* Elide IPv6ish prefix for IPv4 addresses. */ + (void) strcpy( str, &str[7] ); + + return str; + +#else /* USE_IPV6 */ + + return inet_ntoa( saP->sa_in.sin_addr ); + +#endif /* USE_IPV6 */ + } + + +static int +sockaddr_check( httpd_sockaddr* saP ) + { + switch ( saP->sa.sa_family ) + { + case AF_INET: return 1; +#ifdef USE_IPV6 + case AF_INET6: return 1; +#endif /* USE_IPV6 */ + default: + return 0; + } + } + + +static size_t +sockaddr_len( httpd_sockaddr* saP ) + { + switch ( saP->sa.sa_family ) + { + case AF_INET: return sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + case AF_INET6: return sizeof(struct sockaddr_in6); +#endif /* USE_IPV6 */ + default: + return 0; /* shouldn't happen */ + } + } + + +/* Some systems don't have snprintf(), so we make our own that uses +** either vsnprintf() or vsprintf(). If your system doesn't have +** vsnprintf(), it is probably vulnerable to buffer overruns. +** Upgrade! +*/ +static int +my_snprintf( char* str, size_t size, const char* format, ... ) + { + va_list ap; + int r; + + va_start( ap, format ); +#ifdef HAVE_VSNPRINTF + r = vsnprintf( str, size, format, ap ); +#else /* HAVE_VSNPRINTF */ + r = vsprintf( str, format, ap ); +#endif /* HAVE_VSNPRINTF */ + va_end( ap ); + return r; + } + + +#ifndef HAVE_ATOLL +static long long +atoll( const char* str ) + { + long long value; + long long sign; + + while ( isspace( *str ) ) + ++str; + switch ( *str ) + { + case '-': sign = -1; ++str; break; + case '+': sign = 1; ++str; break; + default: sign = 1; break; + } + value = 0; + while ( isdigit( *str ) ) + { + value = value * 10 + ( *str - '0' ); + ++str; + } + return sign * value; + } +#endif /* HAVE_ATOLL */ + + +/* Read the requested buffer completely, accounting for interruptions. */ +int +httpd_read_fully( int fd, void* buf, size_t nbytes ) + { + size_t nread; + + nread = 0; + while ( nread < nbytes ) + { + int r; + + r = read( fd, (char*) buf + nread, nbytes - nread ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r < 0 ) + return r; + if ( r == 0 ) + break; + nread += r; + } + + return nread; + } + + +/* Write the requested buffer completely, accounting for interruptions. */ +int +httpd_write_fully( int fd, const void* buf, size_t nbytes ) + { + size_t nwritten; + + nwritten = 0; + while ( nwritten < nbytes ) + { + int r; + + r = write( fd, (char*) buf + nwritten, nbytes - nwritten ); + if ( r < 0 && ( errno == EINTR || errno == EAGAIN ) ) + { + sleep( 1 ); + continue; + } + if ( r < 0 ) + return r; + if ( r == 0 ) + break; + nwritten += r; + } + + return nwritten; + } + + +/* Generate debugging statistics syslog message. */ +void +httpd_logstats( long secs ) + { + if ( str_alloc_count > 0 ) + /*syslog( LOG_INFO, + " libhttpd - %d strings allocated, %lu bytes (%g bytes/str)", + str_alloc_count, (unsigned long) str_alloc_size, + (float) str_alloc_size / str_alloc_count )*/; + } diff --git a/src/apps/poorman/libhttpd/libhttpd.h b/src/apps/poorman/libhttpd/libhttpd.h new file mode 100644 index 0000000000..f820071fd5 --- /dev/null +++ b/src/apps/poorman/libhttpd/libhttpd.h @@ -0,0 +1,301 @@ +/* libhttpd.h - defines for libhttpd +** +** Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _LIBHTTPD_H_ +#define _LIBHTTPD_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) +#define USE_IPV6 +#endif + + +/* A few convenient defines. */ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#define NEW(t,n) ((t*) malloc( sizeof(t) * (n) )) +#define RENEW(o,t,n) ((t*) realloc( (void*) o, sizeof(t) * (n) )) + + +/* The httpd structs. */ + +/* A multi-family sockaddr. */ +typedef union { + struct sockaddr sa; + struct sockaddr_in sa_in; +#ifdef USE_IPV6 + struct sockaddr_in6 sa_in6; + struct sockaddr_storage sa_stor; +#endif /* USE_IPV6 */ + } httpd_sockaddr; + +/* A server. */ +typedef struct { + char* binding_hostname; + char* server_hostname; + unsigned short port; + char* cgi_pattern; + int cgi_limit, cgi_count; + char* charset; + char* p3p; + int max_age; + char* cwd; + int listen4_fd, listen6_fd; + int no_log; + FILE* logfp; + int no_symlink_check; + int vhost; + int global_passwd; + char* url_pattern; + char* local_pattern; + int no_empty_referers; + //added for poorman + int do_list_dir; + char* index_name; + } httpd_server; + +/* A connection. */ +typedef struct { + int initialized; + httpd_server* hs; + httpd_sockaddr client_addr; + char* read_buf; + size_t read_size, read_idx, checked_idx; + int checked_state; + int method; + int status; + off_t bytes_to_send; + off_t bytes_sent; + char* encodedurl; + char* decodedurl; + char* protocol; + char* origfilename; + char* expnfilename; + char* encodings; + char* pathinfo; + char* query; + char* referer; + char* useragent; + char* accept; + char* accepte; + char* acceptl; + char* cookie; + char* contenttype; + char* reqhost; + char* hdrhost; + char* hostdir; + char* authorization; + char* remoteuser; + char* response; + size_t maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings, + maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir, + maxremoteuser, maxresponse; +#ifdef TILDE_MAP_2 + char* altdir; + size_t maxaltdir; +#endif /* TILDE_MAP_2 */ + size_t responselen; + time_t if_modified_since, range_if; + size_t contentlength; + char* type; /* not malloc()ed */ + char* hostname; /* not malloc()ed */ + int mime_flag; + int one_one; /* HTTP/1.1 or better */ + int got_range; + int tildemapped; /* this connection got tilde-mapped */ + off_t first_byte_index, last_byte_index; + int keep_alive; + int should_linger; + struct stat sb; + int conn_fd; + char* file_address; + } httpd_conn; + +/* Methods. */ +#define METHOD_UNKNOWN 0 +#define METHOD_GET 1 +#define METHOD_HEAD 2 +#define METHOD_POST 3 + +/* States for checked_state. */ +#define CHST_FIRSTWORD 0 +#define CHST_FIRSTWS 1 +#define CHST_SECONDWORD 2 +#define CHST_SECONDWS 3 +#define CHST_THIRDWORD 4 +#define CHST_THIRDWS 5 +#define CHST_LINE 6 +#define CHST_LF 7 +#define CHST_CR 8 +#define CHST_CRLF 9 +#define CHST_CRLFCR 10 +#define CHST_BOGUS 11 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initializes. Does the socket(), bind(), and listen(). Returns an +** httpd_server* which includes a socket fd that you can select() on. +** Return (httpd_server*) 0 on error. +*/ +extern httpd_server* httpd_initialize( + char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, + unsigned short port, char* cgi_pattern, int cgi_limit, char* charset, + char* p3p, int max_age, char* cwd, int no_log, FILE* logfp, + int no_symlink_check, int vhost, int global_passwd, char* url_pattern, + char* local_pattern, int no_empty_referers ); + +/* PoorMan: Initialize_listen_socket() is changed from static to extern. +** httpd_unlisten() needs an opposite operation that can be accessed from +** outside libhttpd. So this is it. +*/ +extern int httpd_initialize_listen_socket( httpd_sockaddr* saP ); + +/* Change the log file. */ +extern void httpd_set_logfp( httpd_server* hs, FILE* logfp ); + +/* Call to unlisten/close socket(s) listening for new connections. */ +extern void httpd_unlisten( httpd_server* hs ); + +/* Call to shut down. */ +extern void httpd_terminate( httpd_server* hs ); + + +/* When a listen fd is ready to read, call this. It does the accept() and +** returns an httpd_conn* which includes the fd to read the request from and +** write the response to. Returns an indication of whether the accept() +** failed, succeeded, or if there were no more connections to accept. +** +** In order to minimize malloc()s, the caller passes in the httpd_conn. +** The caller is also responsible for setting initialized to zero before the +** first call using each different httpd_conn. +*/ +extern int httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc ); +#define GC_FAIL 0 +#define GC_OK 1 +#define GC_NO_MORE 2 + +/* Checks whether the data in hc->read_buf constitutes a complete request +** yet. The caller reads data into hc->read_buf[hc->read_idx] and advances +** hc->read_idx. This routine checks what has been read so far, using +** hc->checked_idx and hc->checked_state to keep track, and returns an +** indication of whether there is no complete request yet, there is a +** complete request, or there won't be a valid request due to a syntax error. +*/ +extern int httpd_got_request( httpd_conn* hc ); +#define GR_NO_REQUEST 0 +#define GR_GOT_REQUEST 1 +#define GR_BAD_REQUEST 2 + +/* Parses the request in hc->read_buf. Fills in lots of fields in hc, +** like the URL and the various headers. +** +** Returns -1 on error. +*/ +extern int httpd_parse_request( httpd_conn* hc ); + +/* Starts sending data back to the client. In some cases (directories, +** CGI programs), finishes sending by itself - in those cases, hc->file_fd +** is <0. If there is more data to be sent, then hc->file_fd is a file +** descriptor for the file to send. If you don't have a current timeval +** handy just pass in 0. +** +** Returns -1 on error. +*/ +extern int httpd_start_request( httpd_conn* hc, struct timeval* nowP ); + +/* Actually sends any buffered response text. */ +extern void httpd_write_response( httpd_conn* hc ); + +/* Call this to close down a connection and free the data. A fine point, +** if you fork() with a connection open you should still call this in the +** parent process - the connection will stay open in the child. +** If you don't have a current timeval handy just pass in 0. +*/ +extern void httpd_close_conn( httpd_conn* hc, struct timeval* nowP ); + +/* Call this to de-initialize a connection struct and *really* free the +** mallocced strings. +*/ +extern void httpd_destroy_conn( httpd_conn* hc ); + + +/* Send an error message back to the client. */ +extern void httpd_send_err( + httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ); + +/* Some error messages. */ +extern char* httpd_err400title; +extern char* httpd_err400form; +extern char* httpd_err408title; +extern char* httpd_err408form; +extern char* httpd_err503title; +extern char* httpd_err503form; + +/* Generate a string representation of a method number. */ +extern char* httpd_method_str( int method ); + +/* Reallocate a string. */ +extern void httpd_realloc_str( char** strP, size_t* maxsizeP, size_t size ); + +/* Format a network socket to a string representation. */ +extern char* httpd_ntoa( httpd_sockaddr* saP ); + +/* Set NDELAY mode on a socket. */ +extern void httpd_set_ndelay( int fd ); + +/* Clear NDELAY mode on a socket. */ +extern void httpd_clear_ndelay( int fd ); + +/* Read the requested buffer completely, accounting for interruptions. */ +extern int httpd_read_fully( int fd, void* buf, size_t nbytes ); + +/* Write the requested buffer completely, accounting for interruptions. */ +extern int httpd_write_fully( int fd, const void* buf, size_t nbytes ); + +/* Generate debugging statistics syslog message. */ +extern void httpd_logstats( long secs ); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBHTTPD_H_ */ diff --git a/src/apps/poorman/libhttpd/match.c b/src/apps/poorman/libhttpd/match.c new file mode 100644 index 0000000000..924718fee2 --- /dev/null +++ b/src/apps/poorman/libhttpd/match.c @@ -0,0 +1,88 @@ +/* match.c - simple shell-style filename matcher +** +** Only does ? * and **, and multiple patterns separated by |. Returns 1 or 0. +** +** Copyright © 1995,2000 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include + +#include "match.h" + +static int match_one( const char* pattern, int patternlen, const char* string ); + +int +match( const char* pattern, const char* string ) + { + const char* or; + + for (;;) + { + or = strchr( pattern, '|' ); + if ( or == (char*) 0 ) + return match_one( pattern, strlen( pattern ), string ); + if ( match_one( pattern, or - pattern, string ) ) + return 1; + pattern = or + 1; + } + } + + +static int +match_one( const char* pattern, int patternlen, const char* string ) + { + const char* p; + + for ( p = pattern; p - pattern < patternlen; ++p, ++string ) + { + if ( *p == '?' && *string != '\0' ) + continue; + if ( *p == '*' ) + { + int i, pl; + ++p; + if ( *p == '*' ) + { + /* Double-wildcard matches anything. */ + ++p; + i = strlen( string ); + } + else + /* Single-wildcard matches anything but slash. */ + i = strcspn( string, "/" ); + pl = patternlen - ( p - pattern ); + for ( ; i >= 0; --i ) + if ( match_one( p, pl, &(string[i]) ) ) + return 1; + return 0; + } + if ( *p != *string ) + return 0; + } + if ( *string == '\0' ) + return 1; + return 0; + } diff --git a/src/apps/poorman/libhttpd/match.h b/src/apps/poorman/libhttpd/match.h new file mode 100644 index 0000000000..ce020ed9f0 --- /dev/null +++ b/src/apps/poorman/libhttpd/match.h @@ -0,0 +1,44 @@ +/* match.h - simple shell-style filename patcher +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _MATCH_H_ +#define _MATCH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Simple shell-style filename pattern matcher. Only does ? * and **, and +** multiple patterns separated by |. Returns 1 or 0. +*/ +extern int match( const char* pattern, const char* string ); + +#ifdef __cplusplus +} +#endif + +#endif /* _MATCH_H_ */ diff --git a/src/apps/poorman/libhttpd/mime_encodings.txt b/src/apps/poorman/libhttpd/mime_encodings.txt new file mode 100644 index 0000000000..2d3952d468 --- /dev/null +++ b/src/apps/poorman/libhttpd/mime_encodings.txt @@ -0,0 +1,8 @@ +# mime_encodings.txt +# +# A list of file extensions followed by the corresponding MIME encoding. +# Extensions not found in the table proceed to the mime_types table. + +Z compress +gz gzip +uu x-uuencode diff --git a/src/apps/poorman/libhttpd/mime_types.txt b/src/apps/poorman/libhttpd/mime_types.txt new file mode 100644 index 0000000000..b86e749a22 --- /dev/null +++ b/src/apps/poorman/libhttpd/mime_types.txt @@ -0,0 +1,195 @@ +# mime_types.txt +# +# A list of file extensions followed by the corresponding MIME type. +# Extensions not found in the table are returned as text/plain. + +a application/octet-stream +aab application/x-authorware-bin +aam application/x-authorware-map +aas application/x-authorware-seg +ai application/postscript +aif audio/x-aiff +aifc audio/x-aiff +aiff audio/x-aiff +asc text/plain +asf video/x-ms-asf +asx video/x-ms-asf +au audio/basic +avi video/x-msvideo +bcpio application/x-bcpio +bin application/octet-stream +bmp image/bmp +cdf application/x-netcdf +class application/x-java-vm +cpio application/x-cpio +cpt application/mac-compactpro +crl application/x-pkcs7-crl +crt application/x-x509-ca-cert +csh application/x-csh +css text/css +dcr application/x-director +dir application/x-director +djv image/vnd.djvu +djvu image/vnd.djvu +dll application/octet-stream +dms application/octet-stream +doc application/msword +dtd text/xml +dump application/octet-stream +dvi application/x-dvi +dxr application/x-director +eps application/postscript +etx text/x-setext +exe application/octet-stream +ez application/andrew-inset +fgd application/x-director +fh image/x-freehand +fh4 image/x-freehand +fh5 image/x-freehand +fh7 image/x-freehand +fhc image/x-freehand +gif image/gif +gtar application/x-gtar +hdf application/x-hdf +hqx application/mac-binhex40 +htm text/html +html text/html +ice x-conference/x-cooltalk +ief image/ief +iges model/iges +igs model/iges +iv application/x-inventor +jar application/x-java-archive +jfif image/jpeg +jpe image/jpeg +jpeg image/jpeg +jpg image/jpeg +js application/x-javascript +kar audio/midi +latex application/x-latex +lha application/octet-stream +lzh application/octet-stream +m3u audio/x-mpegurl +man application/x-troff-man +mathml application/mathml+xml +me application/x-troff-me +mesh model/mesh +mid audio/midi +midi audio/midi +mif application/vnd.mif +mime message/rfc822 +mml application/mathml+xml +mov video/quicktime +movie video/x-sgi-movie +mp2 audio/mpeg +mp3 audio/mpeg +mp4 video/mp4 +mpe video/mpeg +mpeg video/mpeg +mpg video/mpeg +mpga audio/mpeg +ms application/x-troff-ms +msh model/mesh +mv video/x-sgi-movie +mxu video/vnd.mpegurl +nc application/x-netcdf +o application/octet-stream +oda application/oda +ogg application/x-ogg +pac application/x-ns-proxy-autoconfig +pbm image/x-portable-bitmap +pdb chemical/x-pdb +pdf application/pdf +pgm image/x-portable-graymap +pgn application/x-chess-pgn +png image/png +pnm image/x-portable-anymap +ppm image/x-portable-pixmap +ppt application/vnd.ms-powerpoint +ps application/postscript +qt video/quicktime +ra audio/x-realaudio +ram audio/x-pn-realaudio +ras image/x-cmu-raster +rdf application/rdf+xml +rgb image/x-rgb +rm audio/x-pn-realaudio +roff application/x-troff +rpm audio/x-pn-realaudio-plugin +rss application/rss+xml +rtf text/rtf +rtx text/richtext +sgm text/sgml +sgml text/sgml +sh application/x-sh +shar application/x-shar +silo model/mesh +sit application/x-stuffit +skd application/x-koan +skm application/x-koan +skp application/x-koan +skt application/x-koan +smi application/smil +smil application/smil +snd audio/basic +so application/octet-stream +spl application/x-futuresplash +src application/x-wais-source +stc application/vnd.sun.xml.calc.template +std application/vnd.sun.xml.draw.template +sti application/vnd.sun.xml.impress.template +stw application/vnd.sun.xml.writer.template +sv4cpio application/x-sv4cpio +sv4crc application/x-sv4crc +svg image/svg+xml +svgz image/svg+xml +swf application/x-shockwave-flash +sxc application/vnd.sun.xml.calc +sxd application/vnd.sun.xml.draw +sxg application/vnd.sun.xml.writer.global +sxi application/vnd.sun.xml.impress +sxm application/vnd.sun.xml.math +sxw application/vnd.sun.xml.writer +t application/x-troff +tar application/x-tar +tcl application/x-tcl +tex application/x-tex +texi application/x-texinfo +texinfo application/x-texinfo +tif image/tiff +tiff image/tiff +tr application/x-troff +tsp application/dsptype +tsv text/tab-separated-values +txt text/plain +ustar application/x-ustar +vcd application/x-cdlink +vrml model/vrml +vx video/x-rad-screenplay +wav audio/x-wav +wax audio/x-ms-wax +wbmp image/vnd.wap.wbmp +wbxml application/vnd.wap.wbxml +wm video/x-ms-wm +wma audio/x-ms-wma +wmd application/x-ms-wmd +wml text/vnd.wap.wml +wmlc application/vnd.wap.wmlc +wmls text/vnd.wap.wmlscript +wmlsc application/vnd.wap.wmlscriptc +wmv video/x-ms-wmv +wmx video/x-ms-wmx +wmz application/x-ms-wmz +wrl model/vrml +wsrc application/x-wais-source +wvx video/x-ms-wvx +xbm image/x-xbitmap +xht application/xhtml+xml +xhtml application/xhtml+xml +xls application/vnd.ms-excel +xml text/xml +xpm image/x-xpixmap +xsl text/xml +xwd image/x-xwindowdump +xyz chemical/x-xyz +zip application/zip diff --git a/src/apps/poorman/libhttpd/tdate_parse.c b/src/apps/poorman/libhttpd/tdate_parse.c new file mode 100644 index 0000000000..90225c39d6 --- /dev/null +++ b/src/apps/poorman/libhttpd/tdate_parse.c @@ -0,0 +1,356 @@ +/* tdate_parse - parse string dates into internal form, stripped-down version +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* This is a stripped-down version of date_parse.c, available at +** http://www.acme.com/software/date_parse/ +*/ + +#include + +#include +#ifdef HAVE_MEMORY_H +#include +#endif +#include +#include +#include +#include + +#include "tdate_parse.h" + + +struct strlong { + char* s; + long l; + }; + + +static void +pound_case( char* str ) + { + for ( ; *str != '\0'; ++str ) + { + if ( isupper( (int) *str ) ) + *str = tolower( (int) *str ); + } + } + +//static int +//strlong_compare( v1, v2 ) +// char* v1; +// char* v2; +// { +// return strcmp( ((struct strlong*) v1)->s, ((struct strlong*) v2)->s ); +// } + + +static int +strlong_search( char* str, const struct strlong* tab, int n, long* lP ) + { + int i, h, l, r; + + l = 0; + h = n - 1; + for (;;) + { + i = ( h + l ) / 2; + r = strcmp( str, tab[i].s ); + if ( r < 0 ) + h = i - 1; + else if ( r > 0 ) + l = i + 1; + else + { + *lP = tab[i].l; + return 1; + } + if ( h < l ) + return 0; + } + } + + +static int +scan_wday( char* str_wday, long* tm_wdayP ) + { +// static struct strlong wday_tab[] = { +// { "sun", 0 }, { "sunday", 0 }, +// { "mon", 1 }, { "monday", 1 }, +// { "tue", 2 }, { "tuesday", 2 }, +// { "wed", 3 }, { "wednesday", 3 }, +// { "thu", 4 }, { "thursday", 4 }, +// { "fri", 5 }, { "friday", 5 }, +// { "sat", 6 }, { "saturday", 6 }, +// }; +// static int sorted = 0; +// +// if ( ! sorted ) +// { +// (void) qsort( +// wday_tab, sizeof(wday_tab)/sizeof(struct strlong), +// sizeof(struct strlong), strlong_compare ); +// sorted = 1; +// } + + /*manually sorted wday_tab to avoid concurrent accessing problem*/ + static const struct strlong wday_tab[] = { + { "fri", 5 }, { "friday", 5 }, + { "mon", 1 }, { "monday", 1 }, + { "sat", 6 }, { "saturday", 6 }, + { "sun", 0 }, { "sunday", 0 }, + { "thu", 4 }, { "thursday", 4 }, + { "tue", 2 }, { "tuesday", 2 }, + { "wed", 3 }, { "wednesday", 3 } + }; + + pound_case( str_wday ); + return strlong_search( + str_wday, wday_tab, sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP ); + } + + +static int +scan_mon( char* str_mon, long* tm_monP ) + { +// static struct strlong mon_tab[] = { +// { "jan", 0 }, { "january", 0 }, +// { "feb", 1 }, { "february", 1 }, +// { "mar", 2 }, { "march", 2 }, +// { "apr", 3 }, { "april", 3 }, +// { "may", 4 }, +// { "jun", 5 }, { "june", 5 }, +// { "jul", 6 }, { "july", 6 }, +// { "aug", 7 }, { "august", 7 }, +// { "sep", 8 }, { "september", 8 }, +// { "oct", 9 }, { "october", 9 }, +// { "nov", 10 }, { "november", 10 }, +// { "dec", 11 }, { "december", 11 }, +// }; +// static int sorted = 0; +// +// if ( ! sorted ) +// { +// (void) qsort( +// mon_tab, sizeof(mon_tab)/sizeof(struct strlong), +// sizeof(struct strlong), strlong_compare ); +// sorted = 1; +// } + + /*manually sorted mon_tab to avoid concurrent accessing problem*/ + static const struct strlong mon_tab[] = { + { "apr", 3 }, { "april", 3 }, + { "aug", 7 }, { "august", 7 }, + { "dec", 11}, { "december", 11 }, + { "feb", 1 }, { "february", 1 }, + { "jan", 0 }, { "january", 0 }, + { "jul", 6 }, { "july", 6 }, + { "jun", 5 }, { "june", 5 }, + { "mar", 2 }, { "march", 2 }, + { "may", 4 }, + { "nov", 10}, { "november", 10 }, + { "oct", 9 }, { "october", 9 }, + { "sep", 8 }, { "september", 8 } + }; + + pound_case( str_mon ); + return strlong_search( + str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP ); + } + + +static int +is_leap( int year ) + { + return year % 400? ( year % 100 ? ( year % 4 ? 0 : 1 ) : 0 ) : 1; + } + + +/* Basically the same as mktime(). */ +static time_t +tm_to_time( struct tm* tmP ) + { + time_t t; + static int monthtab[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + /* Years since epoch, converted to days. */ + t = ( tmP->tm_year - 70 ) * 365; + /* Leap days for previous years. */ + t += ( tmP->tm_year - 69 ) / 4; + /* Days for the beginning of this month. */ + t += monthtab[tmP->tm_mon]; + /* Leap day for this year. */ + if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) ) + ++t; + /* Days since the beginning of this month. */ + t += tmP->tm_mday - 1; /* 1-based field */ + /* Hours, minutes, and seconds. */ + t = t * 24 + tmP->tm_hour; + t = t * 60 + tmP->tm_min; + t = t * 60 + tmP->tm_sec; + + return t; + } + + +time_t +tdate_parse( char* str ) + { + struct tm tm; + char* cp; + char str_mon[500], str_wday[500]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + long tm_mon, tm_wday; + time_t t; + + /* Initialize. */ + (void) memset( (char*) &tm, 0, sizeof(struct tm) ); + + /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ + for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp ) + continue; + + /* And do the sscanfs. WARNING: you can add more formats here, + ** but be careful! You can easily screw up the parsing of existing + ** formats when you add new ones. The order is important. + */ + + /* DD-mth-YY HH:MM:SS GMT */ + if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* DD mth YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* HH:MM:SS GMT DD-mth-YY */ + else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* HH:MM:SS GMT DD mth YY */ + else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* wdy, DD-mth-YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy, DD mth YY HH:MM:SS GMT */ + else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + + /* wdy mth DD HH:MM:SS GMT YY */ + else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year ) == 7 && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = tm_hour; + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year; + } + else + return (time_t) -1; + + if ( tm.tm_year > 1900 ) + tm.tm_year -= 1900; + else if ( tm.tm_year < 70 ) + tm.tm_year += 100; + + t = tm_to_time( &tm ); + + return t; + } diff --git a/src/apps/poorman/libhttpd/tdate_parse.h b/src/apps/poorman/libhttpd/tdate_parse.h new file mode 100644 index 0000000000..3f3008d560 --- /dev/null +++ b/src/apps/poorman/libhttpd/tdate_parse.h @@ -0,0 +1,41 @@ +/* tdate_parse.h - parse string dates into internal form, stripped-down version +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _TDATE_PARSE_H_ +#define _TDATE_PARSE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern time_t tdate_parse( char* str ); + +#ifdef __cplusplus +} +#endif + +#endif /* _TDATE_PARSE_H_ */ diff --git a/src/apps/poorman/libhttpd/version.h b/src/apps/poorman/libhttpd/version.h new file mode 100644 index 0000000000..e255bc3e55 --- /dev/null +++ b/src/apps/poorman/libhttpd/version.h @@ -0,0 +1,9 @@ +/* version.h - version defines for thttpd and libhttpd */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define SERVER_SOFTWARE "Haiku/PoorMan" +#define SERVER_ADDRESS "http://www.haiku-os.org/" + +#endif /* _VERSION_H_ */