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