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