anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
template_fileserver.cpp (7057B)
1 /* 2 * (C) 2003-2022 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 32 void increment(const TemplateFileServer::Replacements &r) 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 43 bool finished(const TemplateFileServer::Replacements &r) const 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 60 static Anope::string FindReplacement(const TemplateFileServer::Replacements &r, const Anope::string &key) 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 89 TemplateFileServer::TemplateFileServer(const Anope::string &f_n) : file_name(f_n) 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 Anope::string replacement = FindReplacement(r, content.substr(0, f - 1)); 242 243 // htmlescape all text replaced onto the page 244 replacement = HTTPUtils::Escape(replacement); 245 246 finished += replacement; 247 } 248 } 249 250 j += f; // Skip over this whole block 251 } 252 else 253 { 254 escaped = false; 255 256 // If the if stack is empty or we are in a true statement 257 bool ifok = IfStack.empty() || IfStack.top(); 258 bool forok = ForLoop::Stack.empty() || !ForLoop::Stack.back().finished(r); 259 260 if (ifok && forok) 261 finished += buf[j]; 262 } 263 } 264 265 if (!finished.empty()) 266 reply.Write(finished); 267 }