Fawkes API  Fawkes Development Version
webview_thread.cpp
00001 
00002 /***************************************************************************
00003  *  webview_thread.cpp - Thread that handles web interface requests
00004  *
00005  *  Created: Mon Oct 13 17:51:31 2008 (I5 Developer's Day)
00006  *  Copyright  2006-2008  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_thread.h"
00024 #include "static_processor.h"
00025 #include "blackboard_processor.h"
00026 #include "startpage_processor.h"
00027 #include "plugins_processor.h"
00028 #include "service_browse_handler.h"
00029 #include "header_generator.h"
00030 #include "footer_generator.h"
00031 #include "user_verifier.h"
00032 
00033 #include <core/version.h>
00034 #include <core/exceptions/system.h>
00035 #include <utils/system/file.h>
00036 #include <utils/system/hostinfo.h>
00037 #include <webview/request_dispatcher.h>
00038 #include <webview/page_reply.h>
00039 #include <webview/server.h>
00040 #include <webview/url_manager.h>
00041 #include <webview/nav_manager.h>
00042 
00043 #include <sys/wait.h>
00044 
00045 using namespace fawkes;
00046 
00047 
00048 /** Prefix for the WebStaticRequestProcessor. */
00049 const char *WebviewThread::STATIC_URL_PREFIX = "/static";
00050 /** Prefix for the WebBlackBoardRequestProcessor. */
00051 const char *WebviewThread::BLACKBOARD_URL_PREFIX = "/blackboard";
00052 /** Prefix for the WebPluginsRequestProcessor. */
00053 const char *WebviewThread::PLUGINS_URL_PREFIX = "/plugins";
00054 
00055 /** @class WebviewThread "webview_thread.h"
00056  * Webview Thread.
00057  * This thread runs the HTTP server and handles requests via the
00058  * WebRequestDispatcher.
00059  * @author Tim Niemueller
00060  */
00061 
00062 
00063 /** Constructor. */
00064 WebviewThread::WebviewThread()
00065   : Thread("WebviewThread", Thread::OPMODE_CONTINUOUS),
00066     LoggerAspect(&__cache_logger)
00067 {
00068   set_prepfin_conc_loop(true);
00069 }
00070 
00071 
00072 WebviewThread::~WebviewThread()
00073 {
00074 }
00075 
00076 void
00077 WebviewThread::init()
00078 {
00079   __cfg_port = config->get_uint("/webview/port");
00080 
00081   bool __cfg_use_ssl = false;
00082   try {
00083     __cfg_use_ssl = config->get_bool("/webview/use_ssl");
00084   } catch (Exception &e) {}
00085 
00086   if (__cfg_use_ssl) {
00087     __cfg_ssl_create = false;
00088     try {
00089       __cfg_ssl_create = config->get_bool("/webview/ssl_create");
00090     } catch (Exception &e) {}
00091 
00092     __cfg_ssl_key  = config->get_string("/webview/ssl_key");
00093     __cfg_ssl_cert = config->get_string("/webview/ssl_cert");
00094 
00095     if (__cfg_ssl_key[0] != '/')
00096       __cfg_ssl_key = std::string(CONFDIR"/") + __cfg_ssl_key;
00097 
00098     if (__cfg_ssl_cert[0] != '/')
00099       __cfg_ssl_cert = std::string(CONFDIR"/") + __cfg_ssl_cert;
00100 
00101     logger->log_debug(name(), "Key: %s  Cert: %s", __cfg_ssl_key.c_str(),
00102                       __cfg_ssl_cert.c_str());
00103 
00104     if (! File::exists(__cfg_ssl_key.c_str())) {
00105       if (File::exists(__cfg_ssl_cert.c_str())) {
00106         throw Exception("Key file %s does not exist, but certificate file %s "
00107                         "does", __cfg_ssl_key.c_str(), __cfg_ssl_cert.c_str());
00108       } else if (__cfg_ssl_create) {
00109         ssl_create(__cfg_ssl_key.c_str(), __cfg_ssl_cert.c_str());
00110       } else {
00111         throw Exception("Key file %s does not exist", __cfg_ssl_key.c_str());
00112       }
00113     } else if (! File::exists(__cfg_ssl_cert.c_str())) {
00114       throw Exception("Certificate file %s does not exist, but key file %s "
00115                       "does", __cfg_ssl_key.c_str(), __cfg_ssl_cert.c_str());
00116     }
00117   }
00118 
00119   bool __cfg_use_basic_auth = false;
00120   try {
00121     __cfg_use_basic_auth = config->get_bool("/webview/use_basic_auth");
00122   } catch (Exception &e) {}
00123   __cfg_basic_auth_realm = "Fawkes Webview";
00124   try {
00125     __cfg_basic_auth_realm = config->get_bool("/webview/basic_auth_realm");
00126   } catch (Exception &e) {}
00127 
00128 
00129   __cache_logger.clear();
00130 
00131   __webview_service = new NetworkService(nnresolver, "Fawkes Webview on %h",
00132                                          "_http._tcp", __cfg_port);
00133   __webview_service->add_txt("fawkesver=%u.%u.%u",
00134                              FAWKES_VERSION_MAJOR, FAWKES_VERSION_MINOR,
00135                              FAWKES_VERSION_MICRO);
00136   __service_browse_handler = new WebviewServiceBrowseHandler(logger, __webview_service);
00137 
00138   __header_gen = new WebviewHeaderGenerator(webview_nav_manager);
00139   __footer_gen = new WebviewFooterGenerator(__service_browse_handler);
00140 
00141   __dispatcher = new WebRequestDispatcher(webview_url_manager,
00142                                           __header_gen, __footer_gen);
00143 
00144 
00145   try {
00146     if (__cfg_use_ssl) {
00147       __webserver  = new WebServer(__cfg_port, __dispatcher, __cfg_ssl_key.c_str(),
00148                                    __cfg_ssl_cert.c_str(), logger);
00149     } else {
00150       __webserver  = new WebServer(__cfg_port, __dispatcher, logger);
00151     }
00152 
00153     if (__cfg_use_basic_auth) {
00154       __user_verifier = new WebviewUserVerifier(config, logger);
00155       __webserver->setup_basic_auth(__cfg_basic_auth_realm.c_str(),
00156                                     __user_verifier);
00157     }
00158   } catch (Exception &e) {
00159     delete __webview_service;
00160     delete __service_browse_handler;
00161     delete __header_gen;
00162     delete __footer_gen;
00163     delete __dispatcher;
00164     throw;
00165   }
00166 
00167 
00168   __startpage_processor  = new WebviewStartPageRequestProcessor(&__cache_logger);
00169   __static_processor     = new WebviewStaticRequestProcessor(STATIC_URL_PREFIX, RESDIR"/webview", logger);
00170   __blackboard_processor = new WebviewBlackBoardRequestProcessor(BLACKBOARD_URL_PREFIX, blackboard);
00171   __plugins_processor    = new WebviewPluginsRequestProcessor(PLUGINS_URL_PREFIX, plugin_manager);
00172   webview_url_manager->register_baseurl("/", __startpage_processor);
00173   webview_url_manager->register_baseurl(STATIC_URL_PREFIX, __static_processor);
00174   webview_url_manager->register_baseurl(BLACKBOARD_URL_PREFIX, __blackboard_processor);
00175   webview_url_manager->register_baseurl(PLUGINS_URL_PREFIX, __plugins_processor);
00176 
00177   webview_nav_manager->add_nav_entry(BLACKBOARD_URL_PREFIX, "BlackBoard");
00178   webview_nav_manager->add_nav_entry(PLUGINS_URL_PREFIX, "Plugins");
00179 
00180   logger->log_info("WebviewThread", "Listening for HTTP connections on port %u", __cfg_port);
00181 
00182   service_publisher->publish_service(__webview_service);
00183   service_browser->watch_service("_http._tcp", __service_browse_handler);
00184 
00185 }
00186 
00187 void
00188 WebviewThread::finalize()
00189 {
00190   service_publisher->unpublish_service(__webview_service);
00191   service_browser->unwatch_service("_http._tcp", __service_browse_handler);
00192 
00193   webview_url_manager->unregister_baseurl("/");
00194   webview_url_manager->unregister_baseurl(STATIC_URL_PREFIX);
00195   webview_url_manager->unregister_baseurl(BLACKBOARD_URL_PREFIX);
00196   webview_url_manager->unregister_baseurl(PLUGINS_URL_PREFIX);
00197 
00198   webview_nav_manager->remove_nav_entry(BLACKBOARD_URL_PREFIX);
00199   webview_nav_manager->remove_nav_entry(PLUGINS_URL_PREFIX);
00200 
00201   delete __webserver;
00202 
00203   delete __webview_service;
00204   delete __service_browse_handler;
00205 
00206   delete __dispatcher;
00207   delete __static_processor;
00208   delete __blackboard_processor;
00209   delete __startpage_processor;
00210   delete __plugins_processor;
00211   delete __footer_gen;
00212   delete __header_gen;
00213   __dispatcher = NULL;
00214 }
00215 
00216 
00217 void
00218 WebviewThread::loop()
00219 {
00220   __webserver->process();
00221 }
00222 
00223 
00224 void
00225 WebviewThread::ssl_create(const char *ssl_key_file, const char *ssl_cert_file)
00226 {
00227   logger->log_info(name(), "Creating SSL key and certificate. "
00228                    "This may take a while...");
00229   HostInfo h;
00230 
00231   char *cmd;
00232   if (asprintf(&cmd, "openssl req -new -x509 -batch -nodes -days 365 "
00233                "-subj \"/C=XX/L=World/O=Fawkes/CN=%s.local\" "
00234                "-out \"%s\" -keyout \"%s\" >/dev/null 2>&1",
00235                h.short_name(), ssl_cert_file, ssl_key_file) == -1)
00236   {
00237     throw OutOfMemoryException("Webview/SSL: Could not generate OpenSSL string");
00238   }
00239 
00240   int status = system(cmd);
00241   free(cmd);
00242 
00243   if (WEXITSTATUS(status) != 0) {
00244     throw Exception("Failed to auto-generate key/certificate pair");
00245   }
00246 }