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 
43 using 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 
109 void
110 WebviewStaticRequestProcessor::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 
137 const std::string &
138 WebviewStaticRequestProcessor::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 
152 std::string
153 WebviewStaticRequestProcessor::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 
184 WebReply *
185 WebviewStaticRequestProcessor::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(),
196  e.what_no_backtrace());
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(),
204  e.what_no_backtrace());
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(),
210  e.what_no_backtrace());
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",
234  e.what_no_backtrace());
235  return new WebErrorPageReply(WebReply::HTTP_INTERNAL_SERVER_ERROR, e.what_no_backtrace());
236  }
237  }
238  }
239 }
fawkes::IllegalArgumentException
Expected parameter is missing.
Definition: software.h:80
fawkes::WebUrlManager::add_handler
void add_handler(WebRequest::Method method, const std::string &path, Handler handler)
Add a request processor.
Definition: url_manager.cpp:54
fawkes::WebRequest
Web request meta data carrier.
Definition: request.h:42
fawkes::WebRequest::url
const std::string & url() const
Get URL.
Definition: request.h:68
fawkes::WebUrlManager::remove_handler
void remove_handler(WebRequest::Method method, const std::string &path)
Remove a request processor.
Definition: url_manager.cpp:84
fawkes::AccessViolationException
Access violates policy.
Definition: software.h:93
fawkes::WebUrlManager
Manage URL mappings.
Definition: url_manager.h:40
fawkes::CouldNotOpenFileException
File could not be opened.
Definition: system.h:53
fawkes::Logger::log_error
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
fawkes::WebRequest::path_arg
std::string path_arg(const std::string &what) const
Get a path argument.
Definition: request.h:300
fawkes::Logger
Interface for logging.
Definition: logger.h:42
fawkes
Fawkes library namespace.
fawkes::Exception::what_no_backtrace
virtual const char * what_no_backtrace() const
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
fawkes::WebErrorPageReply
Static error page reply.
Definition: error_reply.h:31
WebviewStaticRequestProcessor::WebviewStaticRequestProcessor
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.
Definition: static_processor.cpp:59
fawkes::DynamicFileWebReply
Dynamic raw file transfer reply.
Definition: file_reply.h:33
WebviewStaticRequestProcessor::~WebviewStaticRequestProcessor
~WebviewStaticRequestProcessor()
Destructor.
Definition: static_processor.cpp:101
fawkes::Logger::log_debug
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
fawkes::WebReply
Basic web reply.
Definition: reply.h:34
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36