Fawkes API  Fawkes Development Version
clips-webview-processor.cpp
1 
2 /***************************************************************************
3  * clips-webview-processor.cpp - CLIPS introspection via webview
4  *
5  * Created: Sat Jun 15 20:17:53 2013
6  * Copyright 2006-2013 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 "clips-webview-processor.h"
24 
25 #include <core/exception.h>
26 #include <core/threading/mutex_locker.h>
27 #include <webview/page_reply.h>
28 #include <webview/file_reply.h>
29 #include <webview/error_reply.h>
30 #include <webview/redirect_reply.h>
31 #include <utils/misc/string_conversions.h>
32 #include <plugins/clips/aspect/clips_env_manager.h>
33 
34 #include <cstring>
35 
36 #include <clipsmm.h>
37 #include <clips/clips.h>
38 
39 using namespace fawkes;
40 
41 /** @class ClipsWebRequestProcessor "rrdweb_processor.h"
42  * Clips web request processor.
43  * Process web requests to the rrd URL space.
44  * @author Tim Niemueller
45  */
46 
47 static int
48 clips_router_query(void *env, char *logical_name)
49 {
50  if (strcmp(logical_name, WERROR) == 0) return TRUE;
51  return FALSE;
52 }
53 
54 static int
55 clips_router_print(void *env, char *logical_name, char *str)
56 {
57  void *rc = GetEnvironmentRouterContext(env);
58  ClipsWebRequestProcessor *reqproc = static_cast<ClipsWebRequestProcessor *>(rc);
59  reqproc->add_error(str);
60  EnvDeactivateRouter(env, (char *)"webview-reqproc");
61  EnvPrintRouter(env, logical_name, str);
62  EnvActivateRouter(env, (char *)"webview-reqproc");
63  return TRUE;
64 }
65 
66 static int
67 clips_router_exit(void *env, int exit_code)
68 {
69  return TRUE;
70 }
71 
72 
73 /** Constructor.
74  * @param clips_env_mgr CLIPS environment manager
75  * @param logger logger to report problems
76  * @param baseurl base URL of the Clips webrequest processor
77  */
79  fawkes::Logger *logger, const char *baseurl)
80 {
81  clips_env_mgr_ = clips_env_mgr;
82  logger_ = logger;
83 
84  baseurl_ = baseurl;
85  baseurl_len_ = strlen(baseurl);
86 
87 }
88 
89 
90 /** Destructor. */
92 {
93 }
94 
95 void
96 ClipsWebRequestProcessor::enable_error_log(LockPtr<CLIPS::Environment> &clips)
97 {
98  errors_.clear();
99  EnvAddRouterWithContext(clips->cobj(), (char *)"webview-reqproc",
100  /* exclusive */ 40,
101  clips_router_query,
102  clips_router_print,
103  /* getc */ NULL,
104  /* ungetc */ NULL,
105  clips_router_exit,
106  this);
107 }
108 
109 void
110 ClipsWebRequestProcessor::disable_error_log(LockPtr<CLIPS::Environment> &clips)
111 {
112  EnvDeleteRouter(clips->cobj(), (char *)"webview-reqproc");
113 }
114 
115 
116 /** Add an error string.
117  * Used by CLIPS I/O router to provide error message.
118  * @param str string to add to errors
119  */
120 void
122 {
123  errors_.push_back(str);
124 }
125 
126 void
127 ClipsWebRequestProcessor::retract_fact(LockPtr<CLIPS::Environment> &clips, long int index)
128 {
129  CLIPS::Fact::pointer fact = clips->get_facts();
130  while (fact) {
131  if (fact->index() == index) {
132  fact->retract();
133  break;
134  }
135  fact = fact->next();
136  }
137 }
138 
139 WebReply *
141 {
142  if ( strncmp(baseurl_, request->url().c_str(), baseurl_len_) == 0 ) {
143  // It is in our URL prefix range
144  std::string env_name = request->url().substr(baseurl_len_);
145  std::string::size_type slash_pos = env_name.find("/", 1);
146  std::string subpath;
147  if (slash_pos != std::string::npos) {
148  subpath = env_name.substr(slash_pos);
149  env_name = env_name.substr(1, slash_pos-1);
150  } else if (env_name.length() > 0) {
151  // remove lead slash
152  env_name = env_name.substr(1);
153  }
154 
155  std::map<std::string, LockPtr<CLIPS::Environment>> envs =
156  clips_env_mgr_->environments();
157 
158  if (envs.find(env_name) == envs.end()) {
159  if (envs.size() == 1) {
160  // if there is only one just redirect
161  return new WebRedirectReply(std::string(baseurl_) + "/" + envs.begin()->first);
162  } else {
163  WebPageReply *r = new WebPageReply("CLIPS - Environment not found");
164  *r += "<h2>Environment " + env_name + " not found</h2>\n";
165  if (! envs.empty()) {
166  *r += "<p>Choose on of the following existing environments:</p>\n";
167  *r += "<ul>\n";
168  for (auto env : envs) {
169  *r += std::string("<li><a href=\"") + baseurl_ + "/" +
170  env.first + "\">" + env.first + "</a></li>\n";
171  }
172  *r += "</ul>\n";
173  } else {
174  *r += "<p>No environments have been registered.</p>\n";
175  }
176  return r;
177  }
178  }
179 
180 
181  LockPtr<CLIPS::Environment> &clips = envs[env_name];
182 
183  if (subpath == "/assert") {
184  MutexLocker lock(clips.objmutex_ptr());
185  enable_error_log(clips);
186  if (! request->post_value("index").empty()) {
187  long int index = StringConversions::to_long(request->post_value("index"));
188  retract_fact(clips, index);
189  }
190 
191  clips->assert_fact(request->post_value("fact"));
192  disable_error_log(clips);
193  if (! errors_.empty()) {
194  WebPageReply *r = new WebPageReply("CLIPS");
195  *r += "<h2>CLIPS Fact Assertion</h2>\n";
196  r->append_body("<p><span style=\"color:red\">Asserting '%s' failed:</span>\n<pre>",
197  request->post_value("fact").c_str());
198  for (auto e : errors_) {
199  *r += e;
200  }
201  *r += "</pre></p>";
202  r->append_body("<p><a href=\"%s\">Back</a></p>", baseurl_);
203 
204  r->append_body("<form action=\"%s/%s/assert\" method=\"post\">"
205  "<input type=\"hidden\" name=\"index\" value=\"%s\">"
206  "New fact: <input type=\"text\" name=\"fact\" value=\"%s\"/>"
207  "<input type=\"submit\" value=\"Assert\" />",
208  baseurl_, env_name.c_str(),
209  request->post_value("index").c_str(),
210  request->post_value("fact").c_str());
211 
212  return r;
213  } else {
214  return new WebRedirectReply(std::string(baseurl_) + "/" + env_name);
215  }
216  } else if (subpath.find("/retract") == 0) {
217  std::string index_str = subpath.substr(9); // length of "/retract/"
218  long int index = StringConversions::to_long(index_str);
219  fawkes::MutexLocker lock(clips.objmutex_ptr());
220  retract_fact(clips, index);
221  return new WebRedirectReply(std::string(baseurl_) + "/" + env_name);
222  }
223 
224  MutexLocker lock(clips.objmutex_ptr());
225 
226  WebPageReply *r = new WebPageReply("CLIPS");
227  r->set_html_header(" <link type=\"text/css\" href=\"/static/css/jqtheme/"
228  "jquery-ui.custom.css\" rel=\"stylesheet\" />\n"
229  " <script type=\"text/javascript\" src=\"/static/js/"
230  "jquery.min.js\"></script>\n"
231  " <script type=\"text/javascript\" src=\"/static/js/"
232  "jquery-ui.custom.min.js\"></script>\n");
233 
234  *r +=
235  "<style type=\"text/css\">\n"
236  " tr:hover { background-color: #eeeeee; }\n"
237  " :link:hover, :visited:hover { background-color: #bb0000; color: white; }\n"
238  " .envs { margin: 0px; padding: 0px; display: inline; }\n"
239  " .envs li { display: inline; padding-left: 8px; white-space: no-wrap; }\n"
240  "</style>";
241 
242  if (envs.size() > 1) {
243  *r += "Environments: <ul class=\"envs\">\n";
244  for (auto env : envs) {
245  *r += std::string("<li><a href=\"") + baseurl_ + "/" +
246  env.first + "\">" + env.first + "</a></li>\n";
247  }
248  *r += "</ul>\n";
249  }
250 
251 
252  *r += "<h2>CLIPS Facts</h2>\n";
253 
254  *r += "<table>";
255  *r += "<tr><th>Index</th><th>Fact</th><th>Action</th></tr>\n";
256 
257  CLIPS::Fact::pointer fact = clips->get_facts();
258  while (fact) {
259  CLIPS::Template::pointer tmpl = fact->get_template();
260 
261  char tmp[16384];
262  OpenStringDestination(clips->cobj(), (char *)"ProcPPForm", tmp, 16383);
263  PrintFact(clips->cobj(), (char *)"ProcPPForm",
264  (struct fact *)fact->cobj(), FALSE, FALSE);
265  CloseStringDestination(clips->cobj(), (char *)"ProcPPForm");
266 
267  r->append_body("<tr><td>f-%li</td><td>%s</td>"
268  "<td><a href=\"%s/%s/retract/%li\">Retract</a></td>"
269  "</tr>\n",
270  fact->index(), tmp, baseurl_, env_name.c_str(), fact->index());
271 
272  std::string escaped = tmp;
273  size_t pos = 0;
274  while ((pos = escaped.find("\"", pos)) != std::string::npos) {
275  escaped.replace(pos, 1, "&quot;");
276  }
277 
278  fact = fact->next();
279  }
280 
281 
282  *r += "</table>";
283 
284  r->append_body("<p><form action=\"%s/%s/assert\" method=\"post\">"
285  "<input type=\"hidden\" name=\"index\" value=\"\">"
286  "New fact: <input type=\"text\" name=\"fact\" />"
287  "<input type=\"submit\" value=\"Assert\" /></form></p>",
288  baseurl_, env_name.c_str());
289 
290  return r;
291  } else {
292  return NULL;
293  }
294 }
Fawkes library namespace.
ClipsWebRequestProcessor(fawkes::LockPtr< fawkes::CLIPSEnvManager > &clips, fawkes::Logger *logger, const char *baseurl)
Constructor.
Mutex locking helper.
Definition: mutex_locker.h:33
virtual fawkes::WebReply * process_request(const fawkes::WebRequest *request)
Process a request.
std::string post_value(std::string &key) const
Get specific POST value.
Definition: request.h:123
virtual ~ClipsWebRequestProcessor()
Destructor.
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:262
Web request meta data carrier.
Definition: request.h:42
Basic page reply.
Definition: page_reply.h:36
Basic web reply.
Definition: reply.h:36
static long to_long(std::string s)
Convert string to a long int value.
void append_body(const char *format,...)
Append to body.
Definition: reply.cpp:224
const std::string & url() const
Get URL.
Definition: request.h:69
void add_error(const char *str)
Add an error string.
Clips web request processor.
Redirect reply for webview.
Interface for logging.
Definition: logger.h:34