]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/main.php
Temporarily disabling DEBUG mode until the 1.3.3 release.
[SourceForge/phpwiki.git] / lib / main.php
1 <?php
2 rcs_id('$Id: main.php,v 1.62 2002-02-22 23:25:22 carstenklapp 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 = HTML::em($this->getArg('action'));
265
266         if ($require_level >= WIKIAUTH_FORBIDDEN) {
267             $this->finish(fmt("Action %s is disallowed on this wiki", $what));
268         }
269         elseif ($require_level == WIKIAUTH_BOGO)
270             $msg = fmt("You must sign in to %s this wiki", $what);
271         elseif ($require_level == WIKIAUTH_USER)
272             $msg = fmt("You must log in to %s this wiki", $what);
273         else
274             $msg = fmt("You must be an administrator to %s this wiki", $what);
275
276 WikiUser::PrintLoginForm($this, compact('require_level'), $msg);
277         $this->finish();    // NORETURN
278     }
279
280     function requiredAuthority ($action) {
281         // FIXME: clean up.
282         switch ($action) {
283             case 'browse':
284             case 'viewsource':
285             case 'diff':
286                 return WIKIAUTH_ANON;
287
288             case 'zip':
289                 if (defined('ZIPDUMP_AUTH') && ZIPDUMP_AUTH)
290                     return WIKIAUTH_ADMIN;
291                 return WIKIAUTH_ANON;
292
293             case 'edit':
294                 if (defined('REQUIRE_SIGNIN_BEFORE_EDIT') && REQUIRE_SIGNIN_BEFORE_EDIT)
295                     return WIKIAUTH_BOGO;
296                 return WIKIAUTH_ANON;
297                 // return WIKIAUTH_BOGO;
298
299             case 'upload':
300             case 'dumpserial':
301             case 'dumphtml':
302             case 'loadfile':
303             case 'remove':
304             case 'lock':
305             case 'unlock':
306                 return WIKIAUTH_ADMIN;
307             default:
308                 // Temp workaround for french single-word action page 'Historique'
309                 $singleWordActionPages = array("Historique", "Info");
310                 if (in_array($action, $singleWordActionPages))
311                     return WIKIAUTH_ANON; // ActionPage.
312                 global $WikiNameRegexp;
313                 if (preg_match("/$WikiNameRegexp\Z/A", $action))
314                     return WIKIAUTH_ANON; // ActionPage.
315                 else
316                     return WIKIAUTH_ADMIN;
317         }
318     }
319
320     function possiblyDeflowerVirginWiki () {
321         if ($this->getArg('action') != 'browse')
322             return;
323         if ($this->getArg('pagename') != HomePage)
324             return;
325
326         $page = $this->getPage();
327         $current = $page->getCurrentRevision();
328         if ($current->getVersion() > 0)
329             return;             // Homepage exists.
330
331         include('lib/loadsave.php');
332         SetupWiki($this);
333         $this->finish();        // NORETURN
334     }
335
336     function handleAction () {
337         $action = $this->getArg('action');
338         $method = "action_$action";
339         if (method_exists($this, $method)) {
340             $this->{$method}();
341         }
342         elseif ($this->isActionPage($action)) {
343             $this->actionpage($action);
344         }
345         else {
346             $this->finish(fmt("%s: Bad action", $action));
347         }
348     }
349
350
351     function finish ($errormsg = false) {
352         static $in_exit = 0;
353
354         if ($in_exit)
355             exit();        // just in case CloseDataBase calls us
356         $in_exit = true;
357
358         if (!empty($this->_dbi))
359             $this->_dbi->close();
360         unset($this->_dbi);
361
362
363         global $ErrorManager;
364         $ErrorManager->flushPostponedErrors();
365
366         if (!empty($errormsg)) {
367             PrintXML(HTML::br(),
368                      HTML::hr(),
369                      HTML::h2(_("Fatal PhpWiki Error")),
370                      $errormsg);
371             // HACK:
372             echo "\n</body></html>";
373         }
374
375         Request::finish();
376         exit;
377     }
378
379     function _deducePagename () {
380         if ($this->getArg('pagename'))
381             return $this->getArg('pagename');
382
383         if (USE_PATH_INFO) {
384             $pathinfo = $this->get('PATH_INFO');
385             $tail = substr($pathinfo, strlen(PATH_INFO_PREFIX));
386
387             if ($tail && $pathinfo == PATH_INFO_PREFIX . $tail) {
388                 return $tail;
389             }
390         }
391
392         $query_string = $this->get('QUERY_STRING');
393         if (preg_match('/^[^&=]+$/', $query_string)) {
394             return urldecode($query_string);
395         }
396
397         return HomePage;
398     }
399
400     function _deduceAction () {
401         if (!($action = $this->getArg('action')))
402             return 'browse';
403
404         if (method_exists($this, "action_$action"))
405             return $action;
406
407         // Allow for, e.g. action=LikePages
408         if ($this->isActionPage($action))
409             return $action;
410
411         trigger_error("$action: Unknown action", E_USER_NOTICE);
412         return 'browse';
413     }
414
415     function isActionPage ($pagename) {
416         // Temp workaround for french single-word action page 'Historique'
417         $singleWordActionPages = array("Historique", "Info");
418         if (! in_array($pagename, $singleWordActionPages)) {
419             // Allow for, e.g. action=LikePages
420             global $WikiNameRegexp;
421             if (!preg_match("/$WikiNameRegexp\\Z/A", $pagename))
422                 return false;
423         }
424         $dbi = $this->getDbh();
425         $page = $dbi->getPage($pagename);
426         $rev = $page->getCurrentRevision();
427         // FIXME: more restrictive check for sane plugin?
428         if (strstr($rev->getPackedContent(), '<?plugin'))
429             return true;
430         trigger_error("$pagename: Does not appear to be an 'action page'", E_USER_NOTICE);
431         return false;
432     }
433
434     function action_browse () {
435         $this->compress_output();
436         include_once("lib/display.php");
437         displayPage($this);
438     }
439
440     function actionpage ($action) {
441         $this->compress_output();
442         include_once("lib/display.php");
443         actionPage($this, $action);
444     }
445
446     function action_diff () {
447         $this->compress_output();
448         include_once "lib/diff.php";
449         showDiff($this);
450     }
451
452     function action_search () {
453         // This is obsolete: reformulate URL and redirect.
454         // FIXME: this whole section should probably be deleted.
455         if ($this->getArg('searchtype') == 'full') {
456             $search_page = _("FullTextSearch");
457         }
458         else {
459             $search_page = _("TitleSearch");
460         }
461         $this->redirect(WikiURL($search_page,
462                                 array('s' => $this->getArg('searchterm')),
463                                 'absolute_url'));
464     }
465
466     function action_edit () {
467         $this->compress_output();
468         include "lib/editpage.php";
469         $e = new PageEditor ($this);
470         $e->editPage();
471     }
472
473     function action_viewsource () {
474         $this->compress_output();
475         include "lib/editpage.php";
476         $e = new PageEditor ($this);
477         $e->viewSource();
478     }
479
480     function action_lock () {
481         $page = $this->getPage();
482         $page->set('locked', true);
483         $this->action_browse();
484     }
485
486     function action_unlock () {
487         // FIXME: This check is redundant.
488         //$user->requireAuth(WIKIAUTH_ADMIN);
489         $page = $this->getPage();
490         $page->set('locked', false);
491         $this->action_browse();
492     }
493
494     function action_remove () {
495         // FIXME: This check is redundant.
496         //$user->requireAuth(WIKIAUTH_ADMIN);
497         include('lib/removepage.php');
498         RemovePage($this);
499     }
500
501
502     function action_upload () {
503         include_once("lib/loadsave.php");
504         LoadPostFile($this);
505     }
506
507     function action_zip () {
508         include_once("lib/loadsave.php");
509         MakeWikiZip($this);
510         // I don't think it hurts to add cruft at the end of the zip file.
511         echo "\n========================================================\n";
512         echo "PhpWiki " . PHPWIKI_VERSION . " source:\n$GLOBALS[RCS_IDS]\n";
513     }
514
515     function action_dumpserial () {
516         include_once("lib/loadsave.php");
517         DumpToDir($this);
518     }
519
520     function action_dumphtml () {
521         include_once("lib/loadsave.php");
522         DumpHtmlToDir($this);
523     }
524
525     function action_loadfile () {
526         include_once("lib/loadsave.php");
527         LoadFileOrDir($this);
528     }
529 }
530
531 //FIXME: deprecated
532 function is_safe_action ($action) {
533     return WikiRequest::requiredAuthority($action) < WIKIAUTH_ADMIN;
534 }
535
536
537 function main () {
538     global $request;
539
540     $request = new WikiRequest();
541     $request->updateAuthAndPrefs();
542
543     /* FIXME: is this needed anymore?
544         if (USE_PATH_INFO && ! $request->get('PATH_INFO')
545             && ! preg_match(',/$,', $request->get('REDIRECT_URL'))) {
546             $request->redirect(SERVER_URL
547                                . preg_replace('/(\?|$)/', '/\1',
548                                               $request->get('REQUEST_URI'),
549                                               1));
550             exit;
551         }
552     */
553
554     // Enable the output of most of the warning messages.
555     // The warnings will screw up zip files though.
556     global $ErrorManager;
557     if ($request->getArg('action') != 'zip') {
558         $ErrorManager->setPostponedErrorMask(E_NOTICE|E_USER_NOTICE);
559         //$ErrorManager->setPostponedErrorMask(0);
560     }
561
562     //FIXME:
563     //if ($user->is_authenticated())
564     //  $LogEntry->user = $user->getId();
565
566     $request->possiblyDeflowerVirginWiki();
567
568     $request->handleAction();
569     $request->finish();
570 }
571
572 // Used for debugging purposes
573 function getmicrotime(){
574     list($usec, $sec) = explode(" ", microtime());
575     return ((float)$usec + (float)$sec);
576 }
577 if (defined ('DEBUG')) $GLOBALS['debugclock'] = getmicrotime();
578
579 main();
580
581
582 // Local Variables:
583 // mode: php
584 // tab-width: 8
585 // c-basic-offset: 4
586 // c-hanging-comment-ender-p: nil
587 // indent-tabs-mode: nil
588 // End:
589 ?>