PageRenderTime 8ms CodeModel.GetById 3ms app.highlight 47ms RepoModel.GetById 1ms 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
   1#!/usr/local/bin/php -q
   2<?php
   3
   4/*
   5
   6Nanoweb, the aEGiS PHP web server
   7=================================
   8
   9Copyright (C) 2002-2005 Vincent Negrier aka. sIX <six@aegis-corp.org>
  10
  11This program is free software; you can redistribute it and/or modify
  12it under the terms of the GNU General Public License as published by
  13the Free Software Foundation; either version 2, or (at your option)
  14any later version.
  15
  16This program is distributed in the hope that it will be useful,
  17but WITHOUT ANY WARRANTY; without even the implied warranty of
  18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19GNU General Public License for more details.
  20
  21You should have received a copy of the GNU General Public License
  22along with this program; if not, write to the Free Software
  23Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24
  25*/
  26
  27error_reporting(E_PARSE | E_ERROR);
  28if (strpos($opts='^'.implode('_', $_SERVER["argv"]).'$', "--debug") + strpos($opts, "--verbose")) { error_reporting(E_ALL); } 
  29
  30define("VERSION", "2.2.9");
  31
  32// Hard configuration and defaults
  33
  34define("DEFAULT_CONFIG_FILE", (((strpos(strtoupper(PHP_OS), "WIN")===0) || (strpos(strtoupper(PHP_OS), "CYGWIN")===0))?"C:\\nanoweb\\":"/etc/nanoweb/")."nanoweb.conf");
  35define("DEFAULT_LISTEN_ADDR", "0.0.0.0");
  36define("DEFAULT_LISTEN_PORT", 80);
  37define("DEFAULT_LISTEN_QUEUE", 20);
  38define("DEFAULT_MIMETYPES", "/etc/mime.types");
  39define("DEFAULT_CONTENT_TYPE", "text/plain");
  40define("DEFAULT_REQUEST_TIMEOUT", 15);
  41define("DEFAULT_ACCESS_FILE", ".nwaccess");
  42define("DEFAULT_DOCROOT", "./");
  43define("DEFAULT_LOGFILE", "./access.log");
  44define("DEFAULT_LOGTYPE", "combined");
  45define("DEFAULT_FBWELCOMEFILE", ".welcome");
  46define("DEFAULT_STATIC_BUFFER_SIZE", 1048576);
  47define("DEFAULT_CONTENT_HANDLER", "static");
  48define("DEFAULT_MAX_SERVERS", 25);
  49define("DEFAULT_ACCESS_POLICY", "override");
  50define("DEFAULT_SERVER_THEME", "default");
  51define("DEFAULT_SERVER_LANG", "en-us");
  52
  53define("SCK_WRITE_PACKET_SIZE", 8192);
  54define("SCK_READ_PACKET_SIZE", 4096);
  55define("SCK_READ_SELECT_TIMEOUT", 2);
  56define("SCK_MAX_STALL_TIME", 60);
  57
  58define("SPM_CACHES_LIFETIME", 15);
  59
  60define("HTTP_VERSION", "HTTP/1.1");
  61
  62define("SERVER_STRING", "aEGiS_nanoweb");
  63define("SERVER_STRING_V", SERVER_STRING."/".VERSION);
  64
  65define("INT_MSGSIZE", 4096);
  66
  67define("NM_HIT", "  HIT");
  68define("NM_RESTART_LOGGERS", "LGRST");
  69define("NM_SERVER_STATE", "SRVST");
  70define("NM_RELOAD_THEME", "RLTHM");
  71define("NM_BLOCK_IP", "BANIP");
  72define("NM_UNBLOCK_IP", "DBNIP");
  73
  74define("NW_BAD_OUTSIDE_DOCROOT", 1);
  75define("NW_BAD_DOT_FILE", 2);
  76define("NW_BAD_WIN_DEVICE", 3);
  77
  78define("NW_SB_STATUS", 0);
  79define("NW_SB_PEERHOST", 1);
  80define("NW_SB_FORKTIME", 2);
  81
  82define("NW_EL_DEBUG", 1);
  83define("NW_EL_HIT", 2);
  84define("NW_EL_NOTICE", 4);
  85define("NW_EL_BLOCKING", 8);
  86define("NW_EL_WARNING", 16);
  87define("NW_EL_ERROR", 32);
  88define("NW_EL_ALL", 255);
  89define("NW_EL_DEFAULT", NW_EL_NOTICE | NW_EL_BLOCKING | NW_EL_WARNING | NW_EL_ERROR);
  90
  91define("NW_TMPL_SIGNATURE", "server_signature");
  92define("NW_TMPL_ERROR_PAGE", "error_page");
  93define("NW_TMPL_ERROR_RESOURCE", "error_resource");
  94define("NW_TMPL_ERROR_ADMIN", "error_admin");
  95
  96define("REQUIRED_PHP_VERSION", "4.2.0");
  97
  98// Internally used global vars
  99
 100$HTTP_HEADERS=array(100 => "100 Continue",
 101			200 => "200 OK",
 102			201 => "201 Created",
 103			204 => "204 No Content",
 104			206 => "206 Partial Content",
 105			300 => "300 Multiple Choices",
 106			301 => "301 Moved Permanently",
 107			302 => "302 Found",
 108			303 => "303 See Other",
 109			304 => "304 Not Modified",
 110			307 => "307 Temporary Redirect",
 111			400 => "400 Bad Request",
 112			401 => "401 Unauthorized",
 113			403 => "403 Forbidden",
 114			404 => "404 Not Found",
 115			405 => "405 Method Not Allowed",
 116			406 => "406 Not Acceptable",
 117			408 => "408 Request Timeout",
 118			410 => "410 Gone",
 119			413 => "413 Request Entity Too Large",
 120			414 => "414 Request URI Too Long",
 121			415 => "415 Unsupported Media Type",
 122			416 => "416 Requested Range Not Satisfiable",
 123			417 => "417 Expectation Failed",
 124			500 => "500 Internal Server Error",
 125			501 => "501 Method Not Implemented",
 126			503 => "503 Service Unavailable",
 127			506 => "506 Variant Also Negotiates");
 128
 129$TEST_FUNCS=array(	"pcntl_fork"	=> false, 
 130			"socket_create"	=> true, 
 131			"posix_setuid"	=> false, 
 132			"gzencode"	=> false);
 133
 134$conf_defaults=array(	"listeninterface"	=> DEFAULT_LISTEN_ADDR,
 135			"listenport"		=> DEFAULT_LISTEN_PORT,
 136			"listenqueue"		=> DEFAULT_LISTEN_QUEUE,
 137			"mimetypes"		=> DEFAULT_MIMETYPES,
 138			"requesttimeout"	=> DEFAULT_REQUEST_TIMEOUT,
 139			"accessfile"	=> DEFAULT_ACCESS_FILE,
 140			"documentroot"		=> DEFAULT_DOCROOT,
 141			"log"			=> DEFAULT_LOGFILE,
 142			"logtype"		=> DEFAULT_LOGTYPE,
 143			"fbwelcomefile" 	=> DEFAULT_FBWELCOMEFILE,
 144			"defaultcontenttype" => DEFAULT_CONTENT_TYPE,
 145			"staticbuffersize" => DEFAULT_STATIC_BUFFER_SIZE,
 146			"defaulthandler" => DEFAULT_CONTENT_HANDLER,
 147			"maxservers"	=> DEFAULT_MAX_SERVERS,
 148			"accesspolicy"	=> DEFAULT_ACCESS_POLICY,
 149			"servertheme" => DEFAULT_SERVER_THEME);
 150
 151$conf_vhosts_propagate=array(	"documentroot",
 152			"directoryindex",
 153			"serveradmin",
 154			"user",
 155			"group",
 156			"logdir",
 157			"log",
 158			"logtype",
 159			"filebrowser",
 160			"fbshowdotfiles",
 161			"fbwelcomefile",
 162			"userdir",
 163			"ignoredotfiles",
 164			"allowsymlinkto",
 165			"maxrequestbodylength",
 166			"maxrequesturilength");
 167
 168// Needed as long as PHP filetype() is broken on win32
 169
 170$win_devices=array("nul", "con", "aux", "prn", "clock$", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8");
 171
 172// Command line help page
 173
 174$cmdline_help=<<<EOT
 175Usage: nanoweb.php [/path/to/nanoweb.conf] [options]
 176
 177nanoweb supports the following command line options :
 178
 179--help                                -h : this help screen
 180--version                             -v : show version info
 181--config=/path/to/nanoweb.conf        -c : configuration file
 182--set-option="optionname=optionvalue" -o : set configuration option
 183--add-option="optionname=optionvalue" -a : add configuration option
 184--start-daemon                        -d : start nanoweb and run in background
 185--config-test                         -t : test configuration and exit
 186--quiet                               -q : don't send text to console
 187
 188
 189EOT;
 190
 191// Static response class
 192
 193class static_response {
 194
 195	var $content_length;
 196
 197	function static_response($str) {
 198
 199		$this->str=$str;
 200		$this->content_length=strlen($str);
 201	
 202	}
 203
 204	function parser_open($args, $filename, &$rq_err, &$cgi_headers) {
 205
 206	}
 207
 208	function parser_get_output() {
 209
 210		$s=$this->str;
 211		$this->str="";
 212		return($s);
 213
 214	}
 215
 216	function parser_eof() {
 217
 218		return($this->str === "");
 219	
 220	}
 221
 222	function parser_close() {
 223
 224	}
 225
 226}
 227
 228$null_response =& new static_response("");
 229$lf=$null_response;
 230
 231// Functions
 232
 233function _parseconfig_close_part(&$conf, $cpart, $nodefaults=false) {
 234
 235	if (!$conf[$cpart]["_nw_pcp"]) {
 236	
 237		if (!$nodefaults) {
 238		
 239			if ($cpart=="global") {
 240				
 241				// Use default values for the global config scope
 242				
 243				foreach ($GLOBALS["conf_defaults"] as $key=>$dval) if (!isset($conf[$cpart][$key])) $conf[$cpart][$key][0]=$dval;
 244
 245			} else {
 246
 247				// Propagate appropriate directives to vhosts
 248
 249				foreach ($GLOBALS["conf_vhosts_propagate"] as $key=>$pkey) if (!isset($conf[$cpart][$pkey])) $conf[$cpart][$pkey]=$conf["global"][$pkey];
 250				if (!$conf[$cpart]["servername"]) $conf[$cpart]["servername"][]=$cpart;
 251
 252			}
 253		
 254		}
 255		
 256		// Transform some directives (Dir = Idx Value)
 257		
 258		foreach (array("parseext", "errordocument", "errorheader") as $dir) {
 259		
 260			if ($conf[$cpart][$dir]) {
 261
 262				foreach ($conf[$cpart][$dir] as $ps) {
 263
 264					$ext=strtolower(strtok($ps, " "));
 265					$dext=trim(strtok(""));
 266					
 267					$conf[$cpart]["_".$dir]["_".$ext]=$dext;
 268
 269				}
 270			
 271			}
 272
 273		}
 274	
 275	$conf[$cpart]["_nw_pcp"]=true;
 276	
 277	}
 278
 279}
 280
 281function _parseconfig_parse_line($str) {
 282
 283	$cnfl=array();
 284	ereg("([^ =\n\t]+)[ \t]*=?[ \t]*([^\n]+)", $str, $cnfl);
 285	return(array(strtolower(trim($cnfl[1])), trim($cnfl[2])));
 286
 287}
 288
 289function parseconfig($conf_arr, $nodefaults=false) {
 290
 291	$cpart="global";
 292	$_pcp=array("global");
 293	$included_confs[$GLOBALS["conffile"]]=true;
 294	
 295	if ($clen=count($conf_arr)) {
 296		
 297		// Pass 1 (build the $conf_arr array)
 298
 299		$key=-1;
 300		
 301		while ($key++<=$clen) {
 302
 303			$str=$conf_arr[$key];
 304			
 305			list($cnfk, $cnfv)=_parseconfig_parse_line($str);
 306
 307			if ($cnfk=="configdir") {
 308
 309				$confdir=$cnfv;
 310			
 311			} else if ($cnfk=="include") {
 312
 313				$ifn=($confdir?($confdir.DIRECTORY_SEPARATOR):"").$cnfv;
 314				if (!@is_readable($ifn)) $ifn=$cnfv;
 315				
 316				if ($included_confs[$ifn]) {
 317
 318					$conf_err="configuration includes loop detected line ".$key." : '".trim($str)."'";
 319					break;
 320			
 321				}
 322			
 323				if (!@is_readable($ifn)) {
 324
 325					$conf_err="unable to include configuration file line ".$key." : '".$ifn."'";
 326					break;
 327				
 328				}
 329
 330				$subconf_arr=file($ifn);
 331
 332				$conf_arr=array_merge(array_slice($conf_arr, 0, $key), $subconf_arr, array_slice($conf_arr, $key+1));
 333				$key=-1;
 334				$clen=count($conf_arr);
 335				$included_confs[$ifn]=true;
 336			
 337			}
 338
 339		}
 340
 341		// Pass 2 (build the $conf array)
 342		
 343		foreach ($conf_arr as $key=>$str) {
 344
 345			switch ($str[0]) {
 346
 347				case "#":
 348				case ";":
 349				case "\n":
 350				case "":
 351				break;
 352				
 353				case "[":
 354
 355				if ($cpart!="global") $_pcp[]=$cpart;
 356				
 357				$cpart=substr(trim($str), 1, -1);
 358				if ($cpart{0}=="/") $cpart="global";
 359
 360				if ($cpart!="global") unset($conf[$cpart]);
 361				
 362				break;
 363
 364				default:
 365
 366				list($cnfk, $cnfv)=_parseconfig_parse_line($str);
 367
 368				switch ($cnfk) {
 369
 370					case "documentroot": 
 371					
 372					$rp=nw_realpath($cnfv);
 373
 374					if (!($rp && @is_dir($rp))) {
 375						
 376						$conf_err="directory not found at line ".$key." : '".trim($str)."'";
 377						unset($conf[$cpart]);
 378						$cpart="_err:".$cpart;
 379
 380					}
 381					
 382					if (substr($rp, -1)!=DIRECTORY_SEPARATOR) $rp.=DIRECTORY_SEPARATOR;
 383					$conf[$cpart][$cnfk][]=$rp;
 384
 385					break;
 386
 387					case "serveralias": 
 388					if ($cnfv!=$cpart) $conf[$cnfv]=&$conf[$cpart];
 389					break;
 390					
 391					case "alias":
 392					$conf[$cpart][$cnfk][]=$cnfv;
 393					$aliases=explode(" ", $cnfv);
 394					$conf[$cpart]["_aliases"][$aliases[0]]=$aliases[1];
 395					break;
 396					
 397					case "serverlog":
 398
 399					$lname=strtok($cnfv, " ");
 400					
 401					if ($lmode=strtok("")) {
 402
 403						$lbmode=0;
 404						foreach($GLOBALS["srvlog_levels"] as $lvl=>$bin_lvl) if (strpos($lmode, $lvl)!==false) $lbmode|=$bin_lvl;
 405						foreach($GLOBALS["srvlog_levels"] as $lvl=>$bin_lvl) if (strpos($lmode, "-".$lvl)!==false) $lbmode&=~$bin_lvl;
 406
 407					} else {
 408						
 409						$lbmode=NW_EL_DEFAULT;
 410
 411					}
 412					
 413					$conf[$cpart]["_serverlog"][$lname]=$lbmode;
 414					
 415					break;
 416
 417					case "loadtheme":
 418					$conf[$cpart][$cnfk][]=$cnfv;
 419					$conf[$cpart]["servertheme"][]=$cnfv;
 420					break;
 421
 422					default: 
 423					$conf[$cpart][$cnfk][]=$cnfv;
 424					break;
 425
 426				}
 427
 428				break;
 429
 430			}
 431			
 432		}
 433
 434	}
 435	
 436	// Pass 3 (close all $conf sections)
 437	
 438	if ($cpart!="global") $_pcp[]=$cpart;
 439	
 440	foreach ($_pcp as $clpart) _parseconfig_close_part($conf, $clpart, $nodefaults);
 441
 442	return($conf_err?$conf_err:$conf);
 443
 444}
 445
 446function cmdline_conf_upd($conf, $cmdline_conf_overrides, $cmdline_conf_adds) {
 447
 448	foreach ($cmdline_conf_overrides as $cs) {
 449
 450		$ca=explode("=", $cs);
 451		$conf["global"][strtolower($ca[0])]=array($ca[1]);
 452
 453	}
 454
 455	foreach ($cmdline_conf_adds as $cs) {
 456
 457		$ca=explode("=", $cs);
 458		$conf["global"][strtolower($ca[0])][]=$ca[1];
 459
 460	}
 461
 462	return($conf);
 463
 464}
 465
 466function load_modules($conf) {
 467
 468	global $mod_tokens;
 469	
 470	$mod_tokens=array();
 471	
 472	if ($lm_arr=$conf["global"]["loadmodule"]) foreach ($lm_arr as $key=>$modname) {
 473
 474		$clsname=basename($modname, ".php");
 475		
 476		if (!is_file($modname)) {
 477
 478			foreach (access_query("modulesdir") as $md) if (is_file($md.DIRECTORY_SEPARATOR.$modname)) {
 479
 480				$moddir=$md;
 481				break;
 482			
 483			}
 484			
 485		} else $moddir="";
 486		
 487		if (!$ld_clss[$clsname]) {
 488			
 489			$nload=(!class_exists($clsname));
 490			
 491			// Try to load with given path
 492			
 493			@include_once($modname);
 494			$modloaded=class_exists($clsname);
 495
 496			if (!$modloaded) {
 497
 498				// And try with modulesdir if not found
 499
 500				@include_once($moddir.$modname);
 501				$modloaded=class_exists($clsname);
 502			
 503			}
 504
 505			if ($modloaded) {
 506			
 507				$ld_clss[$clsname]=true;
 508				$tmp=&new $clsname;
 509				$modules[$tmp->modtype][]=&$tmp;
 510				$tmp_modlist[]=array($clsname, $tmp->modname);
 511
 512				if (is_array($tmp->urls)) foreach ($tmp->urls as $url) $modules["url:".$url]=&$tmp;
 513				if (is_array($tmp->methods)) foreach ($tmp->methods as $method) $modules["method:".$method]=&$tmp;
 514				if (is_string($mt=$tmp->sig_token)) $mod_tokens[$tmp->modname]=$mt;
 515
 516				if ($nload) techo("loaded module : ".$tmp->modname);
 517
 518			} else {
 519
 520				techo("WARN: unable to load module '".$modname."'", NW_EL_WARNING);
 521			
 522			}
 523	
 524		}
 525	
 526	}
 527
 528	$GLOBALS["stats_modlist"]=$tmp_modlist;
 529
 530	return ($modules);
 531
 532}
 533
 534function load_theme($themefname, $load_notice=false, $reload=false) {
 535
 536	$tfn=($GLOBALS["conf"]["global"]["configdir"][0]?($GLOBALS["conf"]["global"]["configdir"][0].DIRECTORY_SEPARATOR):"").$themefname;
 537	if (!@is_readable($tfn)) $tfn=$themefname;
 538	
 539	if ($thmarr=@file($tfn)) {
 540
 541		$ts=0;
 542		
 543		foreach ($thmarr as $thml) {
 544
 545			if (strtolower(rtrim($thml))=="[/".$thm_sc."]") {
 546
 547				$theme[$thm_sc]=substr($theme[$thm_sc], 0, -1);
 548				--$ts;
 549				$thm_sc="";
 550				
 551			} else if (($thml{0}=="[") && (substr(rtrim($thml), -1)=="]")) {
 552
 553				$thm_sc=strtolower(substr(rtrim($thml), 1, -1));
 554
 555			} else if ($thm_sc) {
 556
 557				$theme[$thm_sc].=$thml;
 558				$ts+=strlen($thml);
 559			
 560			}
 561
 562		}
 563
 564		if ($theme["theme_id"]) {
 565
 566			if (!$theme["theme_name"]) $theme["theme_name"]=$theme["theme_id"];
 567			$theme["theme_id"]=trim($theme["theme_id"]);
 568			$theme["theme_name"]=trim($theme["theme_name"]);
 569			
 570			$theme["theme_language"]=trim($theme["theme_language"])
 571			or $theme["theme_language"]=DEFAULT_SERVER_LANG;
 572
 573			if ($load_notice) techo(($reload?"re":"")."loaded theme : ".$theme["theme_name"]." (".$ts." bytes)");
 574
 575		} else {
 576
 577			techo("WARN: invalid theme file '".$tfn."'", NW_EL_WARNING);
 578		
 579		}
 580
 581		clearstatcache();
 582
 583		$theme["_fname"]=$tfn;
 584		$theme["_mtime"]=filemtime($tfn);
 585		$theme["_pmode"]=$GLOBALS["pmode"];
 586	
 587	} else {
 588
 589		techo("WARN: unable to load theme file '".$tfn."'", NW_EL_WARNING);
 590	
 591	}
 592
 593	return($theme);
 594
 595}
 596
 597function load_themes($conf) {
 598
 599	if (is_array($conf["global"]["loadtheme"])) foreach ($conf["global"]["loadtheme"] as $themefname) {
 600
 601		$theme=load_theme($themefname, true);
 602		$themes[$theme["theme_id"]]=$theme;
 603		$themes[$theme["_fname"]]=$theme;
 604	
 605	} else {
 606
 607		techo("WARN: 'LoadTheme' directive not found in config file", NW_EL_WARNING);
 608	
 609	}
 610
 611	return($themes);
 612
 613}
 614
 615function modules_init($method="init") {
 616
 617	global $modules;
 618	
 619	foreach ($modules as $modclass)	if (is_array($modclass)) {
 620		
 621		for ($a=0;$a<count($modclass);$a++) {
 622		
 623			if ((method_exists($modclass[$a], $method)) && (!$modinit[$mc=get_class($modclass[$a])])) {
 624				
 625				$modclass[$a]->$method();
 626				$modinit[$mc]=true;
 627
 628			}
 629
 630		} 
 631	
 632	} else {
 633
 634		if ((method_exists($modclass, $method)) && (!$modinit[$mc=get_class($modclass)])) {
 635			
 636			$modclass->$method();
 637			$modinit[$mc]=true;
 638
 639		}
 640
 641	}
 642
 643}
 644
 645function load_access_files($dir, &$access, $rec=0) {
 646
 647	global $conf, $access_cache;
 648
 649	if (is_array($z=$access_cache[$dir])) {
 650
 651		// Access cache hit
 652		
 653		$access=$z;
 654		return;
 655	
 656	} else {
 657	
 658		// Access cache miss
 659		
 660		if (!$rec) $access=array();
 661		
 662		$ndir=substr($dir, 0, strrpos($dir, DIRECTORY_SEPARATOR));
 663
 664		$cont = false;
 665
 666		$r_mdir = nw_realpath($ndir.DIRECTORY_SEPARATOR);
 667		$cont_dirs = array(nw_realpath($GLOBALS["docroot"]));
 668		foreach ($conf[$GLOBALS["vhost"]]["allowsymlinkto"] as $als) $cont_dirs[] = nw_realpath($als);
 669	
 670		foreach ($cont_dirs as $cdir) if (strpos($r_mdir, $cdir)===0) {
 671			
 672			load_access_files($ndir, $access, $rec+1);
 673			break;
 674
 675		}
 676		
 677		$afn=$dir.DIRECTORY_SEPARATOR.$conf["global"]["accessfile"][0];
 678
 679		if (is_readable($afn) && ($accesstmp=@file($afn)) && ($tmp_access=parseconfig($accesstmp, true))) foreach ($tmp_access as $key=>$val_arr) {
 680				
 681			foreach ($val_arr as $ckey=>$cval_arr) {
 682				
 683				$ap=$GLOBALS["access_policy"][$ckey] or
 684				$ap=$conf["global"]["accesspolicy"][0];
 685
 686				switch ($ap) {
 687
 688					case "override":
 689					$access[$key][$ckey]=$cval_arr;
 690					break;
 691
 692					case "merge":
 693					$access[$key][$ckey]=array_merge($access[$key][$ckey], $cval_arr);
 694					break;
 695
 696				}
 697
 698			}
 699
 700			if (!$rec) break;
 701		
 702		}
 703
 704		$access_cache[$dir]=$access;
 705	
 706	}
 707
 708}
 709
 710function access_query($key, $idx=false) {
 711
 712	global $access, $conf;
 713
 714	$ap=$GLOBALS["access_policy"][$key] or
 715	$ap=$conf["global"]["accesspolicy"][0];
 716
 717	switch ($ap) {
 718
 719		case "override":
 720		$tmp=$access["global"][$key] or
 721		$tmp=$conf[$GLOBALS["vhost"]][$key] or
 722		$tmp=$conf["global"][$key];
 723		break;
 724
 725		case "merge":
 726		$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());
 727		break;
 728
 729	}
 730
 731	if ($idx===false) {
 732
 733		return($tmp);
 734
 735	} else {
 736
 737		return($tmp[$idx]);
 738	
 739	}
 740
 741}
 742
 743function core_modules_hook($hname) {
 744
 745	if ($mh_arr=&$GLOBALS["modules"]["core_".$hname]) foreach (array_keys($mh_arr) as $a) $mh_arr[$a]->main();
 746
 747}
 748
 749function log_ids() {
 750
 751	global $conf;
 752	
 753	if ($setgid=@posix_getgrnam($conf["global"]["loggergroup"][0])) $g=$setgid["gid"];
 754	else if ($setgid=@posix_getgrnam($conf["global"]["group"][0])) $g=$setgid["gid"];
 755
 756	if ($setuid=@posix_getpwnam($conf["global"]["loggeruser"][0])) $u=$setuid["uid"];
 757	else if ($setuid=@posix_getpwnam($conf["global"]["user"][0])) $u=$setuid["uid"];
 758
 759	return(array("uid" => $u, "gid" => $g));
 760
 761}
 762
 763function log_srv($str, $loglevel=NW_EL_NOTICE) {
 764
 765	if ($srvlog_arr=$GLOBALS["conf"]["global"]["_serverlog"]) foreach ($srvlog_arr as $s=>$bmode) if ($loglevel & $bmode) {
 766		
 767		if (($GLOBALS["pmode"]=="master") && (!file_exists($s))) $chown=true;
 768		
 769		if ($sl=@fopen($s, NW_BSAFE_APP_OPEN)) {
 770
 771			fputs($sl, $str);
 772			fclose($sl);
 773			
 774		}
 775
 776		if ($chown && $GLOBALS["posix_av"] && ($lids=log_ids())) {
 777
 778			chgrp($s, $lids["gid"]);
 779			chown($s, $lids["uid"]);
 780
 781		}
 782
 783	}
 784
 785}
 786
 787function nw_gethostbyaddr($ip) {
 788
 789	static $ns_lastreq;
 790	
 791	if ($ip != $ns_lastreq[0]) {
 792		
 793		$hostname = @gethostbyaddr($ip);
 794		$fwr_ip = @gethostbyname($hostname);
 795
 796		if ($ip != $fwr_ip) {
 797			
 798			// Inconsistent DNS data
 799			
 800			$hostname = $ip;
 801
 802		}
 803		
 804		$ns_lastreq = array($ip, $hostname);
 805
 806	}
 807
 808	return($ns_lastreq[1]);
 809
 810}
 811
 812function nw_realpath($dir) {
 813
 814	global $rp_cache;
 815	
 816	if ($rp=$rp_cache[$dir]) {
 817
 818		return($rp);
 819	
 820	} else {
 821
 822		return($rp_cache[$dir]=realpath($dir));
 823	
 824	}
 825
 826}
 827
 828function nw_server_string() {
 829
 830	switch (strtolower(access_query("serversignature", 0))) {
 831
 832		case "fake": return(access_query("serverfakesignature", 0));
 833		case "off": return("");
 834		case "prod": return(SERVER_STRING);
 835		case "min": return(SERVER_STRING_V);
 836		case "os": return(SERVER_STRING_V." (".PHP_OS.")");
 837		case "php": return(SERVER_STRING_V." (".PHP_OS."; PHP/".phpversion().")");
 838
 839		case "full": 
 840		default:
 841		return(SERVER_STRING_V." (".PHP_OS."; PHP/".phpversion().($GLOBALS["mod_tokens"]?"; ":"").implode("; ", $GLOBALS["mod_tokens"]).")");
 842
 843	}
 844	
 845}
 846
 847function _genpage_signature() {
 848
 849	return(nw_apply_template(NW_TMPL_SIGNATURE, array("server_string" => nw_server_string(), "server_name" => $GLOBALS["conf"][$GLOBALS["vhost"]]["servername"][0]), true));
 850	
 851}
 852
 853function nw_apply_template($template, $args, $no_add=false) {
 854
 855	global $themes;
 856	
 857	$sts = access_query("servertheme");
 858	$lts = access_query("loadtheme");
 859	
 860	$thmid=array_pop($sts);
 861	$fname=$themes[$thmid]["_fname"];
 862	
 863	if ($thmid==$fname) $thmid=$themes[$thmid]["theme_id"];
 864	if ((!is_array($themes[$thmid])) && ($ltid=array_pop($lts))) $fname=$ltid;
 865	
 866	clearstatcache();
 867	
 868	if (($themes[$thmid]["_mtime"]!=filemtime($fname)) && ($tmp_thm=load_theme($fname))) {
 869
 870		if ($themes[$thmid]["_pmode"]=="master") int_sendtomaster(NM_RELOAD_THEME, $thmid);
 871
 872		$themes[$thmid]=$tmp_thm;
 873		$themes[$fname]=$tmp_thm;
 874		$themes[$tmp_thm["_fname"]]=$tmp_thm;
 875
 876	}
 877
 878	$tlang=strtolower($themes[$thmid]["theme_language"]);
 879
 880	if (($al=$GLOBALS["htreq_headers"]["ACCEPT-LANGUAGE"]) && ($als=nw_decode_mq_hdr($al))) {
 881
 882		foreach (array_keys($als) as $lang) {
 883			
 884			$lang=strtolower($lang);
 885			
 886			if (isset($themes[$thmid][$template.":".$lang])) {
 887
 888				$tname=$template.":".$lang;
 889				break;
 890			
 891			} else if ($tlang==$lang) {
 892
 893				break;
 894			
 895			}
 896
 897		}
 898
 899	}
 900
 901	if (!$tname) $tname=$template;
 902	
 903	$tmpl=$themes[$thmid][$tname] or
 904	$tmpl=$themes[DEFAULT_SERVER_THEME][$tname];
 905
 906	foreach ($args as $k=>$v) $tr_arr["@$".strtolower($k)."@"]=$v;
 907	if (!$no_add) $tr_arr['@$server_signature@']=_genpage_signature();
 908
 909	$trt=strtr($tmpl, $tr_arr);
 910
 911	while ((($p=strpos($trt, "@!"))!==false) && (($p2=strpos(substr($trt, $p+2), "@"))!==false)) {
 912
 913		$ret=substr($trt, 0, $p);
 914		$ret.=access_query(strtolower(substr($trt, $p+2, $p2)), 0);
 915		$ret.=substr($trt, $p+$p2+3);
 916
 917		$trt=$ret;
 918
 919	}
 920
 921	return($trt);
 922
 923}
 924
 925function nw_server_vars($include_cgi_vars=false) {
 926
 927	global $conf;
 928	
 929	$filename=$GLOBALS["docroot"].$GLOBALS["http_uri"];
 930	
 931	$nsv["SERVER_SOFTWARE"]=nw_server_string();
 932	$nsv["SERVER_NAME"]=$conf[$GLOBALS["vhost"]]["servername"][0];
 933	$nsv["SERVER_PROTOCOL"]=HTTP_VERSION;
 934	$nsv["SERVER_PORT"]=$GLOBALS["lport"];
 935	$nsv["SERVER_ADDR"]=$conf["global"]["listeninterface"][0];
 936	$nsv["SERVER_API"]=VERSION;
 937	$nsv["SERVER_ADMIN"]=$conf[$GLOBALS["vhost"]]["serveradmin"][0];
 938	$nsv["REQUEST_METHOD"]=$GLOBALS["http_action"];
 939	$nsv["PATH_TRANSLATED"]=$nsv["SCRIPT_FILENAME"]=nw_realpath($filename);
 940	$nsv["SCRIPT_NAME"]="/".$GLOBALS["docroot_prefix"].$GLOBALS["http_uri"];
 941	$nsv["QUERY_STRING"]=$GLOBALS["query_string"];
 942	$nsv["REMOTE_HOST"]=$GLOBALS["remote_host"];
 943	$nsv["REMOTE_ADDR"]=$GLOBALS["remote_ip"];
 944	$nsv["REMOTE_PORT"]=$GLOBALS["remote_port"];
 945	$nsv["AUTH_TYPE"]=$GLOBALS["auth_type"];
 946	$nsv["DOCUMENT_ROOT"]=$GLOBALS["docroot"];
 947	$nsv["REQUEST_URI"]="/".$GLOBALS["real_uri"].($nsv["QUERY_STRING"]?("?".$nsv["QUERY_STRING"]):"");
 948	$nsv["PATH_INFO"]=$GLOBALS["path_info"];
 949
 950	if (($GLOBALS["logged_user"]) && ($GLOBALS["logged_user"] != " ")) {
 951
 952		$nsv["REMOTE_USER"] = $GLOBALS["logged_user"];
 953
 954	}
 955
 956	if ($asv=access_query("addservervar")) foreach ($asv as $str) {
 957
 958		$k=strtok($str, " ");
 959		$v=strtok("");
 960		if ($k) $nsv[$k]=$v;
 961	
 962	}
 963
 964	if ($GLOBALS["add_nsv"]) foreach ($GLOBALS["add_nsv"] as $key=>$val) $nsv[$key]=$val;
 965
 966	if ($include_cgi_vars && ($rq_hdrs=$GLOBALS["htreq_headers"])) foreach($rq_hdrs as $key=>$val) $nsv["HTTP_".str_replace("-", "_", $key)]=$val;
 967
 968	return $nsv;
 969
 970}
 971
 972function nw_url_addslash($s) {
 973
 974	$ret = strtok($s, "?")."/";
 975	if (($q = strtok("")) !== false) $ret .= "?".$q;
 976
 977	return $ret;
 978
 979}
 980
 981function techo($s, $level=NW_EL_NOTICE, $flush=false) {
 982
 983	global $conf;
 984
 985	static $srv_buf;
 986	
 987	$tl=date("Ymd:His")." $s\n";
 988
 989	if (!$conf["_complete"] && !$flush) {
 990
 991		$srv_buf[]=array($tl, $level);
 992	
 993	} else {
 994	
 995		if (($conf["global"]["servermode"][0]!="inetd") && !$GLOBALS["quiet"]) {
 996
 997			if ($srv_buf) foreach ($srv_buf as $sb_arr) echo $sb_arr[0];
 998			echo $tl;
 999			flush();
1000
1001		}
1002
1003		if ($srv_buf) {
1004			
1005			foreach ($srv_buf as $sb_arr) log_srv($sb_arr[0], $sb_arr[1]);
1006	
1007			$srv_buf=array();
1008
1009		}
1010
1011		log_srv($tl, $level);
1012
1013	}
1014
1015}
1016
1017function errexit($s, $errno=-1) {
1018
1019	global $pidfile, $start_daemon;
1020	
1021	$estr="FATAL: ".$s;
1022	techo($estr, NW_EL_ERROR, true);
1023	if ($pidfile) unlink($pidfile);
1024
1025	if ($start_daemon && ($stderr=@fopen("php://stderr", "w"))) {
1026
1027		fputs($stderr, $estr."\n");
1028		fclose($stderr);
1029
1030	}
1031
1032	exit($errno);
1033
1034}
1035
1036function url_to_absolute($url) {
1037
1038	return("http://".$GLOBALS["conf"][$GLOBALS["vhost"]]["servername"][0].(($GLOBALS["lport"]!=80)?(":".$GLOBALS["lport"]):"").($url!="/"?"/":"").$url);
1039
1040}
1041
1042function loadfile($filename, $extension, &$rq_err, &$cgi_headers, $force_parser=false) {
1043	
1044	global $conf, $modules, $add_nsv;
1045	
1046	if (is_link($filename)) $filename=readlink($filename);
1047	chdir(dirname(nw_realpath($filename)));
1048	$filename=basename($filename);
1049
1050	if (($parser=$force_parser) || ($parser=trim(access_query("_parseext", "_".strtolower($extension))))) {
1051
1052		// Parsed content
1053		
1054		if (strpos($parser, " ")!==false) {
1055		
1056			$ps_type=strtok($parser, " ");
1057			$ps_arg=strtok("");
1058			if (strpos($ps_arg, '$')!==false) foreach (nw_server_vars() as $nkey=>$nval) $ps_arg=str_replace('$'.$nkey, $nval, $ps_arg);
1059
1060			if (!$force_parser) {
1061
1062				$add_nsv["REDIRECT_STATUS"]=$rq_err;
1063				$add_nsv["REDIRECT_URL"]="/".$GLOBALS["real_uri"];
1064
1065			}
1066		
1067		} else $ps_type=$parser;
1068
1069	} else {
1070
1071		// Static content
1072
1073		$ps_type=$conf["global"]["defaulthandler"][0];
1074
1075	}
1076
1077	if ($ps=$modules["parser_".$ps_type][0]) {
1078
1079		if (is_object($rop=$ps->parser_open($ps_arg, $filename, $rq_err, $cgi_headers))) $ps=$rop;
1080
1081	} else {
1082
1083		$rq_err=500;
1084		$GLOBALS["add_errmsg"]="Unable to find an appropriate parser for this content type.<br><br>";
1085		$ps=$GLOBALS["null_response"];
1086	
1087	}
1088
1089	return($ps);
1090
1091}
1092
1093function nw_host_to_vhost($host, $lport=80) {
1094					
1095	global $conf;
1096	
1097	// Try vhost=host:port
1098	
1099	if (is_array($conf[$phost=($host.":".$lport)])) return($phost);
1100	
1101	// Try vhost=host
1102	
1103	if (is_array($conf[$host])) return($host);
1104
1105	// Try wildcards
1106
1107	$hlen=strlen($host);
1108
1109	for($vhlen=0;$vhlen<=$hlen;$vhlen++) {
1110		
1111		$whost="*".substr($host, $vhlen);
1112		if (is_array($conf[$phost=($whost.":".$lport)])) return($phost);
1113		if (is_array($conf[$whost])) return($whost);
1114
1115	}
1116
1117	// Or set to global
1118
1119	return("global");
1120
1121}
1122
1123function nw_error_page($rq_err) {
1124	
1125	global $HTTP_HEADERS, $http_resource, $conf, $vhost, $add_errmsg;
1126	
1127	$err["error_code"]=$rq_err;
1128	$err["error_label"]=$HTTP_HEADERS[$rq_err];
1129	$err["error_add_message"]=$add_errmsg;
1130
1131	$add_errmsg = "";
1132	
1133	$err["error_resource"]=($http_resource?(nw_apply_template(NW_TMPL_ERROR_RESOURCE, array("resource_name" => htmlentities($http_resource)), true)):"");
1134	if ($conf[$vhost]["serveradmin"][0]) $err["error_admin"]=nw_apply_template(NW_TMPL_ERROR_ADMIN, array("admin" => $conf[$vhost]["serveradmin"][0]), true);
1135	
1136	$err_page=nw_apply_template(NW_TMPL_ERROR_PAGE, $err) or
1137	$err_page="<html><head><title>".$HTTP_HEADERS[$rq_err]."</title></head><body><h1>".$HTTP_HEADERS[$rq_err]."</h1></body></html>";
1138
1139	return($err_page);
1140
1141}
1142
1143function nw_use_chunked_encoding() {
1144
1145	if (!isset($GLOBALS["lf"]->content_length) && $GLOBALS["keepalive"]) {
1146
1147		if ($GLOBALS["http_version"]>="1.1") {
1148
1149			return(true);
1150		
1151		} else {
1152
1153			return("CLOSE");
1154		
1155		}
1156	
1157	} else return(false);
1158
1159}
1160
1161function nw_decode_mq_hdr($s) {
1162
1163	if ($l=explode(",", $s)) foreach ($l as $e) {
1164
1165		list($v, $q)=explode(";", $e);
1166		if ($q) list($d, $qn)=explode("=", $q);
1167		if (!$qn) $qn=1;
1168		if ($v) $r[$v]=$qn;
1169	
1170	}
1171
1172	arsort($r);
1173
1174	return($r);
1175
1176}
1177
1178function nw_allow_list($ext) {
1179						
1180	$tmp_marr=array();
1181
1182	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;
1183		
1184	return ("GET, ".(access_query("_parseext", "_".strtolower($rq_file["extension"]))?"POST, ":"")."HEAD, OPTIONS".(count($tmp_marr)?", ":"").implode(", ", $tmp_marr));
1185
1186}
1187
1188function build_response_headers() {
1189
1190	global $HTTP_HEADERS, $rq_err, $out_contenttype, $out_add_headers, $conf, $lf;
1191	
1192	if ($out_add_headers) {
1193		
1194		foreach ($out_add_headers as $key=>$val) switch (strtoupper($key)) {
1195			
1196			case "CONTENT-TYPE":
1197			$out_contenttype=$val;
1198			break;
1199
1200			case "LOCATION":
1201			$rq_err=302;
1202			$add_headers.=$key.": ".$val."\r\n";
1203			break;
1204			
1205			case "COOKIES":
1206			foreach ($val as $cval) $add_headers.="Set-Cookie: ".$cval."\r\n";
1207			break;
1208
1209			case "STATUS":
1210			
1211			$st=(int)strtok($val, " ");
1212
1213			if ($stx=trim(strtok(""))) {
1214
1215				$http_resp=$st." ".$stx;
1216			
1217			} else if ($stx=$HTTP_HEADERS[$st]) {
1218
1219				$http_resp=$stx;
1220			
1221			} else {
1222
1223				$http_resp=$st;
1224			
1225			}
1226
1227			$rq_err=$st;
1228			
1229			break;
1230
1231			default:		
1232			$add_headers.=$key.": ".$val."\r\n";
1233				
1234		}
1235
1236	}
1237	
1238	$clf=($GLOBALS["http_action"]=="HEAD"?$GLOBALS["hlf"]:$lf);
1239	
1240	$out_headers=HTTP_VERSION." ".($http_resp?trim($http_resp):$HTTP_HEADERS[$rq_err])."\r\n";
1241	$out_headers.="Date: ".gmdate("D, d M Y H:i:s T")."\r\n";
1242	if ($ss=nw_server_string()) $out_headers.="Server: ".$ss."\r\n";
1243	$out_headers.="Content-Type: ".$out_contenttype."\r\n";
1244
1245	if ($ahlist=access_query("addheader")) foreach ($ahlist as $val) $out_headers.=trim($val)."\r\n";
1246	if (($rq_err>=400) && ($eh=access_query("_errorheader", "_".$rq_err))) $out_headers.=$eh."\r\n";
1247	
1248	$out_headers.=$add_headers;
1249
1250	if ($GLOBALS["keepalive"]) {
1251	
1252		$out_headers.="Connection: Keep-Alive\r\n";
1253		$out_headers.="Keep-Alive: timeout=".(int)$conf["global"]["requesttimeout"][0].", max=".(int)($conf["global"]["keepalive"][0])."\r\n";
1254		
1255	} else {
1256		
1257		$out_headers.="Connection: close\r\n";
1258
1259	}
1260	
1261	if ($GLOBALS["chunked"]) { 
1262		
1263		$out_headers.="Transfer-Encoding: chunked\r\n";
1264
1265	} else {
1266
1267		if (is_int($clf->content_length)) $out_headers.="Content-Length: ".$clf->content_length."\r\n";
1268
1269	}
1270
1271	return($out_headers);
1272
1273}
1274
1275function nanoweb_init($conffile) {
1276
1277	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;
1278	
1279	$dc=get_defined_constants();
1280	foreach ($dc as $cname=>$cval) if (substr($cname, 0, 6)=="NW_EL_") $srvlog_levels[strtolower(substr($cname, 6))]=$cval;
1281
1282	$iconf=parseconfig(file($conffile));
1283
1284	if (is_string($iconf)) {
1285
1286		if ($icnt) {
1287			
1288			techo($iconf, NW_EL_WARNING);
1289			return(false);
1290
1291		} else {
1292
1293			errexit($iconf);
1294		
1295		}
1296	
1297	} else if (is_array($iconf)) {
1298
1299		$conf=$iconf;
1300	
1301	}
1302
1303	$conf=cmdline_conf_upd($conf, $cmdline_conf_overrides, $cmdline_conf_adds);
1304	$modules=load_modules($conf);
1305	modules_init();
1306	$themes=load_themes($conf);
1307
1308	++$icnt;
1309
1310	$ap_aliases=array(	"parseext"		=> "_parseext",
1311						"alias"			=> "_aliases",
1312						"errordocument"	=> "_errordocument",
1313						"errorheader"	=> "_errorheader"		);
1314
1315	$access_policy=array();
1316	foreach ($conf["global"]["accessoverride"] as $ov_dir) if ($ov_dir) $access_policy[strtolower($ov_dir)]="override";
1317	foreach ($conf["global"]["accessmerge"] as $mg_dir) if ($mg_dir) $access_policy[strtolower($mg_dir)]="merge";
1318	foreach ($conf["global"]["accessblock"] as $bl_dir) if ($bl_dir) $access_policy[strtolower($bl_dir)]="block";
1319
1320	foreach ($ap_aliases as $rk=>$ak) if ($access_policy[$rk]) $access_policy[$ak]=$access_policy[$rk];
1321
1322	$posix_av=is_callable("posix_setuid");
1323	$pcntl_av=is_callable("pcntl_fork");
1324	$gz_av=is_callable("gzencode");
1325
1326	if (count($themes)==0) techo("WARN: No theme loaded, server generated content is disabled", NW_EL_WARNING);
1327	
1328	if ($posix_av) foreach ($conf as $vconf) {
1329
1330		if ($u=$vconf["user"][0]) $sysusr[$u]=@posix_getpwnam($u);
1331		if ($g=$vconf["group"][0]) $sysgrp[$g]=@posix_getgrnam($g);
1332	
1333	}
1334
1335
1336	if ((!$conf["global"]["singleprocessmode"][0]) && (!$posix_av || !$pcntl_av || ($conf["global"]["servermode"][0]=="inetd"))) {
1337
1338		techo("WARN: forcing single process mode", NW_EL_WARNING);
1339		$conf["global"]["singleprocessmode"][0]=true;
1340
1341	}
1342
1343	if ($conf["global"]["servermode"][0]=="inetd") {
1344		
1345		unset($conf["global"]["logtoconsole"]);
1346		unset($conf["global"]["pidfile"]);
1347
1348	}
1349
1350	if ($conf["global"]["singleprocessmode"][0]) {
1351		
1352		$conf["global"]["loggerprocess"]=0;
1353
1354		if ($conf["global"]["keepalive"][0]) techo("WARN: KeepAlive should be set to 0 in single process mode", NW_EL_WARNING);
1355
1356	}
1357
1358	if ($pcntl_av) {
1359
1360		pcntl_signal(SIGTERM, "nanoweb_shutdown");
1361		pcntl_signal(SIGHUP, "nanoweb_reload");
1362
1363	}
1364
1365	$mime=array();
1366	
1367	if (!@is_readable($conf["global"]["mimetypes"][0])) {
1368
1369		techo("WARN: unable to read mime types file (".$conf["global"]["mimetypes"][0]."), using internals", NW_EL_WARNING);
1370
1371		$mime=array(	"html" => "text/html",
1372						"xml"  => "text/xml",
1373						"gif"  => "image/gif",
1374						"jpeg" => "image/jpeg",
1375						"png"  => "image/png",
1376						"tgz"  => "application/gtar");
1377
1378	} else if ($mimetypes=@file($conf["global"]["mimetypes"][0])) {
1379		
1380		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]);
1381
1382		unset($mimetypes);
1383
1384	}
1385
1386	if ($at=$conf["global"]["addtype"]) foreach ($at as $adt) {
1387
1388		$mt=strtok(trim($adt), " ");
1389		while ($s=strtok(" ")) $mime[ltrim($s, ".")]=$mt;
1390
1391	}
1392
1393	$conf["_complete"]=true;
1394
1395	$banned_ips=array();
1396
1397	if (is_array($conf["global"]["blockipaddr"])) foreach ($conf["global"]["blockipaddr"] as $ip) nw_block_ip_address($ip, "PERM", "config.BlockIPAddr");
1398
1399	return(true);
1400
1401}
1402
1403function nanoweb_shutdown($sig_no=SIGTERM) {
1404
1405	global $lsocks, $pidfile, $loggers_sck, $conf, $pmode;
1406	
1407	if ($pmode=="master") {
1408	
1409		modules_init("shutdown");
1410		
1411		if ($lsocks) foreach ($lsocks as $sock) socket_close($sock);
1412
1413		if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
1414		
1415			techo("halting loggers");
1416			
1417			for ($a=0;$a<$nb_loggers;$a++) {
1418
1419				$pkt="TERM";
1420				
1421				socket_write($loggers_sck, $pkt);
1422				usleep(100000);
1423			
1424			}
1425
1426			sleep(1);
1427		
1428		}
1429
1430		if ($pidfile) unlink($pidfile);
1431		techo("daemon stopped\n");
1432
1433	}
1434
1435	exit(0);
1436
1437}
1438
1439function nanoweb_reload($sig_no=SIGHUP) {
1440
1441	global $mypid, $conffile, $conf, $loggers_sck, $logger_pids, $killed_loggers, $access_cache, $rp_cache;
1442
1443	if (!$mypid) {
1444		
1445		techo("received SIGHUP, reloading configuration ...");
1446		
1447		clearstatcache();
1448		unset($access_cache);
1449		unset($rp_cache);
1450
1451		if (nanoweb_init($conffile)) {
1452
1453			if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
1454			
1455				techo("restarting loggers");
1456				
1457				foreach ($logger_pids as $lid) {
1458
1459					$pkt="TERM";
1460					
1461					socket_write($loggers_sck, $pkt);
1462					usleep(100000);
1463
1464					$killed_loggers[] = $lid;
1465
1466				}
1467
1468				sleep(1);
1469			
1470			}
1471		
1472			techo("configuration reloaded from ".$conffile);
1473		
1474		} else {
1475
1476			techo("configuration was not reloaded", NW_EL_WARNING);
1477
1478		}
1479
1480	}
1481
1482}
1483
1484function read_request(&$sck_connected, &$dp, &$pn, $maxlen=0) {
1485			
1486	global $conf;
1487
1488	static $rr_buffer;
1489	
1490	$wstart=time();
1491		
1492	if ($rr_buffer!=="") {
1493		
1494		$buf=$rr_buffer;
1495		$tnreq=true;
1496
1497	}
1498	
1499	while (!$rq_finished && $sck_connected) {
1500	
1501		if (!$tnreq) {
1502		
1503			$fdset=$GLOBALS["pfdset"];
1504
1505			if ($conf["global"]["servermode"][0]=="inetd") {
1506
1507				// Inetd
1508
1509				if (feof($GLOBALS["inetd_in"])) {
1510					
1511					$tmp=false;
1512					$sck_connected=false;
1513
1514				} else $tmp=fgetc($GLOBALS["inetd_in"]);
1515
1516			} else {
1517			
1518				if ($ns=socket_select($fdset, $write=NULL, $fdset, SCK_READ_SELECT_TIMEOUT)) $tmp=@socket_read($GLOBALS["msgsock"], SCK_READ_PACKET_SIZE); else $tmp=false;
1519
1520			} 
1521
1522		}
1523
1524		if ($tmp || $tmp==="0" || $tnreq) {
1525			
1526			$tnreq=false;
1527			$wstart=time();
1528			$buf.=$tmp;
1529			$pn=0;
1530
1531			if (!$maxlen) {
1532			
1533				if (!$rnloop) {
1534					
1535					$buf=ltrim($buf);
1536					$rnloop=true;
1537
1538				}
1539
1540				if (($dp=strpos($buf, "\r\n\r\n"))!==false) $pn=4;
1541				else if (($dp=strpos($buf, "\n\n"))!==false) $pn=2;
1542
1543				if ($pn) $rq_finished=true;
1544
1545			} else {
1546
1547				if (strlen($buf)>=$maxlen) $rq_finished=true;
1548
1549			}
1550
1551		} else if (($ns) || ((!$ns) && ((time()-$wstart)>=$conf["global"]["requesttimeout"][0]))) $sck_connected=false;
1552
1553	}
1554	
1555	if (!$maxlen) {
1556
1557		$tbuf=substr($buf, 0, $dp+$pn);
1558		$rr_buffer=substr($buf, $dp+$pn);
1559		$buf=$tbuf;
1560	
1561	} else {
1562
1563		$tbuf=substr($buf, 0, $maxlen);
1564		$rr_buffer=substr($buf, $maxlen);
1565		$buf=$tbuf;
1566
1567	}
1568	
1569	return($buf);
1570
1571}
1572
1573function send_response($response, &$sck_connected) {
1574
1575	global $msgsock;
1576	
1577	$resp_len=strlen($response);
1578
1579	while($sent_len<$resp_len && $sck_connected) {
1580
1581		if ($GLOBALS["conf"]["global"]["servermode"][0]=="inetd") {
1582
1583			// Inetd
1584			
1585			echo $response;
1586			$sent_len=strlen($response);
1587
1588		} else {
1589
1590			$fdset=$GLOBALS["pfdset"];
1591			
1592			if (($sent_len+SCK_WRITE_PACKET_SIZE)>$resp_len) $size=$resp_len-$sent_len; else $size=SCK_WRITE_PACKET_SIZE;
1593			if (($ret=@socket_write($msgsock, substr($response, $sent_len, $size), $size))>0) $sent_len+=$ret;
1594
1595			if ($ret===false) {
1596				
1597				if (socket_last_error($msgsock)==SOCKET_EWOULDBLOCK) {
1598					
1599					socket_clear_error($msgsock);
1600					if (!socket_select($read=NULL, $fdset, $except=NULL, SCK_MAX_STALL_TIME)) $sck_connected=false;
1601
1602				} else {
1603
1604					$sck_connected=false;
1605				
1606				}
1607
1608			}
1609
1610		}
1611
1612	}
1613
1614	return($sent_len);
1615
1616}
1617
1618function int_sendtomaster($msg_type, $args=false) {
1619
1620	if ((!$GLOBALS["conf"]["global"]["singleprocessmode"][0]) && ($GLOBALS["pmode"]!="master")) {
1621	
1622		$msg=$msg_type;
1623		if ($args!==false) $msg.=serialize($args);
1624		
1625		$ret=socket_write($GLOBALS["master_sck"], $msg);
1626
1627		if ($ret!=strlen($msg)) {
1628
1629			techo("WARN: master process is not responding", NW_EL_WARNING);
1630		
1631		}
1632
1633	}
1634
1635}
1636
1637function _server_report_state($s, $remote_host="") {
1638
1639	$tmp=array($GLOBALS["mypid"], $s);
1640	if ($remote_host) $tmp[]=$remote_host;
1641
1642	int_sendtomaster(NM_SERVER_STATE, $tmp);
1643
1644}
1645
1646function nw_block_ip_address($ip_addr, $type, $source, $expires=0) {
1647
1648	global $conf, $pmode, $banned_ips;
1649
1650	if ((($conf["global"]["singleprocessmode"][0]) || ($pmode=="master")) && (!$banned_ips[$ip_addr])) {
1651	
1652		$banned_ips[$ip_addr]=array("type" => $type, "source" => $source, "expires" => $expires);
1653		techo($source." : blocked IP address ".$ip_addr." (".strtolower($type).")", NW_EL_BLOCKING);
1654
1655	} else {
1656
1657		int_sendtomaster(NM_BLOCK_IP, array($ip_addr, $type, $source, $expires));
1658	
1659	}
1660
1661}
1662
1663function nw_unblock_ip_address($ip_addr, $msg=false) {
1664
1665	global $conf, $pmode, $banned_ips;
1666
1667	if ((($conf["global"]["singleprocessmode"][0]) || ($pmode=="master")) && ($banned_ips[$ip_addr])) {
1668	
1669		$source=strtok($banned_ips[$ip_addr]["source"], ".");
1670		$rejs=$banned_ips[$ip_addr]["rejects"];
1671		
1672		unset($banned_ips[$ip_addr]);
1673		techo($source." : unblocked IP address ".$ip_addr." (".(int)$rejs." rejs".($msg===false?"":(", ".$msg)).")", NW_EL_BLOCKING);
1674
1675	} else {
1676
1677		int_sendtomaster(NM_UNBLOCK_IP, $ip_addr);
1678	
1679	}
1680
1681}
1682
1683function logger_run($logger_id) {
1684
1685	global $conf, $children_logsck, $modules, $plgset, $pmode;
1686
1687	$pmode="logger";
1688
1689	pcntl_signal(SIGTERM, SIG_DFL);
1690	pcntl_signal(SIGHUP, SIG_IGN);
1691
1692	$mypid=posix_getpid();
1693
1694	$lids=log_ids();
1695	posix_setgid($lids["gid"]);
1696	posix_setuid($lids["uid"]);
1697
1698	techo("logger process #".$logger_id." is running (pid=".$mypid.")");
1699
1700	while (!$logger_exit) {
1701		
1702		$r=socket_read($children_logsck, INT_MSGSIZE);
1703
1704		switch($r) {
1705
1706			case "TERM": 
1707			$logger_exit=true;
1708			break;
1709
1710			default:
1711
1712			$l=unserialize($r);
1713
1714			// Reverse DNS query if the server hasn't done it before
1715			
1716			if (($conf["global"]["hostnamelookups"][0]) && ($conf["global"]["hostnamelookupsby"][0]=="logger") && ($rhost=nw_gethostbyaddr($l[2]))) $l[1]=$rhost;
1717
1718			// And call the logging modules
1719			
1720			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]);
1721
1722			break;
1723			
1724		}
1725	
1726	}
1727
1728	techo("logger process #".$logger_id." stopped");
1729
1730	exit(0);
1731
1732}
1733
1734function spawn_loggers($nb_loggers) {
1735	
1736	global $logger_pids;
1737
1738	static $logger_id;
1739
1740	for ($a=0;$a<$nb_loggers;++$a) {
1741
1742		$pid=pcntl_fork();
1743		++$logger_id;		
1744
1745		if ($pid===0) {
1746			
1747			logger_run($logger_id);
1748
1749		} else {
1750			
1751			$logger_pids[$pid]=$logger_id;
1752			if ($nb_loggers>1) usleep(100000);
1753
1754		}
1755			
1756	}
1757
1758}
1759
1760// Begin
1761
1762set_time_limit(0);
1763$pmode="master";
1764
1765techo("aEGiS nanoweb/".VERSION." (C) 2002-2005 by sIX / aEGiS");
1766
1767$stats_start=time();
1768
1769if (version_compare(phpversion(), REQUIRED_PHP_VERSION)<0) errexit("nanoweb needs PHP >= ".REQUIRED_PHP_VERSION." (you are using ".phpversion().")");
1770if (version_compare(phpversion(), "4.3.0")>=0) $sckv3=true;
1771
1772$os=((strpos(strtolower(PHP_OS), "win")===0) || (strpos(strtolower(PHP_OS), "cygwin")!==false))?"win32":"unix";
1773
1774switch ($os) {
1775
1776	case "win32":
1777	define("NW_BSAFE_READ_OPEN", "rb");
1778	define("NW_BSAFE_WRITE_OPEN", "wb");
1779	define("NW_BSAFE_APP_OPEN", "ab");
1780	if (!defined("SOCKET_EWOULDBLOCK")) define("SOCKET_EWOULDBLOCK", 10035);
1781	break;
1782
1783	default:
1784	define("NW_BSAFE_READ_OPEN", "r");
1785	define("NW_BSAFE_WRITE_OPEN", "w");
1786	define("NW_BSAFE_APP_OPEN", "a");
1787	if (!defined("SOCKET_EWOULDBLOCK")) define("SOCKET_EWOULDBLOCK", 11);
1788	break;
1789
1790}
1791
1792foreach ($TEST_FUNCS as $f_name=>$f_mandatory) if (!is_callable($f_name)) {
1793
1794	if ($f_mandatory) errexit("function '".$f_name."' not available, aborting");
1795	else techo("WARN: function '".$f_name."' not available", NW_EL_WARNING);
1796
1797}
1798
1799// Parse command line
1800
1801if ($_SERVER["argc"]>1) for($a=1;$a<$_SERVER["argc"];$a++) {
1802
1803	if (($a==1) && (substr($_SERVER["argv"][$a], 0, 1)!="-")) {
1804
1805		$cmdline_cf=$_SERVER["argv"][1];
1806	
1807	} else {
1808
1809		$ca=explode("=", $_SERVER["argv"][$a]);
1810		$ck=array_shift($ca);
1811		$cv=implode("=", $ca);
1812
1813		switch($ck) {
1814
1815			case "-?":
1816			case "-h":
1817			case "--help":
1818			die($cmdline_help);
1819			break;
1820
1821			case "-v":
1822			case "--version":
1823			die(VERSION."\n");
1824			break;
1825			
1826			case "-c":
1827			case "--config":
1828			$cmdline_cf=$cv;
1829			break;
1830
1831			case "-o":
1832			case "--set-option":
1833			$cmdline_conf_overrides[]=$cv;
1834			break;
1835
1836			case "-a":
1837			case "--add-option":
1838			$cmdline_conf_adds[]=$cv;
1839			break;
1840
1841			case "-d":
1842			case "--start-daemon":
1843			$start_daemon=true;
1844			break;
1845			
1846			case "-q":
1847			case "--quiet":
1848			$quiet=true;
1849			break;
1850
1851			case "--debug":
1852			$nw_debug=true;
1853			case "--verbose":
1854			break;
1855			
1856			case "--config-test":
1857			case "-t":
1858			$config_test=true;
1859			break;
1860			
1861			default:
1862			errexit("unknown argument : ".$_SERVER["argv"][$a].", try --help");
1863			break;
1864
1865		}
1866	
1867	}
1868	
1869}
1870
1871if ($cmdline_cf) $conffile=$cmdline_cf; else $conffile=DEFAULT_CONFIG_FILE;
1872if (!is_readable($conffile)) errexit("unable to read configuration (".$conffile."), aborting");
1873
1874unset($cmdline_help);
1875
1876nanoweb_init($conffile);
1877
1878if ($config_test) {
1879	
1880	techo("configuration test successful");
1881	exit(0);
1882
1883}
1884
1885if ($conf["global"]["servermode"][0]!="inetd") {
1886
1887	// Create socket(s) and start listening
1888	
1889	if ($sckv3) {
1890		
1891		$setsockopt="socket_set_option";
1892		$getsockopt="socket_get_option";
1893
1894	} else {
1895		
1896		$setsockopt="socket_setopt";
1897		$getsockopt="socket_getopt";
1898
1899	}
1900	
1901	foreach ($conf["global"]["listenport"] as $lport) {
1902	
1903		$lport=(int)$lport;
1904		
1905		if (($sock = @socket_create(AF_INET, SOCK_STREAM, 0))<0) errexit("socket create failed : ".socket_strerror(socket_last_error()));
1906
1907		if (is_callable($setsockopt) && is_callable($getsockopt)) {
1908		
1909			$setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1);
1910
1911			$sbuf=$getsockopt($sock, SOL_SOCKET, SO_SNDBUF);
1912			$rbuf=$getsockopt($sock, SOL_SOCKET, SO_RCVBUF);
1913
1914			if ($sbuf<(SCK_WRITE_PACKET_SIZE*32)) $setsockopt($sock, SOL_SOCKET, SO_SNDBUF, SCK_WRITE_PACKET_SIZE*32);
1915			if ($rbuf<(SCK_READ_PACKET_SIZE*32)) $setsockopt($sock, SOL_SOCKET, SO_RCVBUF, SCK_READ_PACKET_SIZE*32);
1916
1917		}
1918		
1919		if (!@socket_bind($sock, $conf["global"]["listeninterface"][0], $lport)) errexit("socket bind failed on port ".$lport." : ".socket_strerror(socket_last_error($sock)));
1920		if (!@socket_listen($sock, $conf["global"]["listenqueue"][0])) errexit("socket listen failed on port ".$lport." : ".socket_strerror(socket_last_error($sock)));
1921
1922		socket_set_nonblock($sock);
1923
1924		$lsocks[$lport]=$sock;
1925		$lports[$sock]=$lport;
1926
1927	}
1928
1929}
1930
1931if ($pcntl_av) {
1932
1933	$sck_pair=array();
1934	socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $sck_pair);
1935
1936	$children_sck=&$sck_pair[0];
1937	$master_sck=&$sck_pair[1];
1938
1939	socket_set_nonblock($children_sck);
1940	socket_set_nonblock($master_sck);
1941
1942}
1943
1944$plnset=array($children_sck);
1945foreach ($lsocks as $sck) $plnset[]=$sck;
1946
1947if ($conf["global"]["servermode"][0]!="inetd") {
1948
1949	techo("listening on port".(count($lports)>1?"s":"")." ".implode(", ", $lports));
1950
1951	$stdfd = fopen("php://stdin", "r");
1952	fclose($stdfd);
1953	$stdfd = fopen(($os == "unix") ? "/dev/null" : "NUL", "r");
1954
1955} else {
1956
1957	$inetd_in=fopen("php://stdin", NW_BSAFE_READ_OPEN);
1958	set_file_buffer($inetd_in, 0);
1959
1960	techo("running in inetd mode");
1961
1962}
1963
1964$def_cnx=($conf["global"]["servermode"][0]=="inetd");
1965
1966if ($start_daemon) {
1967
1968	if (!$posix_av || !$pcntl_av) errexit("posix and pcntl PHP extensions are needed for --start-daemon");
1969
1970	$npid = pcntl_fork();
1971
1972	if ($npid == -1) {
1973		
1974		errexit("unable to pcntl_fork()");
1975
1976	} else if ($npid) {
1977
1978		exit(0);
1979
1980	}
1981
1982	posix_setsid();
1983	usleep(100000);
1984
1985	$npid = pcntl_fork();
1986
1987	if ($npid == -1) {
1988		
1989		errexit("unable to pcntl_fork()");
1990
1991	} else if ($npid) {
1992
1993		techo("running in background");
1994
1995		exit(0);
1996
1997	}
1998
1999}
2000
2001if ($nb_loggers=$conf["global"]["loggerprocess"][0]) {
2002
2003	// Prepare and spawn logger processes if specified
2004	
2005	techo("spawning loggers");
2006
2007	$sck_pair=array();
2008	socket_create_pair(AF_UNIX, SOCK_DGRAM, 0, $sck_pair);
2009
2010	$children_logsck=&$sck_pair[0];
2011	$loggers_sck=&$sck_pair[1];
2012	
2013	socket_set_nonblock($loggers_sck);
2014	
2015	spawn_loggers($nb_loggers);
2016
2017} else {
2018
2019	// Be sure not to ask anything to loggers
2020
2021	$conf["global"]["hostnamelookupsby"][0]="server";
2022
2023}
2024
2025if ($posix_av && $conf["global"]["pidfile"][0]) {
2026	
2027	$pidfile=$conf["global"]["pidfile"][0];
2028	
2029	if ($f=fopen($pidfile, "w")) {
2030	
2031		fputs($f, (string)posix_getpid()."\n");
2032		fclose($f);
2033
2034	} else {
2035
2036		techo("WARN: unable to open pid file '".$pidfile."'", NW_EL_WARNING);
2037		unset($pidfile);
2038	
2039	}
2040
2041}
2042
2043if (empty($nw_debug)) error_reporting(E_PARSE | E_ERROR);
2044
2045if ($conf["global"]["servermode"][0]!="inetd") techo("ready and accepting connections");
2046
2047while (true) {
2048
2049	$cnx=$def_cnx;
2050	
2051	while (!$cnx) {
2052
2053		declare (ticks = 1) {
2054		
2055			// Allow to catch signals here			
2056			
2057			$lnset=$plnset;
2058
2059		}
2060		
2061		$ns=socket_select($lnset, $write=NULL, $except=NULL, 1);
2062		
2063		if ($ns) foreach ($lnset as $lnact) {
2064				
2065			if ($lnact==$children_sck) {
2066			
2067				while ($msg=socket_read($children_sck, INT_MSGSIZE)) {
2068
2069					// Message from a child process
2070					
2071					$mtype=substr($msg, 0, 5);
2072					if (strlen($msg)>5) $mcontent=unserialize(substr($msg, 5));
2073
2074					switch ($mtype) {
2075
2076						case NM_HIT: 
2077							
2078						// content : 0 => pid, 1 => request status, 2 => length, 3 => vhost
2079						
2080						if (is_array($mcontent)) {
2081
2082							++$stats_resperr[$mcontent[1]];
2083							++$stats_vhosts[$conf[$mcontent[3]]["servername"][0]];
2084							++$stats_hits;
2085							$stats_xfer+=(float)$mcontent[2];
2086							
2087							if ($scoreboard[$spid=$mcontent[0]]) {
2088							
2089								$scoreboard[$spid][NW_SB_STATUS]="(waiting for request)";
2090
2091							}
2092
2093						}
2094
2095						break;
2096
2097
2098						case NM_RESTART_LOGGERS:
2099
2100						techo("respawning loggers");
2101						spawn_loggers($conf["global"]["loggerprocess"][0]);
2102						
2103						break;
2104
2105
2106						case NM_RELOAD_THEME:
2107
2108						// content : theme id
2109						
2110						clearstatcache();
2111
2112						if ((is_array($themes[$mcontent])) && ($themes[$mcontent]["_mtime"]!=filemtime($fname=$themes[$mcontent]["_fname"]))) {
2113							
2114							$tmp_thm=load_theme($fname, true, true);
2115							$themes[$mcontent]=$tmp_thm;
2116							$themes[$fname]=$tmp_thm;
2117							$themes[$tmp_thm["_fname"]]=$tmp_thm;
2118
2119						}
2120
2121						break;
2122
2123
2124						case NM_SERVER_STATE:
2125
2126						// content : 0 => pid, 1 => status text, 2 => remote host
2127						
2128						if ($scoreboard[$spid=$mcontent[0]]) {
2129						
2130							$scoreboard[$spid][NW_SB_STATUS]=$mcontent[1];
2131							if ($mcontent[2]) $scoreboard[$spid][NW_SB_PEERHOST]=$mcontent[2];
2132
2133						}
2134						
2135						break;
2136
2137
2138						case NM_BLOCK_IP:
2139
2140						// content : 0 => ip address, 1 => type (PERM|TEMP), 2 => source, 3 => expiration
2141
2142						if (is_array($mcontent)) nw_block_ip_address($mcontent[0], $mcontent[1], $mcontent[2], $mcontent[3]);
2143
2144						break;
2145					
2146
2147						case NM_UNBLOCK_IP:
2148
2149						// content : ip address
2150						
2151						if ($mcontent) nw_unblock_ip_address($mcontent);
2152						
2153						break;
2154					
2155					}
2156
2157				}
2158
2159			} else {
2160
2161				$sock=$lnact;
2162				$lport=$lports[$sock];
2163
2164				if ((($active_servers<$conf["global"]["maxservers"][0]) || (!$conf["global"]["maxservers"][0])) && (is_resource($msgsock=@socket_accept($sock)))) {
2165				
2166					// We do have a connection
2167					
2168					$remote_ip=$remote_port=0;
2169					socket_getpeername($msgsock, $remote_ip, $remote_port);
2170					
2171					if (is_array($banned_ips[$remote_ip])) {
2172
2173						// Disconnect if IP address is banned
2174
2175						techo("rejected connection #".(++$banned_ips[$remote_ip]["rejects"])." from blocked IP address ".$remote_ip, NW_EL_BLOCKING);
2176						socket_close($msgsock);
2177											
2178						++$stats_rej;
2179					
2180					} else if ($remote_ip) {
2181					
2182						// Or handle the new connection
2183						
2184						$cnx=true;
2185
2186						++$stats_cnx;
2187
2188					} else {
2189
2190						// Cannot obtain peer IP address, something is wrong (but not worth throwing a notice)
2191						
2192						socket_close($msgsock);
2193					
2194					}
2195				
2196				}
2197			
2198			}
2199		
2200		} 
2201
2202		if (!$conf["global"]["singleprocessmode"][0]) while (($deadpid=pcntl_waitpid(-1, $cstat, WNOHANG)) && ($deadpid!=-1)) {
2203
2204			// Dead child
2205
2206			if (($dead_logger=$logger_pids[$deadpid]) && ($conf["global"]["loggerprocess"][0])) {
2207
2208				// Dead logger (this is abnormal, we have to restart one)
2209
2210				if (in_array($dead_logger, $killed_loggers)) {
2211
2212					unset($killed_loggers[array_search($dead_logger, $killed_loggers)]);
2213				
2214				} else {
2215				
2216					techo("logger process #".$dead_logger." died (pid=".$deadpid."), respawning", NW_EL_WARNING);
2217
2218				}
2219					
2220				unset($logger_pids[$deadpid]);
2221				
2222				spawn_loggers(1);
2223			
2224			} else {
2225
2226				// Dead child server, clear servers table
2227
2228				unset($scoreboard[$deadpid]);
2229				--$active_servers;
2230			
2231			}
2232		
2233		}
2234
2235		$ct=time();
2236		foreach ($banned_ips as $ip_addr=>$bip) if (($bip["type"]=="TEMP") && ($bip["expires"]<=$ct)) nw_unblock_ip_address($ip_addr, "expired");
2237		
2238	}
2239	
2240	if ($conf["global"]["singleprocessmode"][0]) {
2241		
2242		$pid=0;
2243
2244		// Invalidate access and rp caches every SPM_CACHES_LIFETIME connections
2245
2246		if (($stats_cnx%SPM_CACHES_LIFETIME)==0) {
2247			
2248			clearstatcache();
2249			unset($access_cache);
2250			unset($rp_cache);
2251
2252		}
2253
2254	} else {
2255		
2256		$pid=pcntl_fork();
2257		if ($pid===0) $pmode="server";
2258
2259	}
2260
2261	if ($pid===0) {
2262	
2263		if ($posix_av) $mypid=posix_getpid();
2264		
2265		if (!$conf["global"]["singleprocessmode"][0]) {
2266			
2267			foreach ($lsocks as $sock) socket_close($sock);
2268			
2269			set_time_limit($conf["global"]["childlifetime"][0]);
2270
2271		}
2272		
2273		if ($conf["global"]["servermode"][0]!="inetd") {
2274		
2275			socket_set_nonblock($msgsock);
2276			$pfdset=array($msgsock);
2277
2278			if (($conf["global"]["hostnamelookups"][0]) && ($conf["global"]["hostnamelookupsby"][0]!="logger") && ($rhost=nw_gethostbyaddr($remote_ip))) {
2279				
2280				$remote_host=$rhost;
2281				_server_report_state("(connected)", $remote_host);
2282
2283			} else {
2284			
2285				$remote_host=$remote_ip;
2286
2287			}
2288
2289		} else {
2290
2291			$remote_ip=getenv("INETD_REMOTE_IP");
2292			$remote_port=getenv("INETD_REMOTE_PORT");
2293		
2294		}
2295
2296		$rq_count=0;
2297		
2298		while ($cnx) {
2299		
2300			$sck_connected=true;
2301			$http_continue=false;
2302			$http_rq_block=$buf=read_request($sck_connected, $dp, $pn);
2303			$pri_redir=$http_uri=$out_headers="";
2304			$pri_err=$pri_redir_code=$rq_err=0;
2305			$add_nsv=$htreq_headers=$out_add_headers=array();
2306
2307			if ($sck_connected) {
2308
2309				if (strlen($buf)!=$dp+4) $add_req=substr($buf, $dp+4); else $add_req="";
2310				$tmp_arr=explode("\n", substr($buf, 0, $dp));
2311				$l=false;
2312				
2313				foreach ($tmp_arr as $s) {
2314				
2315					$s=trim($s);
2316
2317					if (!$l) {
2318
2319						$http_action=strtok($s, " ");
2320						$http_resource=strtok(" ");
2321						$http_protocol=strtoupper(strtok("/"));
2322						$http_version=strtok("");
2323						$l=true;
2324
2325						if ($http_protocol!="HTTP") {
2326
2327							// Invalid protocol
2328
2329							$pri_err=400;
2330							$add_errmsg="Unable to serve requested protocol.<br><br>";
2331						
2332						}
2333
2334					} else {
2335
2336						if (strpos($s, ":")===false) {
2337
2338							// Invalid request header
2339
2340							$pri_err=400;
2341							$add_errmsg="Invalid request header received.<br><br>";
2342						
2343						} else {
2344						
2345							$hd_key=strtoupper(strtok($s, ":"));
2346							$hd_val=trim(strtok(""));
2347
2348							if (isset($htreq_headers[$hd_key])) {
2349
2350								$pri_err=400;
2351								$add_errmsg="Duplicate request header '{$hd_key}'<br><br>";
2352
2353							} else {
2354							
2355								$htreq_headers[$hd_key]=$hd_val;
2356
2357							}
2358
2359						}
2360
2361					}
2362				
2363				}
2364
2365				// Decode Host header
2366					
2367				$host=strtok(trim(strtolower($htreq_headers["HOST"])), ":");
2368				$vhost=nw_host_to_vhost($host, $lport);
2369
2370				if ($auth_hdr=$htreq_headers["AUTHORIZATION"]) {
2371
2372					// Decode HTTP Authentication header
2373
2374					$dtmp=explode(" ", $auth_hdr);
2375					$auth_type=$dtmp[0];
2376					$auth_lp=explode(":", base64_decode($dtmp[1]));
2377					$auth_user=$auth_lp[0];
2378					$auth_pass=$auth_lp[1];
2379				
2380				} else $auth_type=$auth_user=$auth_pass="";
2381
2382				// Decode Keep-Alive header
2383				
2384				$keepalive=(strtolower(trim($htreq_headers["CONNECTION"]))=="keep-alive" && (int)$conf["global"]["keepalive"][0]>1);
2385				if ($keepalive && (++$rq_count>=(int)$conf["global"]["keepalive"][0])) $keepalive=false;
2386				$cnx=$keepalive;
2387				
2388				// Set Uid and Gid
2389
2390				$cfgid=$conf[$vhost]["group"][0];
2391				$cfuid=$conf[$vhost]["user"][0];
2392
2393				if ($posix_av) {
2394				
2395					$ugtok=$sysusr[$cfuid]["uid"].$sysgrp[$cfgid]["gid"];
2396					
2397					if ($uid_set) {
2398
2399						if ($uid_set!=$ugtok) {
2400
2401							// Keep-alive request for another user/group vhost, this is bad
2402							
2403							$pri_err=400;
2404						
2405						}
2406					
2407					} else {
2408					
2409						if ($setgid=$sysgrp[$cfgid]["gid"]) posix_setgid($setgid);
2410						if ($setuid=$sysusr[$cfuid]["uid"]) posix_setuid($setuid);
2411
2412						$uid_set=$ugtok;
2413					
2414					}
2415				
2416				}
2417				
2418				$docroot=$conf[$vhost]["documentroot"][0];
2419				$docroot_prefix="";
2420
2421				if ($exp_hdr=$htreq_headers["EXPECT"]) {
2422
2423					// Enforce HTTP Expect header
2424
2425					if (trim(strtolower($exp_hdr))=="100-continue") {
2426						
2427						$http_continue=true;
2428
2429					} else {
2430
2431						$pri_err=417;
2432					
2433					}
2434					
2435				}
2436				
2437				if ($p1=$http_resource) {
2438
2439					$p1=explode("?", $p1);
2440					$real_uri=ltrim($http_uri=rawurldecode($p1[0]), "/");
2441					$http_uri=str_replace(chr(0), "", $http_uri);
2442					$query_string=$p1[1];
2443
2444					$hu=$docroot.$real_uri;
2445
2446					// Load access files if needed
2447					
2448					unset($access);
2449					
2450					if (is_dir($hu)) {
2451						
2452						$uridir=substr($http_uri, 1);
2453						
2454					} else if (is_dir($docroot.($uridn=dirname($http_uri)))) {
2455
2456						$uridir=substr($uridn, 1);
2457					
2458					} else $uridir="";
2459
2460					if (($accessdir=nw_realpath($docroot.$uridir)) && ($conf["global"]["accessfile"][0])) load_access_files($accessdir, $access);
2461
2462					core_modules_hook("before_decode");
2463
2464					foreach (access_query("_aliases") as $key=>$val) if (strpos(rtrim($http_uri, "/"), rtrim($key, "/"))===0) {
2465
2466						// Alias
2467						
2468						$docroot=$val.((substr($val, -1)==DIRECTORY_SEPARATOR)?"":DIRECTORY_SEPARATOR);
2469						$docroot_prefix=trim($key, "/")."/";
2470						$http_uri=str_replace($key, "", $http_uri);
2471
2472						if ((is_dir($docroot.$http_uri)) && (substr($docroot.$http_uri, -1)!="/")) $pri_redir=nw_url_addslash($http_resource);
2473
2474						break;
2475
2476					}
2477
2478					$http_uri=ltrim($http_uri, "/");
2479					
2480					if ($http_uri{0}=="~") {
2481						
2482						// User directory
2483						
2484						if (($udadd=$conf[$vhost]["userdir"][0]) && ($posix_av)) {
2485
2486							$upos=strpos($http_uri, "/");
2487							
2488							$udname=substr($http_uri, 1, (($upos===false)?(strlen($http_uri)):($upos-1)));
2489							$udres=(($upos===false)?"":(substr($http_uri, $upos+1)));
2490							
2491							if ($udinf=@posix_getpwnam($udname)) {
2492
2493								$tmpdr=$udinf["dir"].DIRECTORY_SEPARATOR.$udadd.DIRECTORY_SEPARATOR;
2494								
2495								if (is_dir($tmpdr)) {
2496
2497									if ((is_dir($tmpdr.$udres)) && (substr($http_uri, -1)!="/")) {
2498										
2499										$pri_redir=nw_url_addslash($http_resource);
2500
2501									} else {
2502								
2503										$docroot=$tmpdr;
2504										$docroot_prefix="~".$udname."/";
2505										$http_uri=$udres;
2506								
2507									}
2508								
2509								} else {
2510
2511									// User exists but does not have a public html directory
2512									
2513									$pri_err=404;
2514								
2515								}
2516							
2517							} else {
2518
2519								// User does not exists
2520								
2521								$pri_err=404;
2522							
2523							}
2524
2525						}
2526					
2527					}
2528					
2529					if (is_dir($docroot.$http_uri) && !$pri_redir) {
2530						
2531						if ($http_uri && substr($http_uri, -1)!="/") {
2532
2533							$pri_redir=nw_url_addslash($http_resource);
2534						
2535						} else if ($dilist=access_query("directoryindex", 0)) {
2536						
2537							$dis=explode(" ", $dilist);
2538
2539							foreach ($dis as $diname) {
2540								
2541								switch ($diname{0}) {
2542								
2543									case DIRECTORY_SEPARATOR:
2544
2545									if (@is_readable($diname)) {
2546
2547										$docroot=dirname($diname).DIREECTORY_SEPARATOR;
2548										$http_uri=basename($diname);
2549										break;
2550									
2551									}
2552									
2553									break;
2554									
2555									default:
2556								
2557									if (@is_readable($docroot.$http_uri.$diname)) {
2558
2559										$http_uri.=$diname;
2560										break;
2561
2562									}
2563							
2564								}
2565							
2566							}
2567
2568						}
2569
2570					}
2571
2572					$path_info="";
2573				
2574					if (access_query("allowpathinfo", 0) && !file_exists($docroot.$http_uri)) {
2575						
2576						// Try path_info
2577
2578						$new_uri=$http_uri;
2579						
2580						while (!@is_file($docroot.$new_uri) && $new_uri) {
2581
2582							$new_uri=substr($new_uri, 0, strrpos($new_uri, "/"));
2583
2584							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)) {
2585
2586								$new_uri.=".".$pie_ext;
2587								break;
2588							
2589							}
2590
2591						}
2592
2593						if ($new_uri) {
2594
2595							// Path_info found
2596
2597							$path_info=substr($http_uri, strlen($new_uri));
2598							$http_uri=$new_uri;
2599						
2600						}
2601					
2602					}
2603					
2604					$rq_file=pathinfo($http_uri);
2605
2606				}
2607
2608				$hbn=basename($http_uri);
2609				unset($bad_rq);
2610				
2611				// File access security tests
2612				
2613				if (nw_realpath($docroot.$http_uri) && (strpos(nw_realpath($docroot.$http_uri), nw_realpath($docroot))===false)) {
2614					
2615					$bad_rq=NW_BAD_OUTSIDE_DOCROOT;
2616
2617				} 
2618				
2619				if (($conf[$vhost]["ignoredotfiles"][0]) && ($hbn{0}==".") && ($hbn!="..") && ($hbn!=".")) {
2620
2621					$bad_rq=NW_BAD_DOT_FILE;
2622				
2623				}
2624
2625				if (($os == "win32") && in_array(strtolower(strtok($hbn, ".")), $win_devices)) {
2626					
2627					$bad_rq=NW_BAD_WIN_DEVICE;
2628
2629				}
2630				
2631				if (($bad_rq==NW_BAD_OUTSIDE_DOCROOT) && ($als_arr=$conf[$vhost]["allowsymlinkto"])) {
2632					
2633					// Test for outside-docroot access exemptions (AllowSymlinkTo)
2634					
2635					$tdir=$http_uri;
2636
2637					while ($tdir) {
2638						
2639						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) {
2640
2641							unset($bad_rq);
2642							break 2;
2643
2644						}
2645						
2646						$tdir=substr($tdir, 0, strrpos($tdir, "/"));
2647					
2648					}
2649
2650				}
2651
2652				if ($bad_rq) switch ($bad_rq) {
2653
2654					case NW_BAD_OUTSIDE_DOCROOT:
2655					techo("NOTICE: discarded request outside of document root (".$docroot.$http_uri.")");
2656					$http_uri="";
2657					$pri_err=404;
2658					break;
2659
2660					case NW_BAD_DOT_FILE:
2661					techo("NOTICE: discarded request for dot file (".$docroot.$http_uri.")");
2662					$http_uri="";
2663					$pri_err=404;
2664					break;
2665
2666					case NW_BAD_WIN_DEVICE:
2667					techo("NOTICE: discarded request for windows device file (".$docroot.$http_uri.")");
2668					$http_uri="";
2669					$pri_err=404;
2670					break;
2671					
2672				}
2673
2674				$sst=$http_action." http://".$htreq_headers["HOST"]."/".$real_uri.($query_string?("?".$query_string):"");
2675				_server_report_state($sst);
2676
2677				if ($hu!=($docroot.$http_uri)) {
2678				
2679					// Reload access files if needed
2680					
2681					$hu=$docroot.$http_uri;
2682
2683					unset($access);
2684					
2685					if (is_dir($hu)) {
2686						
2687						$uridir=$http_uri;
2688
2689					} else if (is_dir($docroot.($uridn=dirname($http_uri)))) {
2690
2691						$uridir=$uridn;
2692					
2693					} else $uridir="";
2694
2695					if (($accessdir=nw_realpath($docroot.$uridir)) && ($conf["global"]["accessfile"][0])) {
2696						
2697						load_access_files($accessdir, $access);
2698
2699					}
2700
2701				}
2702				
2703				$out_contenttype=$default_ct=access_query("defaultcontenttype", 0);
2704				
2705				// AuthLocation handler
2706				
2707				$bypass_auth = false;
2708				
2709				if ($authls=access_query("authlocation")) {
2710					
2711					$bypass_auth = true;
2712					
2713					foreach ($authls as $authl) if (strpos("/".$real_uri, $authl) === 0) {
2714
2715						$bypass_auth = false;
2716						break;
2717
2718					}
2719						
2720				}
2721				
2722				// Auth handler
2723				
2724				$logged_user="";
2725				
2726				if (($rauths=access_query("authrequire")) && (!$bypass_auth)) {
2727					
2728					foreach ($rauths as $rauth) {
2729
2730						if ($spos=strpos($rauth, " ")) {
2731						
2732							$authtype=strtolower(strtok($rauth, " "));
2733							$authargs=trim(strtok(""));
2734
2735						} else {
2736
2737							$authtype=strtolower($rauth);
2738							$authargs="";
2739
2740						}
2741						
2742						$authmodn="auth_".strtolower($authtype);
2743
2744						if (is_object($modules[$authmodn][0])) {
2745							
2746							if ($modules[$authmodn][0]->auth($auth_user, $auth_pass, $authargs)) {
2747
2748								$logged_user=$auth_user;
2749								break;
2750							
2751							}
2752
2753						} else {
2754
2755							techo("WARN: authentication module not found for type '".$authtype."'", NW_EL_WARNING);
2756					
2757						}
2758
2759					}
2760					
2761					if ($logged_user==="") {
2762					
2763						$logged_user=" ";
2764						$pri_err=401;
2765						$out_add_headers["WWW-Authenticate"]="Basic realm=\"".access_query("authrealm", 0)."\"";
2766						if ($emsg=access_query("authmessage", 0)) $add_errmsg.=$emsg."<br><br>";
2767
2768					}
2769				
2770				}
2771				
2772				// Test for maximum URI length
2773
2774				if (($conf[$vhost]["maxrequesturilength"][0]) && (strlen($http_resource)>$conf[$vhost]["maxrequesturilength"][0])) {
2775
2776					$pri_err=414;
2777				
2778				}
2779				
2780				if ($htreq_headers["CONTENT-LENGTH"]) {
2781
2782					// Read request content if there is one (POST requests)
2783					
2784					if (($maxblen=$conf[$vhost]["maxrequestbodylength"][0]) && ((int)$htreq_headers["CONTENT-LENGTH"]>$maxblen)) {
2785
2786						// Request content is too large
2787
2788						$pri_err=413;
2789					
2790					} else {
2791							
2792						if ($http_continue && !$pri_err) send_response(HTTP_VERSION." ".$HTTP_HEADERS[100]."\r\n\r\n", $sck_connected);
2793
2794						$buf=$add_req;
2795						if (strlen($buf)<$htreq_headers["CONTENT-LENGTH"]) $buf.=read_request($sck_connected, $dp, $pn, $htreq_headers["CONTENT-LENGTH"]-strlen($buf));
2796						$htreq_content=substr($buf, 0, $htreq_headers["CONTENT-LENGTH"]);
2797
2798					}
2799				
2800				}
2801				
2802				core_modules_hook("after_decode");
2803
2804				if ($sck_connected) {
2805
2806					switch ($http_action) {
2807
2808						case "POST":
2809
2810						if ((!access_query("_parseext", "_".strtolower($rq_file["extension"]))) && (is_file($docroot.$http_uri)) && (!$pri_parser)) {
2811							
2812							// Disallow POST on static content
2813							
2814							$pri_err=405;
2815							$out_add_headers["Allow"]=nw_allow_list($rq_file["extension"]);
2816
2817						}
2818						
2819						case "GET":
2820						case "HEAD":
2821
2822						if ($pri_err) {
2823
2824							// Internal setting of http error
2825
2826							$rq_err=$pri_err;
2827						
2828						} else if ($pri_redir) {
2829
2830							// Internal redirection
2831
2832							if ($rq_err<300) {
2833								
2834								$rq_err=$pri_redir_code
2835								or $rq_err=302;
2836
2837							}
2838
2839							$out_add_headers["Location"]=$pri_redir;
2840							if (version_compare($http_version, "1.0")<=0) $out_add_headers["URI"]=$pri_redir;
2841
2842						} else if (is_object($umod=&$modules["url:/".$http_uri])) {
2843
2844							// Module URL Hook
2845
2846							if ($umod->modtype=="url2") {
2847
2848								$lf=$umod;
2849								$lf->parser_open("", $real_uri, $rq_err, $out_add_headers, $out_contenttype);
2850							
2851							} else {
2852							
2853								$lf =& new static_response($umod->url($rq_err, $out_contenttype, $out_add_headers));
2854
2855							}
2856						
2857						} else if (is_dir($docroot.$http_uri)) {
2858
2859							// Directory without index
2860
2861							$rq_err=404;
2862							core_modules_hook("directory_handler");
2863
2864						} else if (!is_file($docroot.$http_uri)) {
2865							
2866							// 404 Not Found
2867
2868							$rq_err=404;
2869
2870						} else if (!is_readable($docroot.$http_uri)) {
2871							
2872							// 403 Forbidden
2873							
2874							$rq_err=403;
2875
2876						} else  {
2877
2878							// 200 OK
2879
2880							$rq_err=200;
2881							if ($pp=access_query("forcehandler", 0)) $pri_parser=$pp;
2882							$lf=loadfile($docroot.$http_uri, $rq_file["extension"], $rq_err, $out_add_headers, $pri_parser);
2883
2884							/* libphpHACK */
2885							#<off># if (isset($__nw_libphp_script)) { include($__nw_libphp_script); exit; }
2886							
2887							if ($mimetype=$mime[strtolower($rq_file["extension"])]) {
2888								
2889								// Lookup mime type in internal table
2890								
2891								$out_contenttype=$mimetype;
2892								
2893							} else if (is_callable("mime_content_type") && (!access_query("disablemimemagic", 0)) && ($mimetype=mime_content_type($docroot.$http_uri))) {
2894
2895								// Fallback to mime magic if available
2896
2897								$out_contenttype=$mimetype;
2898
2899							} else {
2900								
2901								// Or use default
2902
2903								$out_contenttype=$default_ct;
2904
2905							}
2906
2907						}
2908
2909						break;
2910
2911						case "OPTIONS":
2912						
2913						$rq_err=200;
2914						$out_add_headers["Allow"]=nw_allow_list($rq_file["extension"]);
2915						
2916						break;
2917
2918						default: 
2919
2920						if ($mmod=$modules["method:".$http_action]) {
2921
2922							$rq_err=200;
2923							$lf=$mmod;
2924							$lf->parser_open("", $real_uri, $rq_err, $out_add_headers, $out_contenttype);
2925
2926						} else if (!$http_action) {
2927
2928							$rq_err=400;
2929							
2930						} else {
2931							
2932							$rq_err=501;
2933
2934						}
2935
2936						break;
2937
2938					}
2939
2940					unset($pri_parser);
2941
2942					if ($rq_err!=200 && $rq_err!=416) {
2943
2944						// Error messages
2945						
2946						if ($rq_err>=400) {
2947
2948							if (($errordoc=trim(access_query("_errordocument", "_".$rq_err))) && (@is_readable($docroot.$errordoc))) {
2949
2950									$add_nsv["REDIRECT_STATUS"]=$rq_err;
2951									$add_nsv["REDIRECT_URL"]="/".$GLOBALS["real_uri"];
2952									$http_uri=$errordoc;
2953									$errext=substr(strrchr($errordoc, "."), 1);
2954									$lf=loadfile($docroot.$errordoc, $errext, $rq_err, $out_add_headers);
2955
2956									if ($mimetype=$mime[strtolower($errext)]) $out_contenttype=$mimetype; else $out_contenttype=$default_ct;
2957								
2958							} else {
2959							
2960								$out_contenttype="text/html";
2961								$lf =& new static_response(nw_error_page($rq_err));
2962								
2963								if ($errordoc) techo("WARN: unable to read error document : [".$rq_err."] ".$errordoc, NW_EL_WARNING);
2964
2965							}
2966						
2967							$cnx=false;
2968						
2969						} else if ($rq_err>=301) {
2970
2971							$lf=$null_response;
2972						
2973						}
2974					
2975					}
2976
2977					if ($http_action=="HEAD") {
2978						
2979						$plen=$lf->content_length;
2980						$hlf=$lf;
2981						$lf=$null_response;
2982						$lf->content_length=$plen;
2983
2984					}
2985
2986					core_modules_hook("before_response");
2987
2988					if (!$rq_err) $rq_err=500;
2989					
2990					$chunked=nw_use_chunked_encoding();
2991					if ($chunked==="CLOSE") $cnx=$keepalive=$chunked=false;
2992
2993					// Send the response headers and content
2994
2995					$sent_len=0;
2996					$first_chunk=true;
2997
2998					while ((($buf = $lf->parser_get_output()) !== "") || $first_chunk) {
2999
3000						if ($first_chunk) {
3001
3002							$hbuf=build_response_headers()."\r\n";
3003						
3004						}
3005						
3006						if ($chunked) {
3007
3008							$chunk_header=dechex(strlen($buf))."\r\n";
3009							$metasize=strlen($chunk_header)+2;
3010							$rbytes=send_response($hbuf.($buf!==""?$chunk_header.$buf."\r\n":""), $sck_connected);
3011							$sent_len+=($rbytes-$metasize);
3012
3013						} else {
3014
3015							$sent_len+=send_response($hbuf.$buf, $sck_connected);
3016						
3017						}
3018
3019						if ($first_chunk) {
3020						
3021							$sent_len-=strlen($hbuf);
3022							$hbuf="";
3023							$first_chunk=false;
3024
3025						}
3026						
3027						if ($lf->parser_eof() || !$sck_connected || ($buf === NULL)) break;
3028
3029					}
3030
3031					$lf->parser_close();
3032
3033					if ($chunked) {
3034						
3035						$meta_len=0;
3036						send_response("0\r\n\r\n", $sck_connected);
3037
3038					}
3039
3040					if (!$sck_connected) $cnx=false;
3041					if (($sent_content_length=$sent_len-$meta_len)<0) $sent_content_length=0;
3042
3043					if ($conf["global"]["singleprocessmode"][0]) {
3044		
3045						// Increment stats
3046						
3047						++$stats_resperr[$rq_err];
3048						++$stats_vhosts[$conf[$vhost]["servername"][0]];
3049						++$stats_hits;
3050						$stats_xfer+=(float)$sent_len;
3051
3052					} else {
3053					
3054						// Report hit to master
3055						
3056						int_sendtomaster(NM_HIT, array($mypid, $rq_err, $sent_len, $vhost));
3057
3058					}
3059					
3060					if ($conf["global"]["loggerprocess"][0]) {
3061					
3062						// Send the logging infos to dedicated processes
3063						
3064						$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"]);
3065
3066						$logmsg=serialize($log_arr);
3067						$msglen=strlen($logmsg);
3068
3069						if ($msglen>INT_MSGSIZE) {
3070
3071							techo("WARN: internal communication error (packet too long)", NW_EL_WARNING);
3072						
3073						} else {
3074
3075							$r=socket_write($loggers_sck, $logmsg);
3076
3077							if ($r!=$msglen) techo("WARN: unable to communicate with logger process", NW_EL_WARNING);
3078							
3079						}
3080
3081					} else {
3082
3083						// Or do it ourselves
3084					
3085						if ($nb_loggers=count($modules["log"])) {
3086
3087							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"]);
3088
3089						}
3090
3091					}
3092
3093					$hlf=$lf=$null_response;
3094
3095				}
3096			
3097			} else {
3098
3099				$cnx=false;
3100			
3101			}
3102
3103		}
3104		
3105		socket_shutdown($msgsock);
3106		socket_close($msgsock);
3107		
3108		if ((!$conf["global"]["singleprocessmode"][0]) || ($conf["global"]["servermode"][0]=="inetd")) exit(0);
3109	
3110	} else if ($pid==-1) {
3111
3112		// Fork failed
3113		
3114		techo("WARN: unable to pcntl_fork()", NW_EL_WARNING);
3115
3116	} else {
3117
3118		// Fork successful
3119		
3120		$scoreboard[$pid][NW_SB_STATUS]="(connected)";
3121		$scoreboard[$pid][NW_SB_PEERHOST]=$remote_ip;
3122		$scoreboard[$pid][NW_SB_FORKTIME]=time();
3123
3124		++$active_servers;
3125	
3126	}
3127	
3128}
3129
3130?>