]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
don't set_time_limit with safe_mode on. to overcome warning msg.
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php rcs_id('$Id: loadsave.php,v 1.61 2002-02-23 20:27:03 rurban Exp $');
2
3 require_once("lib/ziplib.php");
4 require_once("lib/Template.php");
5
6 function StartLoadDump(&$request, $title, $html = '')
7 {
8     // FIXME: This is a hack
9     $tmpl = Template('top', array('TITLE' => $title,
10                                   'HEADER' => $title,
11                                   'CONTENT' => '%BODY%'));
12     echo ereg_replace('%BODY%.*', '', $tmpl->getExpansion($html));
13 }
14
15 function EndLoadDump(&$request)
16 {
17     // FIXME: This is a hack
18     $pagelink = WikiLink($request->getPage());
19
20     PrintXML(HTML::p(HTML::strong(_("Complete."))),
21              HTML::p(fmt("Return to %s", $pagelink)));
22     echo "</body></html>\n";
23 }
24
25
26 ////////////////////////////////////////////////////////////////
27 //
28 //  Functions for dumping.
29 //
30 ////////////////////////////////////////////////////////////////
31
32 /**
33  * For reference see:
34  * http://www.nacs.uci.edu/indiv/ehood/MIME/2045/rfc2045.html
35  * http://www.faqs.org/rfcs/rfc2045.html
36  * (RFC 1521 has been superceeded by RFC 2045 & others).
37  *
38  * Also see http://www.faqs.org/rfcs/rfc2822.html
39  */
40 function MailifyPage ($page, $nversions = 1)
41 {
42     $current = $page->getCurrentRevision();
43     $head = '';
44
45     if (STRICT_MAILABLE_PAGEDUMPS) {
46         $from = defined('SERVER_ADMIN') ? SERVER_ADMIN : 'foo@bar';
47         //This is for unix mailbox format: (not RFC (2)822)
48         // $head .= "From $from  " . CTime(time()) . "\r\n";
49         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
50         $head .= "From: $from (PhpWiki)\r\n";
51         // RFC 2822 requires only a Date: and originator (From:)
52         // field, however the obsolete standard RFC 822 also
53         // requires a destination field.
54         $head .= "To: $from (PhpWiki)\r\n";
55     }
56     $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
57     $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
58                      PHPWIKI_VERSION);
59
60     // This should just be entered by hand (or by script?)
61     // in the actual pgsrc files, since only they should have
62     // RCS ids.
63     //$head .= "X-Rcs-Id: \$Id\$\r\n";
64
65     $iter = $page->getAllRevisions();
66     $parts = array();
67     while ($revision = $iter->next()) {
68         $parts[] = MimeifyPageRevision($revision);
69         if ($nversions > 0 && count($parts) >= $nversions)
70             break;
71     }
72     if (count($parts) > 1)
73         return $head . MimeMultipart($parts);
74     assert($parts);
75     return $head . $parts[0];
76 }
77
78 /***
79  * Compute filename to used for storing contents of a wiki page.
80  *
81  * Basically we do a rawurlencode() which encodes everything except
82  * ASCII alphanumerics and '.', '-', and '_'.
83  *
84  * But we also want to encode leading dots to avoid filenames like
85  * '.', and '..'. (Also, there's no point in generating "hidden" file
86  * names, like '.foo'.)
87  *
88  * @param $pagename string Pagename.
89  * @return string Filename for page.
90  */
91 function FilenameForPage ($pagename)
92 {
93     $enc = rawurlencode($pagename);
94     return preg_replace('/^\./', '%2e', $enc);
95 }
96
97 /**
98  * The main() function which generates a zip archive of a PhpWiki.
99  *
100  * If $include_archive is false, only the current version of each page
101  * is included in the zip file; otherwise all archived versions are
102  * included as well.
103  */
104 function MakeWikiZip (&$request)
105 {
106     if ($request->getArg('include') == 'all') {
107         $zipname         = "wikidb.zip";
108         $include_archive = true;
109     }
110     else {
111         $zipname         = "wiki.zip";
112         $include_archive = false;
113     }
114
115
116
117     $zip = new ZipWriter("Created by PhpWiki", $zipname);
118
119     $dbi = $request->getDbh();
120     $pages = $dbi->getAllPages();
121     while ($page = $pages->next()) {
122         if (! get_cfg_var('safe_mode'))
123             set_time_limit(30); // Reset watchdog.
124
125         $current = $page->getCurrentRevision();
126         if ($current->getVersion() == 0)
127             continue;
128
129
130         $attrib = array('mtime'    => $current->get('mtime'),
131                         'is_ascii' => 1);
132         if ($page->get('locked'))
133             $attrib['write_protected'] = 1;
134
135         if ($include_archive)
136             $content = MailifyPage($page, 0);
137         else
138             $content = MailifyPage($page);
139
140         $zip->addRegularFile( FilenameForPage($page->getName()),
141                               $content, $attrib);
142     }
143     $zip->finish();
144 }
145
146 function DumpToDir (&$request)
147 {
148     $directory = $request->getArg('directory');
149     if (empty($directory))
150         $request->finish(_("You must specify a directory to dump to"));
151
152     // see if we can access the directory the user wants us to use
153     if (! file_exists($directory)) {
154         if (! mkdir($directory, 0755))
155             $request->finish(fmt("Cannot create directory '%s'", $directory));
156         else
157             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
158                                 $directory));
159     } else {
160         $html = HTML::p(fmt("Using directory '%s'", $directory));
161     }
162
163     StartLoadDump($request, _("Dumping Pages"), $html);
164
165     $dbi = $request->getDbh();
166     $pages = $dbi->getAllPages();
167
168     while ($page = $pages->next()) {
169
170         $filename = FilenameForPage($page->getName());
171
172         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
173
174         if($page->getName() != $filename) {
175             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
176                               " ... ");
177         }
178
179         if ($request->getArg('include') == 'all')
180             $data = MailifyPage($page, 0);
181         else
182             $data = MailifyPage($page);
183
184         if ( !($fd = fopen("$directory/$filename", "w")) ) {
185             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
186                                                "$directory/$filename")));
187             $request->finish($msg);
188         }
189
190         $num = fwrite($fd, $data, strlen($data));
191         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
192         PrintXML($msg);
193
194         flush();
195         assert($num == strlen($data));
196         fclose($fd);
197     }
198
199     EndLoadDump($request);
200 }
201
202
203 function DumpHtmlToDir (&$request)
204 {
205     $directory = $request->getArg('directory');
206     if (empty($directory))
207         $request->finish(_("You must specify a directory to dump to"));
208
209     // see if we can access the directory the user wants us to use
210     if (! file_exists($directory)) {
211         if (! mkdir($directory, 0755))
212             $request->finish(fmt("Cannot create directory '%s'", $directory));
213         else
214             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
215                                 $directory));
216     } else {
217         $html = HTML::p(fmt("Using directory '%s'", $directory));
218     }
219
220     StartLoadDump($request, _("Dumping Pages"), $html);
221
222     $dbi = $request->getDbh();
223     $pages = $dbi->getAllPages();
224
225     while ($page = $pages->next()) {
226
227         $filename = FilenameForPage($page->getName()); /* . ".html";*/
228
229         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
230
231         if($page->getName() != $filename) {
232             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
233                               " ... ");
234         }
235
236         $revision = $page->getCurrentRevision();
237
238         require_once('lib/PageType.php');
239         $transformedContent = PageType($revision);
240
241         $template = new Template('browse', $request,
242                                   array('revision' => $revision, 'CONTENT' => $transformedContent));
243
244         $data = GeneratePageasXML($template, $page->getName());
245
246         if ( !($fd = fopen("$directory/$filename", "w")) ) {
247             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
248                                                "$directory/$filename")));
249             $request->finish($msg);
250         }
251
252         $num = fwrite($fd, $data, strlen($data));
253         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
254         PrintXML($msg);
255
256         flush();
257         assert($num == strlen($data));
258         fclose($fd);
259     }
260
261     //CopyImageFiles() will go here;
262
263     EndLoadDump($request);
264 }
265
266
267 ////////////////////////////////////////////////////////////////
268 //
269 //  Functions for restoring.
270 //
271 ////////////////////////////////////////////////////////////////
272
273 function SavePage (&$request, $pageinfo, $source, $filename)
274 {
275     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
276     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
277
278     if (empty($pageinfo['pagename'])) {
279         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
280         return;
281     }
282
283     if (empty($versiondata['author_id']))
284         $versiondata['author_id'] = $versiondata['author'];
285
286     $pagename = $pageinfo['pagename'];
287     $content  = $pageinfo['content'];
288
289     if ($pagename ==_("InterWikiMap"))
290         $content = _tryinsertInterWikiMap($content);
291
292     $dbi = $request->getDbh();
293     $page = $dbi->getPage($pagename);
294
295     foreach ($pagedata as $key => $value) {
296         if (!empty($value))
297             $page->set($key, $value);
298     }
299
300     $mesg = HTML::dd();
301     $skip = false;
302     if ($source)
303         $mesg->pushContent(' ', fmt("from %s", $source));
304
305
306     $current = $page->getCurrentRevision();
307     if ($current->getVersion() == 0) {
308         $mesg->pushContent(' ', _("new page"));
309         $isnew = true;
310     }
311     else {
312         if ($current->getPackedContent() == $content
313             && $current->get('author') == $versiondata['author']) {
314             $mesg->pushContent(' ',
315                                fmt("is identical to current version %d - skipped",
316                                    $current->getVersion()));
317             $skip = true;
318         }
319         $isnew = false;
320     }
321
322     if (! $skip) {
323         $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
324                                      $versiondata,
325                                      ExtractWikiPageLinks($content));
326
327         $mesg->pushContent(' ', fmt("- saved to database as version %d",
328                                     $new->getVersion()));
329     }
330
331     PrintXML(HTML::dt(WikiLink($pagename)), $mesg);
332     flush();
333 }
334
335 function _tryinsertInterWikiMap($content) {
336     $goback = false;
337     if (strpos($content, "<verbatim>")) {
338         //$error_html = " The newly loaded pgsrc already contains a verbatim block.";
339         $goback = true;
340     }
341     if (!$goback && !defined('INTERWIKI_MAP_FILE')) {
342         $error_html = sprintf(" "._("%s: not defined"), "INTERWIKI_MAP_FILE");
343         $goback = true;
344     }
345     if (!$goback && !file_exists(INTERWIKI_MAP_FILE)) {
346         $error_html = sprintf(" "._("%s: file not found"), INTERWIKI_MAP_FILE);
347         $goback = true;
348     }
349
350     if (!empty($error_html))
351         trigger_error(_("Default InterWiki map file not loaded.")
352                       . $error_html, E_USER_NOTICE);
353
354     if ($goback)
355         return $content;
356
357     $filename = INTERWIKI_MAP_FILE;
358     trigger_error(sprintf(_("Loading InterWikiMap from external file %s."),
359                           $filename), E_USER_NOTICE);
360
361     $fd = fopen ($filename, "rb");
362     $data = fread ($fd, filesize($filename));
363     fclose ($fd);
364     $content = $content . "\n<verbatim>\n$data</verbatim>\n";
365     return $content;
366 }
367
368 function ParseSerializedPage($text, $default_pagename, $user)
369 {
370     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
371         return false;
372     
373     $pagehash = unserialize($text);
374     
375     // Split up pagehash into four parts:
376     //   pagename
377     //   content
378     //   page-level meta-data
379     //   revision-level meta-data
380     
381     if (!defined('FLAG_PAGE_LOCKED'))
382         define('FLAG_PAGE_LOCKED', 1);
383     $pageinfo = array('pagedata'    => array(),
384                       'versiondata' => array());
385     
386     $pagedata = &$pageinfo['pagedata'];
387     $versiondata = &$pageinfo['versiondata'];
388     
389     // Fill in defaults.
390     if (empty($pagehash['pagename']))
391         $pagehash['pagename'] = $default_pagename;
392     if (empty($pagehash['author'])) {
393         $pagehash['author'] = $user->getId();
394     }
395     
396     foreach ($pagehash as $key => $value) {
397         switch($key) {
398             case 'pagename':
399             case 'version':
400             case 'hits':
401                 $pageinfo[$key] = $value;
402                 break;
403             case 'content':
404                 $pageinfo[$key] = join("\n", $value);
405                 break;
406             case 'flags':
407                 if (($value & FLAG_PAGE_LOCKED) != 0)
408                     $pagedata['locked'] = 'yes';
409                 break;
410             case 'created':
411                 $pagedata[$key] = $value;
412                 break;
413             case 'lastmodified':
414                 $versiondata['mtime'] = $value;
415                 break;
416             case 'author':
417             case 'author_id':
418             case 'summary':
419                 $versiondata[$key] = $value;
420                 break;
421         }
422     }
423     return $pageinfo;
424 }
425
426 function SortByPageVersion ($a, $b) {
427     return $a['version'] - $b['version'];
428 }
429
430 function LoadFile (&$request, $filename, $text = false, $mtime = false)
431 {
432     if (!is_string($text)) {
433         // Read the file.
434         $stat  = stat($filename);
435         $mtime = $stat[9];
436         $text  = implode("", file($filename));
437     }
438
439     if (! get_cfg_var('safe_mode'))
440         set_time_limit(30);     // Reset watchdog.
441
442     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
443     $basename = basename("/dummy/" . $filename);
444
445     if (!$mtime)
446         $mtime = time();        // Last resort.
447
448     $default_pagename = rawurldecode($basename);
449
450     if ( ($parts = ParseMimeifiedPages($text)) ) {
451         usort($parts, 'SortByPageVersion');
452         foreach ($parts as $pageinfo)
453             SavePage($request, $pageinfo, sprintf(_("MIME file %s"),
454                                                   $filename), $basename);
455     }
456     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
457                                                $request->getUser())) ) {
458         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"),
459                                               $filename), $basename);
460     }
461     else {
462         $user = $request->getUser();
463
464         // Assume plain text file.
465         $pageinfo = array('pagename' => $default_pagename,
466                           'pagedata' => array(),
467                           'versiondata'
468                           => array('author' => $user->getId()),
469                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
470                                                      chop($text))
471                           );
472         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
473                  $basename);
474     }
475 }
476
477 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
478     $zip = new ZipReader($zipfile);
479     while (list ($fn, $data, $attrib) = $zip->readFile()) {
480         // FIXME: basename("filewithnoslashes") seems to return
481         // garbage sometimes.
482         $fn = basename("/dummy/" . $fn);
483         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
484
485             PrintXML(HTML::dt(WikiLink($fn)),
486                      HTML::dd(_("Skipping")));
487             continue;
488         }
489
490         LoadFile($request, $fn, $data, $attrib['mtime']);
491     }
492 }
493
494 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
495     $fileset = new LimitedFileSet($dirname, $files, $exclude);
496
497     if (($skiplist = $fileset->getSkippedFiles())) {
498         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
499         $list = HTML::ul();
500         foreach ($skiplist as $file)
501             $list->pushContent(HTML::li(WikiLink($file)));
502         PrintXML(HTML::dd($list));
503     }
504
505     // Defer HomePage loading until the end. If anything goes wrong
506     // the pages can still be loaded again.
507     $files = $fileset->getFiles();
508     if (in_array(HomePage, $files)) {
509         $files = array_diff($files, array(HomePage));
510         $files[] = HomePage;
511     }
512     foreach ($files as $file)
513         LoadFile($request, "$dirname/$file");
514 }
515
516 class LimitedFileSet extends FileSet {
517     function LimitedFileSet($dirname, $_include, $exclude) {
518         $this->_includefiles = $_include;
519         $this->_exclude = $exclude;
520         $this->_skiplist = array();
521         parent::FileSet($dirname);
522     }
523
524     function _filenameSelector($fn) {
525         $incl = &$this->_include;
526         $excl = &$this->_exclude;
527
528         if (($incl && !in_array($fn, $incl)) || ($excl && in_array($fn, $excl))) {
529             $this->_skiplist[] = $fn;
530             return false;
531         } else {
532             return true;
533         }
534     }
535
536     function getSkippedFiles () {
537         return $this->_skiplist;
538     }
539 }
540
541
542 function IsZipFile ($filename_or_fd)
543 {
544     // See if it looks like zip file
545     if (is_string($filename_or_fd))
546     {
547         $fd    = fopen($filename_or_fd, "rb");
548         $magic = fread($fd, 4);
549         fclose($fd);
550     }
551     else
552     {
553         $fpos  = ftell($filename_or_fd);
554         $magic = fread($filename_or_fd, 4);
555         fseek($filename_or_fd, $fpos);
556     }
557
558     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
559 }
560
561
562 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
563 {
564     // Try urlencoded filename for accented characters.
565     if (!file_exists($file_or_dir)) {
566         // Make sure there are slashes first to avoid confusing phps
567         // with broken dirname or basename functions.
568         // FIXME: windows uses \ and :
569         if (is_integer(strpos($file_or_dir, "/"))) {
570             $file_or_dir = dirname($file_or_dir) ."/".
571             urlencode(basename($file_or_dir));
572         } else {
573             // This is probably just a file.
574             $file_or_dir = urlencode($file_or_dir);
575         }
576     }
577
578     $type = filetype($file_or_dir);
579     if ($type == 'link') {
580         // For symbolic links, use stat() to determine
581         // the type of the underlying file.
582         list(,,$mode) = stat($file_or_dir);
583         $type = ($mode >> 12) & 017;
584         if ($type == 010)
585             $type = 'file';
586         elseif ($type == 004)
587             $type = 'dir';
588     }
589
590     if ($type == 'dir') {
591         LoadDir($request, $file_or_dir, $files, $exclude);
592     }
593     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
594     {
595         $request->finish(fmt("Bad file type: %s", $type));
596     }
597     else if (IsZipFile($file_or_dir)) {
598         LoadZip($request, $file_or_dir, $files, $exclude);
599     }
600     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
601     {
602         LoadFile($request, $file_or_dir);
603     }
604 }
605
606 function LoadFileOrDir (&$request)
607 {
608     $source = $request->getArg('source');
609     StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
610     echo "<dl>\n";
611     LoadAny($request, $source);
612     echo "</dl>\n";
613     EndLoadDump($request);
614 }
615
616 function SetupWiki (&$request)
617 {
618     global $GenericPages, $LANG;
619
620
621     //FIXME: This is a hack (err, "interim solution")
622     // This is a bogo-bogo-login:  Login without
623     // saving login information in session state.
624     // This avoids logging in the unsuspecting
625     // visitor as "The PhpWiki programming team".
626     //
627     // This really needs to be cleaned up...
628     // (I'm working on it.)
629     $real_user = $request->_user;
630     $request->_user = new WikiUser(_("The PhpWiki programming team"),
631                                    WIKIAUTH_BOGO);
632
633     StartLoadDump($request, _("Loading up virgin wiki"));
634     echo "<dl>\n";
635
636     LoadAny($request, FindLocalizedFile(WIKI_PGSRC));
637     if ($LANG != "C")
638         LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
639                 $GenericPages);
640
641     echo "</dl>\n";
642     EndLoadDump($request);
643 }
644
645 function LoadPostFile (&$request)
646 {
647     $upload = $request->getUploadedFile('file');
648
649     if (!$upload)
650         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
651
652
653     // Dump http headers.
654     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
655     echo "<dl>\n";
656
657     $fd = $upload->open();
658     if (IsZipFile($fd))
659         LoadZip($request, $fd, false, array(_("RecentChanges")));
660     else
661         LoadFile($request, $upload->getName(), $upload->getContents());
662
663     echo "</dl>\n";
664     EndLoadDump($request);
665 }
666
667 // For emacs users
668 // Local Variables:
669 // mode: php
670 // tab-width: 8
671 // c-basic-offset: 4
672 // c-hanging-comment-ender-p: nil
673 // indent-tabs-mode: nil
674 // End:
675 ?>