From 6c1cc0b2c854dd56dcb6238816a6ce2cb493c71c Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sun, 10 May 2020 18:18:37 +0200 Subject: Separated out lib --- Makefile | 14 ++- libcommon/Makefile | 123 +++++++++++++++++++++++++ libcommon/mime.cpp | 41 +++++++++ libcommon/mime.h | 5 ++ plugins/cgi/Makefile | 11 ++- plugins/cgi/cgi.cpp | 39 +------- plugins/static-files/Makefile | 11 ++- plugins/static-files/static-files.cpp | 40 +-------- plugins/webbox/Makefile | 11 ++- plugins/webbox/webbox.cpp | 164 ++++++++++++++-------------------- plugins/weblog/Makefile | 11 ++- plugins/weblog/weblog.cpp | 41 +-------- response.cpp | 38 +------- 13 files changed, 289 insertions(+), 260 deletions(-) create mode 100644 libcommon/Makefile create mode 100644 libcommon/mime.cpp create mode 100644 libcommon/mime.h diff --git a/Makefile b/Makefile index 2e3f006..b9fb155 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,8 @@ LIBS=\ -lcrypt \ -lpthread \ -lssl -lcrypto \ --ldl +-ldl \ +-lcommon #-lboost_coroutine \ #-lboost_program_options \ @@ -71,7 +72,7 @@ LIBS+= \ -lstdc++fs endif -LDFLAGS+=-pie +LDFLAGS+=-pie -Llibcommon PROGSRC=\ auth.cpp \ @@ -106,9 +107,12 @@ all: build test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ -$(PROJECTNAME): $(SRC:.cpp=.o) +$(PROJECTNAME): libcommon/libcommon.a $(SRC:.cpp=.o) $(CXX) $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ +libcommon/libcommon.a: + $(MAKE) -C libcommon + dep: $(TESTSRC:.cpp=.d) %.d: %.cpp @@ -157,6 +161,7 @@ clean: -rm -f plugins/*.so -find . -name '*.o' -o -name '*.d' -o -name '*.gcno' -o -name '*.gcda' | xargs rm -f for i in $(PLUGINS) ; do make -C plugins/$$i clean ; done + $(MAKE) -C libcommon clean DISTFILES= \ archive.h \ @@ -206,6 +211,9 @@ DISTFILES= \ debian/webserver.install \ debian/webserver.manpages \ debian/webserver.service \ + libcommon/Makefile \ + libcommon/mime.h \ + libcommon/mime.cpp \ plugins/cgi/cgi.h \ plugins/cgi/Makefile \ plugins/cgi/cgi.cpp \ diff --git a/libcommon/Makefile b/libcommon/Makefile new file mode 100644 index 0000000..3314549 --- /dev/null +++ b/libcommon/Makefile @@ -0,0 +1,123 @@ +# Static library to be included both in main program an in plugins (.so) + +DISTROS=debian10 +VERSION=$(shell dpkg-parsechangelog --show-field Version) +PROJECTNAME=libcommon + +CXX=clang++-10 + +ifeq ($(shell which $(CXX)),) +CXX=clang++ +endif + +ifeq ($(shell which $(CXX)),) +CXX=g++-9 +endif + +ifeq ($(CXXFLAGS),) +#CXXFLAGS=-O2 -DNDEBUG +CXXFLAGS=-O0 -g -D_DEBUG +endif +# -fprofile-instr-generate -fcoverage-mapping +# gcc:--coverage + +CXXFLAGS+= -Wall -I. + +CXXFLAGS+= -pthread -fvisibility=hidden -fPIC +ifeq ($(CXX),clang++-10) +CXXFLAGS+=-std=c++20 #-stdlib=libc++ +else +CXXFLAGS+=-std=c++17 +endif + +CXXTESTFLAGS=-Igoogletest/include -Igooglemock/include/ -Igoogletest -Igooglemock + +LIBS=\ +-lboost_context \ +-lboost_coroutine \ +-lboost_program_options \ +-lboost_system \ +-lboost_thread \ +-lboost_filesystem \ +-lboost_regex \ +-lpthread \ +-lssl -lcrypto \ +-ldl + +ifeq ($(CXX),clang++-10) +LIBS+= \ +-fuse-ld=lld-10 \ +-lstdc++ +#-lc++ \ +#-lc++abi +#-lc++fs +#-lstdc++fs +else +LIBS+= \ +-lstdc++ \ +-lstdc++fs +endif + +PROGSRC=\ + mime.cpp + +TESTSRC=\ + test-webserver.cpp \ + googlemock/src/gmock-all.cpp \ + googletest/src/gtest-all.cpp \ + $(PROGSRC) + +SRC=$(PROGSRC) + +all: $(PROJECTNAME).a + +# testsuite ---------------------------------------------- +test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) + $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ + +$(PROJECTNAME).a: $(SRC:.cpp=.o) + ar rcs $@ $^ + +dep: $(TESTSRC:.cpp=.d) + +%.d: %.cpp + $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -MM -MP -MF $@ -c $< + +%.o: %.cpp %.d + $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ + +googletest/src/%.o: googletest/src/%.cc + $(CXX) $(CXXFLAGS) $(CXXTESTFLAGS) -c $< -o $@ + +# dependencies + +ADD_DEP=Makefile + + +# misc --------------------------------------------------- +deb: + # build binary deb package + dpkg-buildpackage -us -uc -rfakeroot + +deb-src: + dpkg-source -b . + +$(DISTROS): deb-src + sudo pbuilder build --basetgz /var/cache/pbuilder/$@.tgz --buildresult result/$@ ../webserver_$(VERSION).dsc ; \ + +debs: $(DISTROS) + +clean: + -rm -f test-$(PROJECTNAME) $(PROJECTNAME) + -find . -name '*.a' -o -name '*.o' -o -name '*.so' -o -name '*.d' -o -name '*.gcno' -o -name '*.gcda' | xargs rm -f + +zip: clean + -rm -f ../$(PROJECTNAME).zip + zip -r ../$(PROJECTNAME).zip * + ls -l ../$(PROJECTNAME).zip + + + +.PHONY: clean all zip install deb deb-src debs all $(DISTROS) + +-include $(wildcard $(SRC:.cpp=.d)) diff --git a/libcommon/mime.cpp b/libcommon/mime.cpp new file mode 100644 index 0000000..f093596 --- /dev/null +++ b/libcommon/mime.cpp @@ -0,0 +1,41 @@ +#include "mime.h" + +#include + +namespace beast = boost::beast; + +// Return a reasonable mime type based on the extension of a file. +std::string mime_type(const std::string& path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if (pos == std::string::npos) + return std::string{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + diff --git a/libcommon/mime.h b/libcommon/mime.h new file mode 100644 index 0000000..c05eb45 --- /dev/null +++ b/libcommon/mime.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +std::string mime_type(const std::string& path); diff --git a/plugins/cgi/Makefile b/plugins/cgi/Makefile index b3e8548..8faebdc 100644 --- a/plugins/cgi/Makefile +++ b/plugins/cgi/Makefile @@ -19,7 +19,7 @@ endif # -fprofile-instr-generate -fcoverage-mapping # gcc:--coverage -CXXFLAGS+= -Wall -I. +CXXFLAGS+= -Wall -I. -I ../.. CXXFLAGS+= -pthread -fvisibility=hidden -fPIC ifeq ($(CXX),clang++-10) @@ -40,7 +40,8 @@ LIBS=\ -lboost_regex \ -lpthread \ -lssl -lcrypto \ --ldl +-ldl \ +-lcommon ifeq ($(CXX),clang++-10) LIBS+= \ @@ -56,6 +57,8 @@ LIBS+= \ -lstdc++fs endif +LDFLAGS=-L../../libcommon + PROGSRC=\ cgi.cpp @@ -73,8 +76,8 @@ all: $(PROJECTNAME).so test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ -$(PROJECTNAME).so: $(SRC:.cpp=.o) - $(CXX) -shared $(CXXFLAGS) $^ $(LIBS) -o $@ +$(PROJECTNAME).so: ../../libcommon/libcommon.a $(SRC:.cpp=.o) + $(CXX) -shared $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ dep: $(TESTSRC:.cpp=.d) diff --git a/plugins/cgi/cgi.cpp b/plugins/cgi/cgi.cpp index 0824d67..480ae9e 100644 --- a/plugins/cgi/cgi.cpp +++ b/plugins/cgi/cgi.cpp @@ -1,5 +1,7 @@ #include "cgi.h" +#include "libcommon/mime.h" + #include #include #include @@ -45,41 +47,6 @@ namespace { } }; - // Return a reasonable mime type based on the extension of a file. - std::string mime_type(fs::path path) - { - using boost::algorithm::iequals; - auto const ext = [&path] - { - size_t pos = path.string().rfind("."); - if (pos == std::string::npos) - return std::string{}; - return path.string().substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; - } - typedef boost::coroutines2::coroutine coro_t; // returns true iff std::string is empty or contains newline @@ -306,7 +273,7 @@ std::string cgi_plugin::generate_page( return HttpStatus("500", "Bad file status access: "s + rel_target, SetResponseHeader); } - SetResponseHeader("content_type", mime_type(path)); + SetResponseHeader("content_type", mime_type(path.string())); CGIContext context(GetServerParam, GetRequestParam, SetResponseHeader, path, file_path, path_info); diff --git a/plugins/static-files/Makefile b/plugins/static-files/Makefile index fdc0896..e96257d 100644 --- a/plugins/static-files/Makefile +++ b/plugins/static-files/Makefile @@ -19,7 +19,7 @@ endif # -fprofile-instr-generate -fcoverage-mapping # gcc:--coverage -CXXFLAGS+= -Wall -I. +CXXFLAGS+= -Wall -I. -I../.. CXXFLAGS+= -pthread -fvisibility=hidden -fPIC ifeq ($(CXX),clang++-10) @@ -40,7 +40,8 @@ LIBS=\ -lboost_regex \ -lpthread \ -lssl -lcrypto \ --ldl +-ldl \ +-lcommon ifeq ($(CXX),clang++-10) LIBS+= \ @@ -56,6 +57,8 @@ LIBS+= \ -lstdc++fs endif +LDFLAGS=-L../../libcommon + PROGSRC=\ static-files.cpp @@ -73,8 +76,8 @@ all: $(PROJECTNAME).so test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ -$(PROJECTNAME).so: $(SRC:.cpp=.o) - $(CXX) -shared $(CXXFLAGS) $^ $(LIBS) -o $@ +$(PROJECTNAME).so: ../../libcommon/libcommon.a $(SRC:.cpp=.o) + $(CXX) -shared $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ dep: $(TESTSRC:.cpp=.d) diff --git a/plugins/static-files/static-files.cpp b/plugins/static-files/static-files.cpp index 011e37f..3f1c63a 100644 --- a/plugins/static-files/static-files.cpp +++ b/plugins/static-files/static-files.cpp @@ -1,5 +1,7 @@ #include "static-files.h" +#include "libcommon/mime.h" + #include #include @@ -12,42 +14,6 @@ namespace fs = std::filesystem; namespace { -// Return a reasonable mime type based on the extension of a file. -std::string -mime_type(fs::path path) -{ - using boost::algorithm::iequals; - auto const ext = [&path] - { - size_t pos = path.string().rfind("."); - if (pos == std::string::npos) - return std::string{}; - return path.string().substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; // TODO: unordered_map - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; -} - std::string getFile(const fs::path& filename) { std::ifstream file(filename.string(), std::ios::in | std::ios::binary | std::ios::ate); @@ -126,7 +92,7 @@ std::string static_files_plugin::generate_page( return HttpStatus("301", "Correcting directory path", SetResponseHeader); } path = {extend_index_html(path)}; - SetResponseHeader("content_type", mime_type(path)); + SetResponseHeader("content_type", mime_type(path.string())); try { return getFile(path); diff --git a/plugins/webbox/Makefile b/plugins/webbox/Makefile index 5ff22f7..1c49fda 100644 --- a/plugins/webbox/Makefile +++ b/plugins/webbox/Makefile @@ -19,7 +19,7 @@ endif # -fprofile-instr-generate -fcoverage-mapping # gcc:--coverage -CXXFLAGS+= -Wall -I. +CXXFLAGS+= -Wall -I. -I../.. CXXFLAGS+= -pthread -fvisibility=hidden -fPIC ifeq ($(CXX),clang++-10) @@ -40,7 +40,8 @@ LIBS=\ -lboost_regex \ -lpthread \ -lssl -lcrypto \ --ldl +-ldl \ +-lcommon ifeq ($(CXX),clang++-10) LIBS+= \ @@ -56,6 +57,8 @@ LIBS+= \ -lstdc++fs endif +LDFLAGS+=-L../../libcommon + PROGSRC=\ file.cpp \ stringutil.cpp \ @@ -75,8 +78,8 @@ all: $(PROJECTNAME).so test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ -$(PROJECTNAME).so: $(SRC:.cpp=.o) - $(CXX) -shared $(CXXFLAGS) $^ $(LIBS) -o $@ +$(PROJECTNAME).so: ../../libcommon/libcommon.a $(SRC:.cpp=.o) + $(CXX) -shared $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ dep: $(TESTSRC:.cpp=.d) diff --git a/plugins/webbox/webbox.cpp b/plugins/webbox/webbox.cpp index c58bd0c..53322b5 100644 --- a/plugins/webbox/webbox.cpp +++ b/plugins/webbox/webbox.cpp @@ -3,6 +3,8 @@ #include "file.h" #include "stringutil.h" +#include "libcommon/mime.h" + #include #include #include @@ -127,6 +129,9 @@ namespace { std::function& m_GetRequestParam; // request including body (POST...) std::function& m_SetResponseHeader; // to be added to result string + std::string m_pathInfo; // path inside webbox, derived from request + fs::path m_path; // local filesystem path + std::unordered_map paramHash; std::string webboxPath; @@ -150,14 +155,27 @@ namespace { { if (webboxStaticHtml == "") webboxStaticHtml = STATIC_HTML_DOC_ROOT; + + m_pathInfo = urlDecode(GetRequestParam("rel_target")); + size_t pos {m_pathInfo.find('?')}; + if (pos != m_pathInfo.npos) { + m_pathInfo = m_pathInfo.substr(0, pos); + } + + if (m_pathInfo.find("..") != m_pathInfo.npos) { + throw std::runtime_error("Bad path: "s + m_pathInfo); + } + + m_path = webboxPath; + if (!m_pathInfo.empty()) + m_path /= m_pathInfo; } }; - // Used to return errors by generating response page and HTTP status code - std::string HttpStatus(std::string status, std::string message, CommandParameters& commandParameters) + std::string HttpStatus(std::string status, std::string message, std::function& SetResponseHeader) { - commandParameters.m_SetResponseHeader("status", status); - commandParameters.m_SetResponseHeader("content_type", "text/html"); + SetResponseHeader("status", status); + SetResponseHeader("content_type", "text/html"); auto it{status_map.find(status)}; std::string description{"(Unknown)"}; @@ -167,6 +185,12 @@ namespace { return "

