]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_httpd.cpp
b453a28056d0f0e9489ee3225115117f7b41f51c
[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         HttpState InternalState;
39         std::string ip;
40
41         HTTPHeaders headers;
42         std::string reqbuffer;
43         std::string postdata;
44         unsigned int postsize;
45         std::string request_type;
46         std::string uri;
47         std::string http_version;
48
49  public:
50
51         HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
52                 : BufferedSocket(newfd), ip(IP), postsize(0)
53         {
54                 InternalState = HTTP_SERVE_WAIT_REQUEST;
55
56                 FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
57                 if (GetIOHook())
58                         GetIOHook()->OnStreamSocketAccept(this, client, server);
59         }
60
61         virtual void OnError(BufferedSocketError)
62         {
63                 ServerInstance->GlobalCulls.AddItem(this);
64         }
65
66         std::string Response(int response)
67         {
68                 switch (response)
69                 {
70                         case 100:
71                                 return "CONTINUE";
72                         case 101:
73                                 return "SWITCHING PROTOCOLS";
74                         case 200:
75                                 return "OK";
76                         case 201:
77                                 return "CREATED";
78                         case 202:
79                                 return "ACCEPTED";
80                         case 203:
81                                 return "NON-AUTHORITATIVE INFORMATION";
82                         case 204:
83                                 return "NO CONTENT";
84                         case 205:
85                                 return "RESET CONTENT";
86                         case 206:
87                                 return "PARTIAL CONTENT";
88                         case 300:
89                                 return "MULTIPLE CHOICES";
90                         case 301:
91                                 return "MOVED PERMENANTLY";
92                         case 302:
93                                 return "FOUND";
94                         case 303:
95                                 return "SEE OTHER";
96                         case 304:
97                                 return "NOT MODIFIED";
98                         case 305:
99                                 return "USE PROXY";
100                         case 307:
101                                 return "TEMPORARY REDIRECT";
102                         case 400:
103                                 return "BAD REQUEST";
104                         case 401:
105                                 return "UNAUTHORIZED";
106                         case 402:
107                                 return "PAYMENT REQUIRED";
108                         case 403:
109                                 return "FORBIDDEN";
110                         case 404:
111                                 return "NOT FOUND";
112                         case 405:
113                                 return "METHOD NOT ALLOWED";
114                         case 406:
115                                 return "NOT ACCEPTABLE";
116                         case 407:
117                                 return "PROXY AUTHENTICATION REQUIRED";
118                         case 408:
119                                 return "REQUEST TIMEOUT";
120                         case 409:
121                                 return "CONFLICT";
122                         case 410:
123                                 return "GONE";
124                         case 411:
125                                 return "LENGTH REQUIRED";
126                         case 412:
127                                 return "PRECONDITION FAILED";
128                         case 413:
129                                 return "REQUEST ENTITY TOO LARGE";
130                         case 414:
131                                 return "REQUEST-URI TOO LONG";
132                         case 415:
133                                 return "UNSUPPORTED MEDIA TYPE";
134                         case 416:
135                                 return "REQUESTED RANGE NOT SATISFIABLE";
136                         case 417:
137                                 return "EXPECTATION FAILED";
138                         case 500:
139                                 return "INTERNAL SERVER ERROR";
140                         case 501:
141                                 return "NOT IMPLEMENTED";
142                         case 502:
143                                 return "BAD GATEWAY";
144                         case 503:
145                                 return "SERVICE UNAVAILABLE";
146                         case 504:
147                                 return "GATEWAY TIMEOUT";
148                         case 505:
149                                 return "HTTP VERSION NOT SUPPORTED";
150                         default:
151                                 return "WTF";
152                         break;
153
154                 }
155         }
156
157         void SendHTTPError(int response)
158         {
159                 HTTPHeaders empty;
160                 std::string data = "<html><head></head><body>Server error "+ConvToStr(response)+": "+Response(response)+"<br>"+
161                                    "<small>Powered by <a href='http://www.inspircd.org'>InspIRCd</a></small></body></html>";
162
163                 SendHeaders(data.length(), response, empty);
164                 WriteData(data);
165         }
166
167         void SendHeaders(unsigned long size, int response, HTTPHeaders &rheaders)
168         {
169
170                 WriteData(http_version + " "+ConvToStr(response)+" "+Response(response)+"\r\n");
171
172                 time_t local = ServerInstance->Time();
173                 struct tm *timeinfo = gmtime(&local);
174                 char *date = asctime(timeinfo);
175                 date[strlen(date) - 1] = '\0';
176                 rheaders.CreateHeader("Date", date);
177
178                 rheaders.CreateHeader("Server", "InspIRCd/m_httpd.so/1.2");
179                 rheaders.SetHeader("Content-Length", ConvToStr(size));
180
181                 if (size)
182                         rheaders.CreateHeader("Content-Type", "text/html");
183                 else
184                         rheaders.RemoveHeader("Content-Type");
185
186                 /* Supporting Connection: keep-alive causes a whole world of hurt syncronizing timeouts,
187                  * so remove it, its not essential for what we need.
188                  */
189                 rheaders.SetHeader("Connection", "Close");
190
191                 WriteData(rheaders.GetFormattedHeaders());
192                 WriteData("\r\n");
193         }
194
195         void OnDataReady()
196         {
197                 if (InternalState == HTTP_SERVE_RECV_POSTDATA)
198                 {
199                         postdata.append(recvq);
200                         if (postdata.length() >= postsize)
201                                 ServeData();
202                 }
203                 else
204                 {
205                         reqbuffer.append(recvq);
206
207                         if (reqbuffer.length() >= 8192)
208                         {
209                                 ServerInstance->Logs->Log("m_httpd",DEBUG, "m_httpd dropped connection due to an oversized request buffer");
210                                 reqbuffer.clear();
211                                 SetError("Buffer");
212                         }
213
214                         if (InternalState == HTTP_SERVE_WAIT_REQUEST)
215                                 CheckRequestBuffer();
216                 }
217         }
218
219         void CheckRequestBuffer()
220         {
221                 std::string::size_type reqend = reqbuffer.find("\r\n\r\n");
222                 if (reqend == std::string::npos)
223                         return;
224
225                 // We have the headers; parse them all
226                 std::string::size_type hbegin = 0, hend;
227                 while ((hend = reqbuffer.find("\r\n", hbegin)) != std::string::npos)
228                 {
229                         if (hbegin == hend)
230                                 break;
231
232                         if (request_type.empty())
233                         {
234                                 std::istringstream cheader(std::string(reqbuffer, hbegin, hend - hbegin));
235                                 cheader >> request_type;
236                                 cheader >> uri;
237                                 cheader >> http_version;
238
239                                 if (request_type.empty() || uri.empty() || http_version.empty())
240                                 {
241                                         SendHTTPError(400);
242                                         return;
243                                 }
244
245                                 hbegin = hend + 2;
246                                 continue;
247                         }
248
249                         std::string cheader = reqbuffer.substr(hbegin, hend - hbegin);
250
251                         std::string::size_type fieldsep = cheader.find(':');
252                         if ((fieldsep == std::string::npos) || (fieldsep == 0) || (fieldsep == cheader.length() - 1))
253                         {
254                                 SendHTTPError(400);
255                                 return;
256                         }
257
258                         headers.SetHeader(cheader.substr(0, fieldsep), cheader.substr(fieldsep + 2));
259
260                         hbegin = hend + 2;
261                 }
262
263                 reqbuffer.erase(0, reqend + 4);
264
265                 std::transform(request_type.begin(), request_type.end(), request_type.begin(), ::toupper);
266                 std::transform(http_version.begin(), http_version.end(), http_version.begin(), ::toupper);
267
268                 if ((http_version != "HTTP/1.1") && (http_version != "HTTP/1.0"))
269                 {
270                         SendHTTPError(505);
271                         return;
272                 }
273
274                 if (headers.IsSet("Content-Length") && (postsize = atoi(headers.GetHeader("Content-Length").c_str())) != 0)
275                 {
276                         InternalState = HTTP_SERVE_RECV_POSTDATA;
277
278                         if (reqbuffer.length() >= postsize)
279                         {
280                                 postdata = reqbuffer.substr(0, postsize);
281                                 reqbuffer.erase(0, postsize);
282                         }
283                         else if (!reqbuffer.empty())
284                         {
285                                 postdata = reqbuffer;
286                                 reqbuffer.clear();
287                         }
288
289                         if (postdata.length() >= postsize)
290                                 ServeData();
291
292                         return;
293                 }
294
295                 ServeData();
296         }
297
298         void ServeData()
299         {
300                 InternalState = HTTP_SERVE_SEND_DATA;
301
302                 claimed = false;
303                 HTTPRequest acl((Module*)HttpModule, "httpd_acl", request_type, uri, &headers, this, ip, postdata);
304                 acl.Send();
305                 if (!claimed)
306                 {
307                         HTTPRequest url((Module*)HttpModule, "httpd_url", request_type, uri, &headers, this, ip, postdata);
308                         url.Send();
309                         if (!claimed)
310                         {
311                                 SendHTTPError(404);
312                         }
313                 }
314         }
315
316         void Page(std::stringstream* n, int response, HTTPHeaders *hheaders)
317         {
318                 SendHeaders(n->str().length(), response, *hheaders);
319                 WriteData(n->str());
320         }
321 };
322
323 class ModuleHttpServer : public Module
324 {
325         std::vector<HttpServerSocket *> httpsocks;
326  public:
327
328         ModuleHttpServer()      {
329                 HttpModule = this;
330                 ServerInstance->Modules->Attach(I_OnAcceptConnection, this);
331         }
332
333         void OnRequest(Request& request)
334         {
335                 if (strcmp(request.id, "HTTP-DOC") != 0)
336                         return;
337                 HTTPDocumentResponse& resp = static_cast<HTTPDocumentResponse&>(request);
338                 claimed = true;
339                 resp.src.sock->Page(resp.document, resp.responsecode, &resp.headers);
340         }
341
342         ModResult OnAcceptConnection(int nfd, ListenSocket* from, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
343         {
344                 if (from->bind_tag->getString("type") != "httpd")
345                         return MOD_RES_PASSTHRU;
346                 int port;
347                 std::string incomingip;
348                 irc::sockets::satoap(*client, incomingip, port);
349                 new HttpServerSocket(nfd, incomingip, from, client, server);
350                 return MOD_RES_ALLOW;
351         }
352
353
354         virtual ~ModuleHttpServer()
355         {
356                 for (size_t i = 0; i < httpsocks.size(); i++)
357                 {
358                         httpsocks[i]->cull();
359                         delete httpsocks[i];
360                 }
361         }
362
363         virtual Version GetVersion()
364         {
365                 return Version("Provides HTTP serving facilities to modules", VF_VENDOR);
366         }
367 };
368
369 MODULE_INIT(ModuleHttpServer)