]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
88b635f6f3008e08a959fec93554e6b38500aabc
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include "httpd.h"
16
17 /* $ModDesc: Provides HTTP serving facilities to modules */
18 /* $ModDep: httpd.h */
19
20 class ModuleHttpServer;
21
22 static ModuleHttpServer* HttpModule;
23 static bool claimed;
24
25 /** HTTP socket states
26  */
27 enum HttpState
28 {
29         HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
30         HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
31         HTTP_SERVE_SEND_DATA = 2 /* Sending response */
32 };
33
34 /** A socket used for HTTP transport
35  */
36 class HttpServerSocket : public BufferedSocket
37 {
38         FileReader* index;
39         HttpState InternalState;
40         std::string ip;
41
42         HTTPHeaders headers;
43         std::string reqbuffer;
44         std::string postdata;
45         unsigned int postsize;
46         std::string request_type;
47         std::string uri;
48         std::string http_version;
49
50  public:
51
52         HttpServerSocket(int newfd, const char* IP, FileReader* ind)
53                 : BufferedSocket(newfd), index(ind), ip(IP), postsize(0)
54         {
55                 InternalState = HTTP_SERVE_WAIT_REQUEST;
56         }
57
58         FileReader* GetIndex()
59         {
60                 return index;
61         }
62
63         ~HttpServerSocket()
64         {
65         }
66
67         virtual void OnError(BufferedSocketError)
68         {
69         }
70
71         std::string Response(int response)
72         {
73                 switch (response)
74                 {
75                         case 100:
76                                 return "CONTINUE";
77                         case 101:
78                                 return "SWITCHING PROTOCOLS";
79                         case 200:
80                                 return "OK";
81                         case 201:
82                                 return "CREATED";
83                         case 202:
84                                 return "ACCEPTED";
85                         case 203:
86                                 return "NON-AUTHORITATIVE INFORMATION";
87                         case 204:
88                                 return "NO CONTENT";
89                         case 205:
90                                 return "RESET CONTENT";
91                         case 206:
92                                 return "PARTIAL CONTENT";
93                         case 300:
94                                 return "MULTIPLE CHOICES";
95                         case 301:
96                                 return "MOVED PERMENANTLY";
97                         case 302:
98                                 return "FOUND";
99                         case 303:
100                                 return "SEE OTHER";
101                         case 304:
102                                 return "NOT MODIFIED";
103                         case 305:
104                                 return "USE PROXY";
105                         case 307:
106                                 return "TEMPORARY REDIRECT";
107                         case 400:
108                                 return "BAD REQUEST";
109                         case 401:
110                                 return "UNAUTHORIZED";
111                         case 402:
112                                 return "PAYMENT REQUIRED";
113                         case 403:
114                                 return "FORBIDDEN";
115                         case 404:
116                                 return "NOT FOUND";
117                         case 405:
118                                 return "METHOD NOT ALLOWED";
119                         case 406:
120                                 return "NOT ACCEPTABLE";
121                         case 407:
122                                 return "PROXY AUTHENTICATION REQUIRED";
123                         case 408:
124                                 return "REQUEST TIMEOUT";
125                         case 409:
126                                 return "CONFLICT";
127                         case 410:
128                                 return "GONE";
129                         case 411:
130                                 return "LENGTH REQUIRED";
131                         case 412:
132                                 return "PRECONDITION FAILED";
133                         case 413:
134                                 return "REQUEST ENTITY TOO LARGE";
135                         case 414:
136                                 return "REQUEST-URI TOO LONG";
137                         case 415:
138                                 return "UNSUPPORTED MEDIA TYPE";
139                         case 416:
140                                 return "REQUESTED RANGE NOT SATISFIABLE";
141                         case 417:
142                                 return "EXPECTATION FAILED";
143                         case 500:
144                                 return "INTERNAL SERVER ERROR";
145                         case 501:
146                                 return "NOT IMPLEMENTED";
147                         case 502:
148                                 return "BAD GATEWAY";
149                         case 503:
150                                 return "SERVICE UNAVAILABLE";
151                         case 504:
152                                 return "GATEWAY TIMEOUT";
153                         case 505:
154                                 return "HTTP VERSION NOT SUPPORTED";
155                         default:
156                                 return "WTF";
157                         break;
158
159                 }
160         }
161
162         void SendHTTPError(int response)
163         {
164                 HTTPHeaders empty;
165                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
166                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
167
168                 SendHeaders(data.length(), response, empty);
169                 WriteData(data);
170         }
171
172         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
173         {
174
175                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
176
177                 time_t local = ServerInstance->Time();
178                 struct tm *timeinfo = gmtime(&local);
179                 char *date = asctime(timeinfo);
180                 date[strlen(date) - 1] = '\0';
181                 rheaders.CreateHeader("Date", date);
182
183                 rheaders.CreateHeader("Server", "InspIRCd/m_httpd.so/1.2");
184                 rheaders.SetHeader("Content-Length", ConvToStr(size));
185
186                 if (size)
187                         rheaders.CreateHeader("Content-Type", "text/html");
188                 else
189                         rheaders.RemoveHeader("Content-Type");
190
191                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
192                  * so remove it, its not essential for what we need.
193                  */
194                 rheaders.SetHeader("Connection", "Close");
195
196                 WriteData(rheaders.GetFormattedHeaders());
197                 WriteData("\r\n");
198         }
199
200         void OnDataReady()
201         {
202                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
203                 {
204                         postdata.append(recvq);
205                         if (postdata.length() >= postsize)
206                                 ServeData();
207                 }
208                 else
209                 {
210                         reqbuffer.append(recvq);
211
212                         if (reqbuffer.length() >= 8192)
213                         {
214                                 ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
215                                 reqbuffer.clear();
216                                 SetError("Buffer");
217                         }
218
219                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
220                                 CheckRequestBuffer();
221                 }
222         }
223
224         void CheckRequestBuffer()
225         {
226                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
227                 if (reqend == std::string::npos)
228                         return;
229
230                 // We have the headers; parse them all
231                 std::string::size_type hbegin = 0, hend;
232                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
233                 {
234                         if (hbegin == hend)
235                                 break;
236
237                         if (request_type.empty())
238                         {
239                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
240                                 cheader >> request_type;
241                                 cheader >> uri;
242                                 cheader >> http_version;
243
244                                 if (request_type.empty() || uri.empty() || http_version.empty())
245                                 {
246                                         SendHTTPError(400);
247                                         return;
248                                 }
249
250                                 hbegin = hend + 2;
251                                 continue;
252                         }
253
254                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
255
256                         std::string::size_type fieldsep = cheader.find(':');
257                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
258                         {
259                                 SendHTTPError(400);
260                                 return;
261                         }
262
263                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
264
265                         hbegin = hend + 2;
266                 }
267
268                 reqbuffer.erase(0, reqend + 4);
269
270                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
271                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
272
273                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
274                 {
275                         SendHTTPError(505);
276                         return;
277                 }
278
279                 if (headers.IsSet("Content-Length") && (postsize = atoi(headers.GetHeader("Content-Length").c_str())) != 0)
280                 {
281                         InternalState = HTTP_SERVE_RECV_POSTDATA;
282
283                         if (reqbuffer.length() >= postsize)
284                         {
285                                 postdata = reqbuffer.substr(0, postsize);
286                                 reqbuffer.erase(0, postsize);
287                         }
288                         else if (!reqbuffer.empty())
289                         {
290                                 postdata = reqbuffer;
291                                 reqbuffer.clear();
292                         }
293
294                         if (postdata.length() >= postsize)
295                                 ServeData();
296
297                         return;
298                 }
299
300                 ServeData();
301         }
302
303         void ServeData()
304         {
305                 InternalState = HTTP_SERVE_SEND_DATA;
306
307                 if ((request_type == "GET") && (uri == "/"))
308                 {
309                         HTTPHeaders empty;
310                         SendHeaders(index->ContentSize(), 200, empty);
311                         WriteData(index->Contents());
312                 }
313                 else
314                 {
315                         claimed = false;
316                         HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
317                         acl.Send();
318                         if (!claimed)
319                         {
320                                 HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
321                                 url.Send();
322                                 if (!claimed)
323                                 {
324                                         SendHTTPError(404);
325                                 }
326                         }
327                 }
328         }
329
330         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
331         {
332                 SendHeaders(n->str().length(), response, *hheaders);
333                 WriteData(n->str());
334         }
335 };
336
337 /** Spawn HTTP sockets from a listener
338  */
339 class HttpListener : public ListenSocketBase
340 {
341         FileReader* index;
342
343  public:
344         HttpListener(FileReader *idx, int port, const std::string &addr)
345                 : ListenSocketBase(port, addr, "httpd", "plaintext")
346         {
347                 this->index = idx;
348         }
349
350         virtual void OnAcceptReady(int nfd)
351         {
352                 int port;
353                 std::string incomingip;
354                 irc::sockets::satoap(&client, incomingip, port);
355                 new HttpServerSocket(nfd, incomingip.c_str(), index);
356         }
357 };
358
359 class ModuleHttpServer : public Module
360 {
361         std::vector<HttpServerSocket *> httpsocks;
362         std::vector<HttpListener *> httplisteners;
363  public:
364
365         void ReadConfig()
366         {
367                 int port;
368                 std::string host;
369                 std::string bindip;
370                 std::string indexfile;
371                 FileReader* index;
372                 HttpListener *http;
373                 ConfigReader c;
374
375                 httpsocks.clear(); // XXX this will BREAK if this module is made rehashable
376                 httplisteners.clear();
377
378                 for (int i = 0; i < c.Enumerate("http"); i++)
379                 {
380                         host = c.ReadValue("http", "host", i);
381                         bindip = c.ReadValue("http", "ip", i);
382                         port = c.ReadInteger("http", "port", i, true);
383                         indexfile = c.ReadValue("http", "index", i);
384                         index = new FileReader(indexfile);
385                         if (!index->Exists())
386                                 throw ModuleException("Can't read index file: "+indexfile);
387                         http = new HttpListener(index, port, bindip);
388                         httplisteners.push_back(http);
389                 }
390         }
391
392         ModuleHttpServer()      {
393                 ReadConfig();
394                 HttpModule = this;
395         }
396
397         void OnRequest(Request& request)
398         {
399                 if (strcmp(request.id, "HTTP-DOC") != 0)
400                         return;
401                 HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
402                 claimed = true;
403                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
404         }
405
406
407         virtual ~ModuleHttpServer()
408         {
409                 for (size_t i = 0; i < httplisteners.size(); i++)
410                 {
411                         delete httplisteners[i];
412                 }
413
414                 for (size_t i = 0; i < httpsocks.size(); i++)
415                 {
416                         ServerInstance->SE->DelFd(httpsocks[i]);
417                         httpsocks[i]->Close();
418                         delete httpsocks[i]->GetIndex();
419                 }
420         }
421
422         virtual Version GetVersion()
423         {
424                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR | VF_SERVICEPROVIDER, API_VERSION);
425         }
426 };
427
428 MODULE_INIT(ModuleHttpServer)