mini_httpd.c 28 KB

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