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