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