Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * request_dispatcher.cpp - Web request dispatcher 00004 * 00005 * Created: Mon Oct 13 22:48:04 2008 00006 * Copyright 2006-2010 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. 00014 * 00015 * This program is distributed in the hope that it will be useful, 00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00018 * GNU Library General Public License for more details. 00019 * 00020 * Read the full text in the LICENSE.GPL file in the doc directory. 00021 */ 00022 00023 #include <webview/request_dispatcher.h> 00024 #include <webview/request_processor.h> 00025 #include <webview/url_manager.h> 00026 #include <webview/page_reply.h> 00027 #include <webview/error_reply.h> 00028 #include <webview/user_verifier.h> 00029 00030 #include <core/threading/mutex_locker.h> 00031 #include <core/exception.h> 00032 #include <utils/misc/string_urlescape.h> 00033 00034 #include <sys/types.h> 00035 #include <sys/socket.h> 00036 #include <cstdarg> 00037 #include <microhttpd.h> 00038 #include <cstring> 00039 #include <cstdlib> 00040 00041 #define UNAUTHORIZED_REPLY \ 00042 "<html>\n" \ 00043 " <head><title>Access denied</title></head>\n" \ 00044 " <body>\n" \ 00045 " <h1>Access denied</h1>\n" \ 00046 " <p>Authentication is required to access Fawkes Webview</p>\n" \ 00047 " </body>\n" \ 00048 "</html>" 00049 00050 namespace fawkes { 00051 #if 0 /* just to make Emacs auto-indent happy */ 00052 } 00053 #endif 00054 00055 /** @class WebRequestDispatcher "request_dispatcher.h" 00056 * Web request dispatcher. 00057 * Takes web request received via a webserver run by libmicrohttpd and dispatches 00058 * pages to registered WebRequestProcessor instances or gives a 404 error if no 00059 * processor was registered for the given base url. 00060 * @author Tim Niemueller 00061 */ 00062 00063 /** Constructor. 00064 * @param url_manager URL manager to use for URL to processor mapping 00065 * @param headergen page header generator 00066 * @param footergen page footer generator 00067 */ 00068 WebRequestDispatcher::WebRequestDispatcher(WebUrlManager *url_manager, 00069 WebPageHeaderGenerator *headergen, 00070 WebPageFooterGenerator *footergen) 00071 { 00072 __realm = NULL; 00073 __url_manager = url_manager; 00074 __page_header_generator = headergen; 00075 __page_footer_generator = footergen; 00076 } 00077 00078 00079 /** Destructor. */ 00080 WebRequestDispatcher::~WebRequestDispatcher() 00081 { 00082 if (__realm) free(__realm); 00083 } 00084 00085 00086 /** Setup basic authentication. 00087 * @param realm authentication realm to display to the user. 00088 * If NULL basic authentication will be disabled. 00089 * @param verifier verifier to use for checking credentials. 00090 * If NULL basic authentication will be disabled. 00091 */ 00092 void 00093 WebRequestDispatcher::setup_basic_auth(const char *realm, 00094 WebUserVerifier *verifier) 00095 { 00096 #if MHD_VERSION >= 0x00090400 00097 if (__realm) free(__realm); 00098 __realm = NULL; 00099 __user_verifier = NULL; 00100 if (realm && verifier) { 00101 __realm = strdup(realm); 00102 __user_verifier = verifier; 00103 } 00104 #else 00105 throw Exception("libmicrohttpd >= 0.9.4 is required for basic authentication, " 00106 "which was not available at compile time."); 00107 #endif 00108 } 00109 00110 /** Process request callback for libmicrohttpd. 00111 * @param callback_data instance of WebRequestDispatcher to call 00112 * @param connection libmicrohttpd connection instance 00113 * @param url URL, may contain escape sequences 00114 * @param method HTTP method 00115 * @param version HTTP version 00116 * @param upload_data uploaded data 00117 * @param upload_data_size size of upload_data parameter 00118 * @param session_data session data pointer 00119 * @return appropriate return code for libmicrohttpd 00120 */ 00121 int 00122 WebRequestDispatcher::process_request_cb(void *callback_data, 00123 struct MHD_Connection * connection, 00124 const char *url, 00125 const char *method, 00126 const char *version, 00127 const char *upload_data, 00128 size_t *upload_data_size, 00129 void **session_data) 00130 { 00131 WebRequestDispatcher *rd = static_cast<WebRequestDispatcher *>(callback_data); 00132 return rd->process_request(connection, url, method, version, 00133 upload_data, upload_data_size, session_data); 00134 } 00135 00136 00137 /** Callback based chunk-wise data. 00138 * Supplies data chunk based. 00139 * @param reply instance of DynamicWebReply 00140 * @param pos position in stream 00141 * @param buf buffer to put data in 00142 * @param max maximum number of bytes that can be put in buf 00143 * @return suitable libmicrohttpd return code 00144 */ 00145 #if MHD_VERSION >= 0x00090200 00146 static ssize_t 00147 dynamic_reply_data_cb(void *reply, uint64_t pos, char *buf, size_t max) 00148 #else 00149 static int 00150 # if MHD_VERSION <= 0x00040000 00151 dynamic_reply_data_cb(void *reply, size_t pos, char *buf, int max) 00152 # else 00153 dynamic_reply_data_cb(void *reply, uint64_t pos, char *buf, int max) 00154 # endif 00155 #endif 00156 { 00157 DynamicWebReply *dreply = static_cast<DynamicWebReply *>(reply); 00158 return dreply->next_chunk(pos, buf, max); 00159 } 00160 00161 00162 /** Callback to free dynamic web reply. 00163 * @param reply Instance of DynamicWebReply to free. 00164 */ 00165 static void 00166 dynamic_reply_free_cb(void *reply) 00167 { 00168 DynamicWebReply *dreply = static_cast<DynamicWebReply *>(reply); 00169 delete dreply; 00170 } 00171 00172 00173 /** Prepare response from static reply. 00174 * @param sreply static reply 00175 * @return response struct ready to be enqueued 00176 */ 00177 struct MHD_Response * 00178 WebRequestDispatcher::prepare_static_response(StaticWebReply *sreply) 00179 { 00180 struct MHD_Response *response; 00181 WebPageReply *wpreply = dynamic_cast<WebPageReply *>(sreply); 00182 if (wpreply) { 00183 wpreply->pack(__active_baseurl, 00184 __page_header_generator, __page_footer_generator); 00185 } else { 00186 sreply->pack(); 00187 } 00188 if (sreply->body_length() > 0) { 00189 response = MHD_create_response_from_data(sreply->body_length(), 00190 (void*) sreply->body().c_str(), 00191 /* free */ MHD_YES, 00192 /* copy */ MHD_YES); 00193 } else { 00194 response = MHD_create_response_from_data(0, (void*) "", 00195 /* free */ MHD_NO, 00196 /* copy */ MHD_NO); 00197 } 00198 00199 const WebReply::HeaderMap &headers = sreply->headers(); 00200 WebReply::HeaderMap::const_iterator i; 00201 for (i = headers.begin(); i != headers.end(); ++i) { 00202 MHD_add_response_header(response, i->first.c_str(), i->second.c_str()); 00203 } 00204 00205 return response; 00206 } 00207 00208 /** Queue a static web reply. 00209 * @param connection libmicrohttpd connection to queue response to 00210 * @param sreply static web reply to queue 00211 * @return suitable libmicrohttpd return code 00212 */ 00213 int 00214 WebRequestDispatcher::queue_static_reply(struct MHD_Connection * connection, 00215 StaticWebReply *sreply) 00216 { 00217 struct MHD_Response *response = prepare_static_response(sreply); 00218 00219 int rv = MHD_queue_response(connection, sreply->code(), response); 00220 MHD_destroy_response(response); 00221 return rv; 00222 } 00223 00224 00225 /** Queue a static web reply after basic authentication failure. 00226 * @param connection libmicrohttpd connection to queue response to 00227 * @return suitable libmicrohttpd return code 00228 */ 00229 int 00230 WebRequestDispatcher::queue_basic_auth_fail(struct MHD_Connection * connection) 00231 { 00232 StaticWebReply sreply(WebReply::HTTP_UNAUTHORIZED, UNAUTHORIZED_REPLY); 00233 #if MHD_VERSION >= 0x00090400 00234 struct MHD_Response *response = prepare_static_response(&sreply); 00235 00236 int rv = MHD_queue_basic_auth_fail_response(connection, __realm, response); 00237 MHD_destroy_response(response); 00238 #else 00239 sreply.add_header(MHD_HTTP_HEADER_WWW_AUTHENTICATE, 00240 (std::string("Basic realm=") + __realm).c_str()); 00241 00242 int rv = queue_static_reply(connection, &sreply); 00243 #endif 00244 return rv; 00245 } 00246 00247 00248 /** Process request callback for libmicrohttpd. 00249 * @param connection libmicrohttpd connection instance 00250 * @param url URL, may contain escape sequences 00251 * @param method HTTP method 00252 * @param version HTTP version 00253 * @param upload_data uploaded data 00254 * @param upload_data_size size of upload_data parameter 00255 * @param session_data session data pointer 00256 * @return appropriate return code for libmicrohttpd 00257 */ 00258 int 00259 WebRequestDispatcher::process_request(struct MHD_Connection * connection, 00260 const char *url, 00261 const char *method, 00262 const char *version, 00263 const char *upload_data, 00264 size_t *upload_data_size, 00265 void **session_data) 00266 { 00267 std::string surl = url; 00268 static int dummy; 00269 int ret; 00270 00271 if ((0 != strcmp(method, "GET")) && (0 != strcmp(method, "POST"))) 00272 return MHD_NO; /* unexpected method */ 00273 00274 MutexLocker lock(__url_manager->mutex()); 00275 WebRequestProcessor *proc = __url_manager->find_processor(surl); 00276 00277 if (proc) { 00278 char *urlc = strdup(url); 00279 fawkes::hex_unescape(urlc); 00280 std::string urls = urlc; 00281 free(urlc); 00282 00283 if (! proc->handles_session_data()) { 00284 if ( *session_data == NULL) { 00285 // The first time only the headers are valid, 00286 // do not respond in the first round... 00287 *session_data = &dummy; 00288 return MHD_YES; 00289 } 00290 *session_data = NULL; /* clear context pointer */ 00291 } else { 00292 if ( *session_data == NULL) { 00293 WebReply *reply = proc->process_request(urls.c_str(), method, version, 00294 upload_data, upload_data_size, 00295 session_data); 00296 if ((reply != NULL) || (*session_data == NULL)) { 00297 return MHD_NO; 00298 } else { 00299 return MHD_YES; 00300 } 00301 } 00302 } 00303 00304 #if MHD_VERSION >= 0x00090400 00305 if (__realm) { 00306 char *user, *pass = NULL; 00307 user = MHD_basic_auth_get_username_password(connection, &pass); 00308 if ( (user == NULL) || (pass == NULL) || 00309 ! __user_verifier->verify_user(user, pass)) 00310 { 00311 return queue_basic_auth_fail(connection); 00312 } 00313 } 00314 #endif 00315 00316 WebReply *reply = proc->process_request(urls.c_str(), method, version, 00317 upload_data, upload_data_size, 00318 session_data); 00319 if ( reply ) { 00320 StaticWebReply *sreply = dynamic_cast<StaticWebReply *>(reply); 00321 DynamicWebReply *dreply = dynamic_cast<DynamicWebReply *>(reply); 00322 if (sreply) { 00323 ret = queue_static_reply(connection, sreply); 00324 delete reply; 00325 } else if (dreply) { 00326 struct MHD_Response *response; 00327 response = MHD_create_response_from_callback(dreply->size(), 00328 dreply->chunk_size(), 00329 dynamic_reply_data_cb, 00330 dreply, 00331 dynamic_reply_free_cb); 00332 ret = MHD_queue_response (connection, dreply->code(), response); 00333 MHD_destroy_response (response); 00334 } else { 00335 WebErrorPageReply ereply(WebReply::HTTP_INTERNAL_SERVER_ERROR); 00336 ret = queue_static_reply(connection, &ereply); 00337 delete reply; 00338 } 00339 } else { 00340 if (proc->handles_session_data()) { 00341 return MHD_YES; 00342 } else { 00343 WebErrorPageReply ereply(WebReply::HTTP_NOT_FOUND); 00344 ret = queue_static_reply(connection, &ereply); 00345 } 00346 } 00347 } else { 00348 if (surl == "/") { 00349 WebPageReply preply("Fawkes", "<h1>Welcome to Fawkes.</h1><hr />"); 00350 ret = queue_static_reply(connection, &preply); 00351 } else { 00352 WebErrorPageReply ereply(WebReply::HTTP_NOT_FOUND); 00353 ret = queue_static_reply(connection, &ereply); 00354 } 00355 } 00356 return ret; 00357 } 00358 00359 } // end namespace fawkes