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