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