4
0

mini_httpd.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. /*
  2. * Copyright (C) 2000,2001 Florian Sander
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. /* mini_httpd.c
  19. *
  20. * minimalistic http server for use in eggdrop modules
  21. *
  22. * Usage:
  23. *
  24. * add init_httpd() to module_start()
  25. * add unload_httpd() to module_close()
  26. * add expmem_httpd() to module_expmem()
  27. *
  28. * call start_httpd(port) to start listening for incoming connections
  29. * on the specified port.
  30. *
  31. * create a function "static void process_get_request(idx);". This function
  32. * gets called when someone connects to your server and sends an GET request.
  33. * you can access the requested path with http_connection(idx)->path.
  34. *
  35. * Don't forget to send the http header with send_http_header(int idx, code)
  36. * before you start sending the output.
  37. *
  38. * cookies are stored in http_connection(idx)->cookies, parameters in ->params.
  39. * You can also access them via get_cookie_value(idx, cookiename) or
  40. * get_param_value(idx, paramname).
  41. *
  42. * Variables (which you might want to tcl-trace and add to your config file)
  43. *
  44. * char httpd_ip[21] = "";
  45. * Defines on which vhost httpd will listen for connections.
  46. * If this is set to "", it'll listen on all vhosts.
  47. *
  48. * static char httpd_log[121] = "";
  49. * Logfile to which http access will be loged (CLF format)
  50. *
  51. * static char httpd_loglevel[20] = "1";
  52. * Defines to which loglevel access will be logged.
  53. *
  54. * static char httpd_ignore_msg[256] = "<H1>You are on ignore.</H1>";
  55. * reply which the recipient will see, if he/she is on ignore
  56. *
  57. * static int max_http_thr = 0;
  58. * static int max_http_time = 0;
  59. * simple flood protection. Allows only thr connections in time seconds.
  60. *
  61. */
  62. static char httpd_ip[21] = "";
  63. static char httpd_loglevel[21] = "1";
  64. static char httpd_ignore_msg[256] = "<H1>You are on ignore.</H1>";
  65. static char httpd_log[121] = "";
  66. static int max_http_thr = 0;
  67. static int max_http_time = 0;
  68. static int http_timeout = 5;
  69. static int httpd_dcc_index = -1;
  70. static char *httpd_text_buf = NULL;
  71. static struct dcc_table MHTTPD_CON_HTTPD =
  72. {
  73. "HTTPD",
  74. DCT_VALIDIDX,
  75. eof_http,
  76. httpd_accept,
  77. 0,
  78. timeout_listen_httpd,
  79. display_httpd_accept,
  80. 0,
  81. NULL,
  82. 0
  83. };
  84. static struct dcc_table MHTTPD_CON_HTTP =
  85. {
  86. "HTTP",
  87. DCT_VALIDIDX,
  88. eof_http,
  89. http_activity,
  90. &http_timeout,
  91. timeout_http,
  92. display_http,
  93. expmem_http,
  94. kill_http,
  95. #ifdef OLDBOT
  96. out_http,
  97. #else
  98. out_http,
  99. outdone_http
  100. #endif
  101. };
  102. #define http_connection(i) ((struct http_connection_data *) dcc[(i)].u.other)
  103. /* init_httpd()
  104. * initializes a few variables
  105. */
  106. static void init_httpd()
  107. {
  108. httpd_text_buf = NULL;
  109. }
  110. /* expmem_httpd()
  111. * expmem function
  112. */
  113. /*
  114. static int expmem_httpd()
  115. {
  116. int size = 0;
  117. if (httpd_text_buf)
  118. size += strlen(httpd_text_buf) + 1;
  119. return size;
  120. }
  121. */
  122. /* unload_httpd():
  123. * frees all allocated memory, stops listening and kills all
  124. * existing connections.
  125. */
  126. static void unload_httpd()
  127. {
  128. stop_httpd();
  129. if (httpd_text_buf)
  130. nfree(httpd_text_buf);
  131. }
  132. /* start_httpd():
  133. * starts listening for http connections on the defined port.
  134. */
  135. static void start_httpd(int port)
  136. {
  137. int i, zz;
  138. char tmp[50];
  139. Context;
  140. // a little hack to make httpd listen on the defined vhost
  141. // (or on all vhosts, if none is defined)
  142. // Just set my-ip to the wanted vhost, since open_listen()
  143. // uses this var
  144. sprintf(tmp, "set my-ip \"%s\";", httpd_ip);
  145. do_tcl("httpd-hack-start",
  146. "set my-ip-httpd-backup ${my-ip};"
  147. "set my-hostname-httpd-backup ${my-hostname};"
  148. "set my-hostname \"\"");
  149. do_tcl("httpd-hack-setip", tmp);
  150. // now get a listening socket
  151. zz = open_listen(&port);
  152. // don't forget to restore my-ip when we're done ^_^
  153. do_tcl("httpd-hack-end",
  154. "set my-ip ${my-ip-httpd-backup};"
  155. "set my-hostname ${my-hostname-httpd-backup}");
  156. // ohoh, we didn't get a socket :(
  157. if (zz == (-1)) {
  158. putlog(LOG_MISC, "*", "ERROR! Cannot open listening socket for httpd!");
  159. return;
  160. }
  161. // now add this new socket to our dcc table and display a warning,
  162. // if the table is full
  163. if ((i = new_dcc(&MHTTPD_CON_HTTPD, 0)) == -1) {
  164. putlog(LOG_MISC, "*", "ERROR! Cannot open listening socket for httpd! DCC table is full!");
  165. // better kill the socket, before we get a "phantom-socket" ^_^
  166. killsock(zz);
  167. return;
  168. }
  169. // store the index in a global var, so we can access it easily later...
  170. httpd_dcc_index = i;
  171. // now fill the dcc-struct with information
  172. dcc[i].sock = zz;
  173. dcc[i].addr = (IP) (-559026163);
  174. dcc[i].port = port;
  175. strcpy(dcc[i].nick, "httpd");
  176. strcpy(dcc[i].host, "*");
  177. dcc[i].timeval = now;
  178. putlog(LOG_MISC, "*", "Now listening for http connections on port %d", port);
  179. }
  180. /* stop_httpd()
  181. * kills all httpd connections and listening sockets
  182. */
  183. static void stop_httpd()
  184. {
  185. int i;
  186. for (i = 0; i < dcc_total; i++) {
  187. if (dcc[i].type == &MHTTPD_CON_HTTPD) {
  188. putlog(LOG_MISC, "*",
  189. "no longer listening for http connections on port %d",
  190. dcc[i].port);
  191. killsock(dcc[i].sock);
  192. lostdcc(i);
  193. } else if (dcc[i].type == &MHTTPD_CON_HTTP) {
  194. putlog(LOG_MISC, "*", "killing http connection from %s", dcc[i].host);
  195. killsock(dcc[i].sock);
  196. lostdcc(i);
  197. }
  198. }
  199. }
  200. /* init_http_connection_data():
  201. * inits all variables in our http_connection_data struct
  202. */
  203. static void init_http_connection_data(int idx)
  204. {
  205. http_connection(idx)->traffic = 0;
  206. http_connection(idx)->code = -1;
  207. http_connection(idx)->browser = NULL;
  208. http_connection(idx)->referer = NULL;
  209. http_connection(idx)->path = NULL;
  210. http_connection(idx)->cmd = NULL;
  211. http_connection(idx)->postparams = NULL;
  212. http_connection(idx)->cookies = NULL;
  213. http_connection(idx)->params = NULL;
  214. http_connection(idx)->headers = NULL;
  215. http_connection(idx)->langs = NULL;
  216. http_connection(idx)->getpostparams = 0;
  217. http_connection(idx)->content_length = 0;
  218. }
  219. /* expmem_http()
  220. * expmem's all data allocated to store browser info, referer, cookies, etc...
  221. */
  222. static int expmem_http(void *x)
  223. {
  224. register struct http_connection_data *p = (struct http_connection_data *) x;
  225. int tot = 0;
  226. Context;
  227. if (!p) {
  228. putlog(LOG_MISC, "*", "Can't expmem clientinfo, no pointer. This should not happen!");
  229. return 0;
  230. }
  231. tot += sizeof(struct http_connection_data);
  232. if (p->browser)
  233. tot += strlen(p->browser) + 1;
  234. if (p->referer)
  235. tot += strlen(p->referer) + 1;
  236. if (p->path)
  237. tot += strlen(p->path) + 1;
  238. if (p->cmd)
  239. tot += strlen(p->cmd) + 1;
  240. if (p->postparams)
  241. tot += strlen(p->postparams) + 1;
  242. if (p->cookies)
  243. tot += llist_2string_expmem(p->cookies);
  244. if (p->params)
  245. tot += llist_2string_expmem(p->params);
  246. if (p->headers)
  247. tot += llist_1string_expmem(p->headers);
  248. if (p->langs)
  249. tot += llist_1string_expmem(p->langs);
  250. return tot;
  251. }
  252. /* free_http_connection_data():
  253. * frees all data of our http_connection_data struct
  254. */
  255. static void free_http_connection_data(int idx)
  256. {
  257. if (http_connection(idx)->browser)
  258. nfree(http_connection(idx)->browser);
  259. if (http_connection(idx)->referer)
  260. nfree(http_connection(idx)->referer);
  261. if (http_connection(idx)->path)
  262. nfree(http_connection(idx)->path);
  263. if (http_connection(idx)->cmd)
  264. nfree(http_connection(idx)->cmd);
  265. if (http_connection(idx)->postparams)
  266. nfree(http_connection(idx)->postparams);
  267. if (http_connection(idx)->cookies)
  268. llist_2string_free(http_connection(idx)->cookies);
  269. if (http_connection(idx)->params)
  270. llist_2string_free(http_connection(idx)->params);
  271. if (http_connection(idx)->headers)
  272. llist_1string_free(http_connection(idx)->headers);
  273. if (http_connection(idx)->langs)
  274. llist_1string_free(http_connection(idx)->langs);
  275. n_free(http_connection(idx), __FILE__, __LINE__);
  276. }
  277. /* http_activity():
  278. * handles all the data that the browser sends to us.
  279. */
  280. static void http_activity(int idx, char *buf, int len)
  281. {
  282. char *cmd, *path, *imask, *params;
  283. #ifdef flush_inbuf
  284. int i;
  285. #endif
  286. int lev, content_length;
  287. struct timeval t;
  288. double pre_time;
  289. debug2("%s: %s", dcc[idx].host, buf);
  290. // at first, check if the user is on ignore and therefore should
  291. // be ignored
  292. imask = nmalloc(strlen(dcc[idx].host) + 11);
  293. sprintf(imask, "http!*@%s", dcc[idx].host);
  294. if (match_ignore(imask)) {
  295. debug1("Ignoring http access from %s", dcc[idx].host);
  296. send_http_header(idx, 401);
  297. if (httpd_ignore_msg[0])
  298. dprintf(idx, "%s", httpd_ignore_msg);
  299. killsock(dcc[idx].sock);
  300. lostdcc(idx);
  301. nfree(imask);
  302. return;
  303. }
  304. nfree(imask);
  305. if ((http_connection(idx)->content_length > 0) && (http_connection(idx)->getpostparams)) {
  306. append_postparam_string(idx, buf);
  307. return;
  308. }
  309. // then check for recognized commands which we want to be logged
  310. // (only GET is supported, at the moment)
  311. if ((!strncasecmp(buf, "GET ", 4) || !strncasecmp(buf, "POST ", 5))
  312. && !http_connection(idx)->cmd) {
  313. http_connection(idx)->cmd = nmalloc(strlen(buf) + 1);
  314. strcpy(http_connection(idx)->cmd, buf);
  315. }
  316. // now check if we know the command and store all info that we need
  317. cmd = newsplit(&buf);
  318. // GET: request for a document
  319. if (!strcasecmp(cmd, "GET") || !strcasecmp(cmd, "POST")) {
  320. // at first, check if we're being flooded and kill the connection
  321. // if there were too many requests.
  322. if (http_flood()) {
  323. http_connection(idx)->code = 401;
  324. killsock(dcc[idx].sock);
  325. lostdcc(idx);
  326. return;
  327. }
  328. // if (!strcasecmp(cmd, "POST"))
  329. // http_connection(idx)->getpostparams = 1;
  330. // now log the access
  331. Assert(http_connection(idx)->cmd);
  332. lev = logmodes(httpd_loglevel);
  333. if (lev)
  334. putlog(lev, "*", "%s: %s", dcc[idx].host, http_connection(idx)->cmd);
  335. // and finally store the request, if there wasn't already one before.
  336. if (!http_connection(idx)->path) {
  337. params = newsplit(&buf);
  338. path = csplit(&params, '?');
  339. // cut the parameters off and store them
  340. add_params(idx, params);
  341. http_connection(idx)->path = nmalloc(strlen(path) + 1);
  342. strcpy(http_connection(idx)->path, path);
  343. }
  344. // user-agent: browser-information
  345. } else if (!strcasecmp(cmd, "User-Agent:")) {
  346. if (http_connection(idx)->browser)
  347. return;
  348. http_connection(idx)->browser = nmalloc(strlen(buf) + 1);
  349. strcpy(http_connection(idx)->browser, buf);
  350. } else if (!strcasecmp(cmd, "Referer:")) {
  351. if (http_connection(idx)->referer)
  352. return;
  353. http_connection(idx)->referer = nmalloc(strlen(buf) + 1);
  354. strcpy(http_connection(idx)->referer, buf);
  355. } else if (!strcasecmp(cmd, "Cookie:")) {
  356. add_cookies(idx, buf);
  357. } else if (!strcasecmp(cmd, "Content-Length:") && !http_connection(idx)->content_length) {
  358. content_length = atoi(buf);
  359. debug1("setting content length to %d", content_length);
  360. http_connection(idx)->content_length = content_length;
  361. } else if (!strcasecmp(cmd, "Accept-language:")) {
  362. add_language(idx, buf);
  363. } else if (!buf[0]) {
  364. if (http_connection(idx)->cmd && !(!strncasecmp(http_connection(idx)->cmd, "POST ", 5))) {
  365. debug0("now sending...");
  366. gettimeofday(&t, NULL);
  367. pre_time = (float) t.tv_sec + (((float) t.tv_usec) / 1000000);
  368. process_get_request(idx);
  369. gettimeofday(&t, NULL);
  370. debug1("Processing time: %.3f", ((float) t.tv_sec + (((float) t.tv_usec) / 1000000)) - pre_time);
  371. dcc[idx].status = 1;
  372. #ifndef OLDBOT
  373. /* If there's no data in our socket, we immediately get rid of it.
  374. */
  375. if (!sock_has_data(SOCK_DATA_OUTGOING, dcc[idx].sock)) {
  376. killsock(dcc[idx].sock);
  377. lostdcc(idx);
  378. }
  379. #endif
  380. } else {
  381. debug0("waiting for post params...");
  382. http_connection(idx)->getpostparams = 1;
  383. #ifdef sockoptions
  384. i = sockoptions(dcc[idx].sock, EGG_OPTION_UNSET, SOCK_BUFFER);
  385. if (i)
  386. debug1("WARNING: sockoptions returned %d while trying to deativate "
  387. "buffering for POST parameters!", i);
  388. #endif
  389. #ifdef flush_inbuf
  390. flush_inbuf(idx);
  391. #endif
  392. }
  393. }
  394. }
  395. /* add_cookies()
  396. * simple function to add one or more cookies to the cookie-list
  397. */
  398. static void add_cookies(int idx, char *buf)
  399. {
  400. char *cookie, *name, *value;
  401. while (buf[0]) {
  402. cookie = csplit(&buf, ';');
  403. while (cookie[0] == ' ')
  404. cookie++;
  405. name = csplit(&cookie, '=');
  406. value = cookie;
  407. http_connection(idx)->cookies
  408. = llist_2string_add(http_connection(idx)->cookies, name, value);
  409. }
  410. }
  411. static char *get_cookie_value(int idx, char *name)
  412. {
  413. Assert(idx >= 0);
  414. return llist_2string_get_s2(http_connection(idx)->cookies, name);
  415. }
  416. /* add_params():
  417. * extracts all parameters from the URL and stores them
  418. * in a simple linked list
  419. */
  420. static void add_params(int idx, char *buf)
  421. {
  422. char *param, *name, *value;
  423. if (strchr(buf, '?')) {
  424. debug1("WARNING: '?' found in paramstring '%s'. This should have been "
  425. "already split!", buf);
  426. return;
  427. }
  428. while (buf[0]) {
  429. param = csplit(&buf, '&');
  430. name = csplit(&param, '=');
  431. value = decode_url(param);
  432. debug2("adding parameter: '%s'='%s'", name, value);
  433. http_connection(idx)->params
  434. = llist_2string_add(http_connection(idx)->params, name, value);
  435. }
  436. }
  437. static char *get_param_value(int idx, char *name)
  438. {
  439. Assert(idx >= 0);
  440. return llist_2string_get_s2(http_connection(idx)->params, name);
  441. }
  442. static void add_language(int idx, char *buf)
  443. {
  444. char *lang;
  445. if (buf)
  446. buf = csplit(&buf, ';'); /* strip "; q=1.5", whatever it means... */
  447. while (buf[0]) {
  448. lang = csplit(&buf, ',');
  449. lang = csplit(&lang, '-'); /* en-us => en */
  450. while (lang[0] == ' ')
  451. lang++;
  452. debug1("adding accepted language: '%s'", lang);
  453. http_connection(idx)->langs =
  454. llist_1string_add(http_connection(idx)->langs, lang);
  455. }
  456. }
  457. #ifndef OLDBOT
  458. static void outdone_http(int idx)
  459. {
  460. if (dcc[idx].status) {
  461. killsock(dcc[idx].sock);
  462. lostdcc(idx);
  463. } else
  464. dcc[idx].status = 1;
  465. }
  466. #endif
  467. static void display_http(int idx, char *buf)
  468. {
  469. sprintf(buf, "http connection");
  470. }
  471. static void display_httpd_accept(int idx, char *buf)
  472. {
  473. sprintf(buf, "httpd");
  474. }
  475. static void timeout_http(int idx)
  476. {
  477. #ifdef flush_inbuf
  478. if (http_connection(idx)->getpostparams && http_connection(idx)->path) {
  479. // If there's still something in the inbuffer, then we might still be receivng
  480. // POST parameters or something. Just let the connection live a bit longer.
  481. // (FIXME: DOSable by flooding with body)
  482. if (flush_inbuf(idx) > 0) {
  483. debug0("inbuf not empty on timeout. Flushed...");
  484. dcc[idx].timeval = now;
  485. return;
  486. }
  487. }
  488. #endif
  489. send_http_header(idx, 408);
  490. dprintf(idx, "<html>\n<head><title>408 Request Time-out</title></head>\n"
  491. "<body>\n<H1>Request Time-out</H1><br>\n<p>Your browser didn't "
  492. "send enough information to process the request within %d "
  493. "seconds.</p>\n", http_timeout);
  494. #ifndef flush_inbuf
  495. dprintf(idx, "<p>If your browser did send the information, then the problem "
  496. "is probably that this server doesn't have the netstuff patch "
  497. "installed. Please ask the admin to install it. This "
  498. "patch is needed to receive login-information with browsers "
  499. "as Netscape Navigator, Opera or some others. (That's not a "
  500. "bug in the browser, but a missing network-related function "
  501. "in eggdrop which gets added by the patch)</p>\n");
  502. #endif
  503. dprintf(idx, "</body>\n</html>\n");
  504. killsock(dcc[idx].sock);
  505. lostdcc(idx);
  506. }
  507. /* kill_http():
  508. * This function called when connection is killed. It
  509. * logs the connection to the logfile, if one exists.
  510. */
  511. static void kill_http(int idx, void *x)
  512. {
  513. char ts[41], test[11];
  514. time_t tt;
  515. FILE *f;
  516. Context;
  517. tt = now;
  518. ctime(&tt);
  519. /* 05/Feb/2000:12:08:17 +0100 */
  520. strftime(test, 19, "%z", localtime(&tt));
  521. // if test contains 'z' then strftime() doesn't support
  522. // %z (timezone-offset) on this system, and we have to
  523. // use a config var instead
  524. if (test[0] != 'z')
  525. strftime(ts, 40, "%d/%b/%Y:%H:%M:%S %z", localtime(&tt));
  526. else
  527. strftime(ts, 40, "%d/%b/%Y:%H:%M:%S", localtime(&tt));
  528. if (httpd_log[0]) {
  529. f = fopen(httpd_log, "a");
  530. if (f == NULL)
  531. putlog(LOG_MISC, "*", "ERROR writing httpd log.");
  532. else {
  533. if (test[0] != 'z')
  534. fprintf(f,
  535. "%s - - [%s] \"%s\" %d %d \"%s\" \"%s\"\n", dcc[idx].host, ts,
  536. http_connection(idx)->cmd ? http_connection(idx)->cmd : "",
  537. http_connection(idx)->code, http_connection(idx)->traffic,
  538. http_connection(idx)->referer ? http_connection(idx)->referer : "-",
  539. http_connection(idx)->browser ? http_connection(idx)->browser : "");
  540. else
  541. fprintf(f,
  542. "%s - - [%s %+05d] \"%s\" %d %d \"%s\" \"%s\"\n",
  543. dcc[idx].host, ts, offset * (-1) * 100,
  544. http_connection(idx)->cmd ? http_connection(idx)->cmd : "",
  545. http_connection(idx)->code, http_connection(idx)->traffic,
  546. http_connection(idx)->referer ? http_connection(idx)->referer : "-",
  547. http_connection(idx)->browser ? http_connection(idx)->browser : "");
  548. fclose(f);
  549. }
  550. }
  551. // don't forget to free the data when we're done.
  552. free_http_connection_data(idx);
  553. }
  554. /* out_http():
  555. * Called when data is sent through the socket. Used to log the
  556. * sent traffic.
  557. */
  558. static void out_http(int idx, char *buf, void *x)
  559. {
  560. register struct http_connection_data *p = (struct http_connection_data *) x;
  561. if (!p) {
  562. putlog(LOG_MISC, "*", "No http_connection pointer. This should not happen!");
  563. return;
  564. }
  565. p->traffic += strlen(buf);
  566. tputs(dcc[idx].sock, buf, strlen(buf));
  567. }
  568. /* http_accept():
  569. * accepts an incoming http connection
  570. */
  571. static void httpd_accept(int idx, char *buf, int len)
  572. {
  573. #if EGG_IS_MIN_VER(10800)
  574. int i, j = 0;
  575. sockname_t name;
  576. unsigned short port;
  577. Context;
  578. if (dcc_total + 1 >= max_dcc) {
  579. j = answer(dcc[idx].sock, &name, &port, 0);
  580. if (j != -1) {
  581. dprintf(-j, "Sorry, too many connections already.\r\n");
  582. killsock(j);
  583. }
  584. return;
  585. }
  586. if ((i = new_dcc(&LIVESTATS, sizeof(struct stats_clientinfo))) == (-1)) {
  587. putlog(LOG_MISC, "*", "Error accepting livestats connection. DCC table is full.");
  588. return;
  589. }
  590. dcc[i].sock = answer(dcc[idx].sock, &dcc[i].sockname,
  591. (short unsigned *) &dcc[i].port, 0);
  592. if (dcc[i].sock < 0) {
  593. putlog(LOG_MISC, "*", "Stats.mod: Error accepting livestats connection: %s", strerror(errno));
  594. lostdcc(i);
  595. return;
  596. }
  597. strcpy(dcc[i].nick, "httpstats");
  598. strcpy(dcc[i].host, iptostr(&dcc[idx].sockname.addr.sa));
  599. dcc[i].timeval = now;
  600. dcc[i].status = 0;
  601. ((struct stats_clientinfo *) dcc[i].u.other)->traffic = 0;
  602. ((struct stats_clientinfo *) dcc[i].u.other)->code = 200;
  603. ((struct stats_clientinfo *) dcc[i].u.other)->browser = NULL;
  604. ((struct stats_clientinfo *) dcc[i].u.other)->referer = NULL;
  605. ((struct stats_clientinfo *) dcc[i].u.other)->cmd = NULL;
  606. #else
  607. unsigned long ip;
  608. unsigned short port;
  609. int j = 0, sock, i;
  610. char s[UHOSTLEN];
  611. Context;
  612. if (dcc_total + 1 >= max_dcc) {
  613. j = answer(dcc[idx].sock, s, &ip, &port, 0);
  614. if (j != -1) {
  615. dprintf(-j, "Sorry, too many connections already.\r\n");
  616. killsock(j);
  617. }
  618. return;
  619. }
  620. sock = answer(dcc[idx].sock, s, &ip, &port, 0);
  621. if (sock < 0) {
  622. neterror(s);
  623. putlog(LOG_MISC, "*", "HTTPd: Error accepting connection: %s", s);
  624. return;
  625. }
  626. if ((i = new_dcc(&MHTTPD_CON_HTTP, sizeof(struct http_connection_data))) == (-1)) {
  627. putlog(LOG_MISC, "*", "Error accepting http connection. DCC table is full.");
  628. killsock(sock);
  629. return;
  630. }
  631. dcc[i].sock = sock;
  632. dcc[i].addr = ip;
  633. dcc[i].port = port;
  634. strcpy(dcc[i].nick, "http");
  635. #ifndef OLDBOT
  636. sprintf(s, "%s", iptostr(my_htonl(ip)));
  637. #else
  638. sprintf(s, "%lu.%lu.%lu.%lu", (ip >> 24) & 0xff, (ip >> 16) & 0xff,
  639. (ip >> 8) & 0xff, ip & 0xff); /* dw */
  640. #endif
  641. strcpy(dcc[i].host, s);
  642. #endif
  643. dcc[i].timeval = now;
  644. dcc[i].status = 0;
  645. // init http_connection_data struct
  646. init_http_connection_data(i);
  647. }
  648. static void eof_http(int idx)
  649. {
  650. debug0("eof http");
  651. killsock(dcc[idx].sock);
  652. lostdcc(idx);
  653. }
  654. static void set_cookie(int idx, char *name, char *value)
  655. {
  656. char tbuf[40], *buf;
  657. time_t tt;
  658. int len;
  659. tt = now + 30 * 60 * 60 * 24;
  660. strftime(tbuf, sizeof(tbuf), "%a, %d-%b-%Y %H:%M:%S GMT", localtime(&tt));
  661. len = 34 + strlen(name) + strlen(value) + strlen(tbuf) + 1;
  662. buf = nmalloc(len);
  663. snprintf(buf, len, "Set-Cookie: %s=%s; expires=%s; path=/\n", name, value, tbuf);
  664. http_connection(idx)->headers = llist_1string_add(http_connection(idx)->headers, buf);
  665. nfree(buf);
  666. }
  667. /* append_postparam_string()
  668. * appends this chunk to the buffer that contains the POST parameters.
  669. * when the buffer is filled, processing gets started automatically.
  670. */
  671. static void append_postparam_string(int idx, char *buf)
  672. {
  673. if (!http_connection(idx)->getpostparams) {
  674. debug2("?!? Tried to append post param string '%s' to connection #%d, "
  675. "but this connection doesn't expect any params... probably a bug. :(",
  676. buf, idx);
  677. return;
  678. }
  679. if (!http_connection(idx)->postparams) {
  680. if (!(http_connection(idx)->content_length > 0)) {
  681. send_http_header(idx, 400);
  682. dprintf(idx, "<html><head><title>400 Bad Request</title></head>"
  683. "<body><H1>Bad Request:</H1> invalid "
  684. "content-length '%d'.</body></html>\n",
  685. http_connection(idx)->content_length);
  686. killsock(dcc[idx].sock);
  687. lostdcc(idx);
  688. return;
  689. }
  690. http_connection(idx)->postparams
  691. = nmalloc(http_connection(idx)->content_length + 1);
  692. http_connection(idx)->postparams[0] = 0;
  693. debug1("allocated %d bytes for params", http_connection(idx)->content_length + 1);
  694. }
  695. debug1("appending content: '%s'", buf);
  696. debug1("old: '%s'", http_connection(idx)->postparams);
  697. strncat(http_connection(idx)->postparams,
  698. buf,
  699. http_connection(idx)->content_length);
  700. http_connection(idx)->postparams[http_connection(idx)->content_length] = 0;
  701. debug1("new: '%s'", http_connection(idx)->postparams);
  702. if ((http_connection(idx)->content_length > 0) &&
  703. http_connection(idx)->getpostparams &&
  704. http_connection(idx)->postparams)
  705. {
  706. if (strlen(http_connection(idx)->postparams) >= http_connection(idx)->content_length) {
  707. debug0("parsing params...");
  708. add_params(idx, http_connection(idx)->postparams);
  709. process_request(idx);
  710. }
  711. }
  712. }
  713. /* send_http_header()
  714. * sends the http header
  715. */
  716. static void send_http_header(int idx, int code)
  717. {
  718. struct llist_1string *h;
  719. if (code == 200)
  720. dprintf(idx, "HTTP/1.0 200 OK\n");
  721. else if (code == 401)
  722. dprintf(idx, "HTTP/1.0 401 Access Forbidden\n");
  723. else if (code == 404)
  724. dprintf(idx, "HTTP/1.1 404 Not Found\n");
  725. else if (code == 500)
  726. dprintf(idx, "HTTP/1.1 500 Internal Server Error\n");
  727. else
  728. dprintf(idx, "HTTP/1.0 %d %d\n", code, code);
  729. dprintf(idx, "Server: EggdropMiniHTTPd/%s\n", HTTPD_VERSION);
  730. dprintf(idx, "Content-Type: text/html\n");
  731. for (h = http_connection(idx)->headers; h; h = h->next) {
  732. debug1("Sending additional header: '%s'", h->s1);
  733. dprintf(idx, "%s", h->s1);
  734. }
  735. dprintf(idx, "\n");
  736. http_connection(idx)->code = code;
  737. }
  738. /* process_request():
  739. * calls the main processing function process_get_request(), takes the
  740. * processing time and tries to kill the socket if everything got already
  741. * sent.
  742. */
  743. static void process_request(int idx)
  744. {
  745. struct timeval t;
  746. double pre_time;
  747. Context;
  748. Assert(idx >= 0);
  749. debug0("now sending...");
  750. gettimeofday(&t, NULL);
  751. pre_time = (float) t.tv_sec + (((float) t.tv_usec) / 1000000);
  752. process_get_request(idx);
  753. gettimeofday(&t, NULL);
  754. debug1("Processing time: %.3f", ((float) t.tv_sec + (((float) t.tv_usec) / 1000000)) - pre_time);
  755. dcc[idx].status = 1;
  756. #ifndef OLDBOT
  757. /* If there's no data in our socket, we immediately get rid of it.
  758. */
  759. if (!sock_has_data(SOCK_DATA_OUTGOING, dcc[idx].sock)) {
  760. killsock(dcc[idx].sock);
  761. lostdcc(idx);
  762. }
  763. #endif
  764. }
  765. /* http_flood()
  766. * simple check for floods
  767. */
  768. static int mhttp_time = 0, mhttp_thr = 0;
  769. static int http_flood()
  770. {
  771. if (!max_http_thr || !max_http_time)
  772. return 0;
  773. if ((now - mhttp_time) > max_http_time) {
  774. mhttp_time = now;
  775. mhttp_thr = 0;
  776. }
  777. mhttp_thr++;
  778. if (mhttp_thr > max_http_thr)
  779. return 1;
  780. return 0;
  781. }
  782. /* csplit()
  783. * basically the same as nsplit, but allows you to define
  784. * the divider.
  785. */
  786. static char *csplit(char **rest, char divider)
  787. {
  788. register char *o, *r;
  789. if (!rest)
  790. return *rest = "";
  791. o = *rest;
  792. while (*o == divider)
  793. o++;
  794. r = o;
  795. while (*o && (*o != divider))
  796. o++;
  797. if (*o)
  798. *o++ = 0;
  799. *rest = o;
  800. return r;
  801. }
  802. /* text2html():
  803. * replaces all strange chars by html-unicode-codes and removes
  804. * stupid color codes */
  805. static char *text2html(char *text)
  806. {
  807. char *buf, ubuf[8];
  808. unsigned char c;
  809. if (httpd_text_buf)
  810. httpd_text_buf = nrealloc(httpd_text_buf, (strlen(text) * sizeof(char) * 7) + 1);
  811. else
  812. httpd_text_buf = nmalloc((strlen(text) * sizeof(char) * 7) + 1);
  813. buf = httpd_text_buf;
  814. *buf = 0;
  815. while (text[0]) {
  816. c = text[0];
  817. if (((c >= 97) && (c <= 122)) || ((c >= 65) && (c <= 90))) {
  818. *buf = c;
  819. buf++;
  820. } else if (c == 3) { /* filter $§%#&-mirc colors! */
  821. /* inspired by src/dcc.c */
  822. if (isdigit(text[1])) {
  823. text++;
  824. if (isdigit(text[1]))
  825. text++;
  826. if (text[1] == ',') {
  827. text++;
  828. if (isdigit(text[1]))
  829. text++;
  830. if (isdigit(text[1]))
  831. text++;
  832. }
  833. }
  834. if (!1) { /* DELETEME!!! */
  835. /* from src/dcc.c */
  836. if (isdigit(text[1])) { /* Is the first char a number? */
  837. text++; /* Skip over the ^C and the first digit */
  838. if (isdigit(text[1]))
  839. text++; /* Is this a double digit number? */
  840. if (text[1] == ',') { /* Do we have a background color next? */
  841. if (isdigit(text[2]))
  842. text += 2; /* Skip over the first background digit */
  843. if (isdigit(text[1]))
  844. text++; /* Is it a double digit? */
  845. }
  846. }
  847. }
  848. } else if ((c == 2) || (c == 7) || (c == 0x16) || (c == 0x1f)) {
  849. /* do nothing, just ignore those $§%#&-codes! */
  850. debug0("deleteme (mini_httpd.c, text2html())");
  851. } else {
  852. snprintf(ubuf, sizeof(ubuf), "&#%d;", (unsigned int) c);
  853. strcpy(buf, ubuf);
  854. buf += strlen(ubuf);
  855. }
  856. text++;
  857. }
  858. *buf = 0;
  859. httpd_text_buf = nrealloc(httpd_text_buf, strlen(httpd_text_buf) + 1);
  860. return httpd_text_buf;
  861. }
  862. /* encode_url():
  863. * encodes all special characters in an url
  864. */
  865. static char encoded_url_buf[128];
  866. static char *eu_last_url;
  867. static char *encode_url(char *url)
  868. {
  869. char *buf;
  870. unsigned char c;
  871. Assert(url);
  872. // if we are going to re-encode the same URL again, then
  873. // save some CPU time and just return our buffer again
  874. // (I guess noone would mess with that buffer, so it _should_
  875. // be save)
  876. if (url == eu_last_url)
  877. return encoded_url_buf;
  878. else
  879. eu_last_url = url;
  880. buf = encoded_url_buf;
  881. while (url[0] && (buf < (encoded_url_buf + 120))) {
  882. c = url[0];
  883. if (((c >= 97) && (c <= 122)) || ((c >= 65) && (c <= 90))) {
  884. buf[0] = c;
  885. buf++;
  886. } else {
  887. buf[0] = '%';
  888. buf++;
  889. snprintf(buf, 3, "%02x", c);
  890. buf += 2;
  891. }
  892. url++;
  893. }
  894. buf[0] = 0;
  895. return encoded_url_buf;
  896. }
  897. /* decode_url():
  898. * Decodes all special characters(%3F == '?', %21 == '!', etc)
  899. * and returns the decoded url
  900. */
  901. static char *decode_url(char *paramurl)
  902. {
  903. char *p, *buf, *url, c, hex[5];
  904. long int i;
  905. Context;
  906. // free url-buffer (global var)
  907. if (httpd_text_buf)
  908. nfree(httpd_text_buf);
  909. // initialize url-buffer
  910. httpd_text_buf = nmalloc(1);
  911. httpd_text_buf[0] = 0;
  912. // copy parameter into a buffer
  913. buf = nmalloc(strlen(paramurl) + 1);
  914. strcpy(buf, paramurl);
  915. url = buf;
  916. // now loop to get every encoded char
  917. while ((p = strchr(url, '%'))) {
  918. // '%' marks the beginning of an encoded char, so cut the string here.
  919. p[0] = 0;
  920. // append the first part of the string to our buffer
  921. httpd_text_buf = nrealloc(httpd_text_buf, strlen(httpd_text_buf) + strlen(url) + 1);
  922. strcat(httpd_text_buf, url);
  923. // set the pointer to the beginning of the next string
  924. url = p + 1;
  925. // first check if there are enough chars left to decode
  926. if (strlen(url) >= 2) {
  927. // the number behind '%' is a hex-number which is the ASCII code of
  928. // the char, so dump the hex into a string and scan it
  929. snprintf(hex, 5, "0x%c%c", p[1], p[2]);
  930. i = strtol(hex, NULL, 0);
  931. if (!i) {
  932. i = '?';
  933. debug0("MiniHTTPd: decode_url(): i is 0");
  934. }
  935. c = (char) i;
  936. // now append the decoded char to the url
  937. httpd_text_buf = nrealloc(httpd_text_buf, strlen(httpd_text_buf) + 1 + 1);
  938. sprintf(httpd_text_buf, "%s%c", httpd_text_buf, c);
  939. // increase the pointer to abandon the encoded char
  940. url += 2;
  941. } else {
  942. // just append the original '%' if there weren't enough chars to decode
  943. httpd_text_buf = nrealloc(httpd_text_buf, strlen(httpd_text_buf) + 1 + 1);
  944. strcat(httpd_text_buf, "%");
  945. }
  946. }
  947. // finally append the rest of the param to our buffer. There are no encoded
  948. // chars left.
  949. httpd_text_buf = nrealloc(httpd_text_buf, strlen(httpd_text_buf) + strlen(url) + 1);
  950. strcat(httpd_text_buf, url);
  951. // free the buffer
  952. nfree(buf);
  953. Context;
  954. return httpd_text_buf;
  955. }
  956. static void timeout_listen_httpd(int idx)
  957. {
  958. debug0("timeout httpd listen");
  959. killsock(dcc[idx].sock);
  960. lostdcc(idx);
  961. }