$search
00001 00002 /*************************************************************************** 00003 * errorkb.cpp - Webview extension for nodemon error KB viewing 00004 * 00005 * Created: Tue May 10 11:28:47 2011 00006 * Copyright 2006-2011 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. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <ros/ros.h> 00025 #include <webview_msgs/NavRegistration.h> 00026 #include <webview_msgs/UrlRegistration.h> 00027 #include <webview_msgs/ProcessRequest.h> 00028 00029 #include <webview/formatters/tracwiki.h> 00030 00031 #include <rospack/rospack.h> 00032 00033 #include <cstdio> 00034 #include <csignal> 00035 #include <regex.h> 00036 00037 #define NODEMON_URL_PREFIX "/errorkb" 00038 #define NODEMON_SERVICE_NAME "/nodemon/errorkb_webview" 00039 #define NODEMON_NAV_ENTRY "Error KB" 00040 00041 class NodemonWebviewErrorKB; 00042 NodemonWebviewErrorKB *g_errorkb; 00043 00044 00045 class NodemonWebviewErrorKB 00046 { 00047 public: 00048 NodemonWebviewErrorKB() 00049 { 00050 // This is a workaround for uninitialized variabls in rospack::ROSPack, 00051 // cf. https://code.ros.org/trac/ros/ticket/3488 00052 const char *argv[] = {"", ""}; 00053 try { 00054 __rospack.run(2, (char **)argv); 00055 } catch (std::runtime_error &e) {} // ignored 00056 00057 __srv_proc = 00058 __n.advertiseService(NODEMON_SERVICE_NAME, 00059 &NodemonWebviewErrorKB::process_request_cb, this); 00060 00061 ros::ServiceClient clt_reg = 00062 __n.serviceClient<webview_msgs::UrlRegistration>("/webview/register"); 00063 webview_msgs::UrlRegistration srv; 00064 srv.request.url_prefix = NODEMON_URL_PREFIX; 00065 srv.request.service_name = NODEMON_SERVICE_NAME; 00066 00067 if (! clt_reg.call(srv)) { 00068 throw ros::Exception("Client registration service call failed"); 00069 } else if (! srv.response.success) { 00070 throw ros::Exception(std::string("Registration failed: ") 00071 + srv.response.error); 00072 } 00073 00074 /* 00075 ros::ServiceClient clt_addnav = 00076 __n.serviceClient<webview_msgs::NavRegistration>("/webview/add_nav_entry"); 00077 webview_msgs::NavRegistration srv_addnav; 00078 srv_addnav.request.url = NODEMON_URL_PREFIX; 00079 srv_addnav.request.name = NODEMON_NAV_ENTRY; 00080 00081 if (! clt_addnav.call(srv_addnav)) { 00082 printf("Nav entry adding service call failed\n"); 00083 } else if (! srv_addnav.response.success) { 00084 printf("Adding nav entry failed: %s\n", srv_addnav.response.error.c_str()); 00085 } 00086 */ 00087 } 00088 00089 00090 void shutdown() 00091 { 00092 ros::ServiceClient clt_ureg = 00093 __n.serviceClient<webview_msgs::UrlRegistration>("/webview/unregister"); 00094 webview_msgs::UrlRegistration usrv; 00095 usrv.request.url_prefix = NODEMON_URL_PREFIX; 00096 usrv.request.service_name = NODEMON_SERVICE_NAME; 00097 00098 if (! clt_ureg.call(usrv)) { 00099 printf("Client DE-registration service call failed\n"); 00100 } else if (! usrv.response.success) { 00101 printf("DE-registration failed: %s\n", usrv.response.error.c_str()); 00102 } 00103 00104 /* 00105 ros::ServiceClient clt_remnav = 00106 __n.serviceClient<webview_msgs::NavRegistration>("/webview/remove_nav_entry"); 00107 webview_msgs::NavRegistration srv_remnav; 00108 srv_remnav.request.url = NODEMON_URL_PREFIX; 00109 srv_remnav.request.name = NODEMON_NAV_ENTRY; 00110 00111 if (! clt_remnav.call(srv_remnav)) { 00112 printf("Nav entry removing service call failed\n"); 00113 } else if (! srv_remnav.response.success) { 00114 printf("Removing nav entry failed: %s\n", srv_remnav.response.error.c_str()); 00115 } 00116 */ 00117 } 00118 00119 bool 00120 process_request_cb(webview_msgs::ProcessRequest::Request &req, 00121 webview_msgs::ProcessRequest::Response &resp) 00122 { 00123 printf("Processing request for %s\n", req.url.c_str()); 00124 // split URL in prefix, package, error ID 00125 std::string::size_type pos1, pos2; 00126 00127 if ( ((pos1 = req.url.find("/", 1)) == std::string::npos) || 00128 ((pos2 = req.url.find("/", pos1 + 1)) == std::string::npos) ) 00129 { 00130 resp.code = webview_msgs::ProcessRequest::Response::HTTP_BAD_REQUEST; 00131 resp.error = "Malformed URL"; 00132 return true; 00133 } 00134 00135 std::string package = req.url.substr(pos1 + 1, pos2 - pos1 - 1); 00136 std::string errorid = req.url.substr(pos2 + 1); 00137 00138 // Check package name and error ID as a very basic security layer 00139 regex_t re; 00140 int regerr = 0; 00141 if ( (regerr = regcomp(&re, "^[a-zA-Z0-9_-]+$", REG_EXTENDED)) != 0 ) { 00142 char errtmp[1024]; 00143 regerror(regerr, &re, errtmp, sizeof(errtmp)); 00144 resp.code = webview_msgs::ProcessRequest::Response::HTTP_INTERNAL_SERVER_ERROR; 00145 resp.error = std::string("Failed to compile regex: ") + errtmp; 00146 return true; 00147 } 00148 00149 if (regexec(&re, package.c_str(), 0, NULL, 0) == REG_NOMATCH) { 00150 resp.code = webview_msgs::ProcessRequest::Response::HTTP_BAD_REQUEST; 00151 resp.error = "Invalid package name"; 00152 return true; 00153 } 00154 00155 if (regexec(&re, errorid.c_str(), 0, NULL, 0) == REG_NOMATCH) { 00156 resp.code = webview_msgs::ProcessRequest::Response::HTTP_BAD_REQUEST; 00157 resp.error = "Invalid error ID"; 00158 return true; 00159 } 00160 regfree(&re); 00161 00162 rospack::Package *pkg = NULL; 00163 try { 00164 pkg = __rospack.get_pkg(package); 00165 } catch (ros::Exception &e) { 00166 resp.code = webview_msgs::ProcessRequest::Response::HTTP_INTERNAL_SERVER_ERROR; 00167 resp.error = e.what(); 00168 return true; 00169 } catch (std::runtime_error &e) { 00170 resp.code = webview_msgs::ProcessRequest::Response::HTTP_INTERNAL_SERVER_ERROR; 00171 resp.error = e.what(); 00172 return true; 00173 } 00174 00175 if (pkg) { 00176 std::string path = pkg->flags("error_kb", "path"); 00177 std::string::size_type endpos = path.find_last_not_of(' '); 00178 path = path.substr(0, endpos+1); 00179 00180 if (path == "") { 00181 resp.code = webview_msgs::ProcessRequest::Response::HTTP_NOT_FOUND; 00182 resp.error = std::string("Package ") + package 00183 + " does not provide an error knowledge base"; 00184 return true; 00185 } 00186 00187 // Find specific documentation 00188 std::string docpath = pkg->path + "/" + path + "/doc/" + errorid + ".txt"; 00189 FILE *f = fopen(docpath.c_str(), "r"); 00190 if (!f) { 00191 resp.code = webview_msgs::ProcessRequest::Response::HTTP_NOT_FOUND; 00192 resp.error = std::string("No documentation for error ") 00193 + package + "/" + errorid; 00194 return true; 00195 } 00196 00197 std::string body; 00198 while (! feof(f) && ! ferror(f)) { 00199 char tmp[1024]; 00200 size_t nr = fread(tmp, 1, sizeof(tmp), f); 00201 if (nr > 0) { 00202 body.append(tmp, nr); 00203 } 00204 } 00205 fclose(f); 00206 00207 // Find generic information 00208 std::string gendoc = ""; 00209 std::string genpath = pkg->path + "/" + path + "/doc/generic.txt"; 00210 f = fopen(genpath.c_str(), "r"); 00211 if (f) { 00212 while (! feof(f) && ! ferror(f)) { 00213 char tmp[1024]; 00214 size_t nr = fread(tmp, 1, sizeof(tmp), f); 00215 if (nr > 0) { 00216 gendoc.append(tmp, nr); 00217 } 00218 } 00219 fclose(f); 00220 } 00221 00222 std::string formatted_body = __twf.format(body); 00223 00224 if (gendoc != "") { 00225 resp.html_header = 00226 " <link type=\"text/css\" href=\"/static/css/jqtheme/jquery-ui.custom.css\" rel=\"stylesheet\" />\n" 00227 " <script type=\"text/javascript\" src=\"/static/js/jquery.min.js\"></script>\n" 00228 " <script type=\"text/javascript\" src=\"/static/js/jquery-ui.custom.min.js\"></script>\n"; 00229 00230 00231 gendoc = 00232 "<script type=\"text/javascript\">\n" 00233 " $(function(){\n" 00234 " $(\"#gendoc-title\").click(function(){\n" 00235 " if ( $(\"#gendoc\").is(\":visible\") ) {\n" 00236 " $(\"#gendoc\").hide(\"blind\");\n" 00237 " $(\"#gendoc-icon\").attr(\"src\", \"/static/images/icon-triangle-e.png\");\n" 00238 " } else {\n" 00239 " $(\"#gendoc\").show(\"blind\");\n" 00240 " $(\"#gendoc-icon\").attr(\"src\", \"/static/images/icon-triangle-s.png\");\n" 00241 " }\n" 00242 " });\n" 00243 " $(\"#gendoc\").hide();\n" 00244 " });\n" 00245 "</script>\n" 00246 "<div id=\"gendoc-box\">\n" 00247 " <div><a id=\"gendoc-title\" href=\"#\"><img id=\"gendoc-icon\" " 00248 "class=\"gendoc-icon\" src=\"/static/images/icon-triangle-e.png\" />" 00249 "Generic information</a></div>\n" 00250 " <div id=\"gendoc\">\n" 00251 + __twf.format(gendoc) + "</div></div>\n"; 00252 } 00253 00254 resp.code = webview_msgs::ProcessRequest::Response::HTTP_OK; 00255 resp.wrap_in_page = true; 00256 resp.title = std::string("Documentation for ") 00257 + package + "/" + errorid; 00258 resp.body = formatted_body + "\n\n" + gendoc; 00259 } else { 00260 resp.code = webview_msgs::ProcessRequest::Response::HTTP_NOT_FOUND; 00261 resp.error = std::string("Package ") + package + " not found"; 00262 } 00263 00264 return true; 00265 } 00266 00267 private: 00268 ros::NodeHandle __n; 00269 rospack::ROSPack __rospack; 00270 00271 ros::ServiceServer __srv_proc; 00272 00273 fawkes::TracWikiHeadingFormatter __twf; 00274 }; 00275 00276 00277 void 00278 handle_signal(int signum) 00279 { 00280 g_errorkb->shutdown(); 00281 00282 ros::shutdown(); 00283 } 00284 00285 00290 int 00291 main(int argc, char **argv) 00292 { 00293 ros::init(argc, argv, "nodemon_webview_errorkb", ros::init_options::NoSigintHandler); 00294 00295 // Note that we have a custom SIGINT handler to allow us to properly 00296 // unregister from webview! We cannot simply call this when ros::spin() 00297 // returns because by that time service calls won't work anymore 00298 signal(SIGINT, handle_signal); 00299 00300 try { 00301 g_errorkb = new NodemonWebviewErrorKB(); 00302 } catch (ros::Exception &e) { 00303 printf("Failed to instantiate: %s\n", e.what()); 00304 return 1; 00305 } 00306 00307 ros::spin(); 00308 delete g_errorkb; 00309 00310 return 0; 00311 }