ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/ermyth/modules/rpc/httpd.C
Revision: 1.8
Committed: Sun Sep 9 20:05:52 2007 UTC (19 years, 1 month ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.7: +11 -10 lines
Log Message:
- changed configurations to the c++ stdlib
- more #defines to enum
- removed getopt.h and link.h from the system as they were unused
- reworked logstreams
- added an itoa with old syntax
- made klines objects
- moved some global variables into appropriate classes
- fixed boost.foreach's compiler workaround #if's
- allow other files to add exceptions with ADD_EXCEPTION
- changed mynick_t to c++ object
- moved servers.h out of atheme.h
- corrected PING from inspircd 1.2

File Contents

# User Rev Content
1 pippijn 1.1 /*
2 pippijn 1.3 * Copyright © 2007 Pippijn van Steenhoven / The Ermyth Team
3     * Copyright © 2005-2007 Jilles Tjoelker et al.
4 pippijn 1.2 * Rights to this code are as documented in doc/pod/license.pod.
5 pippijn 1.1 *
6     * Generic HTTPd
7     *
8 pippijn 1.8 * $Id: httpd.C,v 1.7 2007-09-05 11:23:15 pippijn Exp $
9 pippijn 1.1 */
10    
11     #include "atheme.h"
12 pippijn 1.3 #include <ermyth/module.h>
13 pippijn 1.1 #include "httpd.h"
14     #include "datastream.h"
15     #include "connection.h"
16     #include "confparse.h"
17    
18     #define REQUEST_MAX 65536 /* maximum size of one call */
19    
20 pippijn 1.8 static char const rcsid[] = "$Id: httpd.C,v 1.7 2007-09-05 11:23:15 pippijn Exp $";
21 pippijn 1.1
22 pippijn 1.3 static void on_config_ready (void);
23 pippijn 1.1
24 pippijn 1.3 REGISTER_MODULE ("rpc/httpd", false, "The Ermyth Team <http://ermyth.one09.net>");
25 pippijn 1.1
26 pippijn 1.3 static connection_t *listener;
27 pippijn 1.1 pathvec pathhandlers;
28    
29     /* conf stuff */
30 pippijn 1.8 ConfTable::list_type conf_httpd_table;
31    
32 pippijn 1.3 static struct httpd_configuration
33 pippijn 1.1 {
34     char *host;
35     char *www_root;
36     int port;
37 pippijn 1.3
38     void clear ()
39     {
40     if (host != NULL)
41     sfree (host);
42     if (www_root != NULL)
43     sfree (www_root);
44     host = NULL;
45     www_root = NULL;
46     }
47 pippijn 1.1 } httpd_config;
48    
49     static int
50     conf_httpd_host (config_entry_t *ce)
51     {
52 pippijn 1.4 if (!ce->valid ())
53 pippijn 1.1 return -1;
54    
55     if (httpd_config.host != NULL)
56 pippijn 1.3 sfree (httpd_config.host);
57 pippijn 1.4 httpd_config.host = sstrdup (ce->vardata<char *> ());
58 pippijn 1.1
59     return 0;
60     }
61    
62     static int
63     conf_httpd_www_root (config_entry_t *ce)
64     {
65 pippijn 1.4 if (!ce->valid ())
66 pippijn 1.1 return -1;
67    
68     if (httpd_config.www_root != NULL)
69 pippijn 1.3 sfree (httpd_config.www_root);
70 pippijn 1.4 httpd_config.www_root = sstrdup (ce->vardata<char *> ());
71 pippijn 1.1
72     return 0;
73     }
74    
75     static int
76     conf_httpd_port (config_entry_t *ce)
77     {
78 pippijn 1.4 if (!ce->valid ())
79 pippijn 1.1 return -1;
80    
81 pippijn 1.4 httpd_config.port = ce->vardata<int> ();
82 pippijn 1.1
83     return 0;
84     }
85    
86     static int
87     conf_httpd (config_entry_t *ce)
88     {
89 pippijn 1.8 subblock_handler (ce, conf_httpd_table);
90 pippijn 1.1 return 0;
91     }
92    
93 pippijn 1.3 void
94     httpddata::clear (void)
95 pippijn 1.1 {
96 pippijn 1.3 method[0] = '\0';
97     filename[0] = '\0';
98     if (requestbuf != NULL)
99 pippijn 1.1 {
100 pippijn 1.3 sfree (requestbuf);
101     requestbuf = NULL;
102 pippijn 1.1 }
103 pippijn 1.3 if (replybuf != NULL)
104 pippijn 1.1 {
105 pippijn 1.3 sfree (replybuf);
106     replybuf = NULL;
107 pippijn 1.1 }
108 pippijn 1.3 length = 0;
109     lengthdone = 0;
110     correct_content_type = false;
111     expect_100_continue = false;
112     sent_reply = false;
113 pippijn 1.1 }
114    
115     static int
116     open_file (char const *filename)
117     {
118     char fname[256];
119    
120     if (strstr (filename, ".."))
121     return -1;
122     if (!strcmp (filename, "/"))
123     filename = "/index.html";
124     snprintf (fname, sizeof fname, "%s/%s", httpd_config.www_root, filename);
125     return open (fname, O_RDONLY);
126     }
127    
128     static void
129     process_header (connection_t *cptr, char *line)
130     {
131 pippijn 1.3 httpddata *hd;
132 pippijn 1.1 char *p;
133    
134     hd = static_cast<httpddata *> (cptr->userdata);
135     p = strchr (line, ':');
136     if (p == NULL)
137     return;
138     *p = '\0';
139     p++;
140     while (*p == ' ')
141     p++;
142     if (!strcasecmp (line, "Connection"))
143     {
144     p = strtok (p, ", \t");
145     while (p != NULL)
146     {
147     if (!strcasecmp (p, "close"))
148     {
149     slog (LG_DEBUG, "process_header(): Connection: close requested by fd %d", cptr->fd);
150     hd->connection_close = true;
151     }
152     p = strtok (NULL, ", \t");
153     }
154     }
155     else if (!strcasecmp (line, "Content-Length"))
156 pippijn 1.3 hd->length = atoi (p);
157 pippijn 1.1 else if (!strcasecmp (line, "Content-Type"))
158     {
159     p = strtok (p, "; \t");
160     hd->correct_content_type = p != NULL && (!strcasecmp (p, "text/xml") || !strcasecmp (p, "application/json"));
161     }
162     else if (!strcasecmp (line, "Expect"))
163 pippijn 1.3 hd->expect_100_continue = !strcasecmp (p, "100-continue");
164 pippijn 1.1 }
165    
166     static void
167     check_close (connection_t *cptr)
168     {
169 pippijn 1.3 httpddata *hd;
170 pippijn 1.1
171     hd = static_cast<httpddata *> (cptr->userdata);
172     if (hd->connection_close)
173     sendq_add_eof (cptr);
174     }
175    
176     static void
177 pippijn 1.3 send_error (connection_t *cptr, int errorcode, char const * const text, bool const sendentity)
178 pippijn 1.1 {
179 pippijn 1.3 httpddata *hd;
180 pippijn 1.1 char buf1[300];
181     char buf2[700];
182    
183     hd = static_cast<httpddata *> (cptr->userdata);
184     if (errorcode < 100 || errorcode > 999)
185     errorcode = 500;
186     snprintf (buf2, sizeof buf2, "HTTP/1.1 %d %s\r\n", errorcode, text);
187     snprintf (buf1, sizeof buf1, "HTTP/1.1 %d %s\r\n"
188     "Server: " PACKAGE_NAME "/%s\r\n"
189     "Content-Type: text/plain\r\n"
190 pippijn 1.5 "Content-Length: %" PRIsize "\r\n\r\n%s",
191 pippijn 1.1 errorcode, text, version, strlen (buf2), sendentity ? buf2 : "");
192     sendq_add (cptr, buf1, strlen (buf1));
193     }
194    
195 pippijn 1.3 static char const * const
196     content_type (char const * const filename)
197 pippijn 1.1 {
198     char const *p;
199    
200     if (!strcmp (filename, "/"))
201     return "text/html";
202    
203     p = strrchr (filename, '.');
204     if (p == NULL)
205     return "text/plain";
206     p++;
207     if (!strcasecmp (p, "html") || !strcasecmp (p, "htm"))
208     return "text/html";
209     else if (!strcasecmp (p, "txt"))
210     return "text/plain";
211     else if (!strcasecmp (p, "jpg") || !strcasecmp (p, "jpeg"))
212     return "image/jpeg";
213     else if (!strcasecmp (p, "gif"))
214     return "image/gif";
215     else if (!strcasecmp (p, "png"))
216     return "image/png";
217     return "application/octet-stream";
218     }
219    
220     static void
221     httpd_recvqhandler (connection_t *cptr)
222     {
223     char buf[BUFSIZE * 2];
224     char outbuf[BUFSIZE * 2];
225     int count;
226 pippijn 1.3 httpddata *hd;
227 pippijn 1.1 char *p;
228     int in;
229     struct stat sb;
230     off_t count1;
231 pippijn 1.3 path_handler_t ph;
232     bool is_get, is_post, handling_done = false;
233 pippijn 1.1 pathvec::iterator found, pvend = pathhandlers.end ();
234    
235     hd = static_cast<httpddata *> (cptr->userdata);
236    
237     if ((found = (std::find_if (pathhandlers.begin (), pvend, pathhandler_eq (hd->filename))))
238     != pvend)
239     {
240     handling_done = true;
241     ph = *found;
242     }
243    
244     if (handling_done == true)
245     {
246     if (hd->requestbuf != NULL)
247     {
248     count = recvq_get (cptr, hd->requestbuf + hd->lengthdone, hd->length - hd->lengthdone);
249     if (count <= 0)
250     return;
251     hd->lengthdone += count;
252     if (hd->lengthdone != hd->length)
253     return;
254     hd->requestbuf[hd->length] = '\0';
255    
256 pippijn 1.3 ph.handler (cptr, hd->requestbuf);
257 pippijn 1.1
258 pippijn 1.3 hd->clear ();
259 pippijn 1.1 return;
260     }
261     }
262    
263     count = recvq_getline (cptr, buf, sizeof buf - 1);
264     if (count <= 0)
265     return;
266 pippijn 1.7 if (cptr->flags & CF_NONEWLINE)
267 pippijn 1.1 {
268     slog (LG_INFO, "httpd_recvqhandler(): throwing out fd %d (%s) for excessive line length", cptr->fd, cptr->hbuf);
269     send_error (cptr, 400, "Bad request", true);
270     sendq_add_eof (cptr);
271     return;
272     }
273    
274     cnt.bin += count;
275     if (buf[count - 1] == '\n')
276     count--;
277     if (count > 0 && buf[count - 1] == '\r')
278     count--;
279     buf[count] = '\0';
280    
281     if (hd->method[0] == '\0')
282     {
283     /* make sure they're not sending more requests after
284     * declaring they're not sending any more */
285     if (hd->connection_close)
286     return;
287     p = strtok (buf, " ");
288     if (p == NULL)
289     return;
290     strlcpy (hd->method, p, sizeof hd->method);
291     p = strtok (NULL, " ");
292     if (p == NULL)
293     return;
294     strlcpy (hd->filename, p, sizeof hd->filename);
295     p = strtok (NULL, "");
296     if (p == NULL || !strcmp (p, "HTTP/1.0"))
297     hd->connection_close = true;
298     slog (LG_DEBUG, "httpd_recvqhandler(): request %s for %s", hd->method, hd->filename);
299     }
300     else if (count == 0)
301     {
302     is_get = !strcmp (hd->method, "GET");
303     is_post = !strcmp (hd->method, "POST");
304    
305     if (!is_post && !is_get)
306     {
307     send_error (cptr, 501, "Method Not Implemented", true);
308     sendq_add_eof (cptr);
309     return;
310     }
311    
312     hd->method[0] = '\0';
313    
314     if (!handling_done)
315     {
316     in = open_file (hd->filename);
317     if (in == -1 || fstat (in, &sb) == -1 || !S_ISREG (sb.st_mode))
318     {
319     if (in != -1)
320     close (in);
321     slog (LG_INFO, "httpd_recvqhandler(): 404 for %s", hd->filename);
322     send_error (cptr, 404, "Not Found", is_get);
323     check_close (cptr);
324     return;
325     }
326     slog (LG_INFO, "httpd_recvqhandler(): 200 for %s", hd->filename);
327     snprintf (outbuf, sizeof outbuf, "HTTP/1.1 200 OK\r\n"
328     "Server: " PACKAGE_NAME "/%s\r\n"
329     "Content-Type: %s\r\n"
330     "Content-Length: %lu\r\n"
331     "\r\n",
332     version, content_type (hd->filename), sb.st_size);
333     sendq_add (cptr, outbuf, strlen (outbuf));
334     count1 = is_get ? sb.st_size : 0;
335     while (count1 > 0)
336     {
337     count = sizeof outbuf;
338     if (count > count1)
339     count = count1;
340     count = read (in, outbuf, count);
341     if (count <= 0)
342     break;
343     sendq_add (cptr, outbuf, count);
344     count1 -= count;
345     }
346     close (in);
347     if (count1 > 0)
348     {
349     slog (LG_INFO, "httpd_recvqhandler(): disconnecting fd %d (%s), read failed on %s", cptr->fd, cptr->hbuf, hd->filename);
350 pippijn 1.7 cptr->flags |= CF_DEAD;
351 pippijn 1.1 }
352     else
353     check_close (cptr);
354     }
355     else
356     {
357     if (hd->length <= 0)
358     {
359     send_error (cptr, 411, "Length Required", true);
360     sendq_add_eof (cptr);
361     return;
362     }
363     if (hd->length > REQUEST_MAX)
364     {
365     send_error (cptr, 413, "Request Entity Too Large", true);
366     sendq_add_eof (cptr);
367     return;
368     }
369     if (!hd->correct_content_type)
370     {
371     send_error (cptr, 415, "Unsupported Media Type", true);
372     sendq_add_eof (cptr);
373     return;
374     }
375     if (hd->expect_100_continue)
376     {
377     snprintf (outbuf, sizeof outbuf, "HTTP/1.1 100 Continue\r\n"
378     "Server: " PACKAGE_NAME "/%s\r\n"
379     "\r\n", version);
380     sendq_add (cptr, outbuf, strlen (outbuf));
381     }
382 pippijn 1.6 hd->requestbuf = salloc<char> (hd->length + 1);
383 pippijn 1.1 }
384     }
385     else
386     process_header (cptr, buf);
387     }
388    
389     static void
390     httpd_closehandler (connection_t *cptr)
391     {
392 pippijn 1.3 httpddata *hd;
393 pippijn 1.1
394     slog (LG_DEBUG, "httpd_closehandler(): fd %d (%s) closed", cptr->fd, cptr->hbuf);
395     hd = static_cast<httpddata *> (cptr->userdata);
396     if (hd != NULL)
397     {
398 pippijn 1.3 sfree (hd->requestbuf);
399     delete hd;
400 pippijn 1.1 }
401     cptr->userdata = NULL;
402     }
403    
404     static void
405     do_listen (connection_t *cptr)
406     {
407     connection_t *newptr;
408 pippijn 1.3 httpddata *hd;
409 pippijn 1.1
410 pippijn 1.6 newptr = connection_t::accept_tcp (cptr, recvq_put, sendq_flush);
411 pippijn 1.1 slog (LG_DEBUG, "do_listen(): accepted httpd from %s fd %d", newptr->hbuf, newptr->fd);
412 pippijn 1.3 hd = new httpddata;
413 pippijn 1.1 newptr->userdata = hd;
414     newptr->recvq_handler = httpd_recvqhandler;
415     newptr->close_handler = httpd_closehandler;
416     }
417    
418     static void
419     httpd_checkidle (void *arg)
420     {
421 pippijn 1.7 connection_t::list_type::iterator it, it_end = connection_t::list.end ();
422 pippijn 1.1 connection_t *cptr;
423    
424     if (listener == NULL)
425     return;
426 pippijn 1.3
427 pippijn 1.7 for (it = connection_t::list.begin (); it != it_end; ++it)
428 pippijn 1.3 {
429     cptr = *it;
430     if (cptr->listener == listener && cptr->last_recv + 300 < NOW)
431     {
432     if (sendq_nonempty (cptr))
433     cptr->last_recv = NOW;
434     else
435     /* from a timeout function,
436 pippijn 1.6 * connection_t::close_soon() may take quite
437     * a while, and connection_t::close() is safe
438 pippijn 1.3 * -- jilles */
439 pippijn 1.6 cptr->close ();
440 pippijn 1.3 }
441     }
442 pippijn 1.1 }
443    
444 pippijn 1.3 static void
445     on_config_ready (void)
446 pippijn 1.1 {
447     if (httpd_config.host != NULL && httpd_config.port != 0)
448     {
449 pippijn 1.6 listener = connection_t::open_listener_tcp (httpd_config.host, httpd_config.port, do_listen);
450 pippijn 1.1 if (listener == NULL)
451     slog (LG_ERROR, "httpd_config_ready(): failed to open listener on host %s port %d", httpd_config.host, httpd_config.port);
452     }
453     else
454     slog (LG_ERROR, "httpd_config_ready(): httpd {} block missing or invalid");
455 pippijn 1.3 httpd_config.clear ();
456 pippijn 1.1 }
457    
458 pippijn 1.3 bool
459     _modinit (module *m)
460 pippijn 1.1 {
461     event_add ("httpd_checkidle", httpd_checkidle, NULL, 60);
462    
463     /* This module needs a rehash to initialize fully if loaded
464     * at run time */
465 pippijn 1.3 ConfTable::callback.ready.attach (on_config_ready);
466 pippijn 1.1
467     add_top_conf ("HTTPD", conf_httpd);
468 pippijn 1.8 add_conf_item ("HOST", conf_httpd_table, conf_httpd_host);
469     add_conf_item ("WWW_ROOT", conf_httpd_table, conf_httpd_www_root);
470     add_conf_item ("PORT", conf_httpd_table, conf_httpd_port);
471 pippijn 1.3
472     return true;
473 pippijn 1.1 }
474    
475     void
476     _moddeinit (void)
477     {
478     event_delete (httpd_checkidle, NULL);
479 pippijn 1.6 listener->close_soon_children ();
480 pippijn 1.8 del_conf_item ("HOST", conf_httpd_table);
481     del_conf_item ("WWW_ROOT", conf_httpd_table);
482     del_conf_item ("PORT", conf_httpd_table);
483 pippijn 1.1 del_top_conf ("HTTPD");
484 pippijn 1.3 httpd_config.clear ();
485 pippijn 1.1
486 pippijn 1.3 ConfTable::callback.ready.detach (on_config_ready);
487 pippijn 1.1 }