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