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 }