]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/main.php
fixed TitleSearch and other plugin-form problems with USE_PATH_INFO = false
[SourceForge/phpwiki.git] / lib / main.php
1 <?php
2 rcs_id('$Id: main.php,v 1.65 2002-08-19 11:32:30 rurban Exp $');
3
4
5 include "lib/config.php";
6 include "lib/stdlib.php";
7 require_once('lib/Request.php');
8 require_once("lib/WikiUser.php");
9 require_once('lib/WikiDB.php');
10
11 define ('DEBUG', 1);
12
13 // FIXME: move to config?
14 if (defined('THEME')) {
15     include("themes/" . THEME . "/themeinfo.php");
16 }
17 if (empty($Theme)) {
18     include("themes/default/themeinfo.php");
19 }
20 assert(!empty($Theme));
21
22 class _UserPreference
23 {
24     function _UserPreference ($default_value) {
25         $this->default_value = $default_value;
26     }
27
28     function sanify ($value) {
29         return (string) $value;
30     }
31 }
32
33 class _UserPreference_numeric extends _UserPreference
34 {
35     function _UserPreference_numeric ($default, $minval = false, $maxval = false) {
36         $this->_UserPreference((double) $default);
37         $this->_minval = (double) $minval;
38         $this->_maxval = (double) $maxval;
39     }
40
41     function sanify ($value) {
42         $value = (double) $value;
43         if ($this->_minval !== false && $value < $this->_minval)
44             $value = $this->_minval;
45         if ($this->_maxval !== false && $value > $this->_maxval)
46             $value = $this->_maxval;
47         return $value;
48     }
49 }
50
51 class _UserPreference_int extends _UserPreference_numeric
52 {
53     function _UserPreference_int ($default, $minval = false, $maxval = false) {
54         $this->_UserPreference_numeric((int) $default, (int)$minval, (int)$maxval);
55     }
56
57     function sanify ($value) {
58         return (int) parent::sanify((int)$value);
59     }
60 }
61
62 class _UserPreference_bool extends _UserPreference
63 {
64     function _UserPreference_bool ($default = false) {
65         $this->_UserPreference((bool) $default);
66     }
67
68     function sanify ($value) {
69         if (is_array($value)) {
70             /* This allows for constructs like:
71              *
72              *   <input type="hidden" name="pref[boolPref][]" value="0" />
73              *   <input type="checkbox" name="pref[boolPref][]" value="1" />
74              *
75              * (If the checkbox is not checked, only the hidden input gets sent.
76              * If the checkbox is sent, both inputs get sent.)
77              */
78             foreach ($value as $val) {
79                 if ($val)
80                     return true;
81             }
82             return false;
83         }
84         return (bool) $value;
85     }
86 }
87
88 $UserPreferences = array('editWidth' => new _UserPreference_int(80, 30, 150),
89                          'editHeight' => new _UserPreference_int(22, 5, 80),
90                          'timeOffset' => new _UserPreference_numeric(0, -26, 26),
91                          'relativeDates' => new _UserPreference_bool(),
92                          'userid' => new _UserPreference(''));
93
94 class UserPreferences {
95     function UserPreferences ($saved_prefs = false) {
96         $this->_prefs = array();
97
98         if (isa($saved_prefs, 'UserPreferences')) {
99             foreach ($saved_prefs->_prefs as $name => $value)
100                 $this->set($name, $value);
101         }
102     }
103
104     function _getPref ($name) {
105         global $UserPreferences;
106         if (!isset($UserPreferences[$name])) {
107             trigger_error("$name: unknown preference", E_USER_NOTICE);
108             return false;
109         }
110         return $UserPreferences[$name];
111     }
112
113     function get ($name) {
114         if (isset($this->_prefs[$name]))
115             return $this->_prefs[$name];
116         if (!($pref = $this->_getPref($name)))
117             return false;
118         return $pref->default_value;
119     }
120
121     function set ($name, $value) {
122         if (!($pref = $this->_getPref($name)))
123             return false;
124         $this->_prefs[$name] = $pref->sanify($value);
125     }
126 }
127
128
129 class WikiRequest extends Request {
130
131     function WikiRequest () {
132         $this->Request();
133
134         // Normalize args...
135         $this->setArg('pagename', $this->_deducePagename());
136         $this->setArg('action', $this->_deduceAction());
137
138         // Restore auth state
139         $this->_user = new WikiUser($this->getSessionVar('wiki_user'));
140
141         // Restore saved preferences
142         if (!($prefs = $this->getCookieVar('WIKI_PREFS2')))
143             $prefs = $this->getSessionVar('wiki_prefs');
144         $this->_prefs = new UserPreferences($prefs);
145     }
146
147     // This really maybe should be part of the constructor, but since it
148     // may involve HTML/template output, the global $request really needs
149     // to be initialized before we do this stuff.
150     function updateAuthAndPrefs () {
151         // Handle preference updates, an authentication requests, if any.
152         if ($new_prefs = $this->getArg('pref')) {
153             $this->setArg('pref', false);
154             foreach ($new_prefs as $key => $val)
155                 $this->_prefs->set($key, $val);
156         }
157
158         // Handle authentication request, if any.
159         if ($auth_args = $this->getArg('auth')) {
160             $this->setArg('auth', false);
161             $this->_handleAuthRequest($auth_args); // possible NORETURN
162         }
163         elseif ( ! $this->_user->isSignedIn() ) {
164             // If not auth request, try to sign in as saved user.
165             if (($saved_user = $this->getPref('userid')) != false)
166                 $this->_signIn($saved_user);
167         }
168
169         // Save preferences
170         $this->setSessionVar('wiki_prefs', $this->_prefs);
171         $this->setCookieVar('WIKI_PREFS2', $this->_prefs, 365);
172
173         // Ensure user has permissions for action
174         $require_level = $this->requiredAuthority($this->getArg('action'));
175         if (! $this->_user->hasAuthority($require_level))
176             $this->_notAuthorized($require_level); // NORETURN
177     }
178
179     function getUser () {
180         return $this->_user;
181     }
182
183     function getPrefs () {
184         return $this->_prefs;
185     }
186
187     // Convenience function:
188     function getPref ($key) {
189         return $this->_prefs->get($key);
190     }
191
192     function getDbh () {
193         if (!isset($this->_dbi)) {
194             $this->_dbi = WikiDB::open($GLOBALS['DBParams']);
195         }
196         return $this->_dbi;
197     }
198
199     /**
200      * Get requested page from the page database.
201      *
202      * This is a convenience function.
203      */
204     function getPage () {
205         if (!isset($this->_dbi))
206             $this->getDbh();
207         return $this->_dbi->getPage($this->getArg('pagename'));
208     }
209
210     function _handleAuthRequest ($auth_args) {
211         if (!is_array($auth_args))
212             return;
213
214         // Ignore password unless POSTed.
215         if (!$this->isPost())
216             unset($auth_args['password']);
217
218         $user = WikiUser::AuthCheck($auth_args);
219
220         if (isa($user, 'WikiUser')) {
221             // Successful login (or logout.)
222             $this->_setUser($user);
223         }
224         elseif ($user) {
225             // Login attempt failed.
226             $fail_message = $user;
227             // If no password was submitted, it's not really
228             // a failure --- just need to prompt for password...
229             if (!isset($auth_args['password']))
230                 $fail_message = false;
231             WikiUser::PrintLoginForm($this, $auth_args, $fail_message);
232             $this->finish();    //NORETURN
233         }
234         else {
235             // Login request cancelled.
236         }
237     }
238
239     /**
240      * Attempt to sign in (bogo-login).
241      *
242      * Fails silently.
243      *
244      * @param $userid string Userid to attempt to sign in as.
245      * @access private
246      */
247     function _signIn ($userid) {
248         $user = WikiUser::AuthCheck(array('userid' => $userid));
249         if (isa($user, 'WikiUser'))
250             $this->_setUser($user); // success!
251     }
252
253     function _setUser ($user) {
254         $this->_user = $user;
255         $this->setSessionVar('wiki_user', $user);
256
257         // Save userid to prefs..
258         $this->_prefs->set('userid',
259                            $user->isSignedIn() ? $user->getId() : '');
260     }
261
262     function _notAuthorized ($require_level) {
263         // User does not have required authority.  Prompt for login.
264         $what = $this->getActionDescription($this->getArg('action'));
265
266         if ($require_level >= WIKIAUTH_FORBIDDEN) {
267             $this->finish(fmt("%s is disallowed on this wiki.",
268                               $this->getDisallowedActionDescription($this->getArg('action'))));
269         }
270         elseif ($require_level == WIKIAUTH_BOGO)
271             $msg = fmt("You must sign in to %s.", $what);
272         elseif ($require_level == WIKIAUTH_USER)
273             $msg = fmt("You must log in to %s.", $what);
274         else
275             $msg = fmt("You must be an administrator to %s.", $what);
276
277         WikiUser::PrintLoginForm($this, compact('require_level'), $msg);
278         $this->finish();    // NORETURN
279     }
280
281     function getActionDescription($action) {
282         static $actionDescriptions;
283         if (! $actionDescriptions) {
284             $actionDescriptions
285             = array('browse'     => _("browse pages in this wiki"),
286                     'diff'       => _("diff pages in this wiki"),
287                     'dumphtml'   => _("dump html pages from this wiki"),
288                     'dumpserial' => _("dump serial pages from this wiki"),
289                     'edit'       => _("edit pages in this wiki"),
290                     'loadfile'   => _("load files into this wiki"),
291                     'lock'       => _("lock pages in this wiki"),
292                     'remove'     => _("remove pages from this wiki"),
293                     'unlock'     => _("unlock pages in this wiki"),
294                     'upload'     => _("upload a zip dump to this wiki"),
295                     'viewsource' => _("view the source of pages in this wiki"),
296                     'zip'        => _("download a zip dump from this wiki"),
297                     'ziphtml'    => _("download an html zip dump from this wiki")
298                     );
299         }
300         if (in_array($action, array_keys($actionDescriptions)))
301             return $actionDescriptions[$action];
302         else
303             return $action;
304     }
305     function getDisallowedActionDescription($action) {
306         static $disallowedActionDescriptions;
307         if (! $disallowedActionDescriptions) {
308             $disallowedActionDescriptions
309             = array('browse'     => _("Browsing pages"),
310                     'diff'       => _("Diffing pages"),
311                     'dumphtml'   => _("Dumping html pages"),
312                     'dumpserial' => _("Dumping serial pages"),
313                     'edit'       => _("Editing pages"),
314                     'loadfile'   => _("Loading files"),
315                     'lock'       => _("Locking pages"),
316                     'remove'     => _("Removing pages"),
317                     'unlock'     => _("Unlocking pages"),
318                     'upload'     => _("Uploading zip dumps"),
319                     'viewsource' => _("Viewing the source of pages"),
320                     'zip'        => _("Downloading zip dumps"),
321                     'ziphtml'    => _("Downloading html zip dumps")
322                     );
323         }
324         if (in_array($action, array_keys($disallowedActionDescriptions)))
325             return $disallowedActionDescriptions[$action];
326         else
327             return $action;
328     }
329
330     function requiredAuthority ($action) {
331         // FIXME: clean up.
332         switch ($action) {
333             case 'browse':
334             case 'viewsource':
335             case 'diff':
336                 return WIKIAUTH_ANON;
337
338             case 'zip':
339                 if (defined('ZIPDUMP_AUTH') && ZIPDUMP_AUTH)
340                     return WIKIAUTH_ADMIN;
341                 return WIKIAUTH_ANON;
342
343             case 'ziphtml':
344                 if (defined('ZIPDUMP_AUTH') && ZIPDUMP_AUTH)
345                     return WIKIAUTH_ADMIN;
346                 return WIKIAUTH_ANON;
347
348             case 'edit':
349                 if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
350                     return WIKIAUTH_BOGO;
351                 return WIKIAUTH_ANON;
352                 // return WIKIAUTH_BOGO;
353
354             case 'upload':
355             case 'dumpserial':
356             case 'dumphtml':
357             case 'loadfile':
358             case 'remove':
359             case 'lock':
360             case 'unlock':
361                 return WIKIAUTH_ADMIN;
362             default:
363                 // Temp workaround for french single-word action page 'Historique'
364                 $singleWordActionPages = array("Historique", "Info");
365                 if (in_array($action, $singleWordActionPages))
366                     return WIKIAUTH_ANON; // ActionPage.
367                 global $WikiNameRegexp;
368                 if (preg_match("/$WikiNameRegexp\Z/A", $action))
369                     return WIKIAUTH_ANON; // ActionPage.
370                 else
371                     return WIKIAUTH_ADMIN;
372         }
373     }
374
375     function possiblyDeflowerVirginWiki () {
376         if ($this->getArg('action') != 'browse')
377             return;
378         if ($this->getArg('pagename') != HOME_PAGE)
379             return;
380
381         $page = $this->getPage();
382         $current = $page->getCurrentRevision();
383         if ($current->getVersion() > 0)
384             return;             // Homepage exists.
385
386         include('lib/loadsave.php');
387         SetupWiki($this);
388         $this->finish();        // NORETURN
389     }
390
391     function handleAction () {
392         $action = $this->getArg('action');
393         $method = "action_$action";
394         if (method_exists($this, $method)) {
395             $this->{$method}();
396         }
397         elseif ($this->isActionPage($action)) {
398             $this->actionpage($action);
399         }
400         else {
401             $this->finish(fmt("%s: Bad action", $action));
402         }
403     }
404
405
406     function finish ($errormsg = false) {
407         static $in_exit = 0;
408
409         if ($in_exit)
410             exit();        // just in case CloseDataBase calls us
411         $in_exit = true;
412
413         if (!empty($this->_dbi))
414             $this->_dbi->close();
415         unset($this->_dbi);
416
417
418         global $ErrorManager;
419         $ErrorManager->flushPostponedErrors();
420
421         if (!empty($errormsg)) {
422             PrintXML(HTML::br(),
423                      HTML::hr(),
424                      HTML::h2(_("Fatal PhpWiki Error")),
425                      $errormsg);
426             // HACK:
427             echo "\n</body></html>";
428         }
429
430         Request::finish();
431         exit;
432     }
433
434     function _deducePagename () {
435         if ($this->getArg('pagename'))
436             return $this->getArg('pagename');
437
438         if (USE_PATH_INFO) {
439             $pathinfo = $this->get('PATH_INFO');
440             $tail = substr($pathinfo, strlen(PATH_INFO_PREFIX));
441
442             if ($tail && $pathinfo == PATH_INFO_PREFIX . $tail) {
443                 return $tail;
444             }
445         }
446
447         $query_string = $this->get('QUERY_STRING');
448         if (preg_match('/^[^&=]+$/', $query_string)) {
449             return urldecode($query_string);
450         }
451
452         return HOME_PAGE;
453     }
454
455     function _deduceAction () {
456         if (!($action = $this->getArg('action')))
457             return 'browse';
458
459         if (method_exists($this, "action_$action"))
460             return $action;
461
462         // Allow for, e.g. action=LikePages
463         if ($this->isActionPage($action))
464             return $action;
465
466         trigger_error("$action: Unknown action", E_USER_NOTICE);
467         return 'browse';
468     }
469
470     function isActionPage ($pagename) {
471         // Temp workaround for french single-word action page 'Historique'
472         $singleWordActionPages = array("Historique", "Info");
473         if (! in_array($pagename, $singleWordActionPages)) {
474             // Allow for, e.g. action=LikePages
475             global $WikiNameRegexp;
476             if (!preg_match("/$WikiNameRegexp\\Z/A", $pagename))
477                 return false;
478         }
479         $dbi = $this->getDbh();
480         $page = $dbi->getPage($pagename);
481         $rev = $page->getCurrentRevision();
482         // FIXME: more restrictive check for sane plugin?
483         if (strstr($rev->getPackedContent(), '<?plugin'))
484             return true;
485         trigger_error("$pagename: Does not appear to be an 'action page'", E_USER_NOTICE);
486         return false;
487     }
488
489     function action_browse () {
490         $this->compress_output();
491         include_once("lib/display.php");
492         displayPage($this);
493     }
494
495     function actionpage ($action) {
496         $this->compress_output();
497         include_once("lib/display.php");
498         actionPage($this, $action);
499     }
500
501     function action_diff () {
502         $this->compress_output();
503         include_once "lib/diff.php";
504         showDiff($this);
505     }
506
507     function action_search () {
508         // This is obsolete: reformulate URL and redirect.
509         // FIXME: this whole section should probably be deleted.
510         if ($this->getArg('searchtype') == 'full') {
511             $search_page = _("FullTextSearch");
512         }
513         else {
514             $search_page = _("TitleSearch");
515         }
516         $this->redirect(WikiURL($search_page,
517                                 array('s' => $this->getArg('searchterm')),
518                                 'absolute_url'));
519     }
520
521     function action_edit () {
522         $this->compress_output();
523         include "lib/editpage.php";
524         $e = new PageEditor ($this);
525         $e->editPage();
526     }
527
528     function action_viewsource () {
529         $this->compress_output();
530         include "lib/editpage.php";
531         $e = new PageEditor ($this);
532         $e->viewSource();
533     }
534
535     function action_lock () {
536         $page = $this->getPage();
537         $page->set('locked', true);
538         $this->action_browse();
539     }
540
541     function action_unlock () {
542         // FIXME: This check is redundant.
543         //$user->requireAuth(WIKIAUTH_ADMIN);
544         $page = $this->getPage();
545         $page->set('locked', false);
546         $this->action_browse();
547     }
548
549     function action_remove () {
550         // FIXME: This check is redundant.
551         //$user->requireAuth(WIKIAUTH_ADMIN);
552         include('lib/removepage.php');
553         RemovePage($this);
554     }
555
556
557     function action_upload () {
558         include_once("lib/loadsave.php");
559         LoadPostFile($this);
560     }
561
562     function action_zip () {
563         include_once("lib/loadsave.php");
564         MakeWikiZip($this);
565         // I don't think it hurts to add cruft at the end of the zip file.
566         echo "\n========================================================\n";
567         echo "PhpWiki " . PHPWIKI_VERSION . " source:\n$GLOBALS[RCS_IDS]\n";
568     }
569
570     function action_ziphtml () {
571         include_once("lib/loadsave.php");
572         MakeWikiZipHtml($this);
573         // I don't think it hurts to add cruft at the end of the zip file.
574         echo "\n========================================================\n";
575         echo "PhpWiki " . PHPWIKI_VERSION . " source:\n$GLOBALS[RCS_IDS]\n";
576     }
577
578     function action_dumpserial () {
579         include_once("lib/loadsave.php");
580         DumpToDir($this);
581     }
582
583     function action_dumphtml () {
584         include_once("lib/loadsave.php");
585         DumpHtmlToDir($this);
586     }
587
588     function action_loadfile () {
589         include_once("lib/loadsave.php");
590         LoadFileOrDir($this);
591     }
592 }
593
594 //FIXME: deprecated
595 function is_safe_action ($action) {
596     return WikiRequest::requiredAuthority($action) < WIKIAUTH_ADMIN;
597 }
598
599
600 function main () {
601     global $request;
602
603     $request = new WikiRequest();
604     $request->updateAuthAndPrefs();
605
606     /* FIXME: is this needed anymore?
607         if (USE_PATH_INFO && ! $request->get('PATH_INFO')
608             && ! preg_match(',/$,', $request->get('REDIRECT_URL'))) {
609             $request->redirect(SERVER_URL
610                                . preg_replace('/(\?|$)/', '/\1',
611                                               $request->get('REQUEST_URI'),
612                                               1));
613             exit;
614         }
615     */
616
617     // Enable the output of most of the warning messages.
618     // The warnings will screw up zip files though.
619     global $ErrorManager;
620     if (substr($request->getArg('action'), 0, 3) != 'zip') {
621         $ErrorManager->setPostponedErrorMask(E_NOTICE|E_USER_NOTICE);
622         //$ErrorManager->setPostponedErrorMask(0);
623     }
624
625     //FIXME:
626     //if ($user->is_authenticated())
627     //  $LogEntry->user = $user->getId();
628
629     $request->possiblyDeflowerVirginWiki();
630
631     $request->handleAction();
632     $request->finish();
633 }
634
635 // Used for debugging purposes
636 function getmicrotime(){
637     list($usec, $sec) = explode(" ", microtime());
638     return ((float)$usec + (float)$sec);
639 }
640 if (defined ('DEBUG')) $GLOBALS['debugclock'] = getmicrotime();
641
642 main();
643
644
645 // Local Variables:
646 // mode: php
647 // tab-width: 8
648 // c-basic-offset: 4
649 // c-hanging-comment-ender-p: nil
650 // indent-tabs-mode: nil
651 // End:
652 ?>