]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
Fix PHP warnings in wiki
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php //-*-php-*-
2 // $Id$
3
4 /*
5  * Copyright 1999,2000,2001,2002,2004,2005,2006,2007 $ThePhpWikiProgrammingTeam
6  * Copyright 2008-2010 Marc-Etienne Vargenau, Alcatel-Lucent
7  *
8  * This file is part of PhpWiki.
9  *
10  * PhpWiki is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * PhpWiki is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with PhpWiki; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 require_once("lib/ziplib.php");
26 require_once("lib/Template.php");
27
28 /**
29  * ignore fatal errors during dump
30  */
31 function _dump_error_handler($error) {
32     if ($error->isFatal()) {
33         $error->errno = E_USER_WARNING;
34         return true;
35     }
36     return true;         // Ignore error
37     /*
38     if (preg_match('/Plugin/', $error->errstr))
39         return true;
40     */
41     // let the message come through: call the remaining handlers:
42     // return false;
43 }
44
45 function StartLoadDump(&$request, $title, $html = '')
46 {
47     // MockRequest is from the unit testsuite, a faked request. (may be cmd-line)
48     // We are silent on unittests.
49     if (isa($request,'MockRequest'))
50         return;
51     // FIXME: This is a hack. This really is the worst overall hack in phpwiki.
52     if ($html)
53         $html->pushContent('%BODY%');
54     $tmpl = Template('html', array('TITLE' => $title,
55                                    'HEADER' => $title,
56                                    'CONTENT' => $html ? $html : '%BODY%'));
57     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
58     $request->chunkOutput();
59
60     // set marker for sendPageChangeNotification()
61     $request->_deferredPageChangeNotification = array();
62 }
63
64 function EndLoadDump(&$request)
65 {
66     global $WikiTheme;
67
68     if (isa($request,'MockRequest'))
69         return;
70     $action = $request->getArg('action');
71     $label = '';
72     switch ($action) {
73     case 'zip':        $label = _("ZIP files of database"); break;
74     case 'dumpserial': $label = _("Dump to directory"); break;
75     case 'upload':     $label = _("Upload File"); break;
76     case 'loadfile':   $label = _("Load File"); break;
77     case 'upgrade':    $label = _("Upgrade"); break;
78     case 'dumphtml':
79     case 'ziphtml':    $label = _("Dump pages as XHTML"); break;
80     }
81     if ($label) $label = str_replace(" ","_",$label);
82     if ($action == 'browse') // loading virgin
83         $pagelink = WikiLink(HOME_PAGE);
84     else
85         $pagelink = WikiLink(new WikiPageName(_("PhpWikiAdministration"),false,$label));
86
87     // do deferred sendPageChangeNotification()
88     if (!empty($request->_deferredPageChangeNotification)) {
89         $pages = $all_emails = $all_users = array();
90         foreach ($request->_deferredPageChangeNotification as $p) {
91             list($pagename, $emails, $userids) = $p;
92             $pages[] = $pagename;
93             $all_emails = array_unique(array_merge($all_emails, $emails));
94             $all_users = array_unique(array_merge($all_users, $userids));
95         }
96         $editedby = sprintf(_("Edited by: %s"), $request->_user->getId());
97         $content = "Loaded the following pages:\n" . join("\n", $pages);
98         if (mail(join(',',$all_emails),"[".WIKI_NAME."] "._("LoadDump"),
99                  _("LoadDump")."\n".
100                  $editedby."\n\n".
101                  $content))
102             trigger_error(sprintf(_("PageChange Notification of %s sent to %s"),
103                                   join("\n",$pages), join(',',$all_users)), E_USER_NOTICE);
104         else
105             trigger_error(sprintf(_("PageChange Notification Error: Couldn't send %s to %s"),
106                                   join("\n",$pages), join(',',$all_users)), E_USER_WARNING);
107         unset($pages);
108         unset($all_emails);
109         unset($all_users);
110     }
111     unset($request->_deferredPageChangeNotification);
112
113     PrintXML(HTML::p(HTML::strong(_("Complete."))),
114              HTML::p(fmt("Return to %s", $pagelink)));
115     // Ugly hack to get valid XHTML code
116     if (isa($WikiTheme, 'WikiTheme_fusionforge')) {
117         echo "</div>\n";
118         echo "</td></tr>\n";
119         echo "</table>\n";
120         echo "</div>\n";
121         echo "</td></tr>\n";
122         echo "</table>\n";
123     } else if (isa($WikiTheme, 'WikiTheme_Sidebar')
124            or isa($WikiTheme, 'WikiTheme_MonoBook')) {
125         echo "</div>\n";
126         echo "</div>\n";
127         echo "</div>\n";
128         echo "</div>\n";
129     } else if (isa($WikiTheme, 'WikiTheme_wikilens')) {
130         echo "</div>\n";
131         echo "</td>\n";
132         echo "</tr>\n";
133         echo "</table>\n";
134     } else if (isa($WikiTheme, 'WikiTheme_blog')) {
135         echo "</div>\n";
136         echo "</div>\n";
137     } else if (isa($WikiTheme, 'WikiTheme_Crao')
138            or isa($WikiTheme, 'WikiTheme_Hawaiian')
139            or isa($WikiTheme, 'WikiTheme_MacOSX')
140            or isa($WikiTheme, 'WikiTheme_shamino_com')
141            or isa($WikiTheme, 'WikiTheme_smaller')) {
142         echo "</div>\n";
143     }
144     echo "</body></html>\n";
145 }
146
147 ////////////////////////////////////////////////////////////////
148 //
149 //  Functions for dumping.
150 //
151 ////////////////////////////////////////////////////////////////
152
153 /**
154  * For reference see:
155  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
156  * http://www.faqs.org/rfcs/rfc2045.html
157  * (RFC 1521 has been superceeded by RFC 2045 & others).
158  *
159  * Also see http://www.faqs.org/rfcs/rfc2822.html
160  */
161 function MailifyPage ($page, $nversions = 1)
162 {
163     $current = $page->getCurrentRevision(false);
164     $head = '';
165
166     if (STRICT_MAILABLE_PAGEDUMPS) {
167         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
168         //This is for unix mailbox format: (not RFC (2)822)
169         // $head .= "From $from  " . CTime(time()) . "\r\n";
170         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
171         $head .= "From: $from (PhpWiki)\r\n";
172         // RFC 2822 requires only a Date: and originator (From:)
173         // field, however the obsolete standard RFC 822 also
174         // requires a destination field.
175         $head .= "To: $from (PhpWiki)\r\n";
176     }
177     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
178     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
179                      PHPWIKI_VERSION);
180
181     // This should just be entered by hand (or by script?)
182     // in the actual pgsrc files, since only they should have
183     // RCS ids.
184     //$head .= "X-Rcs-Id: \$Id\$\r\n";
185
186     $iter = $page->getAllRevisions();
187     $parts = array();
188     while ($revision = $iter->next()) {
189         $parts[] = MimeifyPageRevision($page, $revision);
190         if ($nversions > 0 && count($parts) >= $nversions)
191             break;
192     }
193     if (count($parts) > 1)
194         return $head . MimeMultipart($parts);
195     assert($parts);
196     return $head . $parts[0];
197 }
198
199 /***
200  * Compute filename to used for storing contents of a wiki page.
201  *
202  * Basically we do a rawurlencode() which encodes everything except
203  * ASCII alphanumerics and '.', '-', and '_'.
204  *
205  * But we also want to encode leading dots to avoid filenames like
206  * '.', and '..'. (Also, there's no point in generating "hidden" file
207  * names, like '.foo'.)
208  *
209  * We have to apply a different "/" logic for dumpserial, htmldump and zipdump.
210  * dirs are allowed for zipdump and htmldump, not for dumpserial
211  *
212  *
213  * @param $pagename string Pagename.
214  * @return string Filename for page.
215  */
216 function FilenameForPage ($pagename, $action = false)
217 {
218     $enc = rawurlencode($pagename);
219     if (!$action) {
220         global $request;
221     $action = $request->getArg('action');
222     }
223     if ($action != 'dumpserial') { // zip, ziphtml, dumphtml
224     // For every %2F we will need to mkdir -p dirname($pagename)
225     $enc = preg_replace('/%2F/', '/', $enc);
226     }
227     $enc = preg_replace('/^\./', '%2E', $enc);
228     $enc = preg_replace('/%20/', ' ',   $enc);
229     $enc = preg_replace('/\.$/', '%2E', $enc);
230     return $enc;
231 }
232
233 /**
234  * The main() function which generates a zip archive of a PhpWiki.
235  *
236  * If $include_archive is false, only the current version of each page
237  * is included in the zip file; otherwise all archived versions are
238  * included as well.
239  */
240 function MakeWikiZip (&$request)
241 {
242     global $ErrorManager;
243     if ($request->getArg('include') == 'all') {
244         $zipname         = WIKI_NAME . _("FullDump") . date('Ymd-Hi') . '.zip';
245         $include_archive = true;
246     }
247     else {
248         $zipname         = WIKI_NAME . _("LatestSnapshot") . date('Ymd-Hi') . '.zip';
249         $include_archive = false;
250     }
251     $include_empty = false;
252     if ($request->getArg('include') == 'empty') {
253     $include_empty = true;
254     }
255
256     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
257
258     /* ignore fatals in plugins */
259     $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
260
261     $dbi =& $request->_dbi;
262     $thispage = $request->getArg('pagename'); // for "Return to ..."
263     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
264         $excludeList = explodePageList($exclude);
265     } else {
266         $excludeList = array();
267     }
268     if ($pages = $request->getArg('pages')) {  // which pagenames
269         if ($pages == '[]') // current page
270             $pages = $thispage;
271         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
272     } else {
273         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
274     }
275     $request_args = $request->args;
276     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
277
278     while ($page = $page_iter->next()) {
279     $request->args = $request_args; // some plugins might change them (esp. on POST)
280         longer_timeout($timeout);     // Reset watchdog
281
282         $current = $page->getCurrentRevision();
283         if ($current->getVersion() == 0)
284             continue;
285
286         $pagename = $page->getName();
287         $wpn = new WikiPageName($pagename);
288         if (!$wpn->isValid())
289             continue;
290         if (in_array($page->getName(), $excludeList)) {
291             continue;
292         }
293
294         $attrib = array('mtime'    => $current->get('mtime'),
295                         'is_ascii' => 1);
296         if ($page->get('locked'))
297             $attrib['write_protected'] = 1;
298
299         if ($include_archive)
300             $content = MailifyPage($page, 0);
301         else
302             $content = MailifyPage($page);
303
304         $zip->addRegularFile( FilenameForPage($pagename),
305                               $content, $attrib);
306     }
307     $zip->finish();
308
309     $ErrorManager->popErrorHandler();
310 }
311
312 function DumpToDir (&$request)
313 {
314     $directory = $request->getArg('directory');
315     if (empty($directory))
316         $directory = DEFAULT_DUMP_DIR; // See lib/plugin/WikiForm.php:87
317     if (empty($directory))
318         $request->finish(_("You must specify a directory to dump to"));
319
320     // see if we can access the directory the user wants us to use
321     if (! file_exists($directory)) {
322         if (! mkdir($directory, 0755))
323             $request->finish(fmt("Cannot create directory '%s'", $directory));
324         else
325             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
326                                 $directory));
327     } else {
328         $html = HTML::p(fmt("Using directory '%s'", $directory));
329     }
330
331     StartLoadDump($request, _("Dumping Pages"), $html);
332
333     $dbi =& $request->_dbi;
334     $thispage = $request->getArg('pagename'); // for "Return to ..."
335     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
336         $excludeList = explodePageList($exclude);
337     } else {
338         $excludeList = array();
339     }
340     $include_empty = false;
341     if ($request->getArg('include') == 'empty') {
342     $include_empty = true;
343     }
344     if ($pages = $request->getArg('pages')) {  // which pagenames
345         if ($pages == '[]') // current page
346             $pages = $thispage;
347         $page_iter = new WikiDB_Array_PageIterator(explodePageList($pages));
348     } else {
349         $page_iter = $dbi->getAllPages($include_empty,false,false,$excludeList);
350     }
351
352     $request_args = $request->args;
353     $timeout = (! $request->getArg('start_debug')) ? 30 : 240;
354
355     while ($page = $page_iter->next()) {
356     $request->args = $request_args; // some plugins might change them (esp. on POST)
357         longer_timeout($timeout);     // Reset watchdog
358
359         $pagename = $page->getName();
360         if (!isa($request,'MockRequest')) {
361             PrintXML(HTML::br(), $pagename, ' ... ');
362             flush();
363         }
364
365         if (in_array($pagename, $excludeList)) {
366             if (!isa($request, 'MockRequest')) {
367                 PrintXML(_("Skipped."));
368                 flush();
369             }
370             continue;
371         }
372         $filename = FilenameForPage($pagename);
373         $msg = HTML();
374         if($page->getName() != $filename) {
375             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
376                               " ... ");
377         }
378
379         if ($request->getArg('include') == 'all')
380             $data = MailifyPage($page, 0);
381         else
382             $data = MailifyPage($page);
383
384         if ( !($fd = fopen($directory."/".$filename, "wb")) ) {
385             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
386                                                "$directory/$filename")));
387             $request->finish($msg);
388         }
389
390         $num = fwrite($fd, $data, strlen($data));
391         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
392         if (!isa($request, 'MockRequest')) {
393             PrintXML($msg);
394             flush();
395         }
396         assert($num == strlen($data));
397         fclose($fd);
398     }
399
400     EndLoadDump($request);
401 }
402
403 function _copyMsg($page, $smallmsg) {
404     if (!isa($GLOBALS['request'], 'MockRequest')) {
405         if ($page) $msg = HTML(HTML::br(), HTML($page), HTML::small($smallmsg));
406         else $msg = HTML::small($smallmsg);
407         PrintXML($msg);
408         flush();
409     }
410 }
411
412 function mkdir_p($pathname, $permission = 0777) {
413     $arr = explode("/", $pathname);
414     if (empty($arr)) {
415     return mkdir($pathname, $permission);
416     }
417     $s = array_shift($arr);
418     $ok = TRUE;
419     foreach ($arr as $p) {
420     $curr = "$s/$p";
421     if (!is_dir($curr))
422         $ok = mkdir($curr, $permission);
423     $s = $curr;
424     if (!$ok) return FALSE;
425     }
426     return TRUE;
427 }
428
429 /**
430  * Dump all pages as XHTML to a directory, as pagename.html.
431  * Copies all used css files to the directory, all used images to a
432  * "images" subdirectory, and all used buttons to a "images/buttons" subdirectory.
433  * The webserver must have write permissions to these directories.
434  *   chown httpd HTML_DUMP_DIR; chmod u+rwx HTML_DUMP_DIR
435  * should be enough.
436  *
437  * @param string directory (optional) path to dump to. Default: HTML_DUMP_DIR
438  * @param string pages     (optional) Comma-seperated of glob-style pagenames to dump.
439  *                                    Also array of pagenames allowed.
440  * @param string exclude   (optional) Comma-seperated of glob-style pagenames to exclude
441  */
442 function DumpHtmlToDir (&$request)
443 {
444     global $WikiTheme;
445     $directory = $request->getArg('directory');
446     if (empty($directory))
447         $directory = HTML_DUMP_DIR; // See lib/plugin/WikiForm.php:87
448     if (empty($directory))
449         $request->finish(_("You must specify a directory to dump to"));
450
451     // See if we can access the directory the user wants us to use
452     if (! file_exists($directory)) {
453         if (! mkdir($directory, 0755))
454             $request->finish(fmt("Cannot create directory '%s'", $directory));
455         else
456             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
457                                 $directory));
458     } else {
459         $html = HTML::p(fmt("Using directory '%s'", $directory));
460     }
461     StartLoadDump($request, _("Dumping Pages"), $html);
462     $thispage = $request->getArg('pagename'); // for "Return to ..."
463
464     $dbi =& $request->_dbi;
465     if ($exclude = $request->getArg('exclude')) {   // exclude which pagenames
466         $excludeList = explodePageList($exclude);
467     } else {
468         $excludeList = array('DebugAuthInfo', 'DebugGroupInfo', 'AuthInfo');
469     }
470     if ($pages = $request->getArg('pages')) {  // which pagenames
471         if ($pages == '[]') // current page
472             $pages = $thispage;
473         $page_iter = new WikiDB_Array_generic_iter(explodePageList($pages));
474     // not at admin page: dump only the current page
475     } elseif ($thispage != _("PhpWikiAdministration")) {
476         $page_iter = new WikiDB_Array_generic_iter(array($thispage));
477     } else {
478         $page_iter = $dbi->getAllPages(false,false,false,$excludeList);
479     }
480
481     $WikiTheme->DUMP_MODE = 'HTML';
482     _DumpHtmlToDir($directory, $page_iter, $request->getArg('exclude'));
483     $WikiTheme->DUMP_MODE = false;
484
485     $request->setArg('pagename',$thispage); // Template::_basepage fix
486     EndLoadDump($request);
487 }
488
489 /* Known problem: any plugins or other code which echo()s text will
490  * lead to a corrupted html zip file which may produce the following
491  * errors upon unzipping:
492  *
493  * warning [wikihtml.zip]:  2401 extra bytes at beginning or within zipfile
494  * file #58:  bad zipfile offset (local header sig):  177561
495  *  (attempting to re-compensate)
496  *
497  * However, the actual wiki page data should be unaffected.
498  */
499 function MakeWikiZipHtml (&$request)
500 {
501     global $WikiTheme;
502     if ($request->getArg('zipname')) {
503         $zipname = basename($request->getArg('zipname'));
504         if (!preg_match("/\.zip$/i", $zipname))
505             $zipname .= ".zip";
506         $request->setArg('zipname', false);
507     } else {
508         $zipname = "wikihtml.zip";
509     }
510     $zip = new ZipWriter("Created by PhpWiki " . PHPWIKI_VERSION, $zipname);
511     $dbi =& $request->_dbi;
512     $thispage = $request->getArg('pagename'); // for "Return to ..."
513     if ($pages = $request->getArg('pages')) {  // which pagenames
514         if ($pages == '[]') // current page
515             $pages = $thispage;
516         $page_iter = new WikiDB_Array_generic_iter(explodePageList($pages));
517     } else {
518         $page_iter = $dbi->getAllPages(false,false,false,$request->getArg('exclude'));
519     }
520
521     $WikiTheme->DUMP_MODE = 'ZIPHTML';
522     _DumpHtmlToDir($zip, $page_iter, $request->getArg('exclude'));
523     $WikiTheme->DUMP_MODE = false;
524 }
525
526 /*
527  * Internal html dumper. Used for dumphtml, ziphtml and pdf
528  */
529 function _DumpHtmlToDir ($target, $page_iter, $exclude = false)
530 {
531     global $WikiTheme, $request, $ErrorManager;
532     $silent = true; $zip = false; $directory = false;
533     if ($WikiTheme->DUMP_MODE == 'HTML') {
534         $directory = $target;
535         $silent = false;
536     } elseif ($WikiTheme->DUMP_MODE == 'PDFHTML') {
537         $directory = $target;
538     } elseif (is_object($target)) { // $WikiTheme->DUMP_MODE == 'ZIPHTML'
539         $zip = $target;
540     }
541
542     $request->_TemplatesProcessed = array();
543     if ($exclude) {   // exclude which pagenames
544         $excludeList = explodePageList($exclude);
545     } else {
546         $excludeList = array('DebugAuthInfo', 'DebugGroupInfo', 'AuthInfo');
547     }
548     $WikiTheme->VALID_LINKS = array();
549     if ($request->getArg('format')) { // pagelist
550         $page_iter_sav = $page_iter;
551         foreach ($page_iter_sav->asArray() as $handle) {
552             $WikiTheme->VALID_LINKS[] = is_string($handle) ? $handle : $handle->getName();
553         }
554         $page_iter_sav->reset();
555     }
556
557     if (defined('HTML_DUMP_SUFFIX')) {
558         $WikiTheme->HTML_DUMP_SUFFIX = HTML_DUMP_SUFFIX;
559     }
560     if (isset($WikiTheme->_MoreAttr['body'])) {
561         $_bodyAttr = $WikiTheme->_MoreAttr['body'];
562         unset($WikiTheme->_MoreAttr['body']);
563     }
564
565     $ErrorManager->pushErrorHandler(new WikiFunctionCb('_dump_error_handler'));
566
567     // check if the dumped file will be accessible from outside
568     $doc_root = $request->get("DOCUMENT_ROOT");
569     if ($WikiTheme->DUMP_MODE == 'HTML') {
570         $ldir = NormalizeLocalFileName($directory);
571         $wikiroot = NormalizeLocalFileName('');
572         if (string_starts_with($ldir, $doc_root)) {
573             $link_prefix = substr($directory, strlen($doc_root))."/";
574         } elseif (string_starts_with($ldir, $wikiroot)) {
575             $link_prefix = NormalizeWebFileName(substr($directory, strlen($wikiroot)))."/";
576         } else {
577             $prefix = '';
578             if (isWindows()) {
579                 $prefix = '/'; // . substr($doc_root,0,2); // add drive where apache is installed
580             }
581             $link_prefix = "file://".$prefix.$directory."/";
582         }
583     } else {
584         $link_prefix = "";
585     }
586
587     $request_args = $request->args;
588     $timeout = (! $request->getArg('start_debug')) ? 60 : 240;
589     if ($directory) {
590         if (isWindows())
591             $directory = str_replace("\\", "/", $directory); // no Win95 support.
592         if (!is_dir("$directory/images"))
593             mkdir("$directory/images");
594     }
595     $already = array();
596     $outfiles = array();
597     $already_images = array();
598
599     while ($page = $page_iter->next()) {
600         if (is_string($page)) {
601             $pagename = $page;
602             $page = $request->_dbi->getPage($pagename);
603         } else {
604             $pagename = $page->getName();
605         }
606         if (empty($firstpage)) $firstpage = $pagename;
607         if (array_key_exists($pagename, $already))
608             continue;
609         $already[$pagename] = 1;
610         $current = $page->getCurrentRevision();
611         //if ($current->getVersion() == 0)
612         //    continue;
613
614         $request->args = $request_args; // some plugins might change them (esp. on POST)
615         longer_timeout($timeout);     // Reset watchdog
616
617         if ($zip) {
618             $attrib = array('mtime'    => $current->get('mtime'),
619                             'is_ascii' => 1);
620             if ($page->get('locked'))
621                 $attrib['write_protected'] = 1;
622         } elseif (!$silent) {
623             if (!isa($request,'MockRequest')) {
624                 PrintXML(HTML::br(), $pagename, ' ... ');
625                 flush();
626             }
627         }
628         if (in_array($pagename, $excludeList)) {
629             if (!$silent and !isa($request,'MockRequest')) {
630                 PrintXML(_("Skipped."));
631                 flush();
632             }
633             continue;
634         }
635         $relative_base = '';
636         if ($WikiTheme->DUMP_MODE == 'PDFHTML')
637             $request->setArg('action', 'pdf');   // to omit cache headers
638         $request->setArg('pagename', $pagename); // Template::_basepage fix
639         $filename = FilenameForPage($pagename) . $WikiTheme->HTML_DUMP_SUFFIX;
640         $args = array('revision'      => $current,
641                       'CONTENT'       => $current->getTransformedContent(),
642                       'relative_base' => $relative_base);
643         // For every %2F will need to mkdir -p dirname($pagename)
644         if (preg_match("/(%2F|\/)/", $filename)) {
645             // mkdir -p and set relative base for subdir pages
646             $filename = preg_replace("/%2F/", "/", $filename);
647             $count = substr_count($filename, "/");
648             $dirname = dirname($filename);
649             if ($directory)
650                 mkdir_p($directory."/".$dirname);
651             // Fails with "XX / YY", "XX" is created, "XX / YY" cannot be written
652             // if (isWindows()) // interesting Windows bug: cannot mkdir "bla "
653             // Since dumps needs to be copied, we have to disallow this for all platforms.
654             $filename = preg_replace("/ \//", "/", $filename);
655             $relative_base = "../";
656             while ($count > 1) {
657                 $relative_base .= "../";
658                 $count--;
659             }
660             $args['relative_base'] = $relative_base;
661         }
662         $msg = HTML();
663
664         $DUMP_MODE = $WikiTheme->DUMP_MODE;
665         $data = GeneratePageasXML(new Template('browse', $request, $args),
666                      $pagename, $current, $args);
667         $WikiTheme->DUMP_MODE = $DUMP_MODE;
668
669         if (preg_match_all("/<img .*?src=\"(\/.+?)\"/", $data, $m)) {
670             // fix to local relative path for uploaded images, so that pdf will work
671             foreach ($m[1] as $img_file) {
672                 $base = basename($img_file);
673                 $data = str_replace('src="'.$img_file.'"','src="images/'.$base.'"', $data);
674                 if (array_key_exists($img_file, $already_images))
675                     continue;
676                 $already_images[$img_file] = 1;
677                 // resolve src from webdata to file
678                 $src = $doc_root . $img_file;
679                 if (file_exists($src) and $base) {
680                     if ($directory) {
681                         $target = "$directory/images/$base";
682                         if (copy($src, $target)) {
683                             if (!$silent)
684                                 _copyMsg($img_file, fmt("... copied to %s", $target));
685                         } else {
686                             if (!$silent)
687                                 _copyMsg($img_file, fmt("... not copied to %s", $target));
688                         }
689                     } else {
690                         $target = "images/$base";
691                         $zip->addSrcFile($target, $src);
692                     }
693                 }
694             }
695         }
696
697     if ($directory) {
698         $outfile = $directory."/".$filename;
699         if ( !($fd = fopen($outfile, "wb")) ) {
700         $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
701                            $outfile)));
702         $request->finish($msg);
703         }
704         $len = strlen($data);
705         $num = fwrite($fd, $data, $len);
706         if ($pagename != $filename) {
707         $link = LinkURL($link_prefix.$filename, $filename);
708         $msg->pushContent(HTML::small(_("saved as "), $link, " ... "));
709         }
710         $msg->pushContent(HTML::small(fmt("%s bytes written", $num), "\n"));
711         if (!$silent) {
712         if (!isa($request, 'MockRequest')) {
713             PrintXML($msg);
714         }
715         flush();
716         $request->chunkOutput();
717         }
718         assert($num == $len);
719         fclose($fd);
720         $outfiles[] = $outfile;
721     } else {
722         $zip->addRegularFile($filename, $data, $attrib);
723     }
724
725         if (USECACHE) {
726             $request->_dbi->_cache->invalidate_cache($pagename);
727             unset ($request->_dbi->_cache->_pagedata_cache);
728             unset ($request->_dbi->_cache->_versiondata_cache);
729             unset ($request->_dbi->_cache->_glv_cache);
730         }
731         unset ($request->_dbi->_cache->_backend->_page_data);
732
733         unset($msg);
734         unset($current->_transformedContent);
735         unset($current);
736     if (!empty($template)) {
737         unset($template->_request);
738         unset($template);
739     }
740         unset($data);
741     }
742     $page_iter->free();
743
744     $attrib = false; //array('is_ascii' => 0);
745     if (!empty($WikiTheme->dumped_images) and is_array($WikiTheme->dumped_images)) {
746         // @mkdir("$directory/images");
747         foreach ($WikiTheme->dumped_images as $img_file) {
748         if (array_key_exists($img_file, $already_images))
749             continue;
750         $already_images[$img_file] = 1;
751             if ($img_file
752                 and ($from = $WikiTheme->_findFile($img_file, true))
753                 and basename($from))
754             {
755         if ($directory) {
756             $target = "$directory/images/".basename($from);
757             if ($silent)
758             copy($WikiTheme->_path . $from, $target);
759             else {
760             if (copy($WikiTheme->_path . $from, $target)) {
761                 _copyMsg($from, fmt("... copied to %s", $target));
762             } else {
763                 _copyMsg($from, fmt("... not copied to %s", $target));
764             }
765             }
766         } else {
767             $target = "images/".basename($from);
768             $zip->addSrcFile($target, $WikiTheme->_path . $from);
769         }
770             } elseif (!$silent) {
771                 _copyMsg($from, _("... not found"));
772             }
773         }
774     }
775
776     if (!empty($WikiTheme->dumped_buttons)
777          and is_array($WikiTheme->dumped_buttons))
778     {
779         // Buttons also
780         if ($directory && !is_dir("$directory/images/buttons"))
781             mkdir("$directory/images/buttons");
782         foreach ($WikiTheme->dumped_buttons as $text => $img_file) {
783             if (array_key_exists($img_file, $already_images))
784                 continue;
785         $already_images[$img_file] = 1;
786             if ($img_file
787                 and ($from = $WikiTheme->_findFile($img_file, true))
788                 and basename($from))
789             {
790         if ($directory) {
791             $target = "$directory/images/buttons/".basename($from);
792             if ($silent)
793             copy($WikiTheme->_path . $from, $target);
794             else {
795                 if (copy($WikiTheme->_path . $from, $target)) {
796                     _copyMsg($from, fmt("... copied to %s", $target));
797                 } else {
798                     _copyMsg($from, fmt("... not copied to %s", $target));
799                 }
800             }
801         } else {
802             $target = "images/buttons/".basename($from);
803             $zip->addSrcFile($target, $WikiTheme->_path . $from);
804         }
805             } elseif (!$silent) {
806                 _copyMsg($from, _("... not found"));
807             }
808         }
809     }
810     if (!empty($WikiTheme->dumped_css) and is_array($WikiTheme->dumped_css)) {
811         foreach ($WikiTheme->dumped_css as $css_file) {
812             if (array_key_exists($css_file, $already_images))
813                 continue;
814         $already_images[$css_file] = 1;
815             if ($css_file
816                 and ($from = $WikiTheme->_findFile(basename($css_file), true))
817                 and basename($from))
818             {
819         // TODO: fix @import url(main.css);
820         if ($directory) {
821             $target = "$directory/" . basename($css_file);
822             if ($silent)
823             copy($WikiTheme->_path . $from, $target);
824             else {
825             if (copy($WikiTheme->_path . $from, $target)) {
826                 _copyMsg($from, fmt("... copied to %s", $target));
827             } else {
828                 _copyMsg($from, fmt("... not copied to %s", $target));
829             }
830             }
831         } else {
832             //$attrib = array('is_ascii' => 0);
833             $target = basename($css_file);
834             $zip->addSrcFile($target, $WikiTheme->_path . $from);
835         }
836             } elseif (!$silent) {
837                 _copyMsg($from, _("... not found"));
838             }
839         }
840     }
841
842     if ($zip)
843     $zip->finish();
844
845     if ($WikiTheme->DUMP_MODE == 'PDFHTML') {
846     if (USE_EXTERNAL_HTML2PDF and $outfiles) {
847         $cmd = EXTERNAL_HTML2PDF_PAGELIST.' "'.join('" "', $outfiles).'"';
848         $filename = FilenameForPage($firstpage);
849         if (DEBUG) {
850         $tmpfile = $directory . "/createpdf.bat";
851         $fp = fopen($tmpfile, "wb");
852         fwrite($fp, $cmd . " > $filename.pdf");
853         fclose($fp);
854         }
855         if (!headers_sent()) {
856         Header('Content-Type: application/pdf');
857         passthru($cmd);
858         }
859         else {
860         $tmpdir = getUploadFilePath();
861         $s = passthru($cmd . " > $tmpdir/$filename.pdf");
862         $errormsg = "<br />\nGenerated <a href=\"".getUploadDataPath()."$filename.pdf\">Upload:$filename.pdf</a>\n";
863         $errormsg .= $s;
864         echo $errormsg;
865         }
866         if (!DEBUG) {
867         foreach($outfiles as $f) unlink($f);
868         }
869     }
870     if (!empty($errormsg)) {
871         $request->discardOutput();
872         $GLOBALS['ErrorManager']->_postponed_errors = array();
873     }
874     }
875
876     $ErrorManager->popErrorHandler();
877
878     $WikiTheme->HTML_DUMP_SUFFIX = '';
879     $WikiTheme->DUMP_MODE = false;
880     $WikiTheme->_MoreAttr['body'] = isset($_bodyAttr) ? $_bodyAttr : '';
881 }
882
883
884 ////////////////////////////////////////////////////////////////
885 //
886 //  Functions for restoring.
887 //
888 ////////////////////////////////////////////////////////////////
889
890 function SavePage (&$request, &$pageinfo, $source, $filename)
891 {
892     static $overwite_all = false;
893     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
894     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
895
896     if (empty($pageinfo['pagename'])) {
897         PrintXML(HTML::p(HTML::strong(_("Empty pagename!"))));
898         return;
899     }
900
901     if (empty($versiondata['author_id']))
902         $versiondata['author_id'] = $versiondata['author'];
903
904     // remove invalid backend specific chars. utf8 issues mostly
905     $pagename_check = new WikiPagename($pageinfo['pagename']);
906     if (!$pagename_check->isValid()) {
907         PrintXML(HTML::p(HTML::strong(_("Invalid pagename!")." ".$pageinfo['pagename'])));
908         return;
909     }
910     $pagename = $pagename_check->getName();
911     $content  = $pageinfo['content'];
912
913     if ($pagename == _("InterWikiMap"))
914         $content = _tryinsertInterWikiMap($content);
915
916     $dbi =& $request->_dbi;
917     $page = $dbi->getPage($pagename);
918
919     // Try to merge if updated pgsrc contents are different. This
920     // whole thing is hackish
921     //
922     // TODO: try merge unless:
923     // if (current contents = default contents && pgsrc_version >=
924     // pgsrc_version) then just upgrade this pgsrc
925     $needs_merge = false;
926     $merging = false;
927     $overwrite = false;
928
929     if ($request->getArg('merge')) {
930         $merging = true;
931     }
932     else if ($request->getArg('overwrite')) {
933         $overwrite = true;
934     }
935
936     $current = $page->getCurrentRevision();
937     $skip = false;
938     $edit = $request->getArg('edit');
939     if ($merging) {
940         if (isset($edit['keep_old'])) {
941             $merging = false;
942             $skip = true;
943         }
944         elseif (isset($edit['overwrite'])) {
945             $merging = false;
946             $overwrite = true;
947         }
948         elseif ( $current and (! $current->hasDefaultContents())
949          && ($current->getPackedContent() != $content) )
950         {
951             include_once('lib/editpage.php');
952             $request->setArg('pagename', $pagename);
953             $v = $current->getVersion();
954             $request->setArg('revision', $current->getVersion());
955             $p = new LoadFileConflictPageEditor($request);
956             $p->_content = $content;
957             $p->_currentVersion = $v - 1;
958             $p->editPage($saveFailed = true);
959             return; //early return
960        }
961     }
962     if (!$skip)
963       foreach ($pagedata as $key => $value) {
964         if (!empty($value))
965             $page->set($key, $value);
966       }
967
968     $mesg = HTML::p(array('style' => 'text-indent: 3em;'));
969     if ($source)
970         $mesg->pushContent(' ', fmt("from %s", $source));
971
972     if (!$current) {
973         //FIXME: This should not happen! (empty vdata, corrupt cache or db)
974         $current = $page->getCurrentRevision();
975     }
976     if ($current->getVersion() == 0) {
977         $versiondata['author'] = ADMIN_USER;
978         $versiondata['author_id'] = ADMIN_USER;
979         $mesg->pushContent(' - ', _("New page"));
980         $isnew = true;
981     }
982     else {
983         if ( (! $current->hasDefaultContents())
984              && ($current->getPackedContent() != $content) ) {
985             if ($overwrite) {
986                 $mesg->pushContent(' ',
987                                    fmt("has edit conflicts - overwriting anyway"));
988                 $skip = false;
989                 if (substr_count($source, 'pgsrc')) {
990                     $versiondata['author'] = ADMIN_USER;
991                     // but leave authorid as userid who loaded the file
992                 }
993             }
994             else {
995         if (isset($edit['keep_old'])) {
996             $mesg->pushContent(' ', fmt("keep old"));
997         } else {
998             $mesg->pushContent(' ', fmt("has edit conflicts - skipped"));
999             $needs_merge = true; // hackish, to display the buttons
1000         }
1001                 $skip = true;
1002             }
1003         }
1004         else if ($current->getPackedContent() == $content) {
1005             // The page content is the same, we don't need a new revision.
1006             $mesg->pushContent(' ',
1007                                fmt("content is identical to current version %d - no new revision created",
1008                                    $current->getVersion()));
1009             $skip = true;
1010         }
1011         $isnew = false;
1012     }
1013
1014     if (! $skip ) {
1015         // in case of failures print the culprit:
1016         if (!isa($request,'MockRequest')) {
1017             PrintXML(HTML::p(WikiLink($pagename))); flush();
1018         }
1019         $new = $page->save($content, WIKIDB_FORCE_CREATE, $versiondata);
1020         $dbi->touch();
1021         $mesg->pushContent(' ', fmt("- saved to database as version %d",
1022                                     $new->getVersion()));
1023     }
1024     if ($needs_merge) {
1025         $f = $source;
1026         // hackish, $source contains needed path+filename
1027         $f = str_replace(sprintf(_("MIME file %s"), ''), '', $f);
1028         $f = str_replace(sprintf(_("Serialized file %s"), ''), '', $f);
1029         $f = str_replace(sprintf(_("plain file %s"), ''), '', $f);
1030         //check if uploaded file? they pass just the content, but the file is gone
1031         if (@stat($f)) {
1032             global $WikiTheme;
1033             $meb = Button(array('action' => 'loadfile',
1034                                 'merge'=> true,
1035                                 'source'=> $f),
1036                           _("Merge Edit"),
1037                           _("PhpWikiAdministration"),
1038                           'wikiadmin');
1039             $owb = Button(array('action' => 'loadfile',
1040                                 'overwrite'=> true,
1041                                 'source'=> $f),
1042                           _("Restore Anyway"),
1043                           _("PhpWikiAdministration"),
1044                           'wikiunsafe');
1045             $mesg->pushContent(' ', $meb, " ", $owb);
1046             if (!$overwite_all) {
1047                 $args = $request->getArgs();
1048                 $args['overwrite'] = 1;
1049                 $owb = Button($args,
1050                               _("Overwrite All"),
1051                               _("PhpWikiAdministration"),
1052                               'wikiunsafe');
1053                 $mesg->pushContent(HTML::span(array('class' => 'hint'), $owb));
1054                 $overwite_all = true;
1055             }
1056         } else {
1057             $mesg->pushContent(HTML::em(_(" Sorry, cannot merge.")));
1058         }
1059     }
1060
1061     if (!isa($request,'MockRequest')) {
1062       if ($skip)
1063         PrintXML(HTML::p(HTML::em(WikiLink($pagename))), $mesg);
1064       else
1065         PrintXML($mesg);
1066       flush();
1067     }
1068 }
1069
1070 // action=revert (by diff)
1071 function RevertPage (&$request)
1072 {
1073     $mesg = HTML::div();
1074     $pagename = $request->getArg('pagename');
1075     $version = $request->getArg('version');
1076     if (!$version) {
1077         PrintXML(HTML::p(fmt("Revert")," ",WikiLink($pagename)),
1078                  HTML::p(_("missing required version argument")));
1079         return;
1080     }
1081     $dbi =& $request->_dbi;
1082     $page = $dbi->getPage($pagename);
1083     $current = $page->getCurrentRevision();
1084     $currversion = $current->getVersion();
1085     if ($currversion == 0) {
1086         $mesg->pushContent(' ', _("no page content"));
1087         PrintXML(HTML::p(fmt("Revert")," ",WikiLink($pagename)),
1088                  $mesg);
1089         flush();
1090         return;
1091     }
1092     if ($currversion == $version) {
1093         $mesg->pushContent(' ', _("same version page"));
1094         PrintXML(HTML::p(fmt("Revert")," ",WikiLink($pagename)),
1095                  $mesg);
1096         flush();
1097         return;
1098     }
1099     if ($request->getArg('cancel')) {
1100         $mesg->pushContent(' ', _("Cancelled"));
1101         PrintXML(HTML::p(fmt("Revert")," ",WikiLink($pagename)),
1102                  $mesg);
1103         flush();
1104         return;
1105     }
1106     if (!$request->getArg('verify')) {
1107         $mesg->pushContent(HTML::p(fmt("Are you sure to revert %s to version $version?", WikiLink($pagename))),
1108                            HTML::form(array('action' => $request->getPostURL(),
1109                                             'method' => 'post'),
1110                                       HiddenInputs($request->getArgs(), false, array('verify')),
1111                                       HiddenInputs(array('verify' => 1)),
1112                                       Button('submit:verify', _("Yes"), 'button'),
1113                                       HTML::Raw('&nbsp;'),
1114                                       Button('submit:cancel', _("Cancel"), 'button'))
1115                            );
1116         $rev = $page->getRevision($version);
1117         $html = HTML(HTML::fieldset($mesg), HTML::hr(), $rev->getTransformedContent());
1118         $template = Template('browse',
1119                              array('CONTENT' => $html));
1120         GeneratePage($template, $pagename, $rev);
1121         $request->checkValidators();
1122         flush();
1123         return;
1124     }
1125     $rev = $page->getRevision($version);
1126     $content = $rev->getPackedContent();
1127     $versiondata = $rev->_data;
1128     $versiondata['summary'] = sprintf(_("revert to version %d"), $version);
1129     $new = $page->save($content, $currversion + 1, $versiondata);
1130     $dbi->touch();
1131
1132     $mesg = HTML::span();
1133     $pagelink = WikiLink($pagename);
1134     $mesg->pushContent(fmt("Revert: %s", $pagelink),
1135                        fmt("- version %d saved to database as version %d",
1136                            $version, $new->getVersion()));
1137     // Force browse of current page version.
1138     $request->setArg('version', false);
1139     $template = Template('savepage', array());
1140     $template->replace('CONTENT', $new->getTransformedContent());
1141
1142     GeneratePage($template, $mesg, $new);
1143     flush();
1144 }
1145
1146 function _tryinsertInterWikiMap($content) {
1147     $goback = false;
1148     if (strpos($content, "<verbatim>")) {
1149         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
1150         $goback = true;
1151     }
1152     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
1153         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
1154         $goback = true;
1155     }
1156     $mapfile = FindFile(INTERWIKI_MAP_FILE,1);
1157     if (!$goback && !file_exists($mapfile)) {
1158         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
1159         $goback = true;
1160     }
1161
1162     if (!empty($error_html))
1163         trigger_error(_("Default InterWiki map file not loaded.")
1164                       . $error_html, E_USER_NOTICE);
1165     if ($goback)
1166         return $content;
1167
1168     // if loading from virgin setup do echo, otherwise trigger_error E_USER_NOTICE
1169     if (!isa($GLOBALS['request'], 'MockRequest'))
1170         echo sprintf(_("Loading InterWikiMap from external file %s."), $mapfile),"<br />";
1171
1172     $fd = fopen ($mapfile, "rb");
1173     $data = fread ($fd, filesize($mapfile));
1174     fclose ($fd);
1175     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
1176     return $content;
1177 }
1178
1179 function ParseSerializedPage($text, $default_pagename, $user)
1180 {
1181     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
1182         return false;
1183
1184     $pagehash = unserialize($text);
1185
1186     // Split up pagehash into four parts:
1187     //   pagename
1188     //   content
1189     //   page-level meta-data
1190     //   revision-level meta-data
1191
1192     if (!defined('FLAG_PAGE_LOCKED'))
1193         define('FLAG_PAGE_LOCKED', 1);
1194     if (!defined('FLAG_PAGE_EXTERNAL'))
1195         define('FLAG_PAGE_EXTERNAL', 1);
1196     $pageinfo = array('pagedata'    => array(),
1197                       'versiondata' => array());
1198
1199     $pagedata = &$pageinfo['pagedata'];
1200     $versiondata = &$pageinfo['versiondata'];
1201
1202     // Fill in defaults.
1203     if (empty($pagehash['pagename']))
1204         $pagehash['pagename'] = $default_pagename;
1205     if (empty($pagehash['author'])) {
1206         $pagehash['author'] = $user->getId();
1207     }
1208
1209     foreach ($pagehash as $key => $value) {
1210         switch($key) {
1211             case 'pagename':
1212             case 'version':
1213             case 'hits':
1214                 $pageinfo[$key] = $value;
1215                 break;
1216             case 'content':
1217                 $pageinfo[$key] = join("\n", $value);
1218                 break;
1219             case 'flags':
1220                 if (($value & FLAG_PAGE_LOCKED) != 0)
1221                     $pagedata['locked'] = 'yes';
1222                 if (($value & FLAG_PAGE_EXTERNAL) != 0)
1223                     $pagedata['external'] = 'yes';
1224                 break;
1225             case 'owner':
1226             case 'created':
1227                 $pagedata[$key] = $value;
1228                 break;
1229             case 'acl':
1230             case 'perm':
1231                 $pagedata['perm'] = ParseMimeifiedPerm($value);
1232                 break;
1233             case 'lastmodified':
1234                 $versiondata['mtime'] = $value;
1235                 break;
1236             case 'author':
1237             case 'author_id':
1238             case 'summary':
1239                 $versiondata[$key] = $value;
1240                 break;
1241         }
1242     }
1243     if (empty($pagehash['charset']))
1244         $pagehash['charset'] = 'utf-8';
1245     // compare to target charset
1246     if (strtolower($pagehash['charset']) != strtolower($GLOBALS['charset'])) {
1247         $pageinfo['content'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['content']);
1248         $pageinfo['pagename'] = charset_convert($params['charset'], $GLOBALS['charset'], $pageinfo['pagename']);
1249     }
1250     return $pageinfo;
1251 }
1252
1253 function SortByPageVersion ($a, $b) {
1254     return $a['version'] - $b['version'];
1255 }
1256
1257 /**
1258  * Security alert! We should not allow to import config.ini into our wiki (or from a sister wiki?)
1259  * because the sql passwords are in plaintext there. And the webserver must be able to read it.
1260  * Detected by Santtu Jarvi.
1261  */
1262 function LoadFile (&$request, $filename, $text = false, $mtime = false)
1263 {
1264     if (preg_match("/config$/", dirname($filename))             // our or other config
1265         and preg_match("/config.*\.ini/", basename($filename))) // backups and other versions also
1266     {
1267         trigger_error(sprintf("Refused to load %s", $filename), E_USER_WARNING);
1268         return;
1269     }
1270     if (!is_string($text)) {
1271         // Read the file.
1272         $stat  = stat($filename);
1273         $mtime = $stat[9];
1274         $text  = implode("", file($filename));
1275     }
1276
1277     if (! $request->getArg('start_debug')) @set_time_limit(30); // Reset watchdog
1278     else @set_time_limit(240);
1279
1280     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
1281     $basename = basename("/dummy/" . $filename);
1282
1283     if (!$mtime)
1284         $mtime = time();    // Last resort.
1285
1286     // DONE: check source - target charset for content and pagename
1287     // but only for pgsrc'ed content, not from the browser.
1288
1289     $default_pagename = rawurldecode($basename);
1290     if ( ($parts = ParseMimeifiedPages($text)) ) {
1291         if (count($parts) > 1)
1292             $overwrite = $request->getArg('overwrite');
1293         usort($parts, 'SortByPageVersion');
1294         foreach ($parts as $pageinfo) {
1295             // force overwrite
1296             if (count($parts) > 1)
1297                 $request->setArg('overwrite', 1);
1298             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
1299                                                   $filename), $basename);
1300     }
1301         if (count($parts) > 1)
1302             if ($overwrite)
1303                 $request->setArg('overwrite', $overwrite);
1304             else
1305             unset($request->_args['overwrite']);
1306     }
1307     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
1308                                                $request->getUser())) ) {
1309         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
1310                                               $filename), $basename);
1311     }
1312     else {
1313         // plain old file
1314         $user = $request->getUser();
1315
1316         $file_charset = 'utf-8';
1317         // compare to target charset
1318         if ($file_charset != strtolower($GLOBALS['charset'])) {
1319             $text = charset_convert($file_charset, $GLOBALS['charset'], $text);
1320             $default_pagename = charset_convert($file_charset, $GLOBALS['charset'], $default_pagename);
1321         }
1322
1323         // Assume plain text file.
1324         $pageinfo = array('pagename' => $default_pagename,
1325                           'pagedata' => array(),
1326                           'versiondata'
1327                           => array('author' => $user->getId()),
1328                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
1329                                                      chop($text))
1330                           );
1331         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
1332                  $basename);
1333     }
1334 }
1335
1336 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
1337     $zip = new ZipReader($zipfile);
1338     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1339     while (list ($fn, $data, $attrib) = $zip->readFile()) {
1340         // FIXME: basename("filewithnoslashes") seems to return
1341         // garbage sometimes.
1342         $fn = basename("/dummy/" . $fn);
1343         if ( ($files && !in_array($fn, $files))
1344              || ($exclude && in_array($fn, $exclude)) ) {
1345             PrintXML(HTML::p(WikiLink($fn)),
1346                      HTML::p(_("Skipping")));
1347             flush();
1348             continue;
1349         }
1350         longer_timeout($timeout);     // longer timeout per page
1351         LoadFile($request, $fn, $data, $attrib['mtime']);
1352     }
1353 }
1354
1355 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
1356     $fileset = new LimitedFileSet($dirname, $files, $exclude);
1357
1358     if (!$files and ($skiplist = $fileset->getSkippedFiles())) {
1359         PrintXML(HTML::p(HTML::strong(_("Skipping"))));
1360         $list = HTML::ul();
1361         foreach ($skiplist as $file)
1362             $list->pushContent(HTML::li(WikiLink($file)));
1363         PrintXML(HTML::p($list));
1364     }
1365
1366     // Defer HomePage loading until the end. If anything goes wrong
1367     // the pages can still be loaded again.
1368     $files = $fileset->getFiles();
1369     if (in_array(HOME_PAGE, $files)) {
1370         $files = array_diff($files, array(HOME_PAGE));
1371         $files[] = HOME_PAGE;
1372     }
1373     $timeout = (! $request->getArg('start_debug')) ? 20 : 120;
1374     foreach ($files as $file) {
1375         longer_timeout($timeout);     // longer timeout per page
1376         if (substr($file,-1,1) != '~')  // refuse to load backup files
1377             LoadFile($request, "$dirname/$file");
1378     }
1379 }
1380
1381 class LimitedFileSet extends FileSet {
1382     function LimitedFileSet($dirname, $_include, $exclude) {
1383         $this->_includefiles = $_include;
1384         $this->_exclude = $exclude;
1385         $this->_skiplist = array();
1386         parent::FileSet($dirname);
1387     }
1388
1389     function _filenameSelector($fn) {
1390         $incl = &$this->_includefiles;
1391         $excl = &$this->_exclude;
1392
1393         if ( ($incl && !in_array($fn, $incl))
1394              || ($excl && in_array($fn, $excl)) ) {
1395             $this->_skiplist[] = $fn;
1396             return false;
1397         } else {
1398             return true;
1399         }
1400     }
1401
1402     function getSkippedFiles () {
1403         return $this->_skiplist;
1404     }
1405 }
1406
1407
1408 function IsZipFile ($filename_or_fd)
1409 {
1410     // See if it looks like zip file
1411     if (is_string($filename_or_fd))
1412     {
1413         $fd    = fopen($filename_or_fd, "rb");
1414         $magic = fread($fd, 4);
1415         fclose($fd);
1416     }
1417     else
1418     {
1419         $fpos  = ftell($filename_or_fd);
1420         $magic = fread($filename_or_fd, 4);
1421         fseek($filename_or_fd, $fpos);
1422     }
1423
1424     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
1425 }
1426
1427
1428 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
1429 {
1430     // Try urlencoded filename for accented characters.
1431     if (!file_exists($file_or_dir)) {
1432         // Make sure there are slashes first to avoid confusing phps
1433         // with broken dirname or basename functions.
1434         // FIXME: windows uses \ and :
1435         if (is_integer(strpos($file_or_dir, "/"))) {
1436             $newfile = FindFile($file_or_dir, true);
1437             // Panic. urlencoded by the browser (e.g. San%20Diego => San Diego)
1438             if (!$newfile)
1439                 $file_or_dir = dirname($file_or_dir) . "/"
1440                     . rawurlencode(basename($file_or_dir));
1441         } else {
1442             // This is probably just a file.
1443             $file_or_dir = rawurlencode($file_or_dir);
1444         }
1445     }
1446
1447     $type = filetype($file_or_dir);
1448     if ($type == 'link') {
1449         // For symbolic links, use stat() to determine
1450         // the type of the underlying file.
1451         list(,,$mode) = stat($file_or_dir);
1452         $type = ($mode >> 12) & 017;
1453         if ($type == 010)
1454             $type = 'file';
1455         elseif ($type == 004)
1456             $type = 'dir';
1457     }
1458
1459     if (! $type) {
1460         $request->finish(fmt("Empty or not existing source. Unable to load: %s", $file_or_dir));
1461     }
1462     else if ($type == 'dir') {
1463         LoadDir($request, $file_or_dir, $files, $exclude);
1464     }
1465     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
1466     {
1467         $request->finish(fmt("Bad file type: %s", $type));
1468     }
1469     else if (IsZipFile($file_or_dir)) {
1470         LoadZip($request, $file_or_dir, $files, $exclude);
1471     }
1472     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
1473     {
1474         LoadFile($request, $file_or_dir);
1475     }
1476 }
1477
1478 function LoadFileOrDir (&$request)
1479 {
1480     $source = $request->getArg('source');
1481     $finder = new FileFinder;
1482     $source = $finder->slashifyPath($source);
1483     StartLoadDump($request,
1484         sprintf(_("Loading '%s'"), $source));
1485     LoadAny($request, $source);
1486     EndLoadDump($request);
1487 }
1488
1489 /**
1490  * HomePage was not found so first-time install is supposed to run.
1491  * - import all pgsrc pages.
1492  * - Todo: installer interface to edit config/config.ini settings
1493  * - Todo: ask for existing old index.php to convert to config/config.ini
1494  * - Todo: theme-specific pages:
1495  *   blog - HomePage, ADMIN_USER/Blogs
1496  */
1497 function SetupWiki (&$request)
1498 {
1499     global $GenericPages, $LANG;
1500
1501     //FIXME: This is a hack (err, "interim solution")
1502     // This is a bogo-bogo-login:  Login without
1503     // saving login information in session state.
1504     // This avoids logging in the unsuspecting
1505     // visitor as ADMIN_USER
1506     //
1507     // This really needs to be cleaned up...
1508     // (I'm working on it.)
1509     $real_user = $request->_user;
1510     if (ENABLE_USER_NEW)
1511         $request->_user = new _BogoUser(ADMIN_USER);
1512
1513     else
1514         $request->_user = new WikiUser($request, ADMIN_USER, WIKIAUTH_BOGO);
1515
1516     StartLoadDump($request, _("Loading up virgin wiki"));
1517
1518     $pgsrc = FindLocalizedFile(WIKI_PGSRC);
1519     $default_pgsrc = FindFile(DEFAULT_WIKI_PGSRC);
1520
1521     $request->setArg('overwrite', true);
1522     if ($default_pgsrc != $pgsrc) {
1523         LoadAny($request, $default_pgsrc, $GenericPages);
1524     }
1525     $request->setArg('overwrite', false);
1526     LoadAny($request, $pgsrc);
1527     $dbi =& $request->_dbi;
1528
1529     // Ensure that all mandatory pages are loaded
1530     $finder = new FileFinder;
1531
1532     if (!FUSIONFORGE) {
1533         $mandatory = explode(':','SandBox:Template/Category:Template/Talk:SpecialPages:CategoryCategory:CategoryActionPage:Help/OldTextFormattingRules:Help/TextFormattingRules:PhpWikiAdministration');
1534     } else if (WIKI_NAME == "help") {
1535         $mandatory = explode(':','SandBox:Template/Category:Template/Talk:SpecialPages:CategoryCategory:CategoryActionPage:Help/TextFormattingRules:PhpWikiAdministration');
1536     } else {
1537         $mandatory = explode(':','SandBox:Template/UserPage:Template/Category:Template/Talk:SpecialPages:CategoryCategory:CategoryActionPage:TextFormattingRules:PhpWikiAdministration');
1538     }
1539     foreach (array_merge($mandatory,
1540                          $GLOBALS['AllActionPages'],
1541                          array(constant('HOME_PAGE'))) as $f)
1542     {
1543         $page = gettext($f);
1544         $epage = urlencode($page);
1545         if (! $dbi->isWikiPage($page) ) {
1546             // translated version provided?
1547             if ($lf = FindLocalizedFile($pgsrc . $finder->_pathsep . $epage, 1)) {
1548                 LoadAny($request, $lf);
1549             } else { // load english version of required action page
1550                 LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC . $finder->_pathsep . urlencode($f)));
1551                 $page = $f;
1552             }
1553         }
1554         if (! $dbi->isWikiPage($page)) {
1555             trigger_error(sprintf("Mandatory file %s couldn't be loaded!", $page),
1556                           E_USER_WARNING);
1557         }
1558     }
1559
1560     $pagename = _("InterWikiMap");
1561     $map = $dbi->getPage($pagename);
1562     $map->set('locked', true);
1563     PrintXML(HTML::p(HTML::em(WikiLink($pagename)), HTML::strong(" locked")));
1564     EndLoadDump($request);
1565 }
1566
1567 function LoadPostFile (&$request)
1568 {
1569     $upload = $request->getUploadedFile('file');
1570
1571     if (!$upload)
1572         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
1573
1574     // Dump http headers.
1575     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
1576
1577     $fd = $upload->open();
1578     if (IsZipFile($fd))
1579         LoadZip($request, $fd, false, array(_("RecentChanges")));
1580     else
1581         LoadFile($request, $upload->getName(), $upload->getContents());
1582
1583     EndLoadDump($request);
1584 }
1585
1586 // Local Variables:
1587 // mode: php
1588 // tab-width: 8
1589 // c-basic-offset: 4
1590 // c-hanging-comment-ender-p: nil
1591 // indent-tabs-mode: nil
1592 // End:
1593 ?>