PageRenderTime 64ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/source/core/server/src/nanoweb.php

https://bitbucket.org/detroitpro/taphp
PHP | 3130 lines | 2393 code | 645 blank | 92 comment | 292 complexity | fa2926a73f2e0f3d89540af96d45b6c3 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, GPL-3.0
  1. #!/usr/local/bin/php -q
  2. <?php
  3. /*
  4. Nanoweb, the aEGiS PHP web server
  5. =================================
  6. Copyright (C) 2002-2005 Vincent Negrier aka. sIX <six@aegis-corp.org>
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 2, or (at your option)
  10. any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18. */
  19. error_reporting(E_PARSE | E_ERROR);
  20. if (strpos($opts='^'.implode('_', $_SERVER["argv"]).'$', "--debug") + strpos($opts, "--verbose")) { error_reporting(E_ALL); }
  21. define("VERSION", "2.2.9");
  22. // Hard configuration and defaults
  23. define("DEFAULT_CONFIG_FILE", (((strpos(strtoupper(PHP_OS), "WIN")===0) || (strpos(strtoupper(PHP_OS), "CYGWIN")===0))?"C:\\nanoweb\\":"/etc/nanoweb/")."nanoweb.conf");
  24. define("DEFAULT_LISTEN_ADDR", "0.0.0.0");
  25. define("DEFAULT_LISTEN_PORT", 80);
  26. define("DEFAULT_LISTEN_QUEUE", 20);
  27. define("DEFAULT_MIMETYPES", "/etc/mime.types");
  28. define("DEFAULT_CONTENT_TYPE", "text/plain");
  29. define("DEFAULT_REQUEST_TIMEOUT", 15);
  30. define("DEFAULT_ACCESS_FILE", ".nwaccess");
  31. define("DEFAULT_DOCROOT", "./");
  32. define("DEFAULT_LOGFILE", "./access.log");
  33. define("DEFAULT_LOGTYPE", "combined");
  34. define("DEFAULT_FBWELCOMEFILE", ".welcome");
  35. define("DEFAULT_STATIC_BUFFER_SIZE", 1048576);
  36. define("DEFAULT_CONTENT_HANDLER", "static");
  37. define("DEFAULT_MAX_SERVERS", 25);
  38. define("DEFAULT_ACCESS_POLICY", "override");
  39. define("DEFAULT_SERVER_THEME", "default");
  40. define("DEFAULT_SERVER_LANG", "en-us");
  41. define("SCK_WRITE_PACKET_SIZE", 8192);
  42. define("SCK_READ_PACKET_SIZE", 4096);
  43. define("SCK_READ_SELECT_TIMEOUT", 2);
  44. define("SCK_MAX_STALL_TIME", 60);
  45. define("SPM_CACHES_LIFETIME", 15);
  46. define("HTTP_VERSION", "HTTP/1.1");
  47. define("SERVER_STRING", "aEGiS_nanoweb");
  48. define("SERVER_STRING_V", SERVER_STRING."/".VERSION);
  49. define("INT_MSGSIZE", 4096);
  50. define("NM_HIT", " HIT");
  51. define("NM_RESTART_LOGGERS", "LGRST");
  52. define("NM_SERVER_STATE", "SRVST");
  53. define("NM_RELOAD_THEME", "RLTHM");
  54. define("NM_BLOCK_IP", "BANIP");
  55. define("NM_UNBLOCK_IP", "DBNIP");
  56. define("NW_BAD_OUTSIDE_DOCROOT", 1);
  57. define("NW_BAD_DOT_FILE", 2);
  58. define("NW_BAD_WIN_DEVICE", 3);
  59. define("NW_SB_STATUS", 0);
  60. define("NW_SB_PEERHOST", 1);
  61. define("NW_SB_FORKTIME", 2);
  62. define("NW_EL_DEBUG", 1);
  63. define("NW_EL_HIT", 2);
  64. define("NW_EL_NOTICE", 4);
  65. define("NW_EL_BLOCKING", 8);
  66. define("NW_EL_WARNING", 16);
  67. define("NW_EL_ERROR", 32);
  68. define("NW_EL_ALL", 255);
  69. define("NW_EL_DEFAULT", NW_EL_NOTICE | NW_EL_BLOCKING | NW_EL_WARNING | NW_EL_ERROR);
  70. define("NW_TMPL_SIGNATURE", "server_signature");
  71. define("NW_TMPL_ERROR_PAGE", "error_page");
  72. define("NW_TMPL_ERROR_RESOURCE", "error_resource");
  73. define("NW_TMPL_ERROR_ADMIN", "error_admin");
  74. define("REQUIRED_PHP_VERSION", "4.2.0");
  75. // Internally used global vars
  76. $HTTP_HEADERS=array(100 => "100 Continue",
  77. 200 => "200 OK",
  78. 201 => "201 Created",
  79. 204 => "204 No Content",
  80. 206 => "206 Partial Content",
  81. 300 => "300 Multiple Choices",
  82. 301 => "301 Moved Permanently",
  83. 302 => "302 Found",
  84. 303 => "303 See Other",
  85. 304 => "304 Not Modified",
  86. 307 => "307 Temporary Redirect",
  87. 400 => "400 Bad Request",
  88. 401 => "401 Unauthorized",
  89. 403 => "403 Forbidden",
  90. 404 => "404 Not Found",
  91. 405 => "405 Method Not Allowed",
  92. 406 => "406 Not Acceptable",
  93. 408 => "408 Request Timeout",
  94. 410 => "410 Gone",
  95. 413 => "413 Request Entity Too Large",
  96. 414 => "414 Request URI Too Long",
  97. 415 => "415 Unsupported Media Type",
  98. 416 => "416 Requested Range Not Satisfiable",
  99. 417 => "417 Expectation Failed",
  100. 500 => "500 Internal Server Error",
  101. 501 => "501 Method Not Implemented",
  102. 503 => "503 Service Unavailable",
  103. 506 => "506 Variant Also Negotiates");
  104. $TEST_FUNCS=array( "pcntl_fork" => false,
  105. "socket_create" => true,
  106. "posix_setuid" => false,
  107. "gzencode" => false);
  108. $conf_defaults=array( "listeninterface" => DEFAULT_LISTEN_ADDR,
  109. "listenport" => DEFAULT_LISTEN_PORT,
  110. "listenqueue" => DEFAULT_LISTEN_QUEUE,
  111. "mimetypes" => DEFAULT_MIMETYPES,
  112. "requesttimeout" => DEFAULT_REQUEST_TIMEOUT,
  113. "accessfile" => DEFAULT_ACCESS_FILE,
  114. "documentroot" => DEFAULT_DOCROOT,
  115. "log" => DEFAULT_LOGFILE,
  116. "logtype" => DEFAULT_LOGTYPE,
  117. "fbwelcomefile" => DEFAULT_FBWELCOMEFILE,
  118. "defaultcontenttype" => DEFAULT_CONTENT_TYPE,
  119. "staticbuffersize" => DEFAULT_STATIC_BUFFER_SIZE,
  120. "defaulthandler" => DEFAULT_CONTENT_HANDLER,
  121. "maxservers" => DEFAULT_MAX_SERVERS,
  122. "accesspolicy" => DEFAULT_ACCESS_POLICY,
  123. "servertheme" => DEFAULT_SERVER_THEME);
  124. $conf_vhosts_propagate=array( "documentroot",
  125. "directoryindex",
  126. "serveradmin",
  127. "user",
  128. "group",
  129. "logdir",
  130. "log",
  131. "logtype",
  132. "filebrowser",
  133. "fbshowdotfiles",
  134. "fbwelcomefile",
  135. "userdir",
  136. "ignoredotfiles",
  137. "allowsymlinkto",
  138. "maxrequestbodylength",
  139. "maxrequesturilength");
  140. // Needed as long as PHP filetype() is broken on win32
  141. $win_devices=array("nul", "con", "aux", "prn", "clock$", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8");
  142. // Command line help page
  143. $cmdline_help=<<<EOT
  144. Usage: nanoweb.php [/path/to/nanoweb.conf] [options]
  145. nanoweb supports the following command line options :
  146. --help -h : this help screen
  147. --version -v : show version info
  148. --config=/path/to/nanoweb.conf -c : configuration file
  149. --set-option="optionname=optionvalue" -o : set configuration option
  150. --add-option="optionname=optionvalue" -a : add configuration option
  151. --start-daemon -d : start nanoweb and run in background
  152. --config-test -t : test configuration and exit
  153. --quiet -q : don't send text to console
  154. EOT;
  155. // Static response class
  156. class static_response {
  157. var $content_length;
  158. function static_response($str) {
  159. $this->str=$str;
  160. $this->content_length=strlen($str);
  161. }
  162. function parser_open($args, $filename, &$rq_err, &$cgi_headers) {
  163. }
  164. function parser_get_output() {
  165. $s=$this->str;
  166. $this->str="";
  167. return($s);
  168. }
  169. function parser_eof() {
  170. return($this->str === "");
  171. }
  172. function parser_close() {
  173. }
  174. }
  175. $null_response =& new static_response("");
  176. $lf=$null_response;
  177. // Functions
  178. function _parseconfig_close_part(&$conf, $cpart, $nodefaults=false) {
  179. if (!$conf[$cpart]["_nw_pcp"]) {
  180. if (!$nodefaults) {
  181. if ($cpart=="global") {
  182. // Use default values for the global config scope
  183. foreach ($GLOBALS["conf_defaults"] as $key=>$dval) if (!isset($conf[$cpart][$key])) $conf[$cpart][$key][0]=$dval;
  184. } else {
  185. // Propagate appropriate directives to vhosts
  186. foreach ($GLOBALS["conf_vhosts_propagate"] as $key=>$pkey) if (!isset($conf[$cpart][$pkey])) $conf[$cpart][$pkey]=$conf["global"][$pkey];
  187. if (!$conf[$cpart]["servername"]) $conf[$cpart]["servername"][]=$cpart;
  188. }
  189. }
  190. // Transform some directives (Dir = Idx Value)
  191. foreach (array("parseext", "errordocument", "errorheader") as $dir) {
  192. if ($conf[$cpart][$dir]) {
  193. foreach ($conf[$cpart][$dir] as $ps) {
  194. $ext=strtolower(strtok($ps, " "));
  195. $dext=trim(strtok(""));
  196. $conf[$cpart]["_".$dir]["_".$ext]=$dext;
  197. }
  198. }
  199. }
  200. $conf[$cpart]["_nw_pcp"]=true;
  201. }
  202. }
  203. function _parseconfig_parse_line($str) {
  204. $cnfl=array();
  205. ereg("([^ =\n\t]+)[ \t]*=?[ \t]*([^\n]+)", $str, $cnfl);
  206. return(array(strtolower(trim($cnfl[1])), trim($cnfl[2])));
  207. }
  208. function parseconfig($conf_arr, $nodefaults=false) {
  209. $cpart="global";
  210. $_pcp=array("global");
  211. $included_confs[$GLOBALS["conffile"]]=true;
  212. if ($clen=count($conf_arr)) {
  213. // Pass 1 (build the $conf_arr array)
  214. $key=-1;
  215. while ($key++<=$clen) {
  216. $str=$conf_arr[$key];
  217. list($cnfk, $cnfv)=_parseconfig_parse_line($str);
  218. if ($cnfk=="configdir") {
  219. $confdir=$cnfv;
  220. } else if ($cnfk=="include") {
  221. $ifn=($confdir?($confdir.DIRECTORY_SEPARATOR):"").$cnfv;
  222. if (!@is_readable($ifn)) $ifn=$cnfv;
  223. if ($included_confs[$ifn]) {
  224. $conf_err="configuration includes loop detected line ".$key." : '".trim($str)."'";
  225. break;
  226. }
  227. if (!@is_readable($ifn)) {
  228. $conf_err="unable to include configuration file line ".$key." : '".$ifn."'";
  229. break;
  230. }
  231. $subconf_arr=file($ifn);
  232. $conf_arr=array_merge(array_slice($conf_arr, 0, $key), $subconf_arr, array_slice($conf_arr, $key+1));
  233. $key=-1;
  234. $clen=count($conf_arr);
  235. $included_confs[$ifn]=true;
  236. }
  237. }
  238. // Pass 2 (build the $conf array)
  239. foreach ($conf_arr as $key=>$str) {
  240. switch ($str[0]) {
  241. case "#":
  242. case ";":
  243. case "\n":
  244. case "":
  245. break;
  246. case "[":
  247. if ($cpart!="global") $_pcp[]=$cpart;
  248. $cpart=substr(trim($str), 1, -1);
  249. if ($cpart{0}=="/") $cpart="global";
  250. if ($cpart!="global") unset($conf[$cpart]);
  251. break;
  252. default:
  253. list($cnfk, $cnfv)=_parseconfig_parse_line($str);
  254. switch ($cnfk) {
  255. case "documentroot":
  256. $rp=nw_realpath($cnfv);
  257. if (!($rp && @is_dir($rp))) {
  258. $conf_err="directory not found at line ".$key." : '".trim($str)."'";
  259. unset($conf[$cpart]);
  260. $cpart="_err:".$cpart;
  261. }
  262. if (substr($rp, -1)!=DIRECTORY_SEPARATOR) $rp.=DIRECTORY_SEPARATOR;
  263. $conf[$cpart][$cnfk][]=$rp;
  264. break;
  265. case "serveralias":
  266. if ($cnfv!=$cpart) $conf[$cnfv]=&$conf[$cpart];
  267. break;
  268. case "alias":
  269. $conf[$cpart][$cnfk][]=$cnfv;
  270. $aliases=explode(" ", $cnfv);
  271. $conf[$cpart]["_aliases"][$aliases[0]]=$aliases[1];
  272. break;
  273. case "serverlog":
  274. $lname=strtok($cnfv, " ");
  275. if ($lmode=strtok("")) {
  276. $lbmode=0;
  277. foreach($GLOBALS["srvlog_levels"] as $lvl=>$bin_lvl) if (strpos($lmode, $lvl)!==false) $lbmode|=$bin_lvl;
  278. foreach($GLOBALS["srvlog_levels"] as $lvl=>$bin_lvl) if (strpos($lmode, "-".$lvl)!==false) $lbmode&=~$bin_lvl;
  279. } else {
  280. $lbmode=NW_EL_DEFAULT;
  281. }
  282. $conf[$cpart]["_serverlog"][$lname]=$lbmode;
  283. break;
  284. case "loadtheme":
  285. $conf[$cpart][$cnfk][]=$cnfv;
  286. $conf[$cpart]["servertheme"][]=$cnfv;
  287. break;
  288. default:
  289. $conf[$cpart][$cnfk][]=$cnfv;
  290. break;
  291. }
  292. break;
  293. }
  294. }
  295. }
  296. // Pass 3 (close all $conf sections)
  297. if ($cpart!="global") $_pcp[]=$cpart;
  298. foreach ($_pcp as $clpart) _parseconfig_close_part($conf, $clpart, $nodefaults);
  299. return($conf_err?$conf_err:$conf);
  300. }
  301. function cmdline_conf_upd($conf, $cmdline_conf_overrides, $cmdline_conf_adds) {
  302. foreach ($cmdline_conf_overrides as $cs) {
  303. $ca=explode("=", $cs);
  304. $conf["global"][strtolower($ca[0])]=array($ca[1]);
  305. }
  306. foreach ($cmdline_conf_adds as $cs) {
  307. $ca=explode("=", $cs);
  308. $conf["global"][strtolower($ca[0])][]=$ca[1];
  309. }
  310. return($conf);
  311. }
  312. function load_modules($conf) {
  313. global $mod_tokens;
  314. $mod_tokens=array();
  315. if ($lm_arr=$conf["global"]["loadmodule"]) foreach ($lm_arr as $key=>$modname) {
  316. $clsname=basename($modname, ".php");
  317. if (!is_file($modname)) {
  318. foreach (access_query("modulesdir") as $md) if (is_file($md.DIRECTORY_SEPARATOR.$modname)) {
  319. $moddir=$md;
  320. break;
  321. }
  322. } else $moddir="";
  323. if (!$ld_clss[$clsname]) {
  324. $nload=(!class_exists($clsname));
  325. // Try to load with given path
  326. @include_once($modname);
  327. $modloaded=class_exists($clsname);
  328. if (!$modloaded) {
  329. // And try with modulesdir if not found
  330. @include_once($moddir.$modname);
  331. $modloaded=class_exists($clsname);
  332. }
  333. if ($modloaded) {
  334. $ld_clss[$clsname]=true;
  335. $tmp=&new $clsname;
  336. $modules[$tmp->modtype][]=&$tmp;
  337. $tmp_modlist[]=array($clsname, $tmp->modname);
  338. if (is_array($tmp->urls)) foreach ($tmp->urls as $url) $modules["url:".$url]=&$tmp;
  339. if (is_array($tmp->methods)) foreach ($tmp->methods as $method) $modules["method:".$method]=&$tmp;
  340. if (is_string($mt=$tmp->sig_token)) $mod_tokens[$tmp->modname]=$mt;
  341. if ($nload) techo("loaded module : ".$tmp->modname);
  342. } else {
  343. techo("WARN: unable to load module '".$modname."'", NW_EL_WARNING);
  344. }
  345. }
  346. }
  347. $GLOBALS["stats_modlist"]=$tmp_modlist;
  348. return ($modules);
  349. }
  350. function load_theme($themefname, $load_notice=false, $reload=false) {
  351. $tfn=($GLOBALS["conf"]["global"]["configdir"][0]?($GLOBALS["conf"]["global"]["configdir"][0].DIRECTORY_SEPARATOR):"").$themefname;
  352. if (!@is_readable($tfn)) $tfn=$themefname;
  353. if ($thmarr=@file($tfn)) {
  354. $ts=0;
  355. foreach ($thmarr as $thml) {
  356. if (strtolower(rtrim($thml))=="[/".$thm_sc."]") {
  357. $theme[$thm_sc]=substr($theme[$thm_sc], 0, -1);
  358. --$ts;
  359. $thm_sc="";
  360. } else if (($thml{0}=="[") && (substr(rtrim($thml), -1)=="]")) {
  361. $thm_sc=strtolower(substr(rtrim($thml), 1, -1));
  362. } else if ($thm_sc) {
  363. $theme[$thm_sc].=$thml;
  364. $ts+=strlen($thml);
  365. }
  366. }
  367. if ($theme["theme_id"]) {
  368. if (!$theme["theme_name"]) $theme["theme_name"]=$theme["theme_id"];
  369. $theme["theme_id"]=trim($theme["theme_id"]);
  370. $theme["theme_name"]=trim($theme["theme_name"]);
  371. $theme["theme_language"]=trim($theme["theme_language"])
  372. or $theme["theme_language"]=DEFAULT_SERVER_LANG;
  373. if ($load_notice) techo(($reload?"re":"")."loaded theme : ".$theme["theme_name"]." (".$ts." bytes)");
  374. } else {
  375. techo("WARN: invalid theme file '".$tfn."'", NW_EL_WARNING);
  376. }
  377. clearstatcache();
  378. $theme["_fname"]=$tfn;
  379. $theme["_mtime"]=filemtime($tfn);
  380. $theme["_pmode"]=$GLOBALS["pmode"];
  381. } else {
  382. techo("WARN: unable to load theme file '".$tfn."'", NW_EL_WARNING);
  383. }
  384. return($theme);
  385. }
  386. function load_themes($conf) {
  387. if (is_array($conf["global"]["loadtheme"])) foreach ($conf["global"]["loadtheme"] as $themefname) {
  388. $theme=load_theme($themefname, true);
  389. $themes[$theme["theme_id"]]=$theme;
  390. $themes[$theme["_fname"]]=$theme;
  391. } else {
  392. techo("WARN: 'LoadTheme' directive not found in config file", NW_EL_WARNING);
  393. }
  394. return($themes);
  395. }
  396. function modules_init($method="init") {
  397. global $modules;
  398. foreach ($modules as $modclass) if (is_array($modclass)) {
  399. for ($a=0;$a<count($modclass);$a++) {
  400. if ((method_exists($modclass[$a], $method)) && (!$modinit[$mc=get_class($modclass[$a])])) {
  401. $modclass[$a]->$method();
  402. $modinit[$mc]=true;
  403. }
  404. }
  405. } else {
  406. if ((method_exists($modclass, $method)) && (!$modinit[$mc=get_class($modclass)])) {
  407. $modclass->$method();
  408. $modinit[$mc]=true;
  409. }
  410. }
  411. }
  412. function load_access_files($dir, &$access, $rec=0) {
  413. global $conf, $access_cache;
  414. if (is_array($z=$access_cache[$dir])) {
  415. // Access cache hit
  416. $access=$z;
  417. return;
  418. } else {
  419. // Access cache miss
  420. if (!$rec) $access=array();
  421. $ndir=substr($dir, 0, strrpos($dir, DIRECTORY_SEPARATOR));
  422. $cont = false;
  423. $r_mdir = nw_realpath($ndir.DIRECTORY_SEPARATOR);
  424. $cont_dirs = array(nw_realpath($GLOBALS["docroot"]));
  425. foreach ($conf[$GLOBALS["vhost"]]["allowsymlinkto"] as $als) $cont_dirs[] = nw_realpath($als);
  426. foreach ($cont_dirs as $cdir) if (strpos($r_mdir, $cdir)===0) {
  427. load_access_files($ndir, $access, $rec+1);
  428. break;
  429. }
  430. $afn=$dir.DIRECTORY_SEPARATOR.$conf["global"]["accessfile"][0];
  431. if (is_readable($afn) && ($accesstmp=@file($afn)) && ($tmp_access=parseconfig($accesstmp, true))) foreach ($tmp_access as $key=>$val_arr) {
  432. foreach ($val_arr as $ckey=>$cval_arr) {
  433. $ap=$GLOBALS["access_policy"][$ckey] or
  434. $ap=$conf["global"]["accesspolicy"][0];
  435. switch ($ap) {
  436. case "override":
  437. $access[$key][$ckey]=$cval_arr;
  438. break;
  439. case "merge":
  440. $access[$key][$ckey]=array_merge($access[$key][$ckey], $cval_arr);
  441. break;
  442. }
  443. }
  444. if (!$rec) break;
  445. }
  446. $access_cache[$dir]=$access;
  447. }
  448. }
  449. function access_query($key, $idx=false) {
  450. global $access, $conf;
  451. $ap=$GLOBALS["access_policy"][$key] or
  452. $ap=$conf["global"]["accesspolicy"][0];
  453. switch ($ap) {
  454. case "override":
  455. $tmp=$access["global"][$key] or
  456. $tmp=$conf[$GLOBALS["vhost"]][$key] or
  457. $tmp=$conf["global"][$key];
  458. break;
  459. case "merge":
  460. $tmp=array_merge($conf["global"][$key] ? $conf["global"][$key] : array(), $conf[$GLOBALS["vhost"]][$key] ? $conf[$GLOBALS["vhost"]][$key] : array(), $access["global"][$key] ? $access["global"][$key] : array());
  461. break;
  462. }
  463. if ($idx===false) {
  464. return($tmp);
  465. } else {
  466. return($tmp[$idx]);
  467. }
  468. }
  469. function core_modules_hook($hname) {
  470. if ($mh_arr=&$GLOBALS["modules"]["core_".$hname]) foreach (array_keys($mh_arr) as $a) $mh_arr[$a]->main();
  471. }
  472. function log_ids() {
  473. global $conf;
  474. if ($setgid=@posix_getgrnam($conf["global"]["loggergroup"][0])) $g=$setgid["gid"];
  475. else if ($setgid=@posix_getgrnam($conf["global"]["group"][0])) $g=$setgid["gid"];
  476. if ($setuid=@posix_getpwnam($conf["global"]["loggeruser"][0])) $u=$setuid["uid"];
  477. else if ($setuid=@posix_getpwnam($conf["global"]["user"][0])) $u=$setuid["uid"];
  478. return(array("uid" => $u, "gid" => $g));
  479. }
  480. function log_srv($str, $loglevel=NW_EL_NOTICE) {
  481. if ($srvlog_arr=$GLOBALS["conf"]["global"]["_serverlog"]) foreach ($srvlog_arr as $s=>$bmode) if ($loglevel & $bmode) {
  482. if (($GLOBALS["pmode"]=="master") && (!file_exists($s))) $chown=true;
  483. if ($sl=@fopen($s, NW_BSAFE_APP_OPEN)) {
  484. fputs($sl, $str);
  485. fclose($sl);
  486. }
  487. if ($chown && $GLOBALS["posix_av"] && ($lids=log_ids())) {
  488. chgrp($s, $lids["gid"]);
  489. chown($s, $lids["uid"]);
  490. }
  491. }
  492. }
  493. function nw_gethostbyaddr($ip) {
  494. static $ns_lastreq;
  495. if ($ip != $ns_lastreq[0]) {
  496. $hostname = @gethostbyaddr($ip);
  497. $fwr_ip = @gethostbyname($hostname);
  498. if ($ip != $fwr_ip) {
  499. // Inconsistent DNS data
  500. $hostname = $ip;
  501. }
  502. $ns_lastreq = array($ip, $hostname);
  503. }
  504. return($ns_lastreq[1]);
  505. }
  506. function nw_realpath($dir) {
  507. global $rp_cache;
  508. if ($rp=$rp_cache[$dir]) {
  509. return($rp);
  510. } else {
  511. return($rp_cache[$dir]=realpath($dir));
  512. }
  513. }
  514. function nw_server_string() {
  515. switch (strtolower(access_query("serversignature", 0))) {
  516. case "fake": return(access_query("serverfakesignature", 0));
  517. case "off": return("");
  518. case "prod": return(SERVER_STRING);
  519. case "min": return(SERVER_STRING_V);
  520. case "os": return(SERVER_STRING_V." (".PHP_OS.")");
  521. case "php": return(SERVER_STRING_V." (".PHP_OS."; PHP/".phpversion().")");
  522. case "full":
  523. default:
  524. return(SERVER_STRING_V." (".PHP_OS."; PHP/".phpversion().($GLOBALS["mod_tokens"]?"; ":"").implode("; ", $GLOBALS["mod_tokens"]).")");
  525. }
  526. }
  527. function _genpage_signature() {
  528. return(nw_apply_template(NW_TMPL_SIGNATURE, array("server_string" => nw_server_string(), "server_name" => $GLOBALS["conf"][$GLOBALS["vhost"]]["servername"][0]), true));
  529. }
  530. function nw_apply_template($template, $args, $no_add=false) {
  531. global $themes;
  532. $sts = access_query("servertheme");
  533. $lts = access_query("loadtheme");
  534. $thmid=array_pop($sts);
  535. $fname=$themes[$thmid]["_fname"];
  536. if ($thmid==$fname) $thmid=$themes[$thmid]["theme_id"];
  537. if ((!is_array($themes[$thmid])) && ($ltid=array_pop($lts))) $fname=$ltid;
  538. clearstatcache();
  539. if (($themes[$thmid]["_mtime"]!=filemtime($fname)) && ($tmp_thm=load_theme($fname))) {
  540. if ($themes[$thmid]["_pmode"]=="master") int_sendtomaster(NM_RELOAD_THEME, $thmid);
  541. $themes[$thmid]=$tmp_thm;
  542. $themes[$fname]=$tmp_thm;
  543. $themes[$tmp_thm["_fname"]]=$tmp_thm;
  544. }
  545. $tlang=strtolower($themes[$thmid]["theme_language"]);
  546. if (($al=$GLOBALS["htreq_headers"]["ACCEPT-LANGUAGE"]) && ($als=nw_decode_mq_hdr($al))) {
  547. foreach (array_keys($als) as $lang) {
  548. $lang=strtolower($lang);
  549. if (isset($themes[$thmid][$template.":".$lang])) {
  550. $tname=$template.":".$lang;
  551. break;
  552. } else if ($tlang==$lang) {
  553. break;
  554. }
  555. }
  556. }
  557. if (!$tname) $tname=$template;
  558. $tmpl=$themes[$thmid][$tname] or
  559. $tmpl=$themes[DEFAULT_SERVER_THEME][$tname];
  560. foreach ($args as $k=>$v) $tr_arr["@$".strtolower($k)."@"]=$v;
  561. if (!$no_add) $tr_arr['@$server_signature@']=_genpage_signature();
  562. $trt=strtr($tmpl, $tr_arr);
  563. while ((($p=strpos($trt, "@!"))!==false) && (($p2=strpos(substr($trt, $p+2), "@"))!==false)) {
  564. $ret=substr($trt, 0, $p);
  565. $ret.=access_query(strtolower(substr($trt, $p+2, $p2)), 0);
  566. $ret.=substr($trt, $p+$p2+3);
  567. $trt=$ret;
  568. }
  569. return($trt);
  570. }
  571. function nw_server_vars($include_cgi_vars=false) {
  572. global $conf;
  573. $filename=$GLOBALS["docroot"].$GLOBALS["http_uri"];
  574. $nsv["SERVER_SOFTWARE"]=nw_server_string();
  575. $nsv["SERVER_NAME"]=$conf[$GLOBALS["vhost"]]["servername"][0];
  576. $nsv["SERVER_PROTOCOL"]=HTTP_VERSION;
  577. $nsv["SERVER_PORT"]=$GLOBALS["lport"];
  578. $nsv["SERVER_ADDR"]=$conf["global"]["listeninterface"][0];
  579. $nsv["SERVER_API"]=VERSION;
  580. $nsv["SERVER_ADMIN"]=$conf[$GLOBALS["vhost"]]["serveradmin"][0];
  581. $nsv["REQUEST_METHOD"]=$GLOBALS["http_action"];
  582. $nsv["PATH_TRANSLATED"]=$nsv["SCRIPT_FILENAME"]=nw_realpath($filename);
  583. $nsv["SCRIPT_NAME"]="/".$GLOBALS["docroot_prefix"].$GLOBALS["http_uri"];
  584. $nsv["QUERY_STRING"]=$GLOBALS["query_string"];
  585. $nsv["REMOTE_HOST"]=$GLOBALS["remote_host"];
  586. $nsv["REMOTE_ADDR"]=$GLOBALS["remote_ip"];
  587. $nsv["REMOTE_PORT"]=$GLOBALS["remote_port"];
  588. $nsv["AUTH_TYPE"]=$GLOBALS["auth_type"];
  589. $nsv["DOCUMENT_ROOT"]=$GLOBALS["docroot"];
  590. $nsv["REQUEST_URI"]="/".$GLOBALS["real_uri"].($nsv["QUERY_STRING"]?("?".$nsv["QUERY_STRING"]):"");
  591. $nsv["PATH_INFO"]=$GLOBALS["path_info"];
  592. if (($GLOBALS["logged_user"]) && ($GLOBALS["logged_user"] != " ")) {
  593. $nsv["REMOTE_USER"] = $GLOBALS["logged_user"];
  594. }
  595. if ($asv=access_query("addservervar")) foreach ($asv as $str) {
  596. $k=strtok($str, " ");
  597. $v=strtok("");
  598. if ($k) $nsv[$k]=$v;
  599. }
  600. if ($GLOBALS["add_nsv"]) foreach ($GLOBALS["add_nsv"] as $key=>$val) $nsv[$key]=$val;
  601. if ($include_cgi_vars && ($rq_hdrs=$GLOBALS["htreq_headers"])) foreach($rq_hdrs as $key=>$val) $nsv["HTTP_".str_replace("-", "_", $key)]=$val;
  602. return $nsv;
  603. }
  604. function nw_url_addslash($s) {
  605. $ret = strtok($s, "?")."/";
  606. if (($q = strtok("")) !== false) $ret .= "?".$q;
  607. return $ret;
  608. }
  609. function techo($s, $level=NW_EL_NOTICE, $flush=false) {
  610. global $conf;
  611. static $srv_buf;
  612. $tl=date("Ymd:His")." $s\n";
  613. if (!$conf["_complete"] && !$flush) {
  614. $srv_buf[]=array($tl, $level);
  615. } else {
  616. if (($conf["global"]["servermode"][0]!="inetd") && !$GLOBALS["quiet"]) {
  617. if ($srv_buf) foreach ($srv_buf as $sb_arr) echo $sb_arr[0];
  618. echo $tl;
  619. flush();
  620. }
  621. if ($srv_buf) {
  622. foreach ($srv_buf as $sb_arr) log_srv($sb_arr[0], $sb_arr[1]);
  623. $srv_buf=array();
  624. }
  625. log_srv($tl, $level);
  626. }
  627. }
  628. function errexit($s, $errno=-1) {
  629. global $pidfile, $start_daemon;
  630. $estr="FATAL: ".$s;
  631. techo($estr, NW_EL_ERROR, true);
  632. if ($pidfile) unlink($pidfile);
  633. if ($start_daemon && ($stderr=@fopen("php://stderr", "w"))) {
  634. fputs($stderr, $estr."\n");
  635. fclose($stderr);
  636. }
  637. exit($errno);
  638. }
  639. function url_to_absolute($url) {
  640. return("http://".$GLOBALS["conf"][$GLOBALS["vhost"]]["servername"][0].(($GLOBALS["lport"]!=80)?(":".$GLOBALS["lport"]):"").($url!="/"?"/":"").$url);
  641. }
  642. function loadfile($filename, $extension, &$rq_err, &$cgi_headers, $force_parser=false) {
  643. global $conf, $modules, $add_nsv;
  644. if (is_link($filename)) $filename=readlink($filename);
  645. chdir(dirname(nw_realpath($filename)));
  646. $filename=basename($filename);
  647. if (($parser=$force_parser) || ($parser=trim(access_query("_parseext", "_".strtolower($extension))))) {
  648. // Parsed content
  649. if (strpos($parser, " ")!==false) {
  650. $ps_type=strtok($parser, " ");
  651. $ps_arg=strtok("");
  652. if (strpos($ps_arg, '$')!==false) foreach (nw_server_vars() as $nkey=>$nval) $ps_arg=str_replace('$'.$nkey, $nval, $ps_arg);
  653. if (!$force_parser) {
  654. $add_nsv["REDIRECT_STATUS"]=$rq_err;
  655. $add_nsv["REDIRECT_URL"]="/".$GLOBALS["real_uri"];
  656. }
  657. } else $ps_type=$parser;
  658. } else {
  659. // Static content
  660. $ps_type=$conf["global"]["defaulthandler"][0];
  661. }
  662. if ($ps=$modules["parser_".$ps_type][0]) {
  663. if (is_object($rop=$ps->parser_open($ps_arg, $filename, $rq_err, $cgi_headers))) $ps=$rop;
  664. } else {
  665. $rq_err=500;
  666. $GLOBALS["add_errmsg"]="Unable to find an appropriate parser for this content type.<br><br>";
  667. $ps=$GLOBALS["null_response"];
  668. }
  669. return($ps);
  670. }
  671. function nw_host_to_vhost($host, $lport=80) {
  672. global $conf;
  673. // Try vhost=host:port
  674. if (is_array($conf[$phost=($host.":".$lport)])) return($phost);
  675. // Try vhost=host
  676. if (is_array($conf[$host])) return($host);
  677. // Try wildcards
  678. $hlen=strlen($host);
  679. for($vhlen=0;$vhlen<=$hlen;$vhlen++) {
  680. $whost="*".substr($host, $vhlen);
  681. if (is_array($conf[$phost=($whost.":".$lport)])) return($phost);
  682. if (is_array($conf[$whost])) return($whost);
  683. }
  684. // Or set to global
  685. return("global");
  686. }
  687. function nw_error_page($rq_err) {
  688. global $HTTP_HEADERS, $http_resource, $conf, $vhost, $add_errmsg;
  689. $err["error_code"]=$rq_err;
  690. $err["error_label"]=$HTTP_HEADERS[$rq_err];
  691. $err["error_add_message"]=$add_errmsg;
  692. $add_errmsg = "";
  693. $err["error_resource"]=($http_resource?(nw_apply_template(NW_TMPL_ERROR_RESOURCE, array("resource_name" => htmlentities($http_resource)), true)):"");
  694. if ($conf[$vhost]["serveradmin"][0]) $err["error_admin"]=nw_apply_template(NW_TMPL_ERROR_ADMIN, array("admin" => $conf[$vhost]["serveradmin"][0]), true);
  695. $err_page=nw_apply_template(NW_TMPL_ERROR_PAGE, $err) or
  696. $err_page="<html><head><title>".$HTTP_HEADERS[$rq_err]."</title></head><body><h1>".$HTTP_HEADERS[$rq_err]."</h1></body></html>";
  697. return($err_page);
  698. }
  699. function nw_use_chunked_encoding() {
  700. if (!isset($GLOBALS["lf"]->content_length) && $GLOBALS["keepalive"]) {
  701. if ($GLOBALS["http_version"]>="1.1") {
  702. return(true);
  703. } else {
  704. return("CLOSE");
  705. }
  706. } else return(false);
  707. }
  708. function nw_decode_mq_hdr($s) {
  709. if ($l=explode(",", $s)) foreach ($l as $e) {
  710. list($v, $q)=explode(";", $e);
  711. if ($q) list($d, $qn)=explode("=", $q);
  712. if (!$qn) $qn=1;
  713. if ($v) $r[$v]=$qn;
  714. }
  715. arsort($r);
  716. return($r);
  717. }
  718. function nw_allow_list($ext) {
  719. $tmp_marr=array();
  720. foreach ($GLOBALS["modules"] as $tmpmod) if (method_exists($tmpmod, "options")) if ($mod_methods=$tmpmod->options()) foreach ($mod_methods as $mod_method) if (!isset($tmp_marr[$mod_method])) $tmp_marr[$mod_method]=$mod_method;
  721. return ("GET, ".(access_query("_parseext", "_".strtolower($rq_file["extension"]))?"POST, ":"")."HEAD, OPTIONS".(count($tmp_marr)?", ":"").implode(", ", $tmp_marr));
  722. }
  723. function build_response_headers() {
  724. global $HTTP_HEADERS, $rq_err, $out_contenttype, $out_add_headers, $conf, $lf;
  725. if ($out_add_headers) {
  726. foreach ($out_add_headers as $key=>$val) switch (strtoupper($key)) {
  727. case "CONTENT-TYPE":
  728. $out_contenttype=$val;
  729. break;
  730. case "LOCATION":
  731. $rq_err=302;
  732. $add_headers.=$key.": ".$val."\r\n";
  733. break;
  734. case "COOKIES":
  735. foreach ($val as $cval) $add_headers.="Set-Cookie: ".$cval."\r\n";
  736. break;
  737. case "STATUS":
  738. $st=(int)strtok($val, " ");
  739. if ($stx=trim(strtok(""))) {
  740. $http_resp=$st." ".$stx;
  741. } else if ($stx=$HTTP_HEADERS[$st]) {
  742. $http_resp=$stx;
  743. } else {
  744. $http_resp=$st;
  745. }
  746. $rq_err=$st;
  747. break;
  748. default:
  749. $add_headers.=$key.": ".$val."\r\n";
  750. }
  751. }
  752. $clf=($GLOBALS["http_action"]=="HEAD"?$GLOBALS["hlf"]:$lf);
  753. $out_headers=HTTP_VERSION." ".($http_resp?trim($http_resp):$HTTP_HEADERS[$rq_err])."\r\n";
  754. $out_headers.="Date: ".gmdate("D, d M Y H:i:s T")."\r\n";
  755. if ($ss=nw_server_string()) $out_headers.="Server: ".$ss."\r\n";
  756. $out_headers.="Content-Type: ".$out_contenttype."\r\n";
  757. if ($ahlist=access_query("addheader")) foreach ($ahlist as $val) $out_headers.=trim($val)."\r\n";
  758. if (($rq_err>=400) && ($eh=access_query("_errorheader", "_".$rq_err))) $out_headers.=$eh."\r\n";
  759. $out_headers.=$add_headers;
  760. if ($GLOBALS["keepalive"]) {
  761. $out_headers.="Connection: Keep-Alive\r\n";
  762. $out_headers.="Keep-Alive: timeout=".(int)$conf["global"]["requesttimeout"][0].", max=".(int)($conf["global"]["keepalive"][0])."\r\n";
  763. } else {
  764. $out_headers.="Connection: close\r\n";
  765. }
  766. if ($GLOBALS["chunked"]) {
  767. $out_headers.="Transfer-Encoding: chunked\r\n";
  768. } else {
  769. if (is_int($clf->content_length)) $out_headers.="Content-Length: ".$clf->content_length."\r\n";
  770. }
  771. return($out_headers);
  772. }
  773. function nanoweb_init($conffile) {
  774. global $conf, $themes, $cmdline_conf_overrides, $cmdline_conf_adds, $modules, $posix_av, $pcntl_av, $gz_av, $mime, $access_policy, $sysusr, $sysgrp, $icnt, $banned_ips, $srvlog_levels;
  775. $dc=get_defined_constants();
  776. foreach ($dc as $cname=>$cval) if (substr($cname, 0, 6)=="NW_EL_") $srvlog_levels[strtolower(substr($cname, 6))]=$cval;
  777. $iconf=parseconfig(file($conffile));
  778. if (is_string($iconf)) {
  779. if ($icnt) {
  780. techo($iconf, NW_EL_WARNING);
  781. return(false);
  782. } else {
  783. errexit($iconf);
  784. }
  785. } else if (is_array($iconf)) {
  786. $conf=$iconf;
  787. }
  788. $conf=cmdline_conf_upd($conf, $cmdline_conf_overrides, $cmdline_conf_adds);
  789. $modules=load_modules($conf);
  790. modules_init();
  791. $themes=load_themes($conf);
  792. ++$icnt;
  793. $ap_aliases=array( "parseext" => "_parseext",
  794. "alias" => "_aliases",
  795. "errordocument" => "_errordocument",
  796. "errorheader" => "_errorheader" );
  797. $access_policy=array();
  798. foreach ($conf["global"]["accessoverride"] as $ov_dir) if ($ov_dir) $access_policy[strtolower($ov_dir)]="override";
  799. foreach ($conf["global"]["accessmerge"] as $mg_dir) if ($mg_dir) $access_policy[strtolower($mg_dir)]="merge";
  800. foreach ($conf["global"]["accessblock"] as $bl_dir) if ($bl_dir) $access_policy[strtolower($bl_dir)]="block";
  801. foreach ($ap_aliases as $rk=>$ak) if ($access_policy[$rk]) $access_policy[$ak]=$access_policy[$rk];
  802. $posix_av=is_callable("posix_setuid");
  803. $pcntl_av=is_callable("pcntl_fork");
  804. $gz_av=is_callable("gzencode");
  805. if (count($themes)==0) techo("WARN: No theme loaded, server generated content is disabled", NW_EL_WARNING);
  806. if ($posix_av) foreach ($conf as $vconf) {
  807. if ($u=$vconf["user"][0]) $sysusr[$u]=@posix_getpwnam($u);
  808. if ($g=$vconf["group"][0]) $sysgrp[$g]=@posix_getgrnam($g);
  809. }
  810. if ((!$conf["global"]["singleprocessmode"][0]) && (!$posix_av || !$pcntl_av || ($conf["global"]["servermode"][0]=="inetd"))) {
  811. techo("WARN: forcing single process mode", NW_EL_WARNING);
  812. $conf["global"]["singleprocessmode"][0]=true;
  813. }
  814. if ($conf["global"]["servermode"][0]=="inetd") {
  815. unset($conf["global"]["logtoconsole"]);
  816. unset($conf["global"]["pidfile"]);
  817. }
  818. if ($conf["global"]["singleprocessmode"][0]) {
  819. $conf["global"]["loggerprocess"]=0;
  820. if ($conf["global"]["keepalive"][0]) techo("WARN: KeepAlive should be set to 0 in single process mode", NW_EL_WARNING);
  821. }
  822. if ($pcntl_av) {
  823. pcntl_signal(SIGTERM, "nanoweb_shutdown");
  824. pcntl_signal(SIGHUP, "nanoweb_reload");
  825. }
  826. $mime=array();
  827. if (!@is_readable($conf["global"]["mimetypes"][0])) {
  828. techo("WARN: unable to read mime types file (".$conf["global"]["mimetypes"][0]."), using internals", NW_EL_WARNING);
  829. $mime=array( "html" => "text/html",
  830. "xml" => "text/xml",
  831. "gif" => "image/gif",
  832. "jpeg" => "image/jpeg",
  833. "png" => "image/png",
  834. "tgz" => "application/gtar");
  835. } else if ($mimetypes=@file($conf["global"]["mimetypes"][0])) {
  836. foreach ($mimetypes as $s) if (trim($s) && ($s{0}!="#")) if (ereg("([a-zA-Z0-9/.-]+)[ \t]+([a-zA-Z0-9 -]+)", $s, $res)) if ($exts=explode(" ", trim($res[2]))) foreach ($exts as $ext) if (trim($res[1]) && trim($ext)) $mime[$ext]=trim($res[1]);
  837. unset($mimetypes);
  838. }
  839. if ($at=$conf["global"]["addtype"]) foreach ($at as $adt) {
  840. $mt=strtok(trim($adt), " ");
  841. while ($s=strtok(" ")) $mime[ltrim($s, ".")]=$mt;
  842. }
  843. $conf["_complete"]=true;
  844. $banned_ips=array();
  845. if (is_array($conf["global"]["blockipaddr"])) foreach ($conf["global"]["blockipaddr"] as $ip) nw_block_ip_address($ip, "PERM", "config.BlockIPAddr");
  846. return(true);
  847. }
  848. function nanoweb_shutdown($sig_no=SIGTERM) {
  849. global $lsocks, $pidfile, $loggers_sck, $conf, $pmode;
  850. if ($pmode=="master") {
  851. modules_init("shutdown");
  852. if ($lsocks) foreach ($lsocks as $sock) socket_close($sock);
  853. if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
  854. techo("halting loggers");
  855. for ($a=0;$a<$nb_loggers;$a++) {
  856. $pkt="TERM";
  857. socket_write($loggers_sck, $pkt);
  858. usleep(100000);
  859. }
  860. sleep(1);
  861. }
  862. if ($pidfile) unlink($pidfile);
  863. techo("daemon stopped\n");
  864. }
  865. exit(0);
  866. }
  867. function nanoweb_reload($sig_no=SIGHUP) {
  868. global $mypid, $conffile, $conf, $loggers_sck, $logger_pids, $killed_loggers, $access_cache, $rp_cache;
  869. if (!$mypid) {
  870. techo("received SIGHUP, reloading configuration ...");
  871. clearstatcache();
  872. unset($access_cache);
  873. unset($rp_cache);
  874. if (nanoweb_init($conffile)) {
  875. if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
  876. techo("restarting loggers");
  877. foreach ($logger_pids as $lid) {
  878. $pkt="TERM";
  879. socket_write($loggers_sck, $pkt);
  880. usleep(100000);
  881. $killed_loggers[] = $lid;
  882. }
  883. sleep(1);
  884. }
  885. techo("configuration reloaded from ".$conffile);
  886. } else {
  887. techo("configuration was not reloaded", NW_EL_WARNING);
  888. }
  889. }
  890. }
  891. function read_request(&$sck_connected, &$dp, &$pn, $maxlen=0) {
  892. global $conf;
  893. static $rr_buffer;
  894. $wstart=time();
  895. if ($rr_buffer!=="") {
  896. $buf=$rr_buffer;
  897. $tnreq=true;
  898. }
  899. while (!$rq_finished && $sck_connected) {
  900. if (!$tnreq) {
  901. $fdset=$GLOBALS["pfdset"];
  902. if ($conf["global"]["servermode"][0]=="inetd") {
  903. // Inetd
  904. if (feof($GLOBALS["inetd_in"])) {
  905. $tmp=false;
  906. $sck_connected=false;
  907. } else $tmp=fgetc($GLOBALS["inetd_in"]);
  908. } else {
  909. if ($ns=socket_select($fdset, $write=NULL, $fdset, SCK_READ_SELECT_TIMEOUT)) $tmp=@socket_read($GLOBALS["msgsock"], SCK_READ_PACKET_SIZE); else $tmp=false;
  910. }
  911. }
  912. if ($tmp || $tmp==="0" || $tnreq) {
  913. $tnreq=false;
  914. $wstart=time();
  915. $buf.=$tmp;
  916. $pn=0;
  917. if (!$maxlen) {
  918. if (!$rnloop) {
  919. $buf=ltrim($buf);
  920. $rnloop=true;
  921. }
  922. if (($dp=strpos($buf, "\r\n\r\n"))!==false) $pn=4;
  923. else if (($dp=strpos($buf, "\n\n"))!==false) $pn=2;
  924. if ($pn) $rq_finished=true;
  925. } else {
  926. if (strlen($buf)>=$maxlen) $rq_finished=true;
  927. }
  928. } else if (($ns) || ((!$ns) && ((time()-$wstart)>=$conf["global"]["requesttimeout"][0]))) $sck_connected=false;
  929. }
  930. if (!$maxlen) {
  931. $tbuf=substr($buf, 0, $dp+$pn);
  932. $rr_buffer=substr($buf, $dp+$pn);
  933. $buf=$tbuf;
  934. } else {
  935. $tbuf=substr($buf, 0, $maxlen);
  936. $rr_buffer=substr($buf, $maxlen);
  937. $buf=$tbuf;
  938. }
  939. return($buf);
  940. }
  941. function send_response($response, &$sck_connected) {
  942. global $msgsock;
  943. $resp_len=strlen($response);
  944. while($sent_len<$resp_len && $sck_connected) {
  945. if ($GLOBALS["conf"]["global"]["servermode"][0]=="inetd") {
  946. // Inetd
  947. echo $response;
  948. $sent_len=strlen($response);
  949. } else {
  950. $fdset=$GLOBALS["pfdset"];
  951. if (($sent_len+SCK_WRITE_PACKET_SIZE)>$resp_len) $size=$resp_len-$sent_len; else $size=SCK_WRITE_PACKET_SIZE;
  952. if (($ret=@socket_write($msgsock, substr($response, $sent_len, $size), $size))>0) $sent_len+=$ret;
  953. if ($ret===false) {
  954. if (socket_last_error($msgsock)==SOCKET_EWOULDBLOCK) {
  955. socket_clear_error($msgsock);
  956. if (!socket_select($read=NULL, $fdset, $except=NULL, SCK_MAX_STALL_TIME)) $sck_connected=false;
  957. } else {
  958. $sck_connected=false;
  959. }
  960. }
  961. }
  962. }
  963. return($sent_len);
  964. }
  965. function int_sendtomaster($msg_type, $args=false) {
  966. if ((!$GLOBALS["conf"]["global"]["singleprocessmode"][0]) && ($GLOBALS["pmode"]!="master")) {
  967. $msg=$msg_type;
  968. if ($args!==false) $msg.=serialize($args);
  969. $ret=socket_write($GLOBALS["master_sck"], $msg);
  970. if ($ret!=strlen($msg)) {
  971. techo("WARN: master process is not responding", NW_EL_WARNING);
  972. }
  973. }
  974. }
  975. function _server_report_state($s, $remote_host="") {
  976. $tmp=array($GLOBALS["mypid"], $s);
  977. if ($remote_host) $tmp[]=$remote_host;
  978. int_sendtomaster(NM_SERVER_STATE, $tmp);
  979. }
  980. function nw_block_ip_address($ip_addr, $type, $source, $expires=0) {
  981. global $conf, $pmode, $banned_ips;
  982. if ((($conf["global"]["singleprocessmode"][0]) || ($pmode=="master")) && (!$banned_ips[$ip_addr])) {
  983. $banned_ips[$ip_addr]=array("type" => $type, "source" => $source, "expires" => $expires);
  984. techo($source." : blocked IP address ".$ip_addr." (".strtolower($type).")", NW_EL_BLOCKING);
  985. } else {
  986. int_sendtomaster(NM_BLOCK_IP, array($ip_addr, $type, $source, $expires));
  987. }
  988. }
  989. function nw_unblock_ip_address($ip_addr, $msg=false) {
  990. global $conf, $pmode, $banned_ips;
  991. if ((($conf["global"]["singleprocessmode"][0]) || ($pmode=="master")) && ($banned_ips[$ip_addr])) {
  992. $source=strtok($banned_ips[$ip_addr]["source"], ".");
  993. $rejs=$banned_ips[$ip_addr]["rejects"];
  994. unset($banned_ips[$ip_addr]);
  995. techo($source." : unblocked IP address ".$ip_addr." (".(int)$rejs." rejs".($msg===false?"":(", ".$msg)).")", NW_EL_BLOCKING);
  996. } else {
  997. int_sendtomaster(NM_UNBLOCK_IP, $ip_addr);
  998. }
  999. }
  1000. function logger_run($logger_id) {
  1001. global $conf, $children_logsck, $modules, $plgset, $pmode;
  1002. $pmode="logger";
  1003. pcntl_signal(SIGTERM, SIG_DFL);
  1004. pcntl_signal(SIGHUP, SIG_IGN);
  1005. $mypid=posix_getpid();
  1006. $lids=log_ids();
  1007. posix_setgid($lids["gid"]);
  1008. posix_setuid($lids["uid"]);
  1009. techo("logger process #".$logger_id." is running (pid=".$mypid.")");
  1010. while (!$logger_exit) {
  1011. $r=socket_read($children_logsck, INT_MSGSIZE);
  1012. switch($r) {
  1013. case "TERM":
  1014. $logger_exit=true;
  1015. break;
  1016. default:
  1017. $l=unserialize($r);
  1018. // Reverse DNS query if the server hasn't done it before
  1019. if (($conf["global"]["hostnamelookups"][0]) && ($conf["global"]["hostnamelookupsby"][0]=="logger") && ($rhost=nw_gethostbyaddr($l[2]))) $l[1]=$rhost;
  1020. // And call the logging modules
  1021. if ($nb_log_mods=count($modules["log"])) for ($a=0;$a<$nb_log_mods;$a++) $modules["log"][$a]->log_hit($l[0], $l[1], $l[2], $l[3], $l[4], $l[5], $l[6], $l[7], $l[8]);
  1022. break;
  1023. }
  1024. }
  1025. techo("logger process #".$logger_id." stopped");
  1026. exit(0);
  1027. }
  1028. function spawn_loggers($nb_loggers) {
  1029. global $logger_pids;
  1030. static $logger_id;
  1031. for ($a=0;$a<$nb_loggers;++$a) {
  1032. $pid=pcntl_fork();
  1033. ++$logger_id;
  1034. if ($pid===0) {
  1035. logger_run($logger_id);
  1036. } else {
  1037. $logger_pids[$pid]=$logger_id;
  1038. if ($nb_loggers>1) usleep(100000);
  1039. }
  1040. }
  1041. }
  1042. // Begin
  1043. set_time_limit(0);
  1044. $pmode="master";
  1045. techo("aEGiS nanoweb/".VERSION." (C) 2002-2005 by sIX / aEGiS");
  1046. $stats_start=time();
  1047. if (version_compare(phpversion(), REQUIRED_PHP_VERSION)<0) errexit("nanoweb needs PHP >= ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")");
  1048. if (version_compare(phpversion(), "4.3.0")>=0) $sckv3=true;
  1049. $os=((strpos(strtolower(PHP_OS), "win")===0) || (strpos(strtolower(PHP_OS), "cygwin")!==false))?"win32":"unix";
  1050. switch ($os) {
  1051. case "win32":
  1052. define("NW_BSAFE_READ_OPEN", "rb");
  1053. define("NW_BSAFE_WRITE_OPEN", "wb");
  1054. define("NW_BSAFE_APP_OPEN", "ab");
  1055. if (!defined("SOCKET_EWOULDBLOCK")) define("SOCKET_EWOULDBLOCK", 10035);
  1056. break;
  1057. default:
  1058. define("NW_BSAFE_READ_OPEN", "r");
  1059. define("NW_BSAFE_WRITE_OPEN", "w");
  1060. define("NW_BSAFE_APP_OPEN", "a");
  1061. if (!defined("SOCKET_EWOULDBLOCK")) define("SOCKET_EWOULDBLOCK", 11);
  1062. break;
  1063. }
  1064. foreach ($TEST_FUNCS as $f_name=>$f_mandatory) if (!is_callable($f_name)) {
  1065. if ($f_mandatory) errexit("function '".$f_name."' not available, aborting");
  1066. else techo("WARN: function '".$f_name."' not available", NW_EL_WARNING);
  1067. }
  1068. // Parse command line
  1069. if ($_SERVER["argc"]>1) for($a=1;$a<$_SERVER["argc"];$a++) {
  1070. if (($a==1) && (substr($_SERVER["argv"][$a], 0, 1)!="-")) {
  1071. $cmdline_cf=$_SERVER["argv"][1];
  1072. } else {
  1073. $ca=explode("=", $_SERVER["argv"][$a]);
  1074. $ck=array_shift($ca);
  1075. $cv=implode("=", $ca);
  1076. switch($ck) {
  1077. case "-?":
  1078. case "-h":
  1079. case "--help":
  1080. die($cmdline_help);
  1081. break;
  1082. case "-v":
  1083. case "--version":
  1084. die(VERSION."\n");
  1085. break;
  1086. case "-c":
  1087. case "--config":
  1088. $cmdline_cf=$cv;
  1089. break;
  1090. case "-o":
  1091. case "--set-option":
  1092. $cmdline_conf_overrides[]=$cv;
  1093. break;
  1094. case "-a":
  1095. case "--add-option":
  1096. $cmdline_conf_adds[]=$cv;
  1097. break;
  1098. case "-d":
  1099. case "--start-daemon":
  1100. $start_daemon=true;
  1101. break;
  1102. case "-q":
  1103. case "--quiet":
  1104. $quiet=true;
  1105. break;
  1106. case "--debug":
  1107. $nw_debug=true;
  1108. case "--verbose":
  1109. break;
  1110. case "--config-test":
  1111. case "-t":
  1112. $config_test=true;
  1113. break;
  1114. default:
  1115. errexit("unknown argument : ".$_SERVER["argv"][$a].", try --help");
  1116. break;
  1117. }
  1118. }
  1119. }
  1120. if ($cmdline_cf) $conffile=$cmdline_cf; else $conffile=DEFAULT_CONFIG_FILE;
  1121. if (!is_readable($conffile)) errexit("unable to read configuration (".$conffile."), aborting");
  1122. unset($cmdline_help);
  1123. nanoweb_init($conffile);
  1124. if ($config_test) {
  1125. techo("configuration test successful");
  1126. exit(0);
  1127. }
  1128. if ($conf["global"]["servermode"][0]!="inetd") {
  1129. // Create socket(s) and start listening
  1130. if ($sckv3) {
  1131. $setsockopt="socket_set_option";
  1132. $getsockopt="socket_get_option";
  1133. } else {
  1134. $setsockopt="socket_setopt";
  1135. $getsockopt="socket_getopt";
  1136. }
  1137. foreach ($conf["global"]["listenport"] as $lport) {
  1138. $lport=(int)$lport;
  1139. if (($sock = @socket_create(AF_INET, SOCK_STREAM, 0))<0) errexit("socket create failed : ".socket_strerror(socket_last_error()));
  1140. if (is_callable($setsockopt) && is_callable($getsockopt)) {
  1141. $setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1);
  1142. $sbuf=$getsockopt($sock, SOL_SOCKET, SO_SNDBUF);
  1143. $rbuf=$getsockopt($sock, SOL_SOCKET, SO_RCVBUF);
  1144. if ($sbuf<(SCK_WRITE_PACKET_SIZE*32)) $setsockopt($sock, SOL_SOCKET, SO_SNDBUF, SCK_WRITE_PACKET_SIZE*32);
  1145. if ($rbuf<(SCK_READ_PACKET_SIZE*32)) $setsockopt($sock, SOL_SOCKET, SO_RCVBUF, SCK_READ_PACKET_SIZE*32);
  1146. }
  1147. if (!@socket_bind($sock, $conf["global"]["listeninterface"][0], $lport)) errexit("socket bind failed on port ".$lport." : ".socket_strerror(socket_last_error($sock)));
  1148. if (!@socket_listen($sock, $conf["global"]["listenqueue"][0])) errexit("socket listen failed on port ".$lport." : ".socket_strerror(socket_last_error($sock)));
  1149. socket_set_nonblock($sock);
  1150. $lsocks[$lport]=$sock;
  1151. $lports[$sock]=$lport;
  1152. }
  1153. }
  1154. if ($pcntl_av) {
  1155. $sck_pair=array();
  1156. socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $sck_pair);
  1157. $children_sck=&$sck_pair[0];
  1158. $master_sck=&$sck_pair[1];
  1159. socket_set_nonblock($children_sck);
  1160. socket_set_nonblock($master_sck);
  1161. }
  1162. $plnset=array($children_sck);
  1163. foreach ($lsocks as $sck) $plnset[]=$sck;
  1164. if ($conf["global"]["servermode"][0]!="inetd") {
  1165. techo("listening on port".(count($lports)>1?"s":"")." ".implode(", ", $lports));
  1166. $stdfd = fopen("php://stdin", "r");
  1167. fclose($stdfd);
  1168. $stdfd = fopen(($os == "unix") ? "/dev/null" : "NUL", "r");
  1169. } else {
  1170. $inetd_in=fopen("php://stdin", NW_BSAFE_READ_OPEN);
  1171. set_file_buffer($inetd_in, 0);
  1172. techo("running in inetd mode");
  1173. }
  1174. $def_cnx=($conf["global"]["servermode"][0]=="inetd");
  1175. if ($start_daemon) {
  1176. if (!$posix_av || !$pcntl_av) errexit("posix and pcntl PHP extensions are needed for --start-daemon");
  1177. $npid = pcntl_fork();
  1178. if ($npid == -1) {
  1179. errexit("unable to pcntl_fork()");
  1180. } else if ($npid) {
  1181. exit(0);
  1182. }
  1183. posix_setsid();
  1184. usleep(100000);
  1185. $npid = pcntl_fork();
  1186. if ($npid == -1) {
  1187. errexit("unable to pcntl_fork()");
  1188. } else if ($npid) {
  1189. techo("running in background");
  1190. exit(0);
  1191. }
  1192. }
  1193. if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
  1194. // Prepare and spawn logger processes if specified
  1195. techo("spawning loggers");
  1196. $sck_pair=array();
  1197. socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $sck_pair);
  1198. $children_logsck=&$sck_pair[0];
  1199. $loggers_sck=&$sck_pair[1];
  1200. socket_set_nonblock($loggers_sck);
  1201. spawn_loggers($nb_loggers);
  1202. } else {
  1203. // Be sure not to ask anything to loggers
  1204. $conf["global"]["hostnamelookupsby"][0]="server";
  1205. }
  1206. if ($posix_av && $conf["global"]["pidfile"][0]) {
  1207. $pidfile=$conf["global"]["pidfile"][0];
  1208. if ($f=fopen($pidfile, "w")) {
  1209. fputs($f, (string)posix_getpid()."\n");
  1210. fclose($f);
  1211. } else {
  1212. techo("WARN: unable to open pid file '".$pidfile."'", NW_EL_WARNING);
  1213. unset($pidfile);
  1214. }
  1215. }
  1216. if (empty($nw_debug)) error_reporting(E_PARSE | E_ERROR);
  1217. if ($conf["global"]["servermode"][0]!="inetd") techo("ready and accepting connections");
  1218. while (true) {
  1219. $cnx=$def_cnx;
  1220. while (!$cnx) {
  1221. declare (ticks = 1) {
  1222. // Allow to catch signals here
  1223. $lnset=$plnset;
  1224. }
  1225. $ns=socket_select($lnset, $write=NULL, $except=NULL, 1);
  1226. if ($ns) foreach ($lnset as $lnact) {
  1227. if ($lnact==$children_sck) {
  1228. while ($msg=socket_read($children_sck, INT_MSGSIZE)) {
  1229. // Message from a child process
  1230. $mtype=substr($msg, 0, 5);
  1231. if (strlen($msg)>5) $mcontent=unserialize(substr($msg, 5));
  1232. switch ($mtype) {
  1233. case NM_HIT:
  1234. // content : 0 => pid, 1 => request status, 2 => length, 3 => vhost
  1235. if (is_array($mcontent)) {
  1236. ++$stats_resperr[$mcontent[1]];
  1237. ++$stats_vhosts[$conf[$mcontent[3]]["servername"][0]];
  1238. ++$stats_hits;
  1239. $stats_xfer+=(float)$mcontent[2];
  1240. if ($scoreboard[$spid=$mcontent[0]]) {
  1241. $scoreboard[$spid][NW_SB_STATUS]="(waiting for request)";
  1242. }
  1243. }
  1244. break;
  1245. case NM_RESTART_LOGGERS:
  1246. techo("respawning loggers");
  1247. spawn_loggers($conf["global"]["loggerprocess"][0]);
  1248. break;
  1249. case NM_RELOAD_THEME:
  1250. // content : theme id
  1251. clearstatcache();
  1252. if ((is_array($themes[$mcontent])) && ($themes[$mcontent]["_mtime"]!=filemtime($fname=$themes[$mcontent]["_fname"]))) {
  1253. $tmp_thm=load_theme($fname, true, true);
  1254. $themes[$mcontent]=$tmp_thm;
  1255. $themes[$fname]=$tmp_thm;
  1256. $themes[$tmp_thm["_fname"]]=$tmp_thm;
  1257. }
  1258. break;
  1259. case NM_SERVER_STATE:
  1260. // content : 0 => pid, 1 => status text, 2 => remote host
  1261. if ($scoreboard[$spid=$mcontent[0]]) {
  1262. $scoreboard[$spid][NW_SB_STATUS]=$mcontent[1];
  1263. if ($mcontent[2]) $scoreboard[$spid][NW_SB_PEERHOST]=$mcontent[2];
  1264. }
  1265. break;
  1266. case NM_BLOCK_IP:
  1267. // content : 0 => ip address, 1 => type (PERM|TEMP), 2 => source, 3 => expiration
  1268. if (is_array($mcontent)) nw_block_ip_address($mcontent[0], $mcontent[1], $mcontent[2], $mcontent[3]);
  1269. break;
  1270. case NM_UNBLOCK_IP:
  1271. // content : ip address
  1272. if ($mcontent) nw_unblock_ip_address($mcontent);
  1273. break;
  1274. }
  1275. }
  1276. } else {
  1277. $sock=$lnact;
  1278. $lport=$lports[$sock];
  1279. if ((($active_servers<$conf["global"]["maxservers"][0]) || (!$conf["global"]["maxservers"][0])) && (is_resource($msgsock=@socket_accept($sock)))) {
  1280. // We do have a connection
  1281. $remote_ip=$remote_port=0;
  1282. socket_getpeername($msgsock, $remote_ip, $remote_port);
  1283. if (is_array($banned_ips[$remote_ip])) {
  1284. // Disconnect if IP address is banned
  1285. techo("rejected connection #".(++$banned_ips[$remote_ip]["rejects"])." from blocked IP address ".$remote_ip, NW_EL_BLOCKING);
  1286. socket_close($msgsock);
  1287. ++$stats_rej;
  1288. } else if ($remote_ip) {
  1289. // Or handle the new connection
  1290. $cnx=true;
  1291. ++$stats_cnx;
  1292. } else {
  1293. // Cannot obtain peer IP address, something is wrong (but not worth throwing a notice)
  1294. socket_close($msgsock);
  1295. }
  1296. }
  1297. }
  1298. }
  1299. if (!$conf["global"]["singleprocessmode"][0]) while (($deadpid=pcntl_waitpid(-1, $cstat, WNOHANG)) && ($deadpid!=-1)) {
  1300. // Dead child
  1301. if (($dead_logger=$logger_pids[$deadpid]) && ($conf["global"]["loggerprocess"][0])) {
  1302. // Dead logger (this is abnormal, we have to restart one)
  1303. if (in_array($dead_logger, $killed_loggers)) {
  1304. unset($killed_loggers[array_search($dead_logger, $killed_loggers)]);
  1305. } else {
  1306. techo("logger process #".$dead_logger." died (pid=".$deadpid."), respawning", NW_EL_WARNING);
  1307. }
  1308. unset($logger_pids[$deadpid]);
  1309. spawn_loggers(1);
  1310. } else {
  1311. // Dead child server, clear servers table
  1312. unset($scoreboard[$deadpid]);
  1313. --$active_servers;
  1314. }
  1315. }
  1316. $ct=time();
  1317. foreach ($banned_ips as $ip_addr=>$bip) if (($bip["type"]=="TEMP") && ($bip["expires"]<=$ct)) nw_unblock_ip_address($ip_addr, "expired");
  1318. }
  1319. if ($conf["global"]["singleprocessmode"][0]) {
  1320. $pid=0;
  1321. // Invalidate access and rp caches every SPM_CACHES_LIFETIME connections
  1322. if (($stats_cnx%SPM_CACHES_LIFETIME)==0) {
  1323. clearstatcache();
  1324. unset($access_cache);
  1325. unset($rp_cache);
  1326. }
  1327. } else {
  1328. $pid=pcntl_fork();
  1329. if ($pid===0) $pmode="server";
  1330. }
  1331. if ($pid===0) {
  1332. if ($posix_av) $mypid=posix_getpid();
  1333. if (!$conf["global"]["singleprocessmode"][0]) {
  1334. foreach ($lsocks as $sock) socket_close($sock);
  1335. set_time_limit($conf["global"]["childlifetime"][0]);
  1336. }
  1337. if ($conf["global"]["servermode"][0]!="inetd") {
  1338. socket_set_nonblock($msgsock);
  1339. $pfdset=array($msgsock);
  1340. if (($conf["global"]["hostnamelookups"][0]) && ($conf["global"]["hostnamelookupsby"][0]!="logger") && ($rhost=nw_gethostbyaddr($remote_ip))) {
  1341. $remote_host=$rhost;
  1342. _server_report_state("(connected)", $remote_host);
  1343. } else {
  1344. $remote_host=$remote_ip;
  1345. }
  1346. } else {
  1347. $remote_ip=getenv("INETD_REMOTE_IP");
  1348. $remote_port=getenv("INETD_REMOTE_PORT");
  1349. }
  1350. $rq_count=0;
  1351. while ($cnx) {
  1352. $sck_connected=true;
  1353. $http_continue=false;
  1354. $http_rq_block=$buf=read_request($sck_connected, $dp, $pn);
  1355. $pri_redir=$http_uri=$out_headers="";
  1356. $pri_err=$pri_redir_code=$rq_err=0;
  1357. $add_nsv=$htreq_headers=$out_add_headers=array();
  1358. if ($sck_connected) {
  1359. if (strlen($buf)!=$dp+4) $add_req=substr($buf, $dp+4); else $add_req="";
  1360. $tmp_arr=explode("\n", substr($buf, 0, $dp));
  1361. $l=false;
  1362. foreach ($tmp_arr as $s) {
  1363. $s=trim($s);
  1364. if (!$l) {
  1365. $http_action=strtok($s, " ");
  1366. $http_resource=strtok(" ");
  1367. $http_protocol=strtoupper(strtok("/"));
  1368. $http_version=strtok("");
  1369. $l=true;
  1370. if ($http_protocol!="HTTP") {
  1371. // Invalid protocol
  1372. $pri_err=400;
  1373. $add_errmsg="Unable to serve requested protocol.<br><br>";
  1374. }
  1375. } else {
  1376. if (strpos($s, ":")===false) {
  1377. // Invalid request header
  1378. $pri_err=400;
  1379. $add_errmsg="Invalid request header received.<br><br>";
  1380. } else {
  1381. $hd_key=strtoupper(strtok($s, ":"));
  1382. $hd_val=trim(strtok(""));
  1383. if (isset($htreq_headers[$hd_key])) {
  1384. $pri_err=400;
  1385. $add_errmsg="Duplicate request header '{$hd_key}'<br><br>";
  1386. } else {
  1387. $htreq_headers[$hd_key]=$hd_val;
  1388. }
  1389. }
  1390. }
  1391. }
  1392. // Decode Host header
  1393. $host=strtok(trim(strtolower($htreq_headers["HOST"])), ":");
  1394. $vhost=nw_host_to_vhost($host, $lport);
  1395. if ($auth_hdr=$htreq_headers["AUTHORIZATION"]) {
  1396. // Decode HTTP Authentication header
  1397. $dtmp=explode(" ", $auth_hdr);
  1398. $auth_type=$dtmp[0];
  1399. $auth_lp=explode(":", base64_decode($dtmp[1]));
  1400. $auth_user=$auth_lp[0];
  1401. $auth_pass=$auth_lp[1];
  1402. } else $auth_type=$auth_user=$auth_pass="";
  1403. // Decode Keep-Alive header
  1404. $keepalive=(strtolower(trim($htreq_headers["CONNECTION"]))=="keep-alive" && (int)$conf["global"]["keepalive"][0]>1);
  1405. if ($keepalive && (++$rq_count>=(int)$conf["global"]["keepalive"][0])) $keepalive=false;
  1406. $cnx=$keepalive;
  1407. // Set Uid and Gid
  1408. $cfgid=$conf[$vhost]["group"][0];
  1409. $cfuid=$conf[$vhost]["user"][0];
  1410. if ($posix_av) {
  1411. $ugtok=$sysusr[$cfuid]["uid"].$sysgrp[$cfgid]["gid"];
  1412. if ($uid_set) {
  1413. if ($uid_set!=$ugtok) {
  1414. // Keep-alive request for another user/group vhost, this is bad
  1415. $pri_err=400;
  1416. }
  1417. } else {
  1418. if ($setgid=$sysgrp[$cfgid]["gid"]) posix_setgid($setgid);
  1419. if ($setuid=$sysusr[$cfuid]["uid"]) posix_setuid($setuid);
  1420. $uid_set=$ugtok;
  1421. }
  1422. }
  1423. $docroot=$conf[$vhost]["documentroot"][0];
  1424. $docroot_prefix="";
  1425. if ($exp_hdr=$htreq_headers["EXPECT"]) {
  1426. // Enforce HTTP Expect header
  1427. if (trim(strtolower($exp_hdr))=="100-continue") {
  1428. $http_continue=true;
  1429. } else {
  1430. $pri_err=417;
  1431. }
  1432. }
  1433. if ($p1=$http_resource) {
  1434. $p1=explode("?", $p1);
  1435. $real_uri=ltrim($http_uri=rawurldecode($p1[0]), "/");
  1436. $http_uri=str_replace(chr(0), "", $http_uri);
  1437. $query_string=$p1[1];
  1438. $hu=$docroot.$real_uri;
  1439. // Load access files if needed
  1440. unset($access);
  1441. if (is_dir($hu)) {
  1442. $uridir=substr($http_uri, 1);
  1443. } else if (is_dir($docroot.($uridn=dirname($http_uri)))) {
  1444. $uridir=substr($uridn, 1);
  1445. } else $uridir="";
  1446. if (($accessdir=nw_realpath($docroot.$uridir)) && ($conf["global"]["accessfile"][0])) load_access_files($accessdir, $access);
  1447. core_modules_hook("before_decode");
  1448. foreach (access_query("_aliases") as $key=>$val) if (strpos(rtrim($http_uri, "/"), rtrim($key, "/"))===0) {
  1449. // Alias
  1450. $docroot=$val.((substr($val, -1)==DIRECTORY_SEPARATOR)?"":DIRECTORY_SEPARATOR);
  1451. $docroot_prefix=trim($key, "/")."/";
  1452. $http_uri=str_replace($key, "", $http_uri);
  1453. if ((is_dir($docroot.$http_uri)) && (substr($docroot.$http_uri, -1)!="/")) $pri_redir=nw_url_addslash($http_resource);
  1454. break;
  1455. }
  1456. $http_uri=ltrim($http_uri, "/");
  1457. if ($http_uri{0}=="~") {
  1458. // User directory
  1459. if (($udadd=$conf[$vhost]["userdir"][0]) && ($posix_av)) {
  1460. $upos=strpos($http_uri, "/");
  1461. $udname=substr($http_uri, 1, (($upos===false)?(strlen($http_uri)):($upos-1)));
  1462. $udres=(($upos===false)?"":(substr($http_uri, $upos+1)));
  1463. if ($udinf=@posix_getpwnam($udname)) {
  1464. $tmpdr=$udinf["dir"].DIRECTORY_SEPARATOR.$udadd.DIRECTORY_SEPARATOR;
  1465. if (is_dir($tmpdr)) {
  1466. if ((is_dir($tmpdr.$udres)) && (substr($http_uri, -1)!="/")) {
  1467. $pri_redir=nw_url_addslash($http_resource);
  1468. } else {
  1469. $docroot=$tmpdr;
  1470. $docroot_prefix="~".$udname."/";
  1471. $http_uri=$udres;
  1472. }
  1473. } else {
  1474. // User exists but does not have a public html directory
  1475. $pri_err=404;
  1476. }
  1477. } else {
  1478. // User does not exists
  1479. $pri_err=404;
  1480. }
  1481. }
  1482. }
  1483. if (is_dir($docroot.$http_uri) && !$pri_redir) {
  1484. if ($http_uri && substr($http_uri, -1)!="/") {
  1485. $pri_redir=nw_url_addslash($http_resource);
  1486. } else if ($dilist=access_query("directoryindex", 0)) {
  1487. $dis=explode(" ", $dilist);
  1488. foreach ($dis as $diname) {
  1489. switch ($diname{0}) {
  1490. case DIRECTORY_SEPARATOR:
  1491. if (@is_readable($diname)) {
  1492. $docroot=dirname($diname).DIREECTORY_SEPARATOR;
  1493. $http_uri=basename($diname);
  1494. break;
  1495. }
  1496. break;
  1497. default:
  1498. if (@is_readable($docroot.$http_uri.$diname)) {
  1499. $http_uri.=$diname;
  1500. break;
  1501. }
  1502. }
  1503. }
  1504. }
  1505. }
  1506. $path_info="";
  1507. if (access_query("allowpathinfo", 0) && !file_exists($docroot.$http_uri)) {
  1508. // Try path_info
  1509. $new_uri=$http_uri;
  1510. while (!@is_file($docroot.$new_uri) && $new_uri) {
  1511. $new_uri=substr($new_uri, 0, strrpos($new_uri, "/"));
  1512. if (!@is_file($docroot.$new_uri) && $pie_arr=access_query("pathinfotryext")) foreach ($pie_arr as $pie_ext) if (@is_file($docroot.$new_uri.".".$pie_ext)) {
  1513. $new_uri.=".".$pie_ext;
  1514. break;
  1515. }
  1516. }
  1517. if ($new_uri) {
  1518. // Path_info found
  1519. $path_info=substr($http_uri, strlen($new_uri));
  1520. $http_uri=$new_uri;
  1521. }
  1522. }
  1523. $rq_file=pathinfo($http_uri);
  1524. }
  1525. $hbn=basename($http_uri);
  1526. unset($bad_rq);
  1527. // File access security tests
  1528. if (nw_realpath($docroot.$http_uri) && (strpos(nw_realpath($docroot.$http_uri), nw_realpath($docroot))===false)) {
  1529. $bad_rq=NW_BAD_OUTSIDE_DOCROOT;
  1530. }
  1531. if (($conf[$vhost]["ignoredotfiles"][0]) && ($hbn{0}==".") && ($hbn!="..") && ($hbn!=".")) {
  1532. $bad_rq=NW_BAD_DOT_FILE;
  1533. }
  1534. if (($os == "win32") && in_array(strtolower(strtok($hbn, ".")), $win_devices)) {
  1535. $bad_rq=NW_BAD_WIN_DEVICE;
  1536. }
  1537. if (($bad_rq==NW_BAD_OUTSIDE_DOCROOT) && ($als_arr=$conf[$vhost]["allowsymlinkto"])) {
  1538. // Test for outside-docroot access exemptions (AllowSymlinkTo)
  1539. $tdir=$http_uri;
  1540. while ($tdir) {
  1541. if ((is_link($docroot.$tdir)) && (strpos(nw_realpath(dirname($docroot.$tdir)), nw_realpath($docroot))===0)) foreach ($als_arr as $als) if (strpos(nw_realpath($docroot.$http_uri), nw_realpath($als))===0) {
  1542. unset($bad_rq);
  1543. break 2;
  1544. }
  1545. $tdir=substr($tdir, 0, strrpos($tdir, "/"));
  1546. }
  1547. }
  1548. if ($bad_rq) switch ($bad_rq) {
  1549. case NW_BAD_OUTSIDE_DOCROOT:
  1550. techo("NOTICE: discarded request outside of document root (".$docroot.$http_uri.")");
  1551. $http_uri="";
  1552. $pri_err=404;
  1553. break;
  1554. case NW_BAD_DOT_FILE:
  1555. techo("NOTICE: discarded request for dot file (".$docroot.$http_uri.")");
  1556. $http_uri="";
  1557. $pri_err=404;
  1558. break;
  1559. case NW_BAD_WIN_DEVICE:
  1560. techo("NOTICE: discarded request for windows device file (".$docroot.$http_uri.")");
  1561. $http_uri="";
  1562. $pri_err=404;
  1563. break;
  1564. }
  1565. $sst=$http_action." http://".$htreq_headers["HOST"]."/".$real_uri.($query_string?("?".$query_string):"");
  1566. _server_report_state($sst);
  1567. if ($hu!=($docroot.$http_uri)) {
  1568. // Reload access files if needed
  1569. $hu=$docroot.$http_uri;
  1570. unset($access);
  1571. if (is_dir($hu)) {
  1572. $uridir=$http_uri;
  1573. } else if (is_dir($docroot.($uridn=dirname($http_uri)))) {
  1574. $uridir=$uridn;
  1575. } else $uridir="";
  1576. if (($accessdir=nw_realpath($docroot.$uridir)) && ($conf["global"]["accessfile"][0])) {
  1577. load_access_files($accessdir, $access);
  1578. }
  1579. }
  1580. $out_contenttype=$default_ct=access_query("defaultcontenttype", 0);
  1581. // AuthLocation handler
  1582. $bypass_auth = false;
  1583. if ($authls=access_query("authlocation")) {
  1584. $bypass_auth = true;
  1585. foreach ($authls as $authl) if (strpos("/".$real_uri, $authl) === 0) {
  1586. $bypass_auth = false;
  1587. break;
  1588. }
  1589. }
  1590. // Auth handler
  1591. $logged_user="";
  1592. if (($rauths=access_query("authrequire")) && (!$bypass_auth)) {
  1593. foreach ($rauths as $rauth) {
  1594. if ($spos=strpos($rauth, " ")) {
  1595. $authtype=strtolower(strtok($rauth, " "));
  1596. $authargs=trim(strtok(""));
  1597. } else {
  1598. $authtype=strtolower($rauth);
  1599. $authargs="";
  1600. }
  1601. $authmodn="auth_".strtolower($authtype);
  1602. if (is_object($modules[$authmodn][0])) {
  1603. if ($modules[$authmodn][0]->auth($auth_user, $auth_pass, $authargs)) {
  1604. $logged_user=$auth_user;
  1605. break;
  1606. }
  1607. } else {
  1608. techo("WARN: authentication module not found for type '".$authtype."'", NW_EL_WARNING);
  1609. }
  1610. }
  1611. if ($logged_user==="") {
  1612. $logged_user=" ";
  1613. $pri_err=401;
  1614. $out_add_headers["WWW-Authenticate"]="Basic realm=\"".access_query("authrealm", 0)."\"";
  1615. if ($emsg=access_query("authmessage", 0)) $add_errmsg.=$emsg."<br><br>";
  1616. }
  1617. }
  1618. // Test for maximum URI length
  1619. if (($conf[$vhost]["maxrequesturilength"][0]) && (strlen($http_resource)>$conf[$vhost]["maxrequesturilength"][0])) {
  1620. $pri_err=414;
  1621. }
  1622. if ($htreq_headers["CONTENT-LENGTH"]) {
  1623. // Read request content if there is one (POST requests)
  1624. if (($maxblen=$conf[$vhost]["maxrequestbodylength"][0]) && ((int)$htreq_headers["CONTENT-LENGTH"]>$maxblen)) {
  1625. // Request content is too large
  1626. $pri_err=413;
  1627. } else {
  1628. if ($http_continue && !$pri_err) send_response(HTTP_VERSION." ".$HTTP_HEADERS[100]."\r\n\r\n", $sck_connected);
  1629. $buf=$add_req;
  1630. if (strlen($buf)<$htreq_headers["CONTENT-LENGTH"]) $buf.=read_request($sck_connected, $dp, $pn, $htreq_headers["CONTENT-LENGTH"]-strlen($buf));
  1631. $htreq_content=substr($buf, 0, $htreq_headers["CONTENT-LENGTH"]);
  1632. }
  1633. }
  1634. core_modules_hook("after_decode");
  1635. if ($sck_connected) {
  1636. switch ($http_action) {
  1637. case "POST":
  1638. if ((!access_query("_parseext", "_".strtolower($rq_file["extension"]))) && (is_file($docroot.$http_uri)) && (!$pri_parser)) {
  1639. // Disallow POST on static content
  1640. $pri_err=405;
  1641. $out_add_headers["Allow"]=nw_allow_list($rq_file["extension"]);
  1642. }
  1643. case "GET":
  1644. case "HEAD":
  1645. if ($pri_err) {
  1646. // Internal setting of http error
  1647. $rq_err=$pri_err;
  1648. } else if ($pri_redir) {
  1649. // Internal redirection
  1650. if ($rq_err<300) {
  1651. $rq_err=$pri_redir_code
  1652. or $rq_err=302;
  1653. }
  1654. $out_add_headers["Location"]=$pri_redir;
  1655. if (version_compare($http_version, "1.0")<=0) $out_add_headers["URI"]=$pri_redir;
  1656. } else if (is_object($umod=&$modules["url:/".$http_uri])) {
  1657. // Module URL Hook
  1658. if ($umod->modtype=="url2") {
  1659. $lf=$umod;
  1660. $lf->parser_open("", $real_uri, $rq_err, $out_add_headers, $out_contenttype);
  1661. } else {
  1662. $lf =& new static_response($umod->url($rq_err, $out_contenttype, $out_add_headers));
  1663. }
  1664. } else if (is_dir($docroot.$http_uri)) {
  1665. // Directory without index
  1666. $rq_err=404;
  1667. core_modules_hook("directory_handler");
  1668. } else if (!is_file($docroot.$http_uri)) {
  1669. // 404 Not Found
  1670. $rq_err=404;
  1671. } else if (!is_readable($docroot.$http_uri)) {
  1672. // 403 Forbidden
  1673. $rq_err=403;
  1674. } else {
  1675. // 200 OK
  1676. $rq_err=200;
  1677. if ($pp=access_query("forcehandler", 0)) $pri_parser=$pp;
  1678. $lf=loadfile($docroot.$http_uri, $rq_file["extension"], $rq_err, $out_add_headers, $pri_parser);
  1679. /* libphpHACK */
  1680. #<off># if (isset($__nw_libphp_script)) { include($__nw_libphp_script); exit; }
  1681. if ($mimetype=$mime[strtolower($rq_file["extension"])]) {
  1682. // Lookup mime type in internal table
  1683. $out_contenttype=$mimetype;
  1684. } else if (is_callable("mime_content_type") && (!access_query("disablemimemagic", 0)) && ($mimetype=mime_content_type($docroot.$http_uri))) {
  1685. // Fallback to mime magic if available
  1686. $out_contenttype=$mimetype;
  1687. } else {
  1688. // Or use default
  1689. $out_contenttype=$default_ct;
  1690. }
  1691. }
  1692. break;
  1693. case "OPTIONS":
  1694. $rq_err=200;
  1695. $out_add_headers["Allow"]=nw_allow_list($rq_file["extension"]);
  1696. break;
  1697. default:
  1698. if ($mmod=$modules["method:".$http_action]) {
  1699. $rq_err=200;
  1700. $lf=$mmod;
  1701. $lf->parser_open("", $real_uri, $rq_err, $out_add_headers, $out_contenttype);
  1702. } else if (!$http_action) {
  1703. $rq_err=400;
  1704. } else {
  1705. $rq_err=501;
  1706. }
  1707. break;
  1708. }
  1709. unset($pri_parser);
  1710. if ($rq_err!=200 && $rq_err!=416) {
  1711. // Error messages
  1712. if ($rq_err>=400) {
  1713. if (($errordoc=trim(access_query("_errordocument", "_".$rq_err))) && (@is_readable($docroot.$errordoc))) {
  1714. $add_nsv["REDIRECT_STATUS"]=$rq_err;
  1715. $add_nsv["REDIRECT_URL"]="/".$GLOBALS["real_uri"];
  1716. $http_uri=$errordoc;
  1717. $errext=substr(strrchr($errordoc, "."), 1);
  1718. $lf=loadfile($docroot.$errordoc, $errext, $rq_err, $out_add_headers);
  1719. if ($mimetype=$mime[strtolower($errext)]) $out_contenttype=$mimetype; else $out_contenttype=$default_ct;
  1720. } else {
  1721. $out_contenttype="text/html";
  1722. $lf =& new static_response(nw_error_page($rq_err));
  1723. if ($errordoc) techo("WARN: unable to read error document : [".$rq_err."] ".$errordoc, NW_EL_WARNING);
  1724. }
  1725. $cnx=false;
  1726. } else if ($rq_err>=301) {
  1727. $lf=$null_response;
  1728. }
  1729. }
  1730. if ($http_action=="HEAD") {
  1731. $plen=$lf->content_length;
  1732. $hlf=$lf;
  1733. $lf=$null_response;
  1734. $lf->content_length=$plen;
  1735. }
  1736. core_modules_hook("before_response");
  1737. if (!$rq_err) $rq_err=500;
  1738. $chunked=nw_use_chunked_encoding();
  1739. if ($chunked==="CLOSE") $cnx=$keepalive=$chunked=false;
  1740. // Send the response headers and content
  1741. $sent_len=0;
  1742. $first_chunk=true;
  1743. while ((($buf = $lf->parser_get_output()) !== "") || $first_chunk) {
  1744. if ($first_chunk) {
  1745. $hbuf=build_response_headers()."\r\n";
  1746. }
  1747. if ($chunked) {
  1748. $chunk_header=dechex(strlen($buf))."\r\n";
  1749. $metasize=strlen($chunk_header)+2;
  1750. $rbytes=send_response($hbuf.($buf!==""?$chunk_header.$buf."\r\n":""), $sck_connected);
  1751. $sent_len+=($rbytes-$metasize);
  1752. } else {
  1753. $sent_len+=send_response($hbuf.$buf, $sck_connected);
  1754. }
  1755. if ($first_chunk) {
  1756. $sent_len-=strlen($hbuf);
  1757. $hbuf="";
  1758. $first_chunk=false;
  1759. }
  1760. if ($lf->parser_eof() || !$sck_connected || ($buf === NULL)) break;
  1761. }
  1762. $lf->parser_close();
  1763. if ($chunked) {
  1764. $meta_len=0;
  1765. send_response("0\r\n\r\n", $sck_connected);
  1766. }
  1767. if (!$sck_connected) $cnx=false;
  1768. if (($sent_content_length=$sent_len-$meta_len)<0) $sent_content_length=0;
  1769. if ($conf["global"]["singleprocessmode"][0]) {
  1770. // Increment stats
  1771. ++$stats_resperr[$rq_err];
  1772. ++$stats_vhosts[$conf[$vhost]["servername"][0]];
  1773. ++$stats_hits;
  1774. $stats_xfer+=(float)$sent_len;
  1775. } else {
  1776. // Report hit to master
  1777. int_sendtomaster(NM_HIT, array($mypid, $rq_err, $sent_len, $vhost));
  1778. }
  1779. if ($conf["global"]["loggerprocess"][0]) {
  1780. // Send the logging infos to dedicated processes
  1781. $log_arr=array($vhost, $remote_host, $remote_ip, $logged_user, trim($tmp_arr[0]), $rq_err, $sent_content_length, $htreq_headers["REFERER"], $htreq_headers["USER-AGENT"]);
  1782. $logmsg=serialize($log_arr);
  1783. $msglen=strlen($logmsg);
  1784. if ($msglen>INT_MSGSIZE) {
  1785. techo("WARN: internal communication error (packet too long)", NW_EL_WARNING);
  1786. } else {
  1787. $r=socket_write($loggers_sck, $logmsg);
  1788. if ($r!=$msglen) techo("WARN: unable to communicate with logger process", NW_EL_WARNING);
  1789. }
  1790. } else {
  1791. // Or do it ourselves
  1792. if ($nb_loggers=count($modules["log"])) {
  1793. for ($a=0;$a<$nb_loggers;$a++) $modules["log"][$a]->log_hit($vhost, $remote_host, $remote_ip, $logged_user, trim($tmp_arr[0]), $rq_err, $sent_content_length, $htreq_headers["REFERER"], $htreq_headers["USER-AGENT"]);
  1794. }
  1795. }
  1796. $hlf=$lf=$null_response;
  1797. }
  1798. } else {
  1799. $cnx=false;
  1800. }
  1801. }
  1802. socket_shutdown($msgsock);
  1803. socket_close($msgsock);
  1804. if ((!$conf["global"]["singleprocessmode"][0]) || ($conf["global"]["servermode"][0]=="inetd")) exit(0);
  1805. } else if ($pid==-1) {
  1806. // Fork failed
  1807. techo("WARN: unable to pcntl_fork()", NW_EL_WARNING);
  1808. } else {
  1809. // Fork successful
  1810. $scoreboard[$pid][NW_SB_STATUS]="(connected)";
  1811. $scoreboard[$pid][NW_SB_PEERHOST]=$remote_ip;
  1812. $scoreboard[$pid][NW_SB_FORKTIME]=time();
  1813. ++$active_servers;
  1814. }
  1815. }
  1816. ?>