1 <?php rcs_id('$Id: Request.php,v 1.24 2002-12-14 16:21:46 dairiki Exp $');
3 // FIXME: write log entry.
8 $this->_fix_magic_quotes_gpc();
9 $this->_fix_multipart_form_data();
11 switch($this->get('REQUEST_METHOD')) {
14 // $this->sanify_input_array(&$GLOBALS['HTTP_GET_VARS']);
15 $this->args = &$GLOBALS['HTTP_GET_VARS'];
18 // $this->sanify_input_array(&$GLOBALS['HTTP_POST_VARS']);
19 $this->args = &$GLOBALS['HTTP_POST_VARS'];
22 $this->args = array();
26 $this->session = new Request_SessionVars;
27 $this->cookies = new Request_CookieVars;
30 $this->_log_entry = & new Request_AccessLogEntry($this,
33 $GLOBALS['request'] = $this;
37 $vars = &$GLOBALS['HTTP_SERVER_VARS'];
39 if (isset($vars[$key]))
44 $addr = $vars['REMOTE_ADDR'];
45 if (defined('ENABLE_REVERSE_DNS') && ENABLE_REVERSE_DNS)
46 return $vars[$key] = gethostbyaddr($addr);
54 function getArg($key) {
55 if (isset($this->args[$key]))
56 return $this->args[$key];
64 function setArg($key, $val) {
66 unset($this->args[$key]);
68 $this->args[$key] = $val;
71 function debugVars() {
73 foreach (array('start_debug','debug_port','debug_no_cache') as $key) {
74 if (isset($GLOBALS['HTTP_GET_VARS'][$key]))
75 $array[$key] = $GLOBALS['HTTP_GET_VARS'][$key];
81 // Well oh well. Do we really want to pass POST params back as GET?
82 function getURLtoSelf($args = false, $exclude = array()) {
83 $get_args = $this->args;
85 $get_args = array_merge($get_args, $args);
87 $get_args = array_merge($get_args, $this->debugVars());
89 foreach ($exclude as $ex) {
90 if (!empty($get_args[$ex])) unset($get_args[$ex]);
93 $pagename = $get_args['pagename'];
94 unset ($get_args['pagename']);
95 if ($get_args['action'] == 'browse')
96 unset($get_args['action']);
98 return WikiURL($pagename, $get_args);
102 return $this->get("REQUEST_METHOD") == "POST";
105 function redirect($url) {
106 header("Location: $url");
107 if (isset($this->_log_entry))
108 $this->_log_entry->setStatus(302);
111 function setStatus($status) {
112 if (preg_match('|^HTTP/.*?\s(\d+)|i', $status, $m)) {
117 $status = (integer) $status;
118 $reasons = array('200' => 'OK',
120 '400' => 'Bad Request',
121 '401' => 'Unauthorized',
122 '403' => 'Forbidden',
123 '404' => 'Not Found');
124 header(sprintf("HTTP/1.0 %d %s", $status, $reason[$status]));
127 if (isset($this->_log_entry))
128 $this->_log_entry->setStatus($status);
131 function compress_output() {
132 if ( function_exists('ob_gzhandler')
133 && function_exists('version_compare') /* (only in php >= 4.1.0) */
134 && version_compare(phpversion(), '4.2.3', ">=")
136 ob_start('ob_gzhandler');
137 $this->_is_compressing_output = true;
142 if (!empty($this->_is_compressing_output))
146 function getSessionVar($key) {
147 return $this->session->get($key);
149 function setSessionVar($key, $val) {
150 return $this->session->set($key, $val);
152 function deleteSessionVar($key) {
153 return $this->session->delete($key);
156 function getCookieVar($key) {
157 return $this->cookies->get($key);
159 function setCookieVar($key, $val, $lifetime_in_days = false) {
160 return $this->cookies->set($key, $val, $lifetime_in_days);
162 function deleteCookieVar($key) {
163 return $this->cookies->delete($key);
166 function getUploadedFile($key) {
167 return Request_UploadedFile::getUploadedFile($key);
171 function _fix_magic_quotes_gpc() {
172 $needs_fix = array('HTTP_POST_VARS',
179 if (get_magic_quotes_gpc()) {
180 foreach ($needs_fix as $vars)
181 $this->_stripslashes($GLOBALS[$vars]);
185 function _stripslashes(&$var) {
186 if (is_array($var)) {
187 foreach ($var as $key => $val)
188 $this->_stripslashes($var[$key]);
190 elseif (is_string($var))
191 $var = stripslashes($var);
194 function _fix_multipart_form_data () {
195 if (preg_match('|^multipart/form-data|', $this->get('CONTENT_TYPE')))
196 $this->_strip_leading_nl($GLOBALS['HTTP_POST_VARS']);
199 function _strip_leading_nl(&$var) {
200 if (is_array($var)) {
201 foreach ($var as $key => $val)
202 $this->_strip_leading_nl($var[$key]);
204 elseif (is_string($var))
205 $var = preg_replace('|^\r?\n?|', '', $var);
208 // Fixme: Seperate into fields of type: displayed, db, internal, password, ...
209 // and define for each type the allowed characters.
210 function sanify_input_array (&$arr) {
212 foreach (array_keys($arr) as $key) {
213 if (!in_array($key,array('edit','password')))
214 $arr[$key] = $this->sanify_userinput($arr[$key]);
219 function sanify_userinput ($var) {
220 // Prevent possible XSS attacks (cross site scripting attacks)
221 // See http://www.cert.org/advisories/CA-2000-02.html, http://www.perl.com/pub/a/2002/02/20/css.html
222 // <script> tags, ...
223 // /wiki/?pagename=<script>alert(document.cookie)</script>
224 if (is_string($var)) {
225 return strip_tags($var);
226 } elseif (is_array($var)) {
227 $this->sanify_input_array($var);
235 class Request_SessionVars {
236 function Request_SessionVars() {
237 // Prevent cacheing problems with IE 5
238 session_cache_limiter('none');
244 $vars = &$GLOBALS['HTTP_SESSION_VARS'];
245 if (isset($vars[$key]))
250 function set($key, $val) {
251 $vars = &$GLOBALS['HTTP_SESSION_VARS'];
252 if (ini_get('register_globals')) {
253 // This is funky but necessary, at least in some PHP's
254 $GLOBALS[$key] = $val;
257 session_register($key);
260 function delete($key) {
261 $vars = &$GLOBALS['HTTP_SESSION_VARS'];
262 if (ini_get('register_globals'))
263 unset($GLOBALS[$key]);
265 session_unregister($key);
269 class Request_CookieVars {
272 $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
273 if (isset($vars[$key])) {
274 @$val = unserialize($vars[$key]);
281 function set($key, $val, $persist_days = false) {
282 $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
284 if (is_numeric($persist_days)) {
285 $expires = time() + (24 * 3600) * $persist_days;
291 $packedval = serialize($val);
292 $vars[$key] = $packedval;
293 setcookie($key, $packedval, $expires, '/');
296 function delete($key) {
297 $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
303 class Request_UploadedFile {
304 function getUploadedFile($postname) {
305 global $HTTP_POST_FILES;
307 if (!isset($HTTP_POST_FILES[$postname]))
310 $fileinfo = &$HTTP_POST_FILES[$postname];
311 if (!is_uploaded_file($fileinfo['tmp_name']))
312 return false; // possible malicious attack.
314 return new Request_UploadedFile($fileinfo);
317 function Request_UploadedFile($fileinfo) {
318 $this->_info = $fileinfo;
322 return $this->_info['size'];
326 return $this->_info['name'];
330 return $this->_info['type'];
334 if ( ($fd = fopen($this->_info['tmp_name'], "rb")) ) {
335 if ($this->getSize() < filesize($this->_info['tmp_name'])) {
336 // FIXME: Some PHP's (or is it some browsers?) put
337 // HTTP/MIME headers in the file body, some don't.
339 // At least, I think that's the case. I know I used
340 // to need this code, now I don't.
342 // This code is more-or-less untested currently.
344 // Dump HTTP headers.
345 while ( ($header = fgets($fd, 4096)) ) {
346 if (trim($header) == '') {
349 else if (!preg_match('/^content-(length|type):/i', $header)) {
359 function getContents() {
361 $data = fread($fd, $this->getSize());
368 * Create NCSA "combined" log entry for current request.
370 class Request_AccessLogEntry
375 * The log entry will be automatically appended to the log file
376 * when the current request terminates.
378 * If you want to modify a Request_AccessLogEntry before it gets
379 * written (e.g. via the setStatus and setSize methods) you should
380 * use an '&' on the constructor, so that you're working with the
381 * original (rather than a copy) object.
384 * $log_entry = & new Request_AccessLogEntry($req, "/tmp/wiki_access_log");
385 * $log_entry->setStatus(401);
389 * @param $request object Request object for current request.
390 * @param $logfile string Log file name.
392 function Request_AccessLogEntry (&$request, $logfile) {
393 $this->logfile = $logfile;
395 $this->host = $request->get('REMOTE_HOST');
396 $this->ident = $request->get('REMOTE_IDENT');
399 $this->user = '-'; // FIXME: get logged-in user name
400 $this->time = time();
401 $this->request = join(' ', array($request->get('REQUEST_METHOD'),
402 $request->get('REQUEST_URI'),
403 $request->get('SERVER_PROTOCOL')));
406 $this->referer = (string) $request->get('HTTP_REFERER');
407 $this->user_agent = (string) $request->get('HTTP_USER_AGENT');
409 global $Request_AccessLogEntry_entries;
410 if (!isset($Request_AccessLogEntry_entries)) {
411 register_shutdown_function("Request_AccessLogEntry_shutdown_function");
413 $Request_AccessLogEntry_entries[] = &$this;
417 * Set result status code.
419 * @param $status integer HTTP status code.
421 function setStatus ($status) {
422 $this->status = $status;
428 * @param $size integer
430 function setSize ($size) {
435 * Get time zone offset.
437 * This is a static member function.
439 * @param $time integer Unix timestamp (defaults to current time).
440 * @return string Zone offset, e.g. "-0800" for PST.
442 function _zone_offset ($time = false) {
445 $offset = date("Z", $time);
450 $offhours = floor($offset / 3600);
451 $offmins = $offset / 60 - $offhours * 60;
452 return sprintf("%s%02d%02d", $negoffset, $offhours, $offmins);
456 * Format time in NCSA format.
458 * This is a static member function.
460 * @param $time integer Unix timestamp (defaults to current time).
461 * @return string Formatted date & time.
463 function _ncsa_time($time = false) {
467 return date("d/M/Y:H:i:s", $time) .
468 " " . $this->_zone_offset();
472 * Write entry to log file.
475 $entry = sprintf('%s %s %s [%s] "%s" %d %d "%s" "%s"',
476 $this->host, $this->ident, $this->user,
477 $this->_ncsa_time($this->time),
478 $this->request, $this->status, $this->size,
479 $this->referer, $this->user_agent);
481 //Error log doesn't provide locking.
482 //error_log("$entry\n", 3, $this->logfile);
485 if (($fp = fopen($this->logfile, "a"))) {
487 fputs($fp, "$entry\n");
497 * @see Request_AccessLogEntry
499 function Request_AccessLogEntry_shutdown_function ()
501 global $Request_AccessLogEntry_entries;
503 foreach ($Request_AccessLogEntry_entries as $entry) {
506 unset($Request_AccessLogEntry_entries);
513 // c-hanging-comment-ender-p: nil
514 // indent-tabs-mode: nil