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