Anope IRC Services  Version 2.0
template_fileserver.cpp
Go to the documentation of this file.
1 /*
2  * (C) 2003-2014 Anope Team
3  * Contact us at team@anope.org
4  *
5  * Please read COPYING and README for further details.
6  */
7 
8 #include "webcpanel.h"
9 #include <fstream>
10 #include <stack>
11 #include <errno.h>
12 
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 
17 struct ForLoop
18 {
19  static std::vector<ForLoop> Stack;
20 
21  size_t start; /* Index of start of this loop */
22  std::vector<Anope::string> vars; /* User defined variables */
23  typedef std::pair<TemplateFileServer::Replacements::iterator, TemplateFileServer::Replacements::iterator> range;
24  std::vector<range> ranges; /* iterator ranges for each variable */
25 
26  ForLoop(size_t s, TemplateFileServer::Replacements &r, const std::vector<Anope::string> &v, const std::vector<Anope::string> &r_names) : start(s), vars(v)
27  {
28  for (unsigned i = 0; i < r_names.size(); ++i)
29  ranges.push_back(r.equal_range(r_names[i]));
30  }
31 
33  {
34  for (unsigned i = 0; i < ranges.size(); ++i)
35  {
36  range &ra = ranges[i];
37 
38  if (ra.first != r.end() && ra.first != ra.second)
39  ++ra.first;
40  }
41  }
42 
44  {
45  for (unsigned i = 0; i < ranges.size(); ++i)
46  {
47  const range &ra = ranges[i];
48 
49  if (ra.first != r.end() && ra.first != ra.second)
50  return false;
51  }
52 
53  return true;
54  }
55 };
56 std::vector<ForLoop> ForLoop::Stack;
57 
58 std::stack<bool> IfStack;
59 
61 {
62  /* Search first through for loop stack then global replacements */
63  for (unsigned i = ForLoop::Stack.size(); i > 0; --i)
64  {
65  ForLoop &fl = ForLoop::Stack[i - 1];
66 
67  for (unsigned j = 0; j < fl.vars.size(); ++j)
68  {
69  const Anope::string &var_name = fl.vars[j];
70 
71  if (key == var_name)
72  {
73  const ForLoop::range &range = fl.ranges[j];
74 
75  if (range.first != r.end() && range.first != range.second)
76  {
77  return range.first->second;
78  }
79  }
80  }
81  }
82 
83  TemplateFileServer::Replacements::const_iterator it = r.find(key);
84  if (it != r.end())
85  return it->second;
86  return "";
87 }
88 
90 {
91 }
92 
93 void TemplateFileServer::Serve(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply, Replacements &r)
94 {
95  int fd = open((template_base + "/" + this->file_name).c_str(), O_RDONLY);
96  if (fd < 0)
97  {
98  Log(LOG_NORMAL, "httpd") << "Error serving file " << page_name << " (" << (template_base + "/" + this->file_name) << "): " << strerror(errno);
99 
100  client->SendError(HTTP_PAGE_NOT_FOUND, "Page not found");
101  return;
102  }
103 
104  Anope::string buf;
105 
106  int i;
107  char buffer[BUFSIZE];
108  while ((i = read(fd, buffer, sizeof(buffer) - 1)) > 0)
109  {
110  buffer[i] = 0;
111  buf += buffer;
112  }
113 
114  close(fd);
115 
116  Anope::string finished;
117 
118  bool escaped = false;
119  for (unsigned j = 0; j < buf.length(); ++j)
120  {
121  if (buf[j] == '\\' && j + 1 < buf.length() && (buf[j + 1] == '{' || buf[j + 1] == '}'))
122  escaped = true;
123  else if (buf[j] == '{' && !escaped)
124  {
125  size_t f = buf.substr(j).find('}');
126  if (f == Anope::string::npos)
127  break;
128  const Anope::string &content = buf.substr(j + 1, f - 1);
129 
130  if (content.find("IF ") == 0)
131  {
132  std::vector<Anope::string> tokens;
133  spacesepstream(content).GetTokens(tokens);
134 
135  if (tokens.size() == 4 && tokens[1] == "EQ")
136  {
137  Anope::string first = FindReplacement(r, tokens[2]), second = FindReplacement(r, tokens[3]);
138  if (first.empty())
139  first = tokens[2];
140  if (second.empty())
141  second = tokens[3];
142 
143  bool stackok = IfStack.empty() || IfStack.top();
144  IfStack.push(stackok && first == second);
145  }
146  else if (tokens.size() == 3 && tokens[1] == "EXISTS")
147  {
148  bool stackok = IfStack.empty() || IfStack.top();
149  IfStack.push(stackok && r.count(tokens[2]) > 0);
150  }
151  else
152  Log() << "Invalid IF in web template " << this->file_name;
153  }
154  else if (content == "ELSE")
155  {
156  if (IfStack.empty())
157  Log() << "Invalid ELSE with no stack in web template" << this->file_name;
158  else
159  {
160  bool old = IfStack.top();
161  IfStack.pop(); // Pop off previous if()
162  bool stackok = IfStack.empty() || IfStack.top();
163  IfStack.push(stackok && !old); // Push back the opposite of what was popped
164  }
165  }
166  else if (content == "END IF")
167  {
168  if (IfStack.empty())
169  Log() << "END IF with empty stack?";
170  else
171  IfStack.pop();
172  }
173  else if (content.find("FOR ") == 0)
174  {
175  std::vector<Anope::string> tokens;
176  spacesepstream(content).GetTokens(tokens);
177 
178  if (tokens.size() != 4 || tokens[2] != "IN")
179  Log() << "Invalid FOR in web template " << this->file_name;
180  else
181  {
182  std::vector<Anope::string> temp_variables, real_variables;
183  commasepstream(tokens[1]).GetTokens(temp_variables);
184  commasepstream(tokens[3]).GetTokens(real_variables);
185 
186  if (temp_variables.size() != real_variables.size())
187  Log() << "Invalid FOR in web template " << this->file_name << " variable mismatch";
188  else
189  ForLoop::Stack.push_back(ForLoop(j + f, r, temp_variables, real_variables));
190  }
191  }
192  else if (content == "END FOR")
193  {
194  if (ForLoop::Stack.empty())
195  Log() << "END FOR with empty stack?";
196  else
197  {
198  ForLoop &fl = ForLoop::Stack.back();
199  if (fl.finished(r))
200  ForLoop::Stack.pop_back();
201  else
202  {
203  fl.increment(r);
204  if (fl.finished(r))
205  ForLoop::Stack.pop_back();
206  else
207  {
208  j = fl.start; // Move pointer back to start of the loop
209  continue; // To prevent skipping over this block which doesn't exist anymore
210  }
211  }
212  }
213  }
214  else if (content.find("INCLUDE ") == 0)
215  {
216  std::vector<Anope::string> tokens;
217  spacesepstream(content).GetTokens(tokens);
218 
219  if (tokens.size() != 2)
220  Log() << "Invalid INCLUDE in web template " << this->file_name;
221  else
222  {
223  if (!finished.empty())
224  {
225  reply.Write(finished); // Write out what we have currently so we insert this files contents here
226  finished.clear();
227  }
228 
229  TemplateFileServer tfs(tokens[1]);
230  tfs.Serve(server, page_name, client, message, reply, r);
231  }
232  }
233  else
234  {
235  // If the if stack is empty or we are in a true statement
236  bool ifok = IfStack.empty() || IfStack.top();
237  bool forok = ForLoop::Stack.empty() || !ForLoop::Stack.back().finished(r);
238 
239  if (ifok && forok)
240  {
241  const Anope::string &replacement = FindReplacement(r, content.substr(0, f - 1));
242  finished += replacement;
243  }
244  }
245 
246  j += f; // Skip over this whole block
247  }
248  else
249  {
250  escaped = false;
251 
252  // If the if stack is empty or we are in a true statement
253  bool ifok = IfStack.empty() || IfStack.top();
254  bool forok = ForLoop::Stack.empty() || !ForLoop::Stack.back().finished(r);
255 
256  if (ifok && forok)
257  finished += buf[j];
258  }
259  }
260 
261  if (!finished.empty())
262  reply.Write(finished);
263 }
264 
TemplateFileServer(const Anope::string &f_n)
void Serve(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &, Replacements &)
void clear()
Definition: anope.h:187
Anope::string template_base
Definition: webcpanel.cpp:11
void GetTokens(T &token)
Definition: anope.h:587
#define BUFSIZE
Definition: services.h:18
static Anope::string FindReplacement(const TemplateFileServer::Replacements &r, const Anope::string &key)
std::stack< bool > IfStack
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
size_type length() const
Definition: anope.h:131
#define read
Definition: socket.h:8
static const size_type npos
Definition: anope.h:44
bool finished(const TemplateFileServer::Replacements &r) const
bool empty() const
Definition: anope.h:126
std::vector< range > ranges
std::pair< TemplateFileServer::Replacements::iterator, TemplateFileServer::Replacements::iterator > range
ForLoop(size_t s, TemplateFileServer::Replacements &r, const std::vector< Anope::string > &v, const std::vector< Anope::string > &r_names)
void Write(const Anope::string &message)
Definition: httpd.h:62
std::vector< Anope::string > vars
Definition: logger.h:53
static std::vector< ForLoop > Stack
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
void increment(const TemplateFileServer::Replacements &r)
virtual void SendError(HTTPError err, const Anope::string &msg)=0