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