]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
2a430d9674fb9669825604d91db5adf6355b6448
[user/henk/code/inspircd.git] / src / modules / m_httpd.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2007-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
7  *   Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8  *   Copyright (C) 2007 John Brooks <john.brooks@dereferenced.net>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #include "inspircd.h"
26 #include "httpd.h"
27
28 /* $ModDesc: Provides HTTP serving facilities to modules */
29 /* $ModDep: httpd.h */
30
31 class ModuleHttpServer;
32
33 static ModuleHttpServer* HttpModule;
34 static bool claimed;
35 static std::set<HttpServerSocket*> sockets;
36
37 /** HTTP socket states
38  */
39 enum HttpState
40 {
41         HTTP_SERVE_WAIT_REQUEST = 0, /* Waiting for a full request */
42         HTTP_SERVE_RECV_POSTDATA = 1, /* Waiting to finish recieving POST data */
43         HTTP_SERVE_SEND_DATA = 2 /* Sending response */
44 };
45
46 /** A socket used for HTTP transport
47  */
48 class HttpServerSocket : public BufferedSocket
49 {
50         HttpState InternalState;
51         std::string ip;
52
53         HTTPHeaders headers;
54         std::string reqbuffer;
55         std::string postdata;
56         unsigned int postsize;
57         std::string request_type;
58         std::string uri;
59         std::string http_version;
60
61  public:
62         const time_t createtime;
63
64         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
65                 : BufferedSocket(newfd), ip(IP), postsize(0)
66                 , createtime(ServerInstance->Time())
67         {
68                 InternalState = HTTP_SERVE_WAIT_REQUEST;
69
70                 FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
71                 if (GetIOHook())
72                         GetIOHook()->OnStreamSocketAccept(this, client, server);
73         }
74
75         ~HttpServerSocket()
76         {
77                 sockets.erase(this);
78         }
79
80         virtual void OnError(BufferedSocketError)
81         {
82                 ServerInstance->GlobalCulls.AddItem(this);
83         }
84
85         std::string Response(int response)
86         {
87                 switch (response)
88                 {
89                         case 100:
90                                 return "CONTINUE";
91                         case 101:
92                                 return "SWITCHING PROTOCOLS";
93                         case 200:
94                                 return "OK";
95                         case 201:
96                                 return "CREATED";
97                         case 202:
98                                 return "ACCEPTED";
99                         case 203:
100                                 return "NON-AUTHORITATIVE INFORMATION";
101                         case 204:
102                                 return "NO CONTENT";
103                         case 205:
104                                 return "RESET CONTENT";
105                         case 206:
106                                 return "PARTIAL CONTENT";
107                         case 300:
108                                 return "MULTIPLE CHOICES";
109                         case 301:
110                                 return "MOVED PERMANENTLY";
111                         case 302:
112                                 return "FOUND";
113                         case 303:
114                                 return "SEE OTHER";
115                         case 304:
116                                 return "NOT MODIFIED";
117                         case 305:
118                                 return "USE PROXY";
119                         case 307:
120                                 return "TEMPORARY REDIRECT";
121                         case 400:
122                                 return "BAD REQUEST";
123                         case 401:
124                                 return "UNAUTHORIZED";
125                         case 402:
126                                 return "PAYMENT REQUIRED";
127                         case 403:
128                                 return "FORBIDDEN";
129                         case 404:
130                                 return "NOT FOUND";
131                         case 405:
132                                 return "METHOD NOT ALLOWED";
133                         case 406:
134                                 return "NOT ACCEPTABLE";
135                         case 407:
136                                 return "PROXY AUTHENTICATION REQUIRED";
137                         case 408:
138                                 return "REQUEST TIMEOUT";
139                         case 409:
140                                 return "CONFLICT";
141                         case 410:
142                                 return "GONE";
143                         case 411:
144                                 return "LENGTH REQUIRED";
145                         case 412:
146                                 return "PRECONDITION FAILED";
147                         case 413:
148                                 return "REQUEST ENTITY TOO LARGE";
149                         case 414:
150                                 return "REQUEST-URI TOO LONG";
151                         case 415:
152                                 return "UNSUPPORTED MEDIA TYPE";
153                         case 416:
154                                 return "REQUESTED RANGE NOT SATISFIABLE";
155                         case 417:
156                                 return "EXPECTATION FAILED";
157                         case 500:
158                                 return "INTERNAL SERVER ERROR";
159                         case 501:
160                                 return "NOT IMPLEMENTED";
161                         case 502:
162                                 return "BAD GATEWAY";
163                         case 503:
164                                 return "SERVICE UNAVAILABLE";
165                         case 504:
166                                 return "GATEWAY TIMEOUT";
167                         case 505:
168                                 return "HTTP VERSION NOT SUPPORTED";
169                         default:
170                                 return "WTF";
171                         break;
172
173                 }
174         }
175
176         void SendHTTPError(int response)
177         {
178                 HTTPHeaders empty;
179                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
180                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
181
182                 SendHeaders(data.length(), response, empty);
183                 WriteData(data);
184         }
185
186         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
187         {
188
189                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
190
191                 time_t local = ServerInstance->Time();
192                 struct tm *timeinfo = gmtime(&local);
193                 char *date = asctime(timeinfo);
194                 date[strlen(date) - 1] = '\0';
195                 rheaders.CreateHeader("Date", date);
196
197                 rheaders.CreateHeader("Server", BRANCH);
198                 rheaders.SetHeader("Content-Length", ConvToStr(size));
199
200                 if (size)
201                         rheaders.CreateHeader("Content-Type", "text/html");
202                 else
203                         rheaders.RemoveHeader("Content-Type");
204
205                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
206                  * so remove it, its not essential for what we need.
207                  */
208                 rheaders.SetHeader("Connection", "Close");
209
210                 WriteData(rheaders.GetFormattedHeaders());
211                 WriteData("\r\n");
212         }
213
214         void OnDataReady()
215         {
216                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
217                 {
218                         postdata.append(recvq);
219                         if (postdata.length() >= postsize)
220                                 ServeData();
221                 }
222                 else
223                 {
224                         reqbuffer.append(recvq);
225
226                         if (reqbuffer.length() >= 8192)
227                         {
228                                 ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
229                                 reqbuffer.clear();
230                                 SetError("Buffer");
231                         }
232
233                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
234                                 CheckRequestBuffer();
235                 }
236         }
237
238         void CheckRequestBuffer()
239         {
240                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
241                 if (reqend == std::string::npos)
242                         return;
243
244                 // We have the headers; parse them all
245                 std::string::size_type hbegin = 0, hend;
246                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
247                 {
248                         if (hbegin == hend)
249                                 break;
250
251                         if (request_type.empty())
252                         {
253                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
254                                 cheader >> request_type;
255                                 cheader >> uri;
256                                 cheader >> http_version;
257
258                                 if (request_type.empty() || uri.empty() || http_version.empty())
259                                 {
260                                         SendHTTPError(400);
261                                         return;
262                                 }
263
264                                 hbegin = hend + 2;
265                                 continue;
266                         }
267
268                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
269
270                         std::string::size_type fieldsep = cheader.find(':');
271                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
272                         {
273                                 SendHTTPError(400);
274                                 return;
275                         }
276
277                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
278
279                         hbegin = hend + 2;
280                 }
281
282                 reqbuffer.erase(0, reqend + 4);
283
284                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
285                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
286
287                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
288                 {
289                         SendHTTPError(505);
290                         return;
291                 }
292
293                 if (headers.IsSet("Content-Length") && (postsize = ConvToInt(headers.GetHeader("Content-Length"))) > 0)
294                 {
295                         InternalState = HTTP_SERVE_RECV_POSTDATA;
296
297                         if (reqbuffer.length() >= postsize)
298                         {
299                                 postdata = reqbuffer.substr(0, postsize);
300                                 reqbuffer.erase(0, postsize);
301                         }
302                         else if (!reqbuffer.empty())
303                         {
304                                 postdata = reqbuffer;
305                                 reqbuffer.clear();
306                         }
307
308                         if (postdata.length() >= postsize)
309                                 ServeData();
310
311                         return;
312                 }
313
314                 ServeData();
315         }
316
317         void ServeData()
318         {
319                 InternalState = HTTP_SERVE_SEND_DATA;
320
321                 claimed = false;
322                 HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
323                 acl.Send();
324                 if (!claimed)
325                 {
326                         HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
327                         url.Send();
328                         if (!claimed)
329                         {
330                                 SendHTTPError(404);
331                         }
332                 }
333         }
334
335         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
336         {
337                 SendHeaders(n->str().length(), response, *hheaders);
338                 WriteData(n->str());
339         }
340 };
341
342 class ModuleHttpServer : public Module
343 {
344         unsigned int timeoutsec;
345
346  public:
347
348         void init()
349         {
350                 HttpModule = this;
351                 Implementation eventlist[] = { I_OnAcceptConnection, I_OnBackgroundTimer, I_OnRehash };
352                 ServerInstance->Modules->Attach(eventlist, this, sizeof(eventlist)/sizeof(Implementation));
353                 OnRehash(NULL);
354         }
355
356         void OnRehash(User* user)
357         {
358                 ConfigTag* tag = ServerInstance->Config->ConfValue("httpd");
359                 timeoutsec = tag->getInt("timeout");
360         }
361
362         void OnRequest(Request& request)
363         {
364                 if (strcmp(request.id, "HTTP-DOC") != 0)
365                         return;
366                 HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
367                 claimed = true;
368                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
369         }
370
371         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
372         {
373                 if (from->bind_tag->getString("type") != "httpd")
374                         return MOD_RES_PASSTHRU;
375                 int port;
376                 std::string incomingip;
377                 irc::sockets::satoap(*client, incomingip, port);
378                 sockets.insert(new HttpServerSocket(nfd, incomingip, from, client, server));
379                 return MOD_RES_ALLOW;
380         }
381
382         void OnBackgroundTimer(time_t curtime)
383         {
384                 if (!timeoutsec)
385                         return;
386
387                 time_t oldest_allowed = curtime - timeoutsec;
388                 for (std::set<HttpServerSocket*>::const_iterator i = sockets.begin(); i != sockets.end(); )
389                 {
390                         HttpServerSocket* sock = *i;
391                         ++i;
392                         if (sock->createtime < oldest_allowed)
393                         {
394                                 sock->cull();
395                                 delete sock;
396                         }
397                 }
398         }
399
400         CullResult cull()
401         {
402                 std::set<HttpServerSocket*> local;
403                 local.swap(sockets);
404                 for (std::set<HttpServerSocket*>::const_iterator i = local.begin(); i != local.end(); ++i)
405                 {
406                         HttpServerSocket* sock = *i;
407                         sock->cull();
408                         delete sock;
409                 }
410                 return Module::cull();
411         }
412
413         virtual Version GetVersion()
414         {
415                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
416         }
417 };
418
419 MODULE_INIT(ModuleHttpServer)