]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
Bugfix: allow any "Category:Word" instead of just "Category:Category".
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php rcs_id('$Id: loadsave.php,v 1.52 2002-01-30 23:41:54 dairiki 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         set_time_limit(30);     // Reset watchdog.
123         
124         $current = $page->getCurrentRevision();
125         if ($current->getVersion() == 0)
126             continue;
127         
128         
129         $attrib = array('mtime'    => $current->get('mtime'),
130                         'is_ascii' => 1);
131         if ($page->get('locked'))
132             $attrib['write_protected'] = 1;
133         
134         if ($include_archive)
135             $content = MailifyPage($page, 0);
136         else
137             $content = MailifyPage($page);
138         
139         $zip->addRegularFile( FilenameForPage($page->getName()),
140                               $content, $attrib);
141     }
142     $zip->finish();
143 }
144
145 function DumpToDir (&$request) 
146 {
147     $directory = $request->getArg('directory');
148     if (empty($directory))
149         $request->finish(_("You must specify a directory to dump to"));
150     
151     // see if we can access the directory the user wants us to use
152     if (! file_exists($directory)) {
153         if (! mkdir($directory, 0755))
154             $request->finish(fmt("Cannot create directory '%s'", $directory));
155         else
156             $html = HTML::p(fmt("Created directory '%s' for the page dump...",
157                                 $directory));
158     } else {
159         $html = HTML::p(fmt("Using directory '%s'", $directory));
160     }
161
162     StartLoadDump($request, _("Dumping Pages"), $html);
163
164     $dbi = $request->getDbh();
165     $pages = $dbi->getAllPages();
166     
167     while ($page = $pages->next()) {
168         
169         $filename = FilenameForPage($page->getName());
170         
171         $msg = HTML(HTML::br(), $page->getName(), ' ... ');
172         
173         if($page->getName() != $filename) {
174             $msg->pushContent(HTML::small(fmt("saved as %s", $filename)),
175                               " ... ");
176         }
177         
178         if ($request->getArg('include') == 'all')
179             $data = MailifyPage($page, 0);
180         else
181             $data = MailifyPage($page);
182
183         if ( !($fd = fopen("$directory/$filename", "w")) ) {
184             $msg->pushContent(HTML::strong(fmt("couldn't open file '%s' for writing",
185                                                "$directory/$filename")));
186             $request->finish($msg);
187         }
188         
189         $num = fwrite($fd, $data, strlen($data));
190         $msg->pushContent(HTML::small(fmt("%s bytes written", $num)));
191         PrintXML($msg);
192         
193         flush();
194         assert($num == strlen($data));
195         fclose($fd);
196     }
197     
198     EndLoadDump($request);
199 }
200
201 ////////////////////////////////////////////////////////////////
202 //
203 //  Functions for restoring.
204 //
205 ////////////////////////////////////////////////////////////////
206
207 function SavePage (&$request, $pageinfo, $source, $filename)
208 {
209     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
210     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
211     
212     if (empty($pageinfo['pagename'])) {
213         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
214         return;
215     }
216     
217     if (empty($versiondata['author_id']))
218         $versiondata['author_id'] = $versiondata['author'];
219     
220     $pagename = $pageinfo['pagename'];
221     $content  = $pageinfo['content'];
222
223     $dbi = $request->getDbh();
224     $page = $dbi->getPage($pagename);
225     
226     foreach ($pagedata as $key => $value) {
227         if (!empty($value))
228             $page->set($key, $value);
229     }
230     
231     $mesg = HTML::dd();
232     $skip = false;
233     if ($source)
234         $mesg->pushContent(' ', fmt("from %s", $source));
235     
236
237     $current = $page->getCurrentRevision();
238     if ($current->getVersion() == 0) {
239         $mesg->pushContent(' ', _("new page"));
240         $isnew = true;
241     }
242     else {
243         if ($current->getPackedContent() == $content
244             && $current->get('author') == $versiondata['author']) {
245             $mesg->pushContent(' ',
246                                fmt("is identical to current version %d - skipped",
247                                    $current->getVersion()));
248             $skip = true;
249         }
250         $isnew = false;
251     }
252     
253     if (! $skip) {
254         $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
255                                      $versiondata,
256                                      ExtractWikiPageLinks($content));
257         
258         $mesg->pushContent(' ', fmt("- saved to database as version %d",
259                                     $new->getVersion()));
260     }
261
262     PrintXML(HTML::dt(WikiLink($pagename)), $mesg);
263     flush();
264 }
265
266 function ParseSerializedPage($text, $default_pagename, $user)
267 {
268     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
269         return false;
270     
271     $pagehash = unserialize($text);
272     
273     // Split up pagehash into four parts:
274     //   pagename
275     //   content
276     //   page-level meta-data
277     //   revision-level meta-data
278     
279     if (!defined('FLAG_PAGE_LOCKED'))
280         define('FLAG_PAGE_LOCKED', 1);
281     $pageinfo = array('pagedata'    => array(),
282                       'versiondata' => array());
283     
284     $pagedata = &$pageinfo['pagedata'];
285     $versiondata = &$pageinfo['versiondata'];
286     
287     // Fill in defaults.
288     if (empty($pagehash['pagename']))
289         $pagehash['pagename'] = $default_pagename;
290     if (empty($pagehash['author'])) {
291         $pagehash['author'] = $user->getId();
292     }
293     
294     foreach ($pagehash as $key => $value) {
295         switch($key) {
296         case 'pagename':
297         case 'version':
298             $pageinfo[$key] = $value;
299             break;
300         case 'content':
301             $pageinfo[$key] = join("\n", $value);
302         case 'flags':
303             if (($value & FLAG_PAGE_LOCKED) != 0)
304                 $pagedata['locked'] = 'yes';
305             break;
306         case 'created':
307             $pagedata[$key] = $value;
308             break;
309         case 'lastmodified':
310             $versiondata['mtime'] = $value;
311             break;
312         case 'author':
313             $versiondata[$key] = $value;
314             break;
315         }
316     }
317     return $pageinfo;
318 }
319  
320 function SortByPageVersion ($a, $b) {
321     return $a['version'] - $b['version'];
322 }
323
324 function LoadFile (&$request, $filename, $text = false, $mtime = false)
325 {
326     if (!is_string($text)) {
327         // Read the file.
328         $stat  = stat($filename);
329         $mtime = $stat[9];
330         $text  = implode("", file($filename));
331     }
332    
333     set_time_limit(30); // Reset watchdog.
334     
335     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
336     $basename = basename("/dummy/" . $filename);
337    
338     if (!$mtime)
339         $mtime = time();        // Last resort.
340     
341     $default_pagename = rawurldecode($basename);
342     
343     if ( ($parts = ParseMimeifiedPages($text)) ) {
344         usort($parts, 'SortByPageVersion');
345         foreach ($parts as $pageinfo)
346             SavePage($request, $pageinfo, sprintf(_("MIME file %s"), 
347                                                   $filename), $basename);
348     }
349     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename,
350                                                $request->getUser())) ) {
351         SavePage($request, $pageinfo, sprintf(_("Serialized file %s"), 
352                                               $filename), $basename);
353     }
354     else {
355         $user = $request->getUser();
356         
357         // Assume plain text file.
358         $pageinfo = array('pagename' => $default_pagename,
359                           'pagedata' => array(),
360                           'versiondata'
361                           => array('author' => $user->getId()),
362                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
363                                                      chop($text))
364                           );
365         SavePage($request, $pageinfo, sprintf(_("plain file %s"), $filename),
366                  $basename);
367     }
368 }
369
370 function LoadZip (&$request, $zipfile, $files = false, $exclude = false) {
371     $zip = new ZipReader($zipfile);
372     while (list ($fn, $data, $attrib) = $zip->readFile()) {
373         // FIXME: basename("filewithnoslashes") seems to return
374         // garbage sometimes.
375         $fn = basename("/dummy/" . $fn);
376         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
377
378             PrintXML(HTML::dt(WikiLink($fn)),
379                      HTML::dd(_("Skipping")));
380             continue;
381         }
382        
383         LoadFile($request, $fn, $data, $attrib['mtime']);
384     }
385 }
386
387 function LoadDir (&$request, $dirname, $files = false, $exclude = false) {
388     $fileset = new LimitedFileSet($dirname, $files, $exclude);
389
390     if (($skiplist = $fileset->getSkippedFiles())) {
391         PrintXML(HTML::dt(HTML::strong(_("Skipping"))));
392         $list = HTML::ul();
393         foreach ($skiplist as $file)
394             $list->pushContent(HTML::li(WikiLink($file)));
395         PrintXML(HTML::dd($list));
396     }
397
398     // Defer HomePage loading until the end. If anything goes wrong
399     // the pages can still be loaded again.
400     $files = $fileset->getFiles();
401     if ($home = array_search(HomePage, $files)) {
402         $files[$home]='';
403         array_push($files, HomePage);
404     }
405     foreach ($files as $file)
406         LoadFile($request, "$dirname/$file");
407 }
408
409 class LimitedFileSet extends FileSet {
410     function LimitedFileSet($dirname, $_include, $exclude) {
411         $this->_includefiles = $_include;
412         $this->_exclude = $exclude;
413         $this->_skiplist = array();
414         parent::FileSet($dirname);
415     }
416
417     function _filenameSelector($fn) {
418         $incl = &$this->_include;
419         $excl = &$this->_exclude;
420
421         if (($incl && !in_array($fn, $incl)) || ($excl && in_array($fn, $excl))) {
422             $this->_skiplist[] = $fn;
423             return false;
424         } else {
425             return true;
426         }
427     }
428
429     function getSkippedFiles () {
430         return $this->_skiplist;
431     }
432 }
433
434
435 function IsZipFile ($filename_or_fd)
436 {
437     // See if it looks like zip file
438     if (is_string($filename_or_fd))
439         {
440             $fd    = fopen($filename_or_fd, "rb");
441             $magic = fread($fd, 4);
442             fclose($fd);
443         }
444     else
445         {
446             $fpos  = ftell($filename_or_fd);
447             $magic = fread($filename_or_fd, 4);
448             fseek($filename_or_fd, $fpos);
449         }
450     
451     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
452 }
453
454
455 function LoadAny (&$request, $file_or_dir, $files = false, $exclude = false)
456 {
457     // Try urlencoded filename for accented characters.
458     if (!file_exists($file_or_dir)) {
459         // Make sure there are slashes first to avoid confusing phps
460         // with broken dirname or basename functions.
461         // FIXME: windows uses \ and :
462         if (is_integer(strpos($file_or_dir, "/"))) {
463             $file_or_dir = dirname($file_or_dir) ."/".
464                            urlencode(basename($file_or_dir));
465         } else {
466             // This is probably just a file.
467             $file_or_dir = urlencode($file_or_dir);
468         }
469     }
470
471     $type = filetype($file_or_dir);
472     if ($type == 'link') {
473         // For symbolic links, use stat() to determine
474         // the type of the underlying file.
475         list(,,$mode) = stat($file_or_dir);
476         $type = ($mode >> 12) & 017;
477         if ($type == 010)
478             $type = 'file';
479         elseif ($type == 004)
480             $type = 'dir';
481     }
482     
483     if ($type == 'dir') {
484         LoadDir($request, $file_or_dir, $files, $exclude);
485     }
486     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
487     {
488         $request->finish(fmt("Bad file type: %s", $type));
489     }
490     else if (IsZipFile($file_or_dir)) {
491         LoadZip($request, $file_or_dir, $files, $exclude);
492     }
493     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
494     {
495         LoadFile($request, $file_or_dir);
496     }
497 }
498
499 function LoadFileOrDir (&$request)
500 {
501     $source = $request->getArg('source');
502     StartLoadDump($request, sprintf(_("Loading '%s'"), $source));
503     echo "<dl>\n";
504     LoadAny($request, $source);
505     echo "</dl>\n";
506     EndLoadDump($request);
507 }
508
509 function SetupWiki (&$request)
510 {
511     global $GenericPages, $LANG;
512     
513     
514     //FIXME: This is a hack (err, "interim solution")
515     // This is a bogo-bogo-login:  Login without
516     // saving login information in session state.
517     // This avoids logging in the unsuspecting
518     // visitor as "The PhpWiki programming team".
519     //
520     // This really needs to be cleaned up...
521     // (I'm working on it.)
522     $real_user = $request->_user;
523     $request->_user = new WikiUser(_("The PhpWiki programming team"),
524                                    WIKIAUTH_BOGO);
525     
526     StartLoadDump($request, _("Loading up virgin wiki"));
527     echo "<dl>\n";
528     
529     LoadAny($request, FindLocalizedFile(WIKI_PGSRC));
530     if ($LANG != "C")
531         LoadAny($request, FindFile(DEFAULT_WIKI_PGSRC),
532                 $GenericPages);
533     
534     echo "</dl>\n";
535     EndLoadDump($request);
536 }
537
538 function LoadPostFile (&$request)
539 {
540     $upload = $request->getUploadedFile('file');
541     
542     if (!$upload)
543         $request->finish(_("No uploaded file to upload?")); // FIXME: more concise message
544
545     
546     // Dump http headers.
547     StartLoadDump($request, sprintf(_("Uploading %s"), $upload->getName()));
548     echo "<dl>\n";
549     
550     $fd = $upload->open();
551     if (IsZipFile($fd))
552         LoadZip($request, $fd, false, array(_("RecentChanges")));
553     else
554         LoadFile($request, $upload->getName(), $upload->getContents());
555     
556     echo "</dl>\n";
557     EndLoadDump($request);
558 }
559
560 // For emacs users
561 // Local Variables:
562 // mode: php
563 // tab-width: 8
564 // c-basic-offset: 4
565 // c-hanging-comment-ender-p: nil
566 // indent-tabs-mode: nil
567 // End:   
568 ?>