"s + status + " "s + description + "

"s + message + "

"; } + // Used to return errors by generating response page and HTTP status code + std::string HttpStatus(std::string status, std::string message, CommandParameters& commandParameters) + { + return HttpStatus(status, message, commandParameters.m_SetResponseHeader); + } + } // anonymous namespace class Command @@ -186,21 +210,6 @@ public: return HttpStatus("403", "Bad request method", p); } - // Set parameters from FastCGI request environment - m_pathInfo = urlDecode(p.m_GetRequestParam("rel_target")); - size_t pos {m_pathInfo.find('?')}; - if (pos != m_pathInfo.npos) { - m_pathInfo = m_pathInfo.substr(0, pos); - } - - if (m_pathInfo.find("..") != m_pathInfo.npos) { - return HttpStatus("403", "Bad path: "s + m_pathInfo, p); - } - - m_path = p.webboxPath; - if (!m_pathInfo.empty()) - m_path /= m_pathInfo; - return this->start(p); } @@ -220,9 +229,6 @@ protected: std::string m_requestMethod; bool m_isWriteCommand; // if true, command must be prevented if p.webboxReadOnly - // calculated during start of execute() - std::string m_pathInfo; // path inside webbox, derived from request - fs::path m_path; // local filesystem path }; class GetCommand: public Command @@ -306,13 +312,13 @@ protected: pt::ptree list; pt::ptree entry; - if (m_pathInfo != ""s) { // Add ".." if not in top directory of this webbox + if (p.m_pathInfo != ""s) { // Add ".." if not in top directory of this webbox entry.put_value(".."); entry.put(".type", "dir"); list.push_back(pt::ptree::value_type("listentry", entry)); } - fs::directory_iterator dir(m_path); + fs::directory_iterator dir(p.m_path); std::vector files; std::vector dirs; @@ -412,7 +418,7 @@ protected: std::string dirname = tree.get("dirname"); try { - if (fs::create_directory(m_path / dirname)) + if (fs::create_directory(p.m_path / dirname)) return "Successfully created directory"; else return "Error creating directory"; @@ -449,7 +455,7 @@ protected: for (const auto& element: elements) { if (element.first == "file"s) { std::string filename{element.second.data()}; - fs::path path {m_path / filename}; + fs::path path {p.m_path / filename}; auto filesize {fs::file_size(path)}; @@ -514,7 +520,7 @@ protected: return HttpStatus("400", "No files found", p); try { - fs::current_path(m_path); + fs::current_path(p.m_path); } catch (const std::exception& ex) { return HttpStatus("500", "Change path error: "s + ex.what(), p); } @@ -566,7 +572,7 @@ protected: if (element.first == "file"s) { std::string filename{element.second.data()}; - fs::path path{m_path / filename}; + fs::path path{p.m_path / filename}; if (fs::is_directory(path)) { try { @@ -624,10 +630,10 @@ protected: auto elements {tree.get_child("request")}; for (const auto& element: elements) { if (element.first == "target") { - targetDir = m_path / element.second.data(); + targetDir = p.m_path / element.second.data(); } else if (element.first == "file") { std::string filename{element.second.data()}; - fs::path old_path{m_path / filename}; + fs::path old_path{p.m_path / filename}; fs::path new_path{targetDir / filename}; try { fs::rename(old_path, new_path); @@ -674,8 +680,8 @@ protected: std::string oldname{tree.get("request.oldname")}; std::string newname{tree.get("request.newname")}; - fs::path oldpath{m_path / oldname}; - fs::path newpath{m_path / newname}; + fs::path oldpath{p.m_path / oldname}; + fs::path newpath{p.m_path / newname}; try { fs::rename(oldpath, newpath); @@ -753,7 +759,7 @@ protected: } else { filecontent = filecontent.substr(start + "\r\n\r\n"s.size()); - fs::path path{ m_path / filename}; + fs::path path{ p.m_path / filename}; try { File::setFile(path, filecontent); } catch (const std::exception& ex) { @@ -784,54 +790,18 @@ protected: virtual std::string start(CommandParameters& p) { try { - std::string result{File::getFile(m_path)}; + std::string result{File::getFile(p.m_path)}; - p.m_SetResponseHeader("content_disposition", "attachment; filename=\""s + m_path.filename().string() + "\""s); + p.m_SetResponseHeader("content_disposition", "attachment; filename=\""s + p.m_path.filename().string() + "\""s); p.m_SetResponseHeader("content_type", "application/octet-stream"); return result; } catch (const std::exception& ex) { - return HttpStatus("404", "Bad file: "s + m_path.filename().string(), p); + return HttpStatus("404", "Bad file: "s + p.m_path.filename().string(), p); } } }; -// Return a reasonable mime type based on the extension of a file. -static std::string -mime_type(fs::path path) -{ - using boost::algorithm::iequals; - auto const ext = [&path] - { - size_t pos = path.string().rfind("."); - if (pos == std::string::npos) - return std::string{}; - return path.string().substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; // TODO: unordered_map - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; -} - class StaticHtmlCommand: public GetCommand { public: @@ -846,7 +816,7 @@ protected: { // redirect to xyz/ if xyz was requested std::string target = p.m_GetRequestParam("target"); - if (m_pathInfo == "" && !target.empty() && target.back() != '/') { + if (p.m_pathInfo == "" && !target.empty() && target.back() != '/') { p.m_SetResponseHeader("location", target + "/"); return HttpStatus("301", "Use correct index: /"s, p); } @@ -854,20 +824,20 @@ protected: try { fs::path file_path; - if (m_pathInfo == "/" || m_pathInfo == "") { + if (p.m_pathInfo == "/" || p.m_pathInfo == "") { file_path = p.webboxStaticHtml / "index.html"; - } else if (boost::algorithm::starts_with(m_pathInfo, STATIC_HTML_TARGET)) { - file_path = p.webboxStaticHtml / m_pathInfo.substr(STATIC_HTML_TARGET.size()); + } else if (boost::algorithm::starts_with(p.m_pathInfo, STATIC_HTML_TARGET)) { + file_path = p.webboxStaticHtml / p.m_pathInfo.substr(STATIC_HTML_TARGET.size()); } else { - return HttpStatus("500", "Bad request: "s + m_pathInfo, p); + return HttpStatus("500", "Bad request: "s + p.m_pathInfo, p); } - p.m_SetResponseHeader("content_type", mime_type(file_path)); + p.m_SetResponseHeader("content_type", mime_type(file_path.string())); std::string result{File::getFile(file_path)}; return result; } catch (const std::exception& ex) { - return HttpStatus("500", "Server error: "s + m_pathInfo, p); + return HttpStatus("500", "Server error: "s + p.m_pathInfo, p); } } }; @@ -908,23 +878,27 @@ std::string webbox_plugin::generate_page( { // Queries under STATIC_HTML_TARGET will be served statically from STATIC_HTML_DOC_ROOT - CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); - - std::string commandName; - - auto it {commandParameters.paramHash.find("command")}; - if (it != commandParameters.paramHash.end()) - commandName = it->second; + try { + CommandParameters commandParameters(GetServerParam, GetRequestParam, SetResponseHeader); + + std::string commandName; + + auto it {commandParameters.paramHash.find("command")}; + if (it != commandParameters.paramHash.end()) + commandName = it->second; - auto commands_it{m_commands.find(commandName)}; - if (commands_it != m_commands.end()) { - try { - return commands_it->second->execute(commandParameters); - } catch (const std::exception& ex) { - return HttpStatus("500", "Processing command: "s + commandName + ", "s + ex.what(), commandParameters); - } - } else - return HttpStatus("400", "Bad command: "s + commandName, commandParameters); + auto commands_it{m_commands.find(commandName)}; + if (commands_it != m_commands.end()) { + try { + return commands_it->second->execute(commandParameters); + } catch (const std::exception& ex) { + return HttpStatus("500", "Processing command: "s + commandName + ", "s + ex.what(), commandParameters); + } + } else + return HttpStatus("400", "Bad command: "s + commandName, commandParameters); + } catch (const std::exception& ex) { + return HttpStatus("500", ex.what(), SetResponseHeader); + } } void webbox_plugin::registerCommand(std::shared_ptr command) diff --git a/plugins/weblog/Makefile b/plugins/weblog/Makefile index 58d1801..b9f278a 100644 --- a/plugins/weblog/Makefile +++ b/plugins/weblog/Makefile @@ -19,7 +19,7 @@ endif # -fprofile-instr-generate -fcoverage-mapping # gcc:--coverage -CXXFLAGS+= -Wall -I. +CXXFLAGS+= -Wall -I. -I../.. CXXFLAGS+= -pthread -fvisibility=hidden -fPIC ifeq ($(CXX),clang++-10) @@ -40,7 +40,8 @@ LIBS=\ -lboost_regex \ -lpthread \ -lssl -lcrypto \ --ldl +-ldl \ +-lcommon ifeq ($(CXX),clang++-10) LIBS+= \ @@ -56,6 +57,8 @@ LIBS+= \ -lstdc++fs endif +LDFLAGS=-L../../libcommon + PROGSRC=\ stringutil.cpp \ weblog.cpp @@ -74,8 +77,8 @@ all: $(PROJECTNAME).so test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@ -$(PROJECTNAME).so: $(SRC:.cpp=.o) - $(CXX) -shared $(CXXFLAGS) $^ $(LIBS) -o $@ +$(PROJECTNAME).so: ../../libcommon/libcommon.a $(SRC:.cpp=.o) + $(CXX) -shared $(LDFLAGS) $^ $(LDLIBS) $(LIBS) -o $@ dep: $(TESTSRC:.cpp=.d) diff --git a/plugins/weblog/weblog.cpp b/plugins/weblog/weblog.cpp index 4ed468e..4a14799 100644 --- a/plugins/weblog/weblog.cpp +++ b/plugins/weblog/weblog.cpp @@ -2,6 +2,8 @@ #include "stringutil.h" +#include "libcommon/mime.h" + #include #include #include @@ -23,41 +25,6 @@ namespace { const size_t number_of_articles_on_front_page {10}; const std::string article_filename{"article.data"}; - // Return a reasonable mime type based on the extension of a file. - std::string mime_type(fs::path path) - { - using boost::algorithm::iequals; - auto const ext = [&path] - { - size_t pos = path.string().rfind("."); - if (pos == std::string::npos) - return std::string{}; - return path.string().substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; // TODO: unordered_map - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; - } - // Used to return errors by generating response page and HTTP status code std::string HttpStatus(std::string status, std::string message, std::function& SetResponseHeader) { @@ -92,7 +59,7 @@ namespace { bool is_index_file(std::string& rel_target, fs::path& path) { // must be top-level file, recognized as mime_type() - return rel_target.find("/") == rel_target.npos && mime_type(path) != "application/text"; + return rel_target.find("/") == rel_target.npos && mime_type(path.string()) != "application/text"; } bool is_article_page(std::string& rel_target, fs::path& path) @@ -344,7 +311,7 @@ namespace { std::string generateStaticFile(fs::path& path, std::function& SetResponseHeader) { try { - SetResponseHeader("content_type", mime_type(path)); + SetResponseHeader("content_type", mime_type(path.string())); return getFile(path); } catch (const std::exception& ex) { return HttpStatus("500", "Reading Article file: "s + ex.what(), SetResponseHeader); diff --git a/response.cpp b/response.cpp index 2320379..4c76bb3 100644 --- a/response.cpp +++ b/response.cpp @@ -5,6 +5,8 @@ #include "file.h" #include "os.h" +#include "libcommon/mime.h" + #include #include @@ -202,42 +204,6 @@ void SetResponseHeader(const std::string& key, const std::string& value, respons throw std::runtime_error("Unsupported response field: "s + key); } -// Return a reasonable mime type based on the extension of a file. -beast::string_view -mime_type(beast::string_view path) -{ - using beast::iequals; - auto const ext = [&path] - { - auto const pos = path.rfind("."); - if (pos == beast::string_view::npos) - return beast::string_view{}; - return path.substr(pos); - }(); - if(iequals(ext, ".htm")) return "text/html"; - if(iequals(ext, ".html")) return "text/html"; - if(iequals(ext, ".php")) return "text/html"; - if(iequals(ext, ".css")) return "text/css"; - if(iequals(ext, ".txt")) return "text/plain"; - if(iequals(ext, ".js")) return "application/javascript"; - if(iequals(ext, ".json")) return "application/json"; - if(iequals(ext, ".xml")) return "application/xml"; - if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if(iequals(ext, ".flv")) return "video/x-flv"; - if(iequals(ext, ".png")) return "image/png"; - if(iequals(ext, ".jpe")) return "image/jpeg"; - if(iequals(ext, ".jpeg")) return "image/jpeg"; - if(iequals(ext, ".jpg")) return "image/jpeg"; - if(iequals(ext, ".gif")) return "image/gif"; - if(iequals(ext, ".bmp")) return "image/bmp"; - if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if(iequals(ext, ".tiff")) return "image/tiff"; - if(iequals(ext, ".tif")) return "image/tiff"; - if(iequals(ext, ".svg")) return "image/svg+xml"; - if(iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; -} - response_type HttpStatus(std::string status, std::string message, response_type& res) { if (status != "200") { // already handled at res init -- cgit v1.2.3