Fawkes API Fawkes Development Version
static_processor.cpp
1
2/***************************************************************************
3 * static_processor.cpp - Web request processor for static files
4 *
5 * Created: Mon Oct 13 23:41:24 2008
6 * Copyright 2006-2018 Tim Niemueller [www.niemueller.de]
7 *
8 ****************************************************************************/
9
10/* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * Read the full text in the LICENSE.GPL file in the doc directory.
21 */
22
23#include "static_processor.h"
24
25#include <core/exception.h>
26#include <core/exceptions/software.h>
27#include <core/exceptions/system.h>
28#include <logging/logger.h>
29#include <webview/error_reply.h>
30#include <webview/file_reply.h>
31#include <webview/url_manager.h>
32
33#include <boost/filesystem.hpp>
34#include <cerrno>
35#include <climits>
36#include <cstdlib>
37#include <cstring>
38#include <functional>
39#include <regex>
40#include <string>
41#include <unistd.h>
42
43using namespace fawkes;
44
45/** @class WebviewStaticRequestProcessor "static_processor.h"
46 * Static file web processor.
47 * This processor provides access to static files.
48 * @author Tim Niemueller
49 */
50
51/** Constructor.
52 * @param url_manager URL manager to register with
53 * @param base_url base URL for static files
54 * @param htdocs_dirs directories in the file system where to look for static files
55 * @param catchall_file file to be served if a non-existent path is requested.
56 * @param mime_file file with MIME types to read
57 * @param logger logger
58 */
60 const std::string & base_url,
61 std::vector<std::string> &htdocs_dirs,
62 const std::string &catchall_file,
63 const std::string &mime_file,
64 fawkes::Logger * logger)
65: logger_(logger), url_manager_(url_manager), base_url_(base_url), catchall_file_(catchall_file)
66{
67 if (htdocs_dirs.empty()) {
68 throw Exception(errno, "htdocs_dirs is empty");
69 }
70 for (const auto &h : htdocs_dirs) {
71 char htdocs_rp[PATH_MAX];
72 if (realpath(h.c_str(), htdocs_rp) != NULL) {
73 htdocs_dirs_.push_back(htdocs_rp);
74 } else {
75 throw Exception(errno, "Failed to resolve htdocs path '%s'", h.c_str());
76 }
77 }
78
79 //logger_->log_debug("WebStaticReqProc", "Catch-all file: %s", catchall_file_.c_str());
80
81 read_mime_database(mime_file);
82
83 url_manager_->add_handler(WebRequest::METHOD_GET,
84 base_url + "{file+}",
85 std::bind(&WebviewStaticRequestProcessor::process_request,
86 this,
87 std::placeholders::_1),
88 10040);
89
90 if (catchall_file_ != "") {
91 url_manager_->add_handler(WebRequest::METHOD_GET,
92 base_url + "?",
93 std::bind(&WebviewStaticRequestProcessor::process_request,
94 this,
95 std::placeholders::_1),
96 10050);
97 }
98}
99
100/** Destructor. */
102{
103 url_manager_->remove_handler(WebRequest::METHOD_GET, base_url_ + "{file+}");
104 if (catchall_file_ != "") {
105 url_manager_->remove_handler(WebRequest::METHOD_GET, base_url_ + "?");
106 }
107}
108
109void
110WebviewStaticRequestProcessor::read_mime_database(const std::string &mime_file)
111{
112 std::regex words_regex("[^\\s]+");
113
114 mime_types_["unknown"] = "";
115
116 std::ifstream f(mime_file);
117 for (std::string line; std::getline(f, line);) {
118 if (line[0] == '#')
119 continue;
120
121 auto words_begin = std::sregex_iterator(line.begin(), line.end(), words_regex);
122 auto words_end = std::sregex_iterator();
123 if (words_begin == words_end)
124 continue;
125
126 std::string mime_type = words_begin->str();
127 for (std::sregex_iterator i = ++words_begin; i != words_end; ++i) {
128 mime_types_[i->str()] = mime_type;
129 }
130 }
131 logger_->log_debug("WebStaticReqProc",
132 "Read %zu mime types from '%s'",
133 mime_types_.size(),
134 mime_file.c_str());
135}
136
137const std::string &
138WebviewStaticRequestProcessor::get_mime_type(const std::string &file_name)
139{
140 std::string::size_type dot_pos = file_name.rfind(".");
141 if (dot_pos == std::string::npos) {
142 return mime_types_["unknown"];
143 }
144 const auto &m = mime_types_.find(file_name.substr(dot_pos + 1));
145 if (m != mime_types_.end()) {
146 return m->second;
147 } else {
148 return mime_types_["unknown"];
149 }
150}
151
152std::string
153WebviewStaticRequestProcessor::find_file(const std::string &filename)
154{
155 for (const auto &h : htdocs_dirs_) {
156 std::string file_path = h + filename;
157 char rf[PATH_MAX];
158 char * realfile = realpath(file_path.c_str(), rf);
159
160 if (realfile) {
161 if (boost::filesystem::is_directory(realfile))
162 continue;
163
164 if (strncmp(realfile, h.c_str(), h.length()) == 0) {
165 if (access(realfile, R_OK) == 0) {
166 return realfile;
167 } else {
168 switch (errno) {
169 case EACCES: throw AccessViolationException("Access forbidden (file permission)");
170 default:
171 throw IllegalArgumentException("Failed to open %s: %s",
172 filename.c_str(),
173 strerror(errno));
174 }
175 }
176 } else {
177 throw AccessViolationException("Access forbidden (breakout)");
178 }
179 }
180 }
181 throw CouldNotOpenFileException(filename.c_str(), 0);
182}
183
184WebReply *
185WebviewStaticRequestProcessor::process_request(const fawkes::WebRequest *request)
186{
187 try {
188 std::string filename = find_file("/" + request->path_arg("file"));
189 try {
190 DynamicFileWebReply *freply = new DynamicFileWebReply(filename, get_mime_type(filename));
191 return freply;
192 } catch (fawkes::Exception &e) {
193 logger_->log_error("WebStaticReqProc",
194 "Cannot fulfill request for file %s: %s",
195 request->url().c_str(),
197 return new WebErrorPageReply(WebReply::HTTP_INTERNAL_SERVER_ERROR, e.what_no_backtrace());
198 }
199 } catch (AccessViolationException &e) {
200 // Someone tries to trick us to give away files we don't want to give
201 logger_->log_error("WebStaticReqProc",
202 "Access denied for %s: %s",
203 request->url().c_str(),
205 return new WebErrorPageReply(WebReply::HTTP_FORBIDDEN, e.what_no_backtrace());
206 } catch (IllegalArgumentException &e) {
207 logger_->log_error("WebStaticReqProc",
208 "Failed to serve %s: %s",
209 request->url().c_str(),
211 return new WebErrorPageReply(WebReply::HTTP_BAD_REQUEST, e.what_no_backtrace());
212 } catch (CouldNotOpenFileException &e) {
213 std::string catchall_file;
214 try {
215 catchall_file = find_file("/" + catchall_file_);
216 } catch (Exception &e) {
217 } // ignore, serve 404
218
219 if (catchall_file.empty()) {
220 if (catchall_file_.empty()) {
221 return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND, "File not found");
222 } else {
223 return new WebErrorPageReply(WebReply::HTTP_NOT_FOUND,
224 "File not found. <i>Frontend not deployed?</i>");
225 }
226 } else {
227 try {
228 DynamicFileWebReply *freply =
229 new DynamicFileWebReply(catchall_file, get_mime_type(catchall_file));
230 return freply;
231 } catch (Exception &e) {
232 logger_->log_error("WebStaticReqProc",
233 "Failed to serve catchall file: %s",
235 return new WebErrorPageReply(WebReply::HTTP_INTERNAL_SERVER_ERROR, e.what_no_backtrace());
236 }
237 }
238 }
239}
WebviewStaticRequestProcessor(fawkes::WebUrlManager *url_manager, const std::string &base_url, std::vector< std::string > &htdocs_dir, const std::string &catchall_file, const std::string &mime_file, fawkes::Logger *logger)
Constructor.
Access violates policy.
Definition: software.h:93
File could not be opened.
Definition: system.h:53
Dynamic raw file transfer reply.
Definition: file_reply.h:33
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
Expected parameter is missing.
Definition: software.h:80
Interface for logging.
Definition: logger.h:42
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
Static error page reply.
Definition: error_reply.h:31
Basic web reply.
Definition: reply.h:34
Web request meta data carrier.
Definition: request.h:42
const std::string & url() const
Get URL.
Definition: request.h:68
std::string path_arg(const std::string &what) const
Get a path argument.
Definition: request.h:300
Manage URL mappings.
Definition: url_manager.h:40
void remove_handler(WebRequest::Method method, const std::string &path)
Remove a request processor.
Definition: url_manager.cpp:84
void add_handler(WebRequest::Method method, const std::string &path, Handler handler)
Add a request processor.
Definition: url_manager.cpp:54
Fawkes library namespace.