]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
FilenameForPage:
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php //-*-php-*-
2 rcs_id('$Id: loadsave.php,v 1.154 2007-08-10 22:00:43 rurban Exp $');
3
4 /*
5  Copyright 1999,2000,2001,2002,2004,2005,2006,2007 $ThePhpWikiProgrammingTeam
6
7  This file is part of PhpWiki.
8
9  PhpWiki is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  PhpWiki is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with PhpWiki; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 require_once("lib/ziplib.php");
25 require_once("lib/Template.php");
26
27 /**
28  * ignore fatal errors during dump
29  */
30 function _dump_error_handler(&$error) {
31     if ($error->isFatal()) {
32         $error->errno = E_USER_WARNING;
33         return true;
34     }
35     return true;         // Ignore error
36     /*
37     if (preg_match('/Plugin/', $error->errstr))
38         return true;
39     */
40     // let the message come through: call the remaining handlers:
41     // return false; 
42 }
43
44 function StartLoadDump(&$request, $title, $html = '')
45 {
46     // MockRequest is from the unit testsuite, a faked request. (may be cmd-line)
47     // We are silent on unittests.
48     if (isa($request,'MockRequest'))
49         return;
50     // FIXME: This is a hack. This really is the worst overall hack in phpwiki.
51     if ($html)
52         $html->pushContent('%BODY%');
53     $tmpl = Template('html', array('TITLE' => $title,
54                                    'HEADER' => $title,
55                                    'CONTENT' => $html ? $html : '%BODY%'));
56     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
57     $request->chunkOutput();
58     
59     // set marker for sendPageChangeNotification()
60     $request->_deferredPageChangeNotification = array();
61 }
62
63 function EndLoadDump(&$request)
64 {
65     if (isa($request,'MockRequest'))
66         return;
67     $action = $request->getArg('action');
68     $label = '';
69     switch ($action) {
70     case 'zip':        $label = _("ZIP files of database"); break;
71     case 'dumpserial': $label = _("Dump to directory"); break;
72     case 'upload':     $label = _("Upload File"); break;
73     case 'loadfile':   $label = _("Load File"); break;
74     case 'upgrade':    $label = _("Upgrade"); break;
75     case 'dumphtml': 
76     case 'ziphtml':    $label = _("Dump pages as XHTML"); break;
77     }
78     if ($label) $label = str_replace(" ","_",$label);
79     if ($action == 'browse') // loading virgin 
80         $pagelink = WikiLink(HOME_PAGE);
81     else
82         $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
83
84     // do deferred sendPageChangeNotification()
85     if (!empty($request->_deferredPageChangeNotification)) {
86         $pages = $all_emails = $all_users = array();
87         foreach ($request->_deferredPageChangeNotification as $p) {
88             list($pagename, $emails, $userids) = $p;
89             $pages[] = $pagename;
90             $all_emails = array_unique(array_merge($all_emails, $emails));
91             $all_users = array_unique(array_merge($all_users, $userids));
92         }
93         $editedby = sprintf(_("Edited by: %s"), $request->_user->getId());
94         $content = "Loaded the following pages:\n" . join("\n", $pages);
95         if (mail(join(',',$all_emails),"[".WIKI_NAME."] "._("LoadDump"), 
96                  _("LoadDump")."\n".
97                  $editedby."\n\n".
98                  $content))
99             trigger_error(sprintf(_("PageChange Notification of %s sent to %s"),
100                                   join("\n",$pages), join(',',$all_users)), E_USER_NOTICE);
101         else
102             trigger_error(sprintf(_("PageChange Notification Error: Couldn't send %s to %s"),
103                                   join("\n",$pages), join(',',$all_users)), E_USER_WARNING);
104         unset($pages);
105         unset($all_emails);
106         unset($all_users);
107     }
108     unset($request->_deferredPageChangeNotification);
109
110     PrintXML(HTML::p(HTML::strong(_("Complete."))),
111              HTML::p(fmt("Return to %s", $pagelink)));
112     echo "</body></html>\n";
113 }
114
115
116 ////////////////////////////////////////////////////////////////
117 //
118 //  Functions for dumping.
119 //
120 ////////////////////////////////////////////////////////////////
121
122 /**
123  * For reference see:
124  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
125  * http://www.faqs.org/rfcs/rfc2045.html
126  * (RFC 1521 has been superceeded by RFC 2045 & others).
127  *
128  * Also see http://www.faqs.org/rfcs/rfc2822.html
129  */
130 function MailifyPage ($page, $nversions = 1)
131 {
132     $current = $page->getCurrentRevision(false);
133     $head = '';
134
135     if (STRICT_MAILABLE_PAGEDUMPS) {
136         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
137         //This is for unix mailbox format: (not RFC (2)822)
138         // $head .= "From $from  " . CTime(time()) . "\r\n";
139         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
140         $head .= "From: $from (PhpWiki)\r\n";
141         // RFC 2822 requires only a Date: and originator (From:)
142         // field, however the obsolete standard RFC 822 also
143         // requires a destination field.
144         $head .= "To: $from (PhpWiki)\r\n";
145     }
146     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
147     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
148                      PHPWIKI_VERSION);
149
150     // This should just be entered by hand (or by script?)
151     // in the actual pgsrc files, since only they should have
152     // RCS ids.
153     //$head .= "X-Rcs-Id: \$Id\$\r\n";
154
155     $iter = $page->getAllRevisions();
156     $parts = array();
157     while ($revision = $iter->next()) {
158         $parts[] = MimeifyPageRevision($page, $revision);
159         if ($nversions > 0 && count($parts) >= $nversions)
160             break;
161     }
162     if (count($parts) > 1)
163         return $head . MimeMultipart($parts);
164     assert($parts);
165     return $head . $parts[0];
166 }
167
168 /***
169  * Compute filename to used for storing contents of a wiki page.
170  *
171  * Basically we do a rawurlencode() which encodes everything except
172  * ASCII alphanumerics and '.', '-', and '_'.
173  *
174  * But we also want to encode leading dots to avoid filenames like
175  * '.', and '..'. (Also, there's no point in generating "hidden" file
176  * names, like '.foo'.)
177  *
178  * We have to apply a different "/" logic for dumpserial, htmldump and zipdump.
179  * dirs are allowed for zipdump and htmldump, not for dumpserial
180  * 
181  *
182  * @param $pagename string Pagename.
183  * @return string Filename for page.
184  */
185 function FilenameForPage ($pagename, $action = false)
186 {
187     $enc = rawurlencode($pagename);
188     if (!$action) {
189         global $request;
190         $action = $request->getArg('action');
191     }
192     if ($action != 'dumpserial') { // zip, ziphtml, dumphtml
193         // For every %2F we will need to mkdir -p dirname($pagename)
194         $enc = preg_replace('/%2F/', '/', $enc);
195     }
196     $enc = preg_replace('/^\./', '%2E', $enc);
197     $enc = preg_replace('/%20/', ' ', $enc);
198     return $enc;
199 }
200
201 /**
202  * The main() function which generates a zip archive of a PhpWiki.
203  *
204  * If $include_archive is false, only the current version of each page
205  * is included in the zip file; otherwise all archived versions are
206  * included as well.
207  */
208 function MakeWikiZip (&$request)
209 {
210     if ($request->getArg('include') == 'all') {
211         $zipname         = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
212         $include_archive = true;
213     }
214     else {
215         $zipname         = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
216         $include_archive = false;
217     }
218
219
220     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
221
222     /* ignore fatals in plugins */
223     if (check_php_version(4,1)) {
224         global $ErrorManager;
225         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
226     }
227
228     $dbi =& $request->_dbi;
229     $thispage = $request->getArg('pagename'); // for "Return to ..."
230     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
231         $excludeList = explodePageList($exclude); 
232     } else {
233         $excludeList = array();
234     }
235     if ($pages = $request->getArg('pages')) {  // which pagenames
236         if ($pages == '[]') // current page
237             $pages = $thispage;
238         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
239     } else {
240         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
241     }
242     $request_args = $request->args;
243     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
244     
245     while ($page = $page_iter->next()) {
246         $request->args = $request_args; // some plugins might change them (esp. on POST)
247         longer_timeout($timeout);       // Reset watchdog
248
249         $current = $page->getCurrentRevision();
250         if ($current->getVersion() == 0)
251             continue;
252
253         $pagename = $page->getName();
254         $wpn = new WikiPageName($pagename);
255         if (!$wpn->isValid())
256             continue;
257         if (in_array($page->getName(), $excludeList)) {
258             continue;
259         }
260
261         $attrib = array('mtime'    => $current->get('mtime'),
262                         'is_ascii' => 1);
263         if ($page->get('locked'))
264             $attrib['write_protected'] = 1;
265
266         if ($include_archive)
267             $content = MailifyPage($page, 0);
268         else
269             $content = MailifyPage($page);
270
271         $zip->addRegularFile( FilenameForPage($pagename),
272                               $content, $attrib);
273     }
274     $zip->finish();
275     if (check_php_version(4,1)) {
276         global $ErrorManager;
277         $ErrorManager->popErrorHandler();
278     }
279 }
280
281 function DumpToDir (&$request)
282 {
283     $directory = $request->getArg('directory');
284     if (empty($directory))
285         $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
286     if (empty($directory))
287         $request->finish(_("You must specify a directory to dump to"));
288
289     // see if we can access the directory the user wants us to use
290     if (! file_exists($directory)) {
291         if (! mkdir($directory, 0755))
292             $request->finish(fmt("Cannot create directory '%s'", $directory));
293         else
294             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
295                                 $directory));
296     } else {
297         $html = HTML::p(fmt("Using directory '%s'", $directory));
298     }
299
300     StartLoadDump($request, _("Dumping Pages"), $html);
301
302     $dbi =& $request->_dbi;
303     $thispage = $request->getArg('pagename'); // for "Return to ..."
304     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
305         $excludeList = explodePageList($exclude); 
306     } else {
307         $excludeList = array();
308     }
309     if ($pages = $request->getArg('pages')) {  // which pagenames
310         if ($pages == '[]') // current page
311             $pages = $thispage;
312         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
313     } else {
314         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
315     }
316
317     $request_args = $request->args;
318     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
319
320     while ($page = $page_iter->next()) {
321         $request->args = $request_args; // some plugins might change them (esp. on POST)
322         longer_timeout($timeout);       // Reset watchdog
323
324         $pagename = $page->getName();
325         if (!isa($request,'MockRequest')) {
326             PrintXML(HTML::br(), $pagename, ' ... ');
327             flush();
328         }
329
330         if (in_array($pagename, $excludeList)) {
331             if (!isa($request, 'MockRequest')) {
332                 PrintXML(_("Skipped."));
333                 flush();
334             }
335             continue;
336         }
337         $filename = FilenameForPage($pagename);
338         $msg = HTML();
339         if($page->getName() != $filename) {
340             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
341                               " ... ");
342         }
343
344         if ($request->getArg('include') == 'all')
345             $data = MailifyPage($page, 0);
346         else
347             $data = MailifyPage($page);
348
349         if ( !($fd = fopen($directory."/".$filename, "wb")) ) {
350             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
351                                                "$directory/$filename")));
352             $request->finish($msg);
353         }
354
355         $num = fwrite($fd, $data, strlen($data));
356         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
357         if (!isa($request, 'MockRequest')) {
358             PrintXML($msg);
359             flush();
360         }
361         assert($num == strlen($data));
362         fclose($fd);
363     }
364
365     EndLoadDump($request);
366 }
367
368 function _copyMsg($page, $smallmsg) {
369     if (!isa($GLOBALS['request'], 'MockRequest')) {
370         if ($page) $msg = HTML(HTML::br(), HTML($page), HTML::small($smallmsg));
371         else $msg = HTML::small($smallmsg);
372         PrintXML($msg);
373         flush();
374     }
375 }
376
377 function mkdir_p($pathname, $permission = 0777) {
378     $arr = explode("/", $pathname);
379     if (empty($arr)) {
380         return mkdir($pathname, $permission);
381     }
382     $s = array_shift($arr);
383     $ok = TRUE;
384     foreach ($arr as $p) {
385         $curr = "$s/$p";
386         if (!is_dir($curr))
387             $ok = mkdir($curr, $permission);
388         $s = $curr;
389         if (!$ok) return FALSE;
390     }
391     return TRUE;
392 }
393
394 /**
395  * Dump all pages as XHTML to a directory, as pagename.html.
396  * Copies all used css files to the directory, all used images to a 
397  * "images" subdirectory, and all used buttons to a "images/buttons" subdirectory.
398  * The webserver must have write permissions to these directories. 
399  *   chown httpd HTML_DUMP_DIR; chmod u+rwx HTML_DUMP_DIR 
400  * should be enough.
401  *
402  * @param string directory (optional) path to dump to. Default: HTML_DUMP_DIR
403  * @param string pages     (optional) Comma-seperated of glob-style pagenames to dump
404  * @param string exclude   (optional) Comma-seperated of glob-style pagenames to exclude
405  */
406 function DumpHtmlToDir (&$request)
407 {
408     $directory = $request->getArg('directory');
409     if (empty($directory))
410         $directory = HTML_DUMP_DIR; // See lib/plugin/WikiForm.php:87
411     if (empty($directory))
412         $request->finish(_("You must specify a directory to dump to"));
413
414     // see if we can access the directory the user wants us to use
415     if (! file_exists($directory)) {
416         if (! mkdir($directory, 0755))
417             $request->finish(fmt("Cannot create directory '%s'", $directory));
418         else
419             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
420                                 $directory));
421     } else {
422         $html = HTML::p(fmt("Using directory '%s'", $directory));
423     }
424     $request->_TemplatesProcessed = array();
425     StartLoadDump($request, _("Dumping Pages"), $html);
426     $thispage = $request->getArg('pagename'); // for "Return to ..."
427
428     $dbi =& $request->_dbi;
429     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
430         $excludeList = explodePageList($exclude); 
431     } else {
432         $excludeList = array();
433     }
434     if ($pages = $request->getArg('pages')) {  // which pagenames
435         if ($pages == '[]') // current page
436             $pages = $thispage;
437         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
438     // not at admin page: dump only the current page
439     } elseif ($thispage != _("PhpWikiAdministration")) { 
440         $page_iter = new WikiDB_Array_PageIterator(array($thispage));
441     } else {
442         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
443     }
444
445     global $WikiTheme;
446     if (defined('HTML_DUMP_SUFFIX'))
447         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
448     $WikiTheme->DUMP_MODE = 'HTML';
449     $_bodyAttr = @$WikiTheme->_MoreAttr['body'];
450     unset($WikiTheme->_MoreAttr['body']);
451
452     // check if the dumped file will be accessible from outside
453     $doc_root = $request->get("DOCUMENT_ROOT");
454     $ldir = NormalizeLocalFileName($directory);
455     $wikiroot = NormalizeLocalFileName('');
456     if (string_starts_with($ldir, $doc_root)) {
457         $link_prefix = substr($directory, strlen($doc_root))."/";
458     } elseif (string_starts_with($ldir, $wikiroot)) {
459         $link_prefix = NormalizeWebFileName(substr($directory, strlen($wikiroot)))."/";
460     } else {
461         $prefix = '';
462         if (isWindows()) {
463             $prefix = '/'; // . substr($doc_root,0,2); // add drive where apache is installed
464         }
465         $link_prefix = "file://".$prefix.$directory."/";
466     }
467
468     $request_args = $request->args;
469     $timeout = (! $request->getArg('start_debug')) ? 20 : 240;
470     
471     while ($page = $page_iter->next()) {
472         $request->args = $request_args; // some plugins might change them (esp. on POST)
473         longer_timeout($timeout);       // Reset watchdog
474           
475         $pagename = $page->getName();
476         if (!isa($request,'MockRequest')) {
477             PrintXML(HTML::br(), $pagename, ' ... ');
478             flush();
479         }
480         if (in_array($pagename, $excludeList)) {
481             if (!isa($request,'MockRequest')) {
482                 PrintXML(_("Skipped."));
483                 flush();
484             }
485             continue;
486         }
487         $relative_base = '';
488         $request->setArg('pagename', $pagename); // Template::_basepage fix
489         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
490         $revision = $page->getCurrentRevision();
491         $args = array('revision' => $revision,
492                       'CONTENT' => $revision->getTransformedContent(),
493                       'relative_base' => $relative_base);
494         // For every %2F will need to mkdir -p dirname($pagename)
495         if (preg_match("/(%2F|\/)/", $filename)) {
496             // mkdir -p and set relative base for subdir pages
497             $count = substr_count($filename, "%2F");
498             $filename = preg_replace("/%2F/", "/", $filename);
499             $dirname = dirname($filename);
500             mkdir_p($directory."/".$dirname);
501             $relative_base = "../";
502             while ($count > 1) {
503                 $relative_base .= "../";
504                 $count--;
505             }
506             $args['relative_base'] = $relative_base;
507         }
508         $msg = HTML();
509         $template = new Template('browse', $request, $args);
510
511         $data = GeneratePageasXML($template, $pagename, $revision, $args);
512
513         if ( !($fd = fopen($directory."/".$filename, "wb")) ) {
514             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
515                                                "$directory/$filename")));
516             $request->finish($msg);
517         }
518         $len = strlen($data);
519         $num = fwrite($fd, $data, $len);
520         if ($page->getName() != $filename) {
521             $link = LinkURL($link_prefix.$filename, $filename);
522             $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
523         }
524         $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
525         if (!isa($request, 'MockRequest')) {
526             PrintXML($msg);
527         }
528         flush();
529         $request->chunkOutput();
530
531         assert($num == $len);
532         fclose($fd);
533
534         if (USECACHE) {
535             $request->_dbi->_cache->invalidate_cache($pagename);
536             unset ($request->_dbi->_cache->_pagedata_cache);
537             unset ($request->_dbi->_cache->_versiondata_cache);
538             unset ($request->_dbi->_cache->_glv_cache);
539         }
540         unset ($request->_dbi->_cache->_backend->_page_data);
541
542         unset($msg);
543         unset($revision->_transformedContent);
544         unset($revision);
545         unset($template->_request);
546         unset($template);
547         unset($data);
548     }
549     $page_iter->free();
550
551     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
552         @mkdir("$directory/images");
553         foreach ($WikiTheme->dumped_images as $img_file) {
554             if ($img_file 
555                 and ($from = $WikiTheme->_findFile($img_file, true)) 
556                 and basename($from)) 
557             {
558                 $target = "$directory/images/".basename($img_file);
559                 if (copy($WikiTheme->_path . $from, $target)) {
560                     _copyMsg($from, fmt("... copied to %s", $target));
561                 } else {
562                     _copyMsg($from, fmt("... not copied to %s", $target));
563                 }
564                 //TODO: fix to local path for uploaded images, so that pdf will work
565             } else {
566                 _copyMsg($from, _("... not found"));
567             }
568         }
569     }
570     if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
571         // Buttons also
572         @mkdir("$directory/images/buttons");
573         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
574             if ($img_file 
575                 and ($from = $WikiTheme->_findFile($img_file, true)) 
576                 and basename($from)) 
577             {
578                 $target = "$directory/images/buttons/".basename($img_file);
579                 if (copy($WikiTheme->_path . $from, $target)) {
580                     _copyMsg($from, fmt("... copied to %s", $target));
581                 } else {
582                     _copyMsg($from, fmt("... not copied to %s", $target));
583                 }
584             } else {
585                 _copyMsg($from, _("... not found"));
586             }
587         }
588     }
589     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
590         foreach ($WikiTheme->dumped_css as $css_file) {
591             if ($css_file 
592                 and ($from = $WikiTheme->_findFile(basename($css_file), true)) 
593                 and basename($from)) 
594             {
595                 $target = "$directory/" . basename($css_file);
596                 if (copy($WikiTheme->_path . $from, $target)) {
597                     _copyMsg($from, fmt("... copied to %s", $target));
598                 } else {
599                     _copyMsg($from, fmt("... not copied to %s", $target));
600                 }
601                 // TODO: fix @import url(main.css);
602             } else {
603                 _copyMsg($from, _("... not found"));
604             }
605         }
606     }
607     $WikiTheme->HTML_DUMP_SUFFIX = '';
608     $WikiTheme->DUMP_MODE = false;
609     $WikiTheme->_MoreAttr['body'] = $_bodyAttr;
610
611     $request->setArg('pagename',$thispage); // Template::_basepage fix
612     EndLoadDump($request);
613 }
614
615 /* Known problem: any plugins or other code which echo()s text will
616  * lead to a corrupted html zip file which may produce the following
617  * errors upon unzipping:
618  *
619  * warning [wikihtml.zip]:  2401 extra bytes at beginning or within zipfile
620  * file #58:  bad zipfile offset (local header sig):  177561
621  *  (attempting to re-compensate)
622  *
623  * However, the actual wiki page data should be unaffected.
624  */
625 function MakeWikiZipHtml (&$request)
626 {
627     $request->_TemplatesProcessed = array();
628     $zipname = "wikihtml.zip";
629     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
630     $dbi =& $request->_dbi;
631     $thispage = $request->getArg('pagename'); // for "Return to ..."
632     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
633         $excludeList = explodePageList($exclude); 
634     } else {
635         $excludeList = array();
636     }
637     if ($pages = $request->getArg('pages')) {  // which pagenames
638         if ($pages == '[]') // current page
639             $pages = $thispage;
640         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
641     } else {
642         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
643     }
644
645     global $WikiTheme;
646     if (defined('HTML_DUMP_SUFFIX'))
647         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
648     $WikiTheme->DUMP_MODE = 'ZIPHTML';
649     $_bodyAttr = @$WikiTheme->_MoreAttr['body'];
650     unset($WikiTheme->_MoreAttr['body']);
651
652     /* ignore fatals in plugins */
653     if (check_php_version(4,1)) {
654         global $ErrorManager;
655         $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
656     }
657
658     $request_args = $request->args;
659     $timeout = (! $request->getArg('start_debug')) ? 20 : 240;
660     
661     while ($page = $page_iter->next()) {
662         $request->args = $request_args; // some plugins might change them (esp. on POST)
663         longer_timeout($timeout);       // Reset watchdog
664
665         $current = $page->getCurrentRevision();
666         if ($current->getVersion() == 0)
667             continue;
668         $pagename = $page->getName();
669         if (in_array($pagename, $excludeList)) {
670             continue;
671         }
672
673         $attrib = array('mtime'    => $current->get('mtime'),
674                         'is_ascii' => 1);
675         if ($page->get('locked'))
676             $attrib['write_protected'] = 1;
677
678         $request->setArg('pagename', $pagename); // Template::_basepage fix
679         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
680         $revision = $page->getCurrentRevision();
681
682         $transformedContent = $revision->getTransformedContent();
683
684         $template = new Template('browse', $request,
685                                  array('revision' => $revision,
686                                        'CONTENT' => $transformedContent));
687
688         $data = GeneratePageasXML($template, $pagename);
689
690         $zip->addRegularFile( $filename, $data, $attrib );
691         
692         if (USECACHE) {
693             $request->_dbi->_cache->invalidate_cache($pagename);
694             unset ($request->_dbi->_cache->_pagedata_cache);
695             unset ($request->_dbi->_cache->_versiondata_cache);
696             unset ($request->_dbi->_cache->_glv_cache);
697         }
698         unset ($request->_dbi->_cache->_backend->_page_data);
699
700         unset($revision->_transformedContent);
701         unset($revision);
702         unset($template->_request);
703         unset($template);
704         unset($data);
705     }
706     $page_iter->free();
707
708     $attrib = false;
709     // Deal with css and images here.
710     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
711         // dirs are created automatically
712         //if ($WikiTheme->dumped_images) $zip->addRegularFile("images", "", $attrib);
713         foreach ($WikiTheme->dumped_images as $img_file) {
714             if (($from = $WikiTheme->_findFile($img_file, true)) and basename($from)) {
715                 $target = "images/".basename($img_file);
716                 if (check_php_version(4,3))
717                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
718                 else
719                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
720             }
721         }
722     }
723     if (!empty($WikiTheme->dumped_buttons) and is_array($WikiTheme->dumped_buttons)) {
724         //if ($WikiTheme->dumped_buttons) $zip->addRegularFile("images/buttons", "", $attrib);
725         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
726             if (($from = $WikiTheme->_findFile($img_file, true)) and basename($from)) {
727                 $target = "images/buttons/".basename($img_file);
728                 if (check_php_version(4,3))
729                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
730                 else
731                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
732             }
733         }
734     }
735     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
736         foreach ($WikiTheme->dumped_css as $css_file) {
737             if (($from = $WikiTheme->_findFile(basename($css_file), true)) and basename($from)) {
738                 $target = basename($css_file);
739                 if (check_php_version(4,3))
740                     $zip->addRegularFile($target, file_get_contents($WikiTheme->_path . $from), $attrib);
741                 else
742                     $zip->addRegularFile($target, join('', file($WikiTheme->_path . $from)), $attrib);
743             }
744         }
745     }
746
747     $zip->finish();
748     if (check_php_version(4,1)) {
749         global $ErrorManager;
750         $ErrorManager->popErrorHandler();
751     }
752     $WikiTheme->HTML_DUMP_SUFFIX = '';
753     $WikiTheme->DUMP_MODE = false;
754     $WikiTheme->_MoreAttr['body'] = $_bodyAttr;
755 }
756
757
758 ////////////////////////////////////////////////////////////////
759 //
760 //  Functions for restoring.
761 //
762 ////////////////////////////////////////////////////////////////
763
764 function SavePage (&$request, &$pageinfo, $source, $filename)
765 {
766     static $overwite_all = false;
767     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
768     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
769
770     if (empty($pageinfo['pagename'])) {
771         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
772         return;
773     }
774
775     if (empty($versiondata['author_id']))
776         $versiondata['author_id'] = $versiondata['author'];
777
778     // remove invalid backend specific chars. utf8 issues mostly
779     $pagename_check = new WikiPagename($pageinfo['pagename']);
780     if (!$pagename_check->isValid()) {
781         PrintXML(HTML::dt(HTML::strong(_("Invalid pagename!")." ".$pageinfo['pagename'])));
782         return;
783     }
784     $pagename = $pagename_check->getName();
785     $content  = $pageinfo['content'];
786
787     if ($pagename == _("InterWikiMap"))
788         $content = _tryinsertInterWikiMap($content);
789
790     $dbi =& $request->_dbi;
791     $page = $dbi->getPage($pagename);
792
793     // Try to merge if updated pgsrc contents are different. This
794     // whole thing is hackish
795     //
796     // TODO: try merge unless:
797     // if (current contents = default contents && pgsrc_version >=
798     // pgsrc_version) then just upgrade this pgsrc
799     $needs_merge = false;
800     $merging = false;
801     $overwrite = false;
802
803     if ($request->getArg('merge')) {
804         $merging = true;
805     }
806     else if ($request->getArg('overwrite')) {
807         $overwrite = true;
808     }
809
810     $current = $page->getCurrentRevision();
811     $skip = false;
812     $edit = $request->getArg('edit');
813     if ($merging) { 
814         if (isset($edit['keep_old'])) {
815             $merging = false;
816             $skip = true;
817         }
818         elseif (isset($edit['overwrite'])) {
819             $merging = false;
820             $overwrite = true;
821         }
822         elseif ( $current and (! $current->hasDefaultContents())
823          && ($current->getPackedContent() != $content) ) 
824         {
825             include_once('lib/editpage.php');
826             $request->setArg('pagename', $pagename);
827             $v = $current->getVersion();
828             $request->setArg('revision', $current->getVersion());
829             $p = new LoadFileConflictPageEditor($request);
830             $p->_content = $content;
831             $p->_currentVersion = $v - 1;
832             $p->editPage($saveFailed = true);
833             return; //early return
834        }
835     }
836     if (!$skip)
837       foreach ($pagedata as $key => $value) {
838         if (!empty($value))
839             $page->set($key, $value);
840       }
841
842     $mesg = HTML::dd();
843     if ($source)
844         $mesg->pushContent(' ', fmt("from %s", $source));
845
846
847     if (!$current) {
848         //FIXME: This should not happen! (empty vdata, corrupt cache or db)
849         $current = $page->getCurrentRevision();
850     }
851     if ($current->getVersion() == 0) {
852         $mesg->pushContent(' - ', _("New page"));
853         $isnew = true;
854     }
855     else {
856         if ( (! $current->hasDefaultContents())
857              && ($current->getPackedContent() != $content) ) {
858             if ($overwrite) {
859                 $mesg->pushContent(' ',
860                                    fmt("has edit conflicts - overwriting anyway"));
861                 $skip = false;
862                 if (substr_count($source, 'pgsrc')) {
863                     $versiondata['author'] = _("The PhpWiki programming team");
864                     // but leave authorid as userid who loaded the file
865                 }
866             }
867             else {
868                 if (isset($edit['keep_old'])) {
869                     $mesg->pushContent(' ', fmt("keep old"));
870                 } else {
871                     $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
872                     $needs_merge = true; // hackish, to display the buttons
873                 }
874                 $skip = true;
875             }
876         }
877         else if ($current->getPackedContent() == $content
878                  && $current->get('author') == $versiondata['author']) {
879             // The page metadata is already changed, we don't need a new revision.
880             // This was called previously "is identical to current version %d - skipped"
881             // which is wrong, since the pagedata was stored, not skipped.
882             $mesg->pushContent(' ',
883                                fmt("content is identical to current version %d - no new revision created",
884                                    $current->getVersion()));
885             $skip = true;
886         }
887         $isnew = false;
888     }
889
890     if (! $skip ) {
891         // in case of failures print the culprit:
892         if (!isa($request,'MockRequest')) {
893             PrintXML(HTML::dt(WikiLink($pagename))); flush();
894         }
895         $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
896         $dbi->touch();
897         $mesg->pushContent(' ', fmt("- saved to database as version %d",
898                                     $new->getVersion()));
899     }
900     if ($needs_merge) {
901         $f = $source;
902         // hackish, $source contains needed path+filename
903         $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
904         $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
905         $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
906         //check if uploaded file? they pass just the content, but the file is gone
907         if (@stat($f)) {
908             global $WikiTheme;
909             $meb = Button(array('action' => 'loadfile',
910                                 'merge'=> true,
911                                 'source'=> $f),
912                           _("Merge Edit"),
913                           _("PhpWikiAdministration"),
914                           'wikiadmin');
915             $owb = Button(array('action' => 'loadfile',
916                                 'overwrite'=> true,
917                                 'source'=> $f),
918                           _("Restore Anyway"),
919                           _("PhpWikiAdministration"),
920                           'wikiunsafe');
921             $mesg->pushContent(' ', $meb, " ", $owb);
922             if (!$overwite_all) {
923                 $args = $request->getArgs();
924                 $args['overwrite'] = 1;
925                 $owb = Button($args,
926                               _("Overwrite All"),
927                               _("PhpWikiAdministration"),
928                               'wikiunsafe');
929                 $mesg->pushContent(HTML::div(array('class' => 'hint'), $owb));
930                 $overwite_all = true;
931             }
932         } else {
933             $mesg->pushContent(HTML::em(_(" Sorry, cannot merge.")));
934         }
935     }
936
937     if (!isa($request,'MockRequest')) {
938       if ($skip)
939         PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), $mesg);
940       else
941         PrintXML($mesg);
942       flush();
943     }
944 }
945
946 // action=revert (by diff)
947 function RevertPage (&$request)
948 {
949     $mesg = HTML::dd();
950     $pagename = $request->getArg('pagename');
951     $version = $request->getArg('version');
952     if (!$version) {
953         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
954                  HTML::dd(_("missing required version argument")));
955         return;
956     }
957     $dbi =& $request->_dbi;
958     $page = $dbi->getPage($pagename);
959     $current = $page->getCurrentRevision();
960     $currversion = $current->getVersion();
961     if ($currversion == 0) {
962         $mesg->pushContent(' ', _("no page content"));
963         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
964                  $mesg);
965         flush();
966         return;
967     }
968     if ($currversion == $version) {
969         $mesg->pushContent(' ', _("same version page"));
970         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
971                  $mesg);
972         flush();
973         return;
974     }
975     if ($request->getArg('cancel')) {
976         $mesg->pushContent(' ', _("Cancelled"));
977         PrintXML(HTML::dt(fmt("Revert")," ",WikiLink($pagename)),
978                  $mesg);
979         flush();
980         return;
981     }
982     if (!$request->getArg('verify')) {
983         $mesg->pushContent(HTML::br(),
984                            _("Are you sure?"),
985                            HTML::br(),
986                            HTML::form(array('action' => $request->getPostURL(),
987                                             'method' => 'post'),
988                                       HiddenInputs($request->getArgs(), false, array('verify')),
989                                       HiddenInputs(array('verify' => 1)),
990                                       Button('submit:verify', _("Yes"), 'button'),
991                                       HTML::Raw('&nbsp;'),
992                                       Button('submit:cancel', _("Cancel"), 'button')),
993                            HTML::hr());
994         $rev = $page->getRevision($version);
995         $html = HTML(HTML::dt(fmt("Revert %s to version $version", WikiLink($pagename))), 
996                      $mesg,
997                      $rev->getTransformedContent()); 
998         $template = Template('browse', 
999                              array('CONTENT' => $html));
1000         GeneratePage($template, $pagename, $rev);
1001         $request->checkValidators();
1002         flush();
1003         return;
1004     }
1005     $rev = $page->getRevision($version);
1006     $content = $rev->getPackedContent();
1007     $versiondata = $rev->_data;
1008     $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
1009     $new = $page->save($content, $currversion + 1, $versiondata);
1010     $dbi->touch();
1011     
1012     $pagelink = WikiLink($pagename);
1013     $mesg->pushContent(fmt("Revert: %s", $pagelink), 
1014                        fmt("- version %d saved to database as version %d",
1015                            $version, $new->getVersion()));
1016     // Force browse of current page version.
1017     $request->setArg('version', false);
1018     $template = Template('savepage', array());
1019     $template->replace('CONTENT', $new->getTransformedContent());
1020     
1021     GeneratePage($template, $mesg, $new);
1022     flush();
1023 }
1024
1025 function _tryinsertInterWikiMap($content) {
1026     $goback = false;
1027     if (strpos($content, "<verbatim>")) {
1028         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
1029         $goback = true;
1030     }
1031     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
1032         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
1033         $goback = true;
1034     }
1035     $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
1036     if (!$goback && !file_exists($mapfile)) {
1037         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
1038         $goback = true;
1039     }
1040
1041     if (!empty($error_html))
1042         trigger_error(_("Default InterWiki map file not loaded.")
1043                       . $error_html, E_USER_NOTICE);
1044     if ($goback)
1045         return $content;
1046
1047     // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
1048     if (!isa($GLOBALS['request'], 'MockRequest'))
1049         echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
1050
1051     $fd = fopen ($mapfile, "rb");
1052     $data = fread ($fd, filesize($mapfile));
1053     fclose ($fd);
1054     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
1055     return $content;
1056 }
1057
1058 function ParseSerializedPage($text, $default_pagename, $user)
1059 {
1060     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
1061         return false;
1062
1063     $pagehash = unserialize($text);
1064
1065     // Split up pagehash into four parts:
1066     //   pagename
1067     //   content
1068     //   page-level meta-data
1069     //   revision-level meta-data
1070
1071     if (!defined('FLAG_PAGE_LOCKED'))
1072         define('FLAG_PAGE_LOCKED', 1);
1073     $pageinfo = array('pagedata'    => array(),
1074                       'versiondata' => array());
1075
1076     $pagedata = &$pageinfo['pagedata'];
1077     $versiondata = &$pageinfo['versiondata'];
1078
1079     // Fill in defaults.
1080     if (empty($pagehash['pagename']))
1081         $pagehash['pagename'] = $default_pagename;
1082     if (empty($pagehash['author'])) {
1083         $pagehash['author'] = $user->getId();
1084     }
1085
1086     foreach ($pagehash as $key => $value) {
1087         switch($key) {
1088             case 'pagename':
1089             case 'version':
1090             case 'hits':
1091                 $pageinfo[$key] = $value;
1092                 break;
1093             case 'content':
1094                 $pageinfo[$key] = join("\n", $value);
1095                 break;
1096             case 'flags':
1097                 if (($value & FLAG_PAGE_LOCKED) != 0)
1098                     $pagedata['locked'] = 'yes';
1099                 break;
1100             case 'owner':
1101             case 'created':
1102                 $pagedata[$key] = $value;
1103                 break;
1104             case 'acl':
1105             case 'perm':
1106                 $pagedata['perm'] = ParseMimeifiedPerm($value);
1107                 break;
1108             case 'lastmodified':
1109                 $versiondata['mtime'] = $value;
1110                 break;
1111             case 'author':
1112             case 'author_id':
1113             case 'summary':
1114                 $versiondata[$key] = $value;
1115                 break;
1116         }
1117     }
1118     if (empty($pagehash['charset']))
1119         $pagehash['charset'] = 'iso-8859-1';
1120     // compare to target charset
1121     if (strtolower($pagehash['charset']) != strtolower($GLOBALS['charset'])) {
1122         $pageinfo['content'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['content']);
1123         $pageinfo['pagename'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['pagename']);
1124     }
1125     return $pageinfo;
1126 }
1127
1128 function SortByPageVersion ($a, $b) {
1129     return $a['version'] - $b['version'];
1130 }
1131
1132 /**
1133  * Security alert! We should not allow to import config.ini into our wiki (or from a sister wiki?)
1134  * because the sql passwords are in plaintext there. And the webserver must be able to read it.
1135  * Detected by Santtu Jarvi.
1136  */
1137 function LoadFile (&$request, $filename, $text = false, $mtime = false)
1138 {
1139     if (preg_match("/config$/", dirname($filename))             // our or other config
1140         and preg_match("/config.*\.ini/", basename($filename))) // backups and other versions also
1141     {
1142         trigger_error(sprintf("Refused to load %s", $filename), E_USER_WARNING);
1143         return;
1144     }
1145     if (!is_string($text)) {
1146         // Read the file.
1147         $stat  = stat($filename);
1148         $mtime = $stat[9];
1149         $text  = implode("", file($filename));
1150     }
1151
1152     if (! $request->getArg('start_debug')) @set_time_limit(30); // Reset watchdog
1153     else @set_time_limit(240);
1154
1155     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
1156     $basename = basename("/dummy/" . $filename);
1157
1158     if (!$mtime)
1159         $mtime = time();    // Last resort.
1160
1161     // DONE: check source - target charset for content and pagename
1162     // but only for pgsrc'ed content, not from the browser.
1163
1164     $default_pagename = rawurldecode($basename);
1165     if ( ($parts = ParseMimeifiedPages($text)) ) {
1166         if (count($parts) > 1)
1167             $overwrite = $request->getArg('overwrite');
1168         usort($parts, 'SortByPageVersion');
1169         foreach ($parts as $pageinfo) {
1170             // force overwrite
1171             if (count($parts) > 1)
1172                 $request->setArg('overwrite', 1);
1173             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
1174                                                   $filename), $basename);
1175     }
1176         if (count($parts) > 1)
1177             if ($overwrite) 
1178                 $request->setArg('overwrite', $overwrite);
1179             else     
1180                 unset($request->_args['overwrite']);
1181     }
1182     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
1183                                                $request->getUser())) ) {
1184         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
1185                                               $filename), $basename);
1186     }
1187     else {
1188         // plain old file
1189         $user = $request->getUser();
1190
1191         $file_charset = 'iso-8859-1';
1192         // compare to target charset
1193         if ($file_charset != strtolower($GLOBALS['charset'])) {
1194             $text = charset_convert($file_charset, $GLOBALS['charset'], $text);
1195             $default_pagename = charset_convert($file_charset, $GLOBALS['charset'], $default_pagename);
1196         }
1197
1198         // Assume plain text file.
1199         $pageinfo = array('pagename' => $default_pagename,
1200                           'pagedata' => array(),
1201                           'versiondata'
1202                           => array('author' => $user->getId()),
1203                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
1204                                                      chop($text))
1205                           );
1206         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
1207                  $basename);
1208     }
1209 }
1210
1211 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
1212     $zip = new ZipReader($zipfile);
1213     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1214     while (list ($fn, $data, $attrib) = $zip->readFile()) {
1215         // FIXME: basename("filewithnoslashes") seems to return
1216         // garbage sometimes.
1217         $fn = basename("/dummy/" . $fn);
1218         if ( ($files && !in_array($fn, $files))
1219              || ($exclude && in_array($fn, $exclude)) ) {
1220             PrintXML(HTML::dt(WikiLink($fn)),
1221                      HTML::dd(_("Skipping")));
1222             flush();
1223             continue;
1224         }
1225         longer_timeout($timeout);       // longer timeout per page
1226         LoadFile($request, $fn, $data, $attrib['mtime']);
1227     }
1228 }
1229
1230 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
1231     $fileset = new LimitedFileSet($dirname, $files, $exclude);
1232
1233     if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
1234         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
1235         $list = HTML::ul();
1236         foreach ($skiplist as $file)
1237             $list->pushContent(HTML::li(WikiLink($file)));
1238         PrintXML(HTML::dd($list));
1239     }
1240
1241     // Defer HomePage loading until the end. If anything goes wrong
1242     // the pages can still be loaded again.
1243     $files = $fileset->getFiles();
1244     if (in_array(HOME_PAGE, $files)) {
1245         $files = array_diff($files, array(HOME_PAGE));
1246         $files[] = HOME_PAGE;
1247     }
1248     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1249     foreach ($files as $file) {
1250         longer_timeout($timeout);       // longer timeout per page
1251         if (substr($file,-1,1) != '~')  // refuse to load backup files
1252             LoadFile($request, "$dirname/$file");
1253     }
1254 }
1255
1256 class LimitedFileSet extends FileSet {
1257     function LimitedFileSet($dirname, $_include, $exclude) {
1258         $this->_includefiles = $_include;
1259         $this->_exclude = $exclude;
1260         $this->_skiplist = array();
1261         parent::FileSet($dirname);
1262     }
1263
1264     function _filenameSelector($fn) {
1265         $incl = &$this->_includefiles;
1266         $excl = &$this->_exclude;
1267
1268         if ( ($incl && !in_array($fn, $incl))
1269              || ($excl && in_array($fn, $excl)) ) {
1270             $this->_skiplist[] = $fn;
1271             return false;
1272         } else {
1273             return true;
1274         }
1275     }
1276
1277     function getSkippedFiles () {
1278         return $this->_skiplist;
1279     }
1280 }
1281
1282
1283 function IsZipFile ($filename_or_fd)
1284 {
1285     // See if it looks like zip file
1286     if (is_string($filename_or_fd))
1287     {
1288         $fd    = fopen($filename_or_fd, "rb");
1289         $magic = fread($fd, 4);
1290         fclose($fd);
1291     }
1292     else
1293     {
1294         $fpos  = ftell($filename_or_fd);
1295         $magic = fread($filename_or_fd, 4);
1296         fseek($filename_or_fd, $fpos);
1297     }
1298
1299     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
1300 }
1301
1302
1303 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
1304 {
1305     // Try urlencoded filename for accented characters.
1306     if (!file_exists($file_or_dir)) {
1307         // Make sure there are slashes first to avoid confusing phps
1308         // with broken dirname or basename functions.
1309         // FIXME: windows uses \ and :
1310         if (is_integer(strpos($file_or_dir, "/"))) {
1311             $newfile = FindFile($file_or_dir, true);
1312             // Panic. urlencoded by the browser (e.g. San%20Diego => San Diego)
1313             if (!$newfile)
1314                 $file_or_dir = dirname($file_or_dir) . "/"
1315                     . rawurlencode(basename($file_or_dir));
1316         } else {
1317             // This is probably just a file.
1318             $file_or_dir = rawurlencode($file_or_dir);
1319         }
1320     }
1321
1322     $type = filetype($file_or_dir);
1323     if ($type == 'link') {
1324         // For symbolic links, use stat() to determine
1325         // the type of the underlying file.
1326         list(,,$mode) = stat($file_or_dir);
1327         $type = ($mode >> 12) & 017;
1328         if ($type == 010)
1329             $type = 'file';
1330         elseif ($type == 004)
1331             $type = 'dir';
1332     }
1333
1334     if (! $type) {
1335         $request->finish(fmt("Empty or not existing source. Unable to load: %s", $file_or_dir));
1336     }
1337     else if ($type == 'dir') {
1338         LoadDir($request, $file_or_dir, $files, $exclude);
1339     }
1340     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
1341     {
1342         $request->finish(fmt("Bad file type: %s", $type));
1343     }
1344     else if (IsZipFile($file_or_dir)) {
1345         LoadZip($request, $file_or_dir, $files, $exclude);
1346     }
1347     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
1348     {
1349         LoadFile($request, $file_or_dir);
1350     }
1351 }
1352
1353 function LoadFileOrDir (&$request)
1354 {
1355     $source = $request->getArg('source');
1356     $finder = new FileFinder;
1357     $source = $finder->slashifyPath($source);
1358     $page = rawurldecode(basename($source));
1359     StartLoadDump($request, fmt("Loading '%s'", 
1360         HTML(dirname($source),
1361              dirname($source) ? "/" : "",
1362              WikiLink($page,'auto'))));
1363     echo "<dl>\n";
1364     LoadAny($request, $source);
1365     echo "</dl>\n";
1366     EndLoadDump($request);
1367 }
1368
1369 /**
1370  * HomePage was not found so first-time install is supposed to run.
1371  * - import all pgsrc pages.
1372  * - Todo: installer interface to edit config/config.ini settings
1373  * - Todo: ask for existing old index.php to convert to config/config.ini
1374  * - Todo: theme-specific pages: 
1375  *   blog - HomePage, ADMIN_USER/Blogs
1376  */
1377 function SetupWiki (&$request)
1378 {
1379     global $GenericPages, $LANG;
1380
1381     //FIXME: This is a hack (err, "interim solution")
1382     // This is a bogo-bogo-login:  Login without
1383     // saving login information in session state.
1384     // This avoids logging in the unsuspecting
1385     // visitor as "The PhpWiki programming team".
1386     //
1387     // This really needs to be cleaned up...
1388     // (I'm working on it.)
1389     $real_user = $request->_user;
1390     if (ENABLE_USER_NEW)
1391         $request->_user = new _BogoUser(_("The PhpWiki programming team"));
1392
1393     else
1394         $request->_user = new WikiUser($request, _("The PhpWiki programming team"),
1395                                        WIKIAUTH_BOGO);
1396
1397     StartLoadDump($request, _("Loading up virgin wiki"));
1398     echo "<dl>\n";
1399
1400     $pgsrc = FindLocalizedFile(WIKI_PGSRC);
1401     $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
1402
1403     $request->setArg('overwrite', true);
1404     if ($default_pgsrc != $pgsrc) {
1405         LoadAny($request, $default_pgsrc, $GenericPages);
1406     }
1407     $request->setArg('overwrite', false);
1408     LoadAny($request, $pgsrc);
1409     $dbi =& $request->_dbi;
1410
1411     // Ensure that all mandatory pages are loaded
1412     $finder = new FileFinder;
1413     foreach (array_merge(explode(':','Help/OldTextFormattingRules:Help/TextFormattingRules:PhpWikiAdministration'),
1414                          $GLOBALS['AllActionPages'],
1415                          array(constant('HOME_PAGE'))) as $f) 
1416     {
1417         $page = gettext($f);
1418         $epage = urlencode($page);
1419         if (! $dbi->isWikiPage($page) ) {
1420             // translated version provided?
1421             if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $epage, 1)) {
1422                 LoadAny($request, $lf);
1423             } else { // load english version of required action page
1424                 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1425                 $page = $f;
1426             }
1427         }
1428         if (! $dbi->isWikiPage($page)) {
1429             trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1430                           E_USER_WARNING);
1431         }
1432     }
1433     echo "</dl>\n";
1434     
1435     $pagename = _("InterWikiMap");
1436     $map = $dbi->getPage($pagename);
1437     $map->set('locked', true);
1438     PrintXML(HTML::dt(HTML::em(WikiLink($pagename))), HTML::dd("locked"));
1439     EndLoadDump($request);
1440 }
1441
1442 function LoadPostFile (&$request)
1443 {
1444     $upload = $request->getUploadedFile('file');
1445
1446     if (!$upload)
1447         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1448
1449
1450     // Dump http headers.
1451     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1452     echo "<dl>\n";
1453
1454     $fd = $upload->open();
1455     if (IsZipFile($fd))
1456         LoadZip($request, $fd, false, array(_("RecentChanges")));
1457     else
1458         LoadFile($request, $upload->getName(), $upload->getContents());
1459
1460     echo "</dl>\n";
1461     EndLoadDump($request);
1462 }
1463
1464 /**
1465  $Log: not supported by cvs2svn $
1466  Revision 1.153  2007/05/28 20:54:40  rurban
1467  fix DumpToHtml creating dirs
1468
1469  Revision 1.152  2007/05/01 16:22:41  rurban
1470  lock InterWikiMap on init
1471
1472  Revision 1.151  2007/02/17 14:17:34  rurban
1473  only media=print css for htmldump and pdf
1474
1475  Revision 1.150  2007/01/20 15:53:42  rurban
1476  Use WikiPagename treatment for imported pagenames
1477
1478  Revision 1.149  2007/01/03 21:25:10  rurban
1479  Use convert_charset()
1480
1481  Revision 1.148  2007/01/02 13:21:57  rurban
1482  omit want_content if not necessary. support keep_old and overwrite buttons
1483
1484  Revision 1.147  2006/12/22 17:44:15  rurban
1485  support importing foreign charsets. e.g latin1 => utf8
1486
1487  Revision 1.146  2006/12/17 18:35:23  rurban
1488  Create the right subdirectory name, urlencoded.
1489
1490  Revision 1.145  2006/09/06 06:01:18  rurban
1491  support loadfile multipart archives automatically
1492
1493  Revision 1.144  2006/08/25 22:06:13  rurban
1494  args fix to pass $args to the template
1495
1496  Revision 1.143  2006/08/25 21:48:39  rurban
1497  dumphtml subpages
1498
1499  Revision 1.142  2006/03/19 17:16:32  rurban
1500  remove remaining cruft
1501
1502  Revision 1.141  2006/03/19 17:11:32  rurban
1503  add verify to RevertPage, display reverted page as template
1504
1505  Revision 1.140  2006/03/07 20:45:43  rurban
1506  wikihash for php-5.1
1507
1508  Revision 1.139  2005/08/27 18:02:43  rurban
1509  fix and expand pages
1510
1511  Revision 1.138  2005/08/27 09:39:10  rurban
1512  dumphtml when not at admin page: dump the current or given page
1513
1514  Revision 1.137  2005/01/30 23:14:38  rurban
1515  simplify page names
1516
1517  Revision 1.136  2005/01/25 07:07:24  rurban
1518  remove body tags in html dumps, add css and images to zipdumps, simplify printing
1519
1520  Revision 1.135  2004/12/26 17:17:25  rurban
1521  announce dumps - mult.requests to avoid request::finish, e.g. LinkDatabase, PdfOut, ...
1522
1523  Revision 1.134  2004/12/20 16:05:01  rurban
1524  gettext msg unification
1525
1526  Revision 1.133  2004/12/08 12:57:41  rurban
1527  page-specific timeouts for long multi-page requests
1528
1529  Revision 1.132  2004/12/08 01:18:33  rurban
1530  Disallow loading config*.ini files. Detected by Santtu Jarvi.
1531
1532  Revision 1.131  2004/11/30 17:48:38  rurban
1533  just comments
1534
1535  Revision 1.130  2004/11/25 08:28:12  rurban
1536  dont fatal on missing css or imgfiles and actually print the miss
1537
1538  Revision 1.129  2004/11/25 08:11:40  rurban
1539  pass exclude to the get_all_pages backend
1540
1541  Revision 1.128  2004/11/16 16:16:44  rurban
1542  enable Overwrite All for upgrade
1543
1544  Revision 1.127  2004/11/01 10:43:57  rurban
1545  seperate PassUser methods into seperate dir (memory usage)
1546  fix WikiUser (old) overlarge data session
1547  remove wikidb arg from various page class methods, use global ->_dbi instead
1548  ...
1549
1550  Revision 1.126  2004/10/16 15:13:39  rurban
1551  new [Overwrite All] button
1552
1553  Revision 1.125  2004/10/14 19:19:33  rurban
1554  loadsave: check if the dumped file will be accessible from outside.
1555  and some other minor fixes. (cvsclient native not yet ready)
1556
1557  Revision 1.124  2004/10/04 23:44:28  rurban
1558  for older or CGI phps
1559
1560  Revision 1.123  2004/09/25 16:26:54  rurban
1561  deferr notifies (to be improved)
1562
1563  Revision 1.122  2004/09/17 14:25:45  rurban
1564  update comments
1565
1566  Revision 1.121  2004/09/08 13:38:00  rurban
1567  improve loadfile stability by using markup=2 as default for undefined markup-style.
1568  use more refs for huge objects.
1569  fix debug=static issue in WikiPluginCached
1570
1571  Revision 1.120  2004/07/08 19:04:42  rurban
1572  more unittest fixes (file backend, metadata RatingsDb)
1573
1574  Revision 1.119  2004/07/08 15:23:59  rurban
1575  less verbose for tests
1576
1577  Revision 1.118  2004/07/08 13:50:32  rurban
1578  various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop
1579
1580  Revision 1.117  2004/07/02 09:55:58  rurban
1581  more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
1582
1583  Revision 1.116  2004/07/01 09:05:41  rurban
1584  support pages and exclude arguments for all 4 dump methods
1585
1586  Revision 1.115  2004/07/01 08:51:22  rurban
1587  dumphtml: added exclude, print pagename before processing
1588
1589  Revision 1.114  2004/06/28 12:51:41  rurban
1590  improved dumphtml and virgin setup
1591
1592  Revision 1.113  2004/06/27 10:26:02  rurban
1593  oci8 patch by Philippe Vanhaesendonck + some ADODB notes+fixes
1594
1595  Revision 1.112  2004/06/25 14:29:20  rurban
1596  WikiGroup refactoring:
1597    global group attached to user, code for not_current user.
1598    improved helpers for special groups (avoid double invocations)
1599  new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1600  fixed a XHTML validation error on userprefs.tmpl
1601
1602  Revision 1.111  2004/06/21 16:38:55  rurban
1603  fixed the StartLoadDump html argument hack.
1604
1605  Revision 1.110  2004/06/21 16:22:30  rurban
1606  add DEFAULT_DUMP_DIR and HTML_DUMP_DIR constants, for easier cmdline dumps,
1607  fixed dumping buttons locally (images/buttons/),
1608  support pages arg for dumphtml,
1609  optional directory arg for dumpserial + dumphtml,
1610  fix a AllPages warning,
1611  show dump warnings/errors on DEBUG,
1612  don't warn just ignore on wikilens pagelist columns, if not loaded.
1613  RateIt pagelist column is called "rating", not "ratingwidget" (Dan?)
1614
1615  Revision 1.109  2004/06/17 11:31:05  rurban
1616  jump back to label after dump/upgrade
1617
1618  Revision 1.108  2004/06/16 12:43:01  rurban
1619  4.0.6 cannot use this errorhandler (not found)
1620
1621  Revision 1.107  2004/06/14 11:31:37  rurban
1622  renamed global $Theme to $WikiTheme (gforge nameclash)
1623  inherit PageList default options from PageList
1624    default sortby=pagename
1625  use options in PageList_Selectable (limit, sortby, ...)
1626  added action revert, with button at action=diff
1627  added option regex to WikiAdminSearchReplace
1628
1629  Revision 1.106  2004/06/13 13:54:25  rurban
1630  Catch fatals on the four dump calls (as file and zip, as html and mimified)
1631  FoafViewer: Check against external requirements, instead of fatal.
1632  Change output for xhtmldumps: using file:// urls to the local fs.
1633  Catch SOAP fatal by checking for GOOGLE_LICENSE_KEY
1634  Import GOOGLE_LICENSE_KEY and FORTUNE_DIR from config.ini.
1635
1636  Revision 1.105  2004/06/08 19:48:16  rurban
1637  fixed foreign setup: no ugly skipped msg for the GenericPages, load english actionpages if translated not found
1638
1639  Revision 1.104  2004/06/08 13:51:57  rurban
1640  some comments only
1641
1642  Revision 1.103  2004/06/08 10:54:46  rurban
1643  better acl dump representation, read back acl and owner
1644
1645  Revision 1.102  2004/06/06 16:58:51  rurban
1646  added more required ActionPages for foreign languages
1647  install now english ActionPages if no localized are found. (again)
1648  fixed default anon user level to be 0, instead of -1
1649    (wrong "required administrator to view this page"...)
1650
1651  Revision 1.101  2004/06/04 20:32:53  rurban
1652  Several locale related improvements suggested by Pierrick Meignen
1653  LDAP fix by John Cole
1654  reenable admin check without ENABLE_PAGEPERM in the admin plugins
1655
1656  Revision 1.100  2004/05/02 21:26:38  rurban
1657  limit user session data (HomePageHandle and auth_dbi have to invalidated anyway)
1658    because they will not survive db sessions, if too large.
1659  extended action=upgrade
1660  some WikiTranslation button work
1661  revert WIKIAUTH_UNOBTAINABLE (need it for main.php)
1662  some temp. session debug statements
1663
1664  Revision 1.99  2004/05/02 15:10:07  rurban
1665  new finally reliable way to detect if /index.php is called directly
1666    and if to include lib/main.php
1667  new global AllActionPages
1668  SetupWiki now loads all mandatory pages: HOME_PAGE, action pages, and warns if not.
1669  WikiTranslation what=buttons for Carsten to create the missing MacOSX buttons
1670  PageGroupTestOne => subpages
1671  renamed PhpWikiRss to PhpWikiRecentChanges
1672  more docs, default configs, ...
1673
1674  Revision 1.98  2004/04/29 23:25:12  rurban
1675  re-ordered locale init (as in 1.3.9)
1676  fixed loadfile with subpages, and merge/restore anyway
1677    (sf.net bug #844188)
1678
1679  Revision 1.96  2004/04/19 23:13:03  zorloc
1680  Connect the rest of PhpWiki to the IniConfig system.  Also the keyword regular expression is not a config setting
1681
1682  Revision 1.95  2004/04/18 01:11:52  rurban
1683  more numeric pagename fixes.
1684  fixed action=upload with merge conflict warnings.
1685  charset changed from constant to global (dynamic utf-8 switching)
1686
1687  Revision 1.94  2004/03/14 16:36:37  rurban
1688  dont load backup files
1689
1690  Revision 1.93  2004/02/26 03:22:05  rurban
1691  also copy css and images with XHTML Dump
1692
1693  Revision 1.92  2004/02/26 02:25:54  rurban
1694  fix empty and #-anchored links in XHTML Dumps
1695
1696  Revision 1.91  2004/02/24 17:19:37  rurban
1697  debugging helpers only
1698
1699  Revision 1.90  2004/02/24 17:09:24  rurban
1700  fixed \r\r\n with dumping on windows
1701
1702  Revision 1.88  2004/02/22 23:20:31  rurban
1703  fixed DumpHtmlToDir,
1704  enhanced sortby handling in PageList
1705    new button_heading th style (enabled),
1706  added sortby and limit support to the db backends and plugins
1707    for paging support (<<prev, next>> links on long lists)
1708
1709  Revision 1.87  2004/01/26 09:17:49  rurban
1710  * changed stored pref representation as before.
1711    the array of objects is 1) bigger and 2)
1712    less portable. If we would import packed pref
1713    objects and the object definition was changed, PHP would fail.
1714    This doesn't happen with an simple array of non-default values.
1715  * use $prefs->retrieve and $prefs->store methods, where retrieve
1716    understands the interim format of array of objects also.
1717  * simplified $prefs->get() and fixed $prefs->set()
1718  * added $user->_userid and class '_WikiUser' portability functions
1719  * fixed $user object ->_level upgrading, mostly using sessions.
1720    this fixes yesterdays problems with loosing authorization level.
1721  * fixed WikiUserNew::checkPass to return the _level
1722  * fixed WikiUserNew::isSignedIn
1723  * added explodePageList to class PageList, support sortby arg
1724  * fixed UserPreferences for WikiUserNew
1725  * fixed WikiPlugin for empty defaults array
1726  * UnfoldSubpages: added pagename arg, renamed pages arg,
1727    removed sort arg, support sortby arg
1728
1729  Revision 1.86  2003/12/02 16:18:26  carstenklapp
1730  Minor enhancement: Provide more meaningful filenames for WikiDB zip
1731  dumps & snapshots.
1732
1733  Revision 1.85  2003/11/30 18:18:13  carstenklapp
1734  Minor code optimization: use include_once instead of require_once
1735  inside functions that might not always called.
1736
1737  Revision 1.84  2003/11/26 20:47:47  carstenklapp
1738  Redo bugfix: My last refactoring broke merge-edit & overwrite
1739  functionality again, should be fixed now. Sorry.
1740
1741  Revision 1.83  2003/11/20 22:18:54  carstenklapp
1742  New feature: h1 during merge-edit displays WikiLink to original page.
1743  Internal changes: Replaced some hackish url-generation code in
1744  function SavePage (for pgsrc merge-edit) with appropriate Button()
1745  calls.
1746
1747  Revision 1.82  2003/11/18 19:48:01  carstenklapp
1748  Fixed missing gettext _() for button name.
1749
1750  Revision 1.81  2003/11/18 18:28:35  carstenklapp
1751  Bugfix: In the Load File function of PhpWikiAdministration: When doing
1752  a "Merge Edit" or "Restore Anyway", page names containing accented
1753  letters (such as locale/de/pgsrc/G%E4steBuch) would produce a file not
1754  found error (Use FilenameForPage funtion to urlencode page names).
1755
1756  Revision 1.80  2003/03/07 02:46:57  dairiki
1757  Omit checks for safe_mode before set_time_limit().  Just prefix the
1758  set_time_limit() calls with @ so that they fail silently if not
1759  supported.
1760
1761  Revision 1.79  2003/02/26 01:56:05  dairiki
1762  Only zip pages with legal pagenames.
1763
1764  Revision 1.78  2003/02/24 02:05:43  dairiki
1765  Fix "n bytes written" message when dumping HTML.
1766
1767  Revision 1.77  2003/02/21 04:12:05  dairiki
1768  Minor fixes for new cached markup.
1769
1770  Revision 1.76  2003/02/16 19:47:17  dairiki
1771  Update WikiDB timestamp when editing or deleting pages.
1772
1773  Revision 1.75  2003/02/15 03:04:30  dairiki
1774  Fix for WikiUser constructor API change.
1775
1776  Revision 1.74  2003/02/15 02:18:04  dairiki
1777  When default language was English (at least), pgsrc was being
1778  loaded twice.
1779
1780  LimitedFileSet: Fix typo/bug. ($include was being ignored.)
1781
1782  SetupWiki(): Fix bugs in loading of $GenericPages.
1783
1784  Revision 1.73  2003/01/28 21:09:17  zorloc
1785  The get_cfg_var() function should only be used when one is
1786  interested in the value from php.ini or similar. Use ini_get()
1787  instead to get the effective value of a configuration variable.
1788  -- Martin Geisler
1789
1790  Revision 1.72  2003/01/03 22:25:53  carstenklapp
1791  Cosmetic fix to "Merge Edit" & "Overwrite" buttons. Added "The PhpWiki
1792  programming team" as author when loading from pgsrc. Source
1793  reformatting.
1794
1795  Revision 1.71  2003/01/03 02:48:05  carstenklapp
1796  function SavePage: Added loadfile options for overwriting or merge &
1797  compare a loaded pgsrc file with an existing page.
1798
1799  function LoadAny: Added a general error message when unable to load a
1800  file instead of defaulting to "Bad file type".
1801
1802  */
1803
1804 // For emacs users
1805 // Local Variables:
1806 // mode: php
1807 // tab-width: 8
1808 // c-basic-offset: 4
1809 // c-hanging-comment-ender-p: nil
1810 // indent-tabs-mode: nil
1811 // End:
1812 ?>