1 /*
2  *
3  * (C) 2012-2020 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  */
8 
9 #ifndef ANOPE_HTTPD_H
10 #define ANOPE_HTTPD_H
11 
12 enum HTTPError
13 {
14 	HTTP_ERROR_OK = 200,
15 	HTTP_FOUND = 302,
16 	HTTP_BAD_REQUEST = 400,
17 	HTTP_PAGE_NOT_FOUND = 404,
18 	HTTP_NOT_SUPPORTED = 505
19 };
20 
21 /* A message to someone */
22 struct HTTPReply
23 {
24 	HTTPError error;
25 	Anope::string content_type;
26 	std::map<Anope::string, Anope::string, ci::less> headers;
27 	typedef std::list<std::pair<Anope::string, Anope::string> > cookie;
28 	std::vector<cookie> cookies;
29 
HTTPReplyHTTPReply30 	HTTPReply() : error(HTTP_ERROR_OK), length(0) { }
31 
HTTPReplyHTTPReply32 	HTTPReply(const HTTPReply& other) : error(other.error), length(other.length)
33 	{
34 		content_type = other.content_type;
35 		headers = other.headers;
36 		cookies = other.cookies;
37 
38 		for (unsigned i = 0; i < other.out.size(); ++i)
39 			out.push_back(new Data(other.out[i]->buf, other.out[i]->len));
40 	}
41 
~HTTPReplyHTTPReply42 	~HTTPReply()
43 	{
44 		for (unsigned i = 0; i < out.size(); ++i)
45 			delete out[i];
46 		out.clear();
47 	}
48 
49 	struct Data
50 	{
51 		char *buf;
52 		size_t len;
53 
DataHTTPReply::Data54 		Data(const char *b, size_t l)
55 		{
56 			this->buf = new char[l];
57 			memcpy(this->buf, b, l);
58 			this->len = l;
59 		}
60 
~DataHTTPReply::Data61 		~Data()
62 		{
63 			delete [] buf;
64 		}
65 	};
66 
67 	std::deque<Data *> out;
68 	size_t length;
69 
WriteHTTPReply70 	void Write(const Anope::string &message)
71 	{
72 		this->out.push_back(new Data(message.c_str(), message.length()));
73 		this->length += message.length();
74 	}
75 
WriteHTTPReply76 	void Write(const char *b, size_t l)
77 	{
78 		this->out.push_back(new Data(b, l));
79 		this->length += l;
80 	}
81 };
82 
83 /* A message from soneone */
84 struct HTTPMessage
85 {
86 	std::map<Anope::string, Anope::string> headers;
87 	std::map<Anope::string, Anope::string> cookies;
88 	std::map<Anope::string, Anope::string> get_data;
89 	std::map<Anope::string, Anope::string> post_data;
90 	Anope::string content;
91 };
92 
93 class HTTPClient;
94 class HTTPProvider;
95 
96 class HTTPPage : public Base
97 {
98 	Anope::string url;
99 	Anope::string content_type;
100 
101  public:
url(u)102 	HTTPPage(const Anope::string &u, const Anope::string &ct = "text/html") : url(u), content_type(ct) { }
103 
GetURL()104 	const Anope::string &GetURL() const { return this->url; }
105 
GetContentType()106 	const Anope::string &GetContentType() const { return this->content_type; }
107 
108 	/** Called when this page is requested
109 	 * @param The server this page is on
110 	 * @param The page name
111 	 * @param The client requesting the page
112 	 * @param The HTTP header sent from the client to request the page
113 	 * @param The HTTP header that will be sent back to the client
114 	 */
115 	virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) = 0;
116 };
117 
118 class HTTPClient : public ClientSocket, public BinarySocket, public Base
119 {
120  protected:
WriteClient(const Anope::string & message)121 	void WriteClient(const Anope::string &message)
122 	{
123 		BinarySocket::Write(message + "\r\n");
124 	}
125 
126  public:
HTTPClient(ListenSocket * l,int f,const sockaddrs & a)127 	HTTPClient(ListenSocket *l, int f, const sockaddrs &a) : ClientSocket(l, a), BinarySocket() { }
128 
GetIP()129 	virtual const Anope::string GetIP()
130 	{
131 		return this->clientaddr.addr();
132 	}
133 
134 	virtual void SendError(HTTPError err, const Anope::string &msg) = 0;
135 	virtual void SendReply(HTTPReply *) = 0;
136 };
137 
138 class HTTPProvider : public ListenSocket, public Service
139 {
140 	Anope::string ip;
141 	unsigned short port;
142 	bool ssl;
143  public:
144 	std::vector<Anope::string> ext_ips;
145 	std::vector<Anope::string> ext_headers;
146 
HTTPProvider(Module * c,const Anope::string & n,const Anope::string & i,const unsigned short p,bool s)147 	HTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, bool s) : ListenSocket(i, p, i.find(':') != Anope::string::npos), Service(c, "HTTPProvider", n), ip(i), port(p), ssl(s) { }
148 
GetIP()149 	const Anope::string &GetIP() const
150 	{
151 		return this->ip;
152 	}
153 
GetPort()154 	unsigned short GetPort() const
155 	{
156 		return this->port;
157 	}
158 
IsSSL()159 	bool IsSSL() const
160 	{
161 		return this->ssl;
162 	}
163 
164 	virtual bool RegisterPage(HTTPPage *page) = 0;
165 	virtual void UnregisterPage(HTTPPage *page) = 0;
166 	virtual HTTPPage* FindPage(const Anope::string &name) = 0;
167 };
168 
169 namespace HTTPUtils
170 {
URLDecode(const Anope::string & url)171 	inline Anope::string URLDecode(const Anope::string &url)
172 	{
173 		Anope::string decoded;
174 
175 		for (unsigned i = 0; i < url.length(); ++i)
176 		{
177 			const char& c = url[i];
178 
179 			if (c == '%' && i + 2 < url.length())
180 			{
181 				Anope::string dest;
182 				Anope::Unhex(url.substr(i + 1, 2), dest);
183 				decoded += dest;
184 				i += 2;
185 			}
186 			else if (c == '+')
187 				decoded += ' ';
188 			else
189 				decoded += c;
190 		}
191 
192 		return decoded;
193 	}
194 
URLEncode(const Anope::string & url)195 	inline Anope::string URLEncode(const Anope::string &url)
196 	{
197 		Anope::string encoded;
198 
199 		for (unsigned i = 0; i < url.length(); ++i)
200 		{
201 			const char& c = url[i];
202 
203 			if (isalnum(c) || c == '.' || c == '-' || c == '*' || c == '_')
204 				encoded += c;
205 			else if (c == ' ')
206 				encoded += '+';
207 			else
208 				encoded += "%" + Anope::Hex(c);
209 		}
210 
211 		return encoded;
212 	}
213 
Escape(const Anope::string & src)214 	inline Anope::string Escape(const Anope::string &src)
215 	{
216 		Anope::string dst;
217 
218 		for (unsigned i = 0; i < src.length(); ++i)
219 		{
220 			switch (src[i])
221 			{
222 				case '<':
223 					dst += "&lt;";
224 					break;
225 				case '>':
226 					dst += "&gt;";
227 					break;
228 				case '"':
229 					dst += "&quot;";
230 					break;
231 				case '&':
232 					dst += "&amp;";
233 					break;
234 				default:
235 					dst += src[i];
236 			}
237 		}
238 
239 		return dst;
240 	}
241 }
242 
243 #endif // ANOPE_HTTPD_H
244