]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/Request.php
fixed DB_Session
[SourceForge/phpwiki.git] / lib / Request.php
1 <?php rcs_id('$Id: Request.php,v 1.20 2002-09-12 11:45:33 rurban Exp $');
2
3 // FIXME: write log entry.
4
5 class Request {
6         
7     function Request() {
8         $this->_fix_magic_quotes_gpc();
9         $this->_fix_multipart_form_data();
10         
11         switch($this->get('REQUEST_METHOD')) {
12         case 'GET':
13         case 'HEAD':
14             // $this->sanify_input_array(&$GLOBALS['HTTP_GET_VARS']);
15             $this->args = &$GLOBALS['HTTP_GET_VARS'];
16             break;
17         case 'POST':
18             // $this->sanify_input_array(&$GLOBALS['HTTP_POST_VARS']);
19             $this->args = &$GLOBALS['HTTP_POST_VARS'];
20             break;
21         default:
22             $this->args = array();
23             break;
24         }
25         
26         // Defer session_start until we decided to load DB_Session (or not).
27         // $this->session = new Request_SessionVars; 
28         $this->cookies = new Request_CookieVars;
29         
30         if (ACCESS_LOG)
31             $this->_log_entry = & new Request_AccessLogEntry($this,
32                                                              ACCESS_LOG);
33         
34         $GLOBALS['request'] = $this;
35     }
36
37     function get($key) {
38         $vars = &$GLOBALS['HTTP_SERVER_VARS'];
39
40         if (isset($vars[$key]))
41             return $vars[$key];
42
43         switch ($key) {
44         case 'REMOTE_HOST':
45             $addr = $vars['REMOTE_ADDR'];
46             if (defined('ENABLE_REVERSE_DNS') && ENABLE_REVERSE_DNS)
47                 return $vars[$key] = gethostbyaddr($addr);
48             else
49                 return $addr;
50         default:
51             return false;
52         }
53     }
54
55     function getArg($key) {
56         if (isset($this->args[$key]))
57             return $this->args[$key];
58         return false;
59     }
60
61     function getArgs () {
62         return $this->args;
63     }
64     
65     function setArg($key, $val) {
66         if ($val === false)
67             unset($this->args[$key]);
68         else
69             $this->args[$key] = $val;
70     }
71     
72     function debugVars() {
73         $array = array();
74         foreach (array('start_debug','debug_port','debug_no_cache') as $key) {
75             if (isset($GLOBALS['HTTP_GET_VARS'][$key]))
76                 $array[$key] = $GLOBALS['HTTP_GET_VARS'][$key];
77         }
78         return $array;
79     }
80
81
82     // Well oh well. Do we really want to pass POST params back as GET?
83     function getURLtoSelf($args = false, $exclude = array()) {
84         $get_args = $this->args;
85         if ($args)
86             $get_args = array_merge($get_args, $args);
87         if (DEBUG) 
88             $get_args = array_merge($get_args, $this->debugVars());
89
90         foreach ($exclude as $ex) {
91             if (!empty($get_args[$ex])) unset($get_args[$ex]);
92         }
93
94         $pagename = $get_args['pagename'];
95         unset ($get_args['pagename']);
96         if ($get_args['action'] == 'browse')
97             unset($get_args['action']);
98
99         return WikiURL($pagename, $get_args);
100     }
101
102     function isPost () {
103         return $this->get("REQUEST_METHOD") == "POST";
104     }
105     
106     function redirect($url) {
107         header("Location: $url");
108         if (isset($this->_log_entry))
109             $this->_log_entry->setStatus(302);
110     }
111
112     function setStatus($status) {
113         if (preg_match('|^HTTP/.*?\s(\d+)|i', $status, $m)) {
114             header($status);
115             $status = $m[1];
116         }
117         else {
118             $status = (integer) $status;
119             $reasons = array('200' => 'OK',
120                              '302' => 'Found',
121                              '400' => 'Bad Request',
122                              '401' => 'Unauthorized',
123                              '403' => 'Forbidden',
124                              '404' => 'Not Found');
125             header(sprintf("HTTP/1.0 %d %s", $status, $reason[$status]));
126         }
127
128         if (isset($this->_log_entry))
129             $this->_log_entry->setStatus($status);
130     }
131
132     function compress_output() {
133         if (function_exists('ob_gzhandler')) {
134             ob_start('ob_gzhandler');
135             $this->_is_compressing_output = true;
136         }
137     }
138
139     function finish() {
140         if (!empty($this->_is_compressing_output))
141             ob_end_flush();
142     }
143
144     function getSessionVar($key) {
145         return $this->session->get($key);
146     }
147     function setSessionVar($key, $val) {
148         return $this->session->set($key, $val);
149     }
150     function deleteSessionVar($key) {
151         return $this->session->delete($key);
152     }
153
154     function getCookieVar($key) {
155         return $this->cookies->get($key);
156     }
157     function setCookieVar($key, $val, $lifetime_in_days = false) {
158         return $this->cookies->set($key, $val, $lifetime_in_days);
159     }
160     function deleteCookieVar($key) {
161         return $this->cookies->delete($key);
162     }
163     
164     function getUploadedFile($key) {
165         return Request_UploadedFile::getUploadedFile($key);
166     }
167     
168
169     function _fix_magic_quotes_gpc() {
170         $needs_fix = array('HTTP_POST_VARS',
171                            'HTTP_GET_VARS',
172                            'HTTP_COOKIE_VARS',
173                            'HTTP_SERVER_VARS',
174                            'HTTP_POST_FILES');
175         
176         // Fix magic quotes.
177         if (get_magic_quotes_gpc()) {
178             foreach ($needs_fix as $vars)
179                 $this->_stripslashes($GLOBALS[$vars]);
180         }
181     }
182
183     function _stripslashes(&$var) {
184         if (is_array($var)) {
185             foreach ($var as $key => $val)
186                 $this->_stripslashes($var[$key]);
187         }
188         elseif (is_string($var))
189             $var = stripslashes($var);
190     }
191     
192     function _fix_multipart_form_data () {
193         if (preg_match('|^multipart/form-data|', $this->get('CONTENT_TYPE')))
194             $this->_strip_leading_nl($GLOBALS['HTTP_POST_VARS']);
195     }
196     
197     function _strip_leading_nl(&$var) {
198         if (is_array($var)) {
199             foreach ($var as $key => $val)
200                 $this->_strip_leading_nl($var[$key]);
201         }
202         elseif (is_string($var))
203             $var = preg_replace('|^\r?\n?|', '', $var);
204     }
205
206     // Fixme: Seperate into fields of type: displayed, db, internal, password, ...
207     // and define for each type the allowed characters.
208     function sanify_input_array (&$arr) {
209         if (!empty($arr)) {
210             foreach (array_keys($arr) as $key) {
211                 if (!in_array($key,array('edit','password')))
212                     $arr[$key] = $this->sanify_userinput($arr[$key]);
213             }
214         }
215     }
216
217     function sanify_userinput ($var) {
218         // Prevent possible XSS attacks (cross site scripting attacks)
219         // See http://www.cert.org/advisories/CA-2000-02.html, http://www.perl.com/pub/a/2002/02/20/css.html
220         // <script> tags, ...
221         // /wiki/?pagename=<script>alert(document.cookie)</script>
222         if (is_string($var)) {
223             return strip_tags($var);
224         } elseif (is_array($var)) {
225             $this->sanify_input_array($var);
226             return $var;
227         } else {
228             return $var;
229         }
230     }
231 }
232
233 class Request_SessionVars {
234     function Request_SessionVars() {
235         // Prevent cacheing problems with IE 5
236         session_cache_limiter('none');
237                                         
238         session_start();
239     }
240     
241     function get($key) {
242         $vars = &$GLOBALS['HTTP_SESSION_VARS'];
243         if (isset($vars[$key]))
244             return $vars[$key];
245         return false;
246     }
247     
248     function set($key, $val) {
249         $vars = &$GLOBALS['HTTP_SESSION_VARS'];
250         if (ini_get('register_globals')) {
251             // This is funky but necessary, at least in some PHP's
252             $GLOBALS[$key] = $val;
253         }
254         $vars[$key] = $val;
255         session_register($key);
256     }
257     
258     function delete($key) {
259         $vars = &$GLOBALS['HTTP_SESSION_VARS'];
260         if (ini_get('register_globals'))
261             unset($GLOBALS[$key]);
262         unset($vars[$key]);
263         session_unregister($key);
264     }
265 }
266
267 class Request_CookieVars {
268     
269     function get($key) {
270         $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
271         if (isset($vars[$key])) {
272             @$val = unserialize($vars[$key]);
273             if (!empty($val))
274                 return $val;
275         }
276         return false;
277     }
278         
279     function set($key, $val, $persist_days = false) {
280         $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
281         
282         if (is_numeric($persist_days)) {
283             $expires = time() + (24 * 3600) * $persist_days;
284         }
285         else {
286             $expires = 0;
287         }
288         
289         $packedval = serialize($val);
290         $vars[$key] = $packedval;
291         setcookie($key, $packedval, $expires, '/');
292     }
293     
294     function delete($key) {
295         $vars = &$GLOBALS['HTTP_COOKIE_VARS'];
296         setcookie($key);
297         unset($vars[$key]);
298     }
299 }
300
301 class Request_UploadedFile {
302     function getUploadedFile($postname) {
303         global $HTTP_POST_FILES;
304         
305         if (!isset($HTTP_POST_FILES[$postname]))
306             return false;
307         
308         $fileinfo = &$HTTP_POST_FILES[$postname];
309         if (!is_uploaded_file($fileinfo['tmp_name']))
310             return false;       // possible malicious attack.
311
312         return new Request_UploadedFile($fileinfo);
313     }
314     
315     function Request_UploadedFile($fileinfo) {
316         $this->_info = $fileinfo;
317     }
318
319     function getSize() {
320         return $this->_info['size'];
321     }
322
323     function getName() {
324         return $this->_info['name'];
325     }
326
327     function getType() {
328         return $this->_info['type'];
329     }
330
331     function open() {
332         if ( ($fd = fopen($this->_info['tmp_name'], "rb")) ) {
333             if ($this->getSize() < filesize($this->_info['tmp_name'])) {
334                 // FIXME: Some PHP's (or is it some browsers?) put
335                 //    HTTP/MIME headers in the file body, some don't.
336                 //
337                 // At least, I think that's the case.  I know I used
338                 // to need this code, now I don't.
339                 //
340                 // This code is more-or-less untested currently.
341                 //
342                 // Dump HTTP headers.
343                 while ( ($header = fgets($fd, 4096)) ) {
344                     if (trim($header) == '') {
345                         break;
346                     }
347                     else if (!preg_match('/^content-(length|type):/i', $header)) {
348                         rewind($fd);
349                         break;
350                     }
351                 }
352             }
353         }
354         return $fd;
355     }
356
357     function getContents() {
358         $fd = $this->open();
359         $data = fread($fd, $this->getSize());
360         fclose($fd);
361         return $data;
362     }
363 }
364
365 /**
366  * Create NCSA "combined" log entry for current request.
367  */
368 class Request_AccessLogEntry
369 {
370     /**
371      * Constructor.
372      *
373      * The log entry will be automatically appended to the log file
374      * when the current request terminates.
375      *
376      * If you want to modify a Request_AccessLogEntry before it gets
377      * written (e.g. via the setStatus and setSize methods) you should
378      * use an '&' on the constructor, so that you're working with the
379      * original (rather than a copy) object.
380      *
381      * <pre>
382      *    $log_entry = & new Request_AccessLogEntry($req, "/tmp/wiki_access_log");
383      *    $log_entry->setStatus(401);
384      * </pre>
385      *
386      *
387      * @param $request object  Request object for current request.
388      * @param $logfile string  Log file name.
389      */
390     function Request_AccessLogEntry (&$request, $logfile) {
391         $this->logfile = $logfile;
392         
393         $this->host  = $request->get('REMOTE_HOST');
394         $this->ident = $request->get('REMOTE_IDENT');
395         if (!$this->ident)
396             $this->ident = '-';
397         $this->user = '-';        // FIXME: get logged-in user name
398         $this->time = time();
399         $this->request = join(' ', array($request->get('REQUEST_METHOD'),
400                                          $request->get('REQUEST_URI'),
401                                          $request->get('SERVER_PROTOCOL')));
402         $this->status = 200;
403         $this->size = 0;
404         $this->referer = (string) $request->get('HTTP_REFERER');
405         $this->user_agent = (string) $request->get('HTTP_USER_AGENT');
406
407         global $Request_AccessLogEntry_entries;
408         if (!isset($Request_AccessLogEntry_entries)) {
409             register_shutdown_function("Request_AccessLogEntry_shutdown_function");
410         }
411         $Request_AccessLogEntry_entries[] = &$this;
412     }
413
414     /**
415      * Set result status code.
416      *
417      * @param $status integer  HTTP status code.
418      */
419     function setStatus ($status) {
420         $this->status = $status;
421     }
422     
423     /**
424      * Set response size.
425      *
426      * @param $size integer
427      */
428     function setSize ($size) {
429         $this->size = $size;
430     }
431     
432     /**
433      * Get time zone offset.
434      *
435      * This is a static member function.
436      *
437      * @param $time integer Unix timestamp (defaults to current time).
438      * @return string Zone offset, e.g. "-0800" for PST.
439      */
440     function _zone_offset ($time = false) {
441         if (!$time)
442             $time = time();
443         $offset = date("Z", $time);
444         if ($offset < 0) {
445             $negoffset = "-";
446             $offset = -$offset;
447         }
448         $offhours = floor($offset / 3600);
449         $offmins  = $offset / 60 - $offhours * 60;
450         return sprintf("%s%02d%02d", $negoffset, $offhours, $offmins);
451     }
452
453     /**
454      * Format time in NCSA format.
455      *
456      * This is a static member function.
457      *
458      * @param $time integer Unix timestamp (defaults to current time).
459      * @return string Formatted date & time.
460      */
461     function _ncsa_time($time = false) {
462         if (!$time)
463             $time = time();
464
465         return date("d/M/Y:H:i:s", $time) .
466             " " . $this->_zone_offset();
467     }
468
469     /**
470      * Write entry to log file.
471      */
472     function write() {
473         $entry = sprintf('%s %s %s [%s] "%s" %d %d "%s" "%s"',
474                          $this->host, $this->ident, $this->user,
475                          $this->_ncsa_time($this->time),
476                          $this->request, $this->status, $this->size,
477                          $this->referer, $this->user_agent);
478
479         //Error log doesn't provide locking.
480         //error_log("$entry\n", 3, $this->logfile);
481
482         // Alternate method 
483         if (($fp = fopen($this->logfile, "a"))) {
484             flock($fp, LOCK_EX);
485             fputs($fp, "$entry\n");
486             fclose($fp);
487         }
488     }
489 }
490
491 /**
492  * Shutdown callback.
493  *
494  * @access private
495  * @see Request_AccessLogEntry
496  */
497 function Request_AccessLogEntry_shutdown_function ()
498 {
499     global $Request_AccessLogEntry_entries;
500     
501     foreach ($Request_AccessLogEntry_entries as $entry) {
502         $entry->write();
503     }
504     unset($Request_AccessLogEntry_entries);
505 }
506
507 // Local Variables:
508 // mode: php
509 // tab-width: 8
510 // c-basic-offset: 4
511 // c-hanging-comment-ender-p: nil
512 // indent-tabs-mode: nil
513 // End:   
514 ?>