]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/loadsave.php
Fix to handle loads from symbolic links.
[SourceForge/phpwiki.git] / lib / loadsave.php
1 <?php
2 rcs_id('$Id: loadsave.php,v 1.30 2002-01-22 17:01:39 dairiki Exp $');
3 require_once("lib/ziplib.php");
4 require_once("lib/Template.php");
5
6 function StartLoadDump($title, $html = '')
7 {
8     // FIXME: This is a hack
9     echo ereg_replace('</body>.*', '',
10                       GeneratePage('MESSAGE', $html, $title, 0));
11 }
12
13 function EndLoadDump()
14 {
15     // FIXME: This is a hack
16     global $request;
17     $pagelink = LinkExistingWikiWord($request->getArg('pagename'));
18     
19     PrintXML(array(HTML::p(HTML::strong(_("Complete."))),
20                    HTML::p(fmt("Return to %s", $pagelink))));
21     echo "</body></html>\n";
22 }
23
24
25 ////////////////////////////////////////////////////////////////
26 //
27 //  Functions for dumping.
28 //
29 ////////////////////////////////////////////////////////////////
30
31 function MailifyPage ($page, $nversions = 1)
32 {
33     global $SERVER_ADMIN, $pagedump_format;
34     
35     $current = $page->getCurrentRevision();
36     $from = isset($SERVER_ADMIN) ? $SERVER_ADMIN : 'foo@bar';
37     $head = "";
38     if ($pagedump_format == 'quoted-printable') {
39         $head = "From $from  " . CTime(time()) . "\r\n";
40         $head .= "Subject: " . rawurlencode($page->getName()) . "\r\n";
41         $head .= "From: $from (PhpWiki)\r\n";
42         $head .= "Date: " . Rfc2822DateTime($current->get('mtime')) . "\r\n";
43         $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
44                          PHPWIKI_VERSION);
45     } else {
46         $head .= sprintf("Mime-Version: 1.0 (Produced by PhpWiki %s)\r\n",
47                          PHPWIKI_VERSION."+carsten's-binary-hack");
48     }
49     $head .= "X-RCS_ID: $" ."Id" ."$" ."\r\n";
50     
51     $iter = $page->getAllRevisions();
52     $parts = array();
53     while ($revision = $iter->next()) {
54         $parts[] = MimeifyPageRevision($revision);
55         if ($nversions > 0 && count($parts) >= $nversions)
56             break;
57     }
58     if (count($parts) > 1)
59         return $head . MimeMultipart($parts);
60     assert($parts);
61     return $head . $parts[0];
62 }
63
64 /***
65  * Compute filename to used for storing contents of a wiki page.
66  *
67  * Basically we do a rawurlencode() which encodes everything except
68  * ASCII alphanumerics and '.', '-', and '_'.
69  *
70  * But we also want to encode leading dots to avoid filenames like
71  * '.', and '..'. (Also, there's no point in generating "hidden" file
72  * names, like '.foo'.)
73  *
74  * @param $pagename string Pagename.
75  * @return string Filename for page.
76  */
77 function FilenameForPage ($pagename)
78 {
79     $enc = rawurlencode($pagename);
80     return preg_replace('/^\./', '%2e', $enc);
81 }
82
83 /**
84  * The main() function which generates a zip archive of a PhpWiki.
85  *
86  * If $include_archive is false, only the current version of each page
87  * is included in the zip file; otherwise all archived versions are
88  * included as well.
89  */
90 function MakeWikiZip ($dbi, $request)
91 {
92     if ($request->getArg('include') == 'all') {
93         $zipname         = "wikidb.zip";
94         $include_archive = true;
95     }
96     else {
97         $zipname         = "wiki.zip";
98         $include_archive = false;
99     }
100     
101     
102
103     $zip = new ZipWriter("Created by PhpWiki", $zipname);
104
105     $pages = $dbi->getAllPages();
106     while ($page = $pages->next()) {
107         set_time_limit(30);     // Reset watchdog.
108         
109         $current = $page->getCurrentRevision();
110         if ($current->getVersion() == 0)
111             continue;
112         
113         
114         $attrib = array('mtime'    => $current->get('mtime'),
115                         'is_ascii' => 1);
116         if ($page->get('locked'))
117             $attrib['write_protected'] = 1;
118         
119         if ($include_archive)
120             $content = MailifyPage($page, 0);
121         else
122             $content = MailifyPage($page);
123         
124         $zip->addRegularFile( FilenameForPage($page->getName()),
125                               $content, $attrib);
126     }
127     $zip->finish();
128 }
129
130 function DumpToDir ($dbi, $request) 
131 {
132     global $pagedump_format;
133     $directory = $request->getArg('directory');
134     if (empty($directory))
135         ExitWiki(_("You must specify a directory to dump to"));
136     
137     // see if we can access the directory the user wants us to use
138     if (! file_exists($directory)) {
139         if (! mkdir($directory, 0755))
140             ExitWiki( sprintf(_("Cannot create directory '%s'"), 
141                               $directory) . "<br />\n");
142         else
143             $html = sprintf(_("Created directory '%s' for the page dump..."),
144                             $directory) . "<br />\n";
145     } else {
146         $html = sprintf(_("Using directory '%s'"),$directory) . "<br />\n";
147     }
148
149     $html .= "MIME " . $pagedump_format . "<br />\n";
150     StartLoadDump( _("Dumping Pages"), $html);
151     
152     $pages = $dbi->getAllPages();
153     
154     while ($page = $pages->next()) {
155         
156         $filename = FilenameForPage($page->getName());
157         
158         $msg = array(HTML::br(), $page->getName(), ' ... ');
159         
160         if($page->getName() != $filename) {
161             $msg[] = HTTP::small(fmt("saved as %s", $filename));
162             $msg[] = " ... ";
163         }
164         
165         $data = MailifyPage($page);
166         
167         if ( !($fd = fopen("$directory/$filename", "w")) ) {
168             $msg[] = HTML::strong(fmt("couldn't open file '%s' for writing",
169                                       "$directory/$filename"));
170             ExitWiki($msg);
171         }
172         
173         $num = fwrite($fd, $data, strlen($data));
174         $msg[] = HTML::small(fmt("%s bytes written", $num));
175         PrintXML($msg);
176         
177         flush();
178         assert($num == strlen($data));
179         fclose($fd);
180     }
181     
182     EndLoadDump();
183 }
184
185 ////////////////////////////////////////////////////////////////
186 //
187 //  Functions for restoring.
188 //
189 ////////////////////////////////////////////////////////////////
190
191 function SavePage ($dbi, $pageinfo, $source, $filename)
192 {
193     $pagedata    = $pageinfo['pagedata'];    // Page level meta-data.
194     $versiondata = $pageinfo['versiondata']; // Revision level meta-data.
195     
196     if (empty($pageinfo['pagename'])) {
197         PrintXML(HTML::dt(HTML::strong(_("Empty pagename!"))));
198         return;
199     }
200     
201     if (empty($versiondata['author_id']))
202         $versiondata['author_id'] = $versiondata['author'];
203     
204     $pagename = $pageinfo['pagename'];
205     $content  = $pageinfo['content'];
206     
207     $page = $dbi->getPage($pagename);
208     
209     foreach ($pagedata as $key => $value) {
210         if (!empty($value))
211             $page->set($key, $value);
212     }
213     
214     $mesg = HTML::dd();
215     $skip = false;
216     if ($source)
217         $mesg->pushContent(' ', fmt("from %s", $source));
218     
219
220     $current = $page->getCurrentRevision();
221     if ($current->getVersion() == 0) {
222         $mesg->pushContent(' ', _("new page"));
223         $isnew = true;
224     }
225     else {
226         if ($current->getPackedContent() == $content
227             && $current->get('author') == $versiondata['author']) {
228             $mesg->pushContent(' ',
229                                fmt("is identical to current version %d - skipped",
230                                    $current->getVersion()));
231             $skip = true;
232         }
233         $isnew = false;
234     }
235     
236     if (! $skip) {
237         $new = $page->createRevision(WIKIDB_FORCE_CREATE, $content,
238                                      $versiondata,
239                                      ExtractWikiPageLinks($content));
240         
241         $mesg->pushContent(' ', fmt("- saved to database as version %d",
242                                     $new->getVersion()));
243     }
244
245     $pagelink = LinkExistingWikiWord($pagename);
246     
247     PrintXML(array(HTML::dt($pagelink), $mesg));
248     flush();
249 }
250
251 function ParseSerializedPage($text, $default_pagename)
252 {
253     if (!preg_match('/^a:\d+:{[si]:\d+/', $text))
254         return false;
255     
256     $pagehash = unserialize($text);
257     
258     // Split up pagehash into four parts:
259     //   pagename
260     //   content
261     //   page-level meta-data
262     //   revision-level meta-data
263     
264     if (!defined('FLAG_PAGE_LOCKED'))
265         define('FLAG_PAGE_LOCKED', 1);
266     $pageinfo = array('pagedata'    => array(),
267                       'versiondata' => array());
268     
269     $pagedata = &$pageinfo['pagedata'];
270     $versiondata = &$pageinfo['versiondata'];
271     
272     // Fill in defaults.
273     if (empty($pagehash['pagename']))
274         $pagehash['pagename'] = $default_pagename;
275     if (empty($pagehash['author']))
276         $pagehash['author'] = $GLOBALS['user']->getId(); // FIXME:?
277     
278     
279     foreach ($pagehash as $key => $value) {
280         switch($key) {
281         case 'pagename':
282         case 'version':
283             $pageinfo[$key] = $value;
284             break;
285         case 'content':
286             $pageinfo[$key] = join("\n", $value);
287         case 'flags':
288             if (($value & FLAG_PAGE_LOCKED) != 0)
289                 $pagedata['locked'] = 'yes';
290             break;
291         case 'created':
292             $pagedata[$key] = $value;
293             break;
294         case 'lastmodified':
295             $versiondata['mtime'] = $value;
296             break;
297         case 'author':
298             $versiondata[$key] = $value;
299             break;
300         }
301     }
302     return $pageinfo;
303 }
304  
305 function SortByPageVersion ($a, $b) {
306     return $a['version'] - $b['version'];
307 }
308
309 function LoadFile ($dbi, $filename, $text = false, $mtime = false)
310 {
311     if (!is_string($text)) {
312         // Read the file.
313         $stat  = stat($filename);
314         $mtime = $stat[9];
315         $text  = implode("", file($filename));
316     }
317    
318     set_time_limit(30); // Reset watchdog.
319     
320     // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
321     $basename = basename("/dummy/" . $filename);
322    
323     if (!$mtime)
324         $mtime = time();        // Last resort.
325     
326     $default_pagename = rawurldecode($basename);
327     
328     if ( ($parts = ParseMimeifiedPages($text)) ) {
329         usort($parts, 'SortByPageVersion');
330         foreach ($parts as $pageinfo)
331             SavePage($dbi, $pageinfo, sprintf(_("MIME file %s"),$filename),
332                      $basename);
333     }
334     else if ( ($pageinfo = ParseSerializedPage($text, $default_pagename)) ) {
335         SavePage($dbi, $pageinfo, sprintf(_("Serialized file %s"),$filename),
336                  $basename);
337     }
338     else {
339         // Assume plain text file.
340         $pageinfo = array('pagename' => $default_pagename,
341                           'pagedata' => array(),
342                           'versiondata'
343                           => array('author' => $GLOBALS['user']->getId()), //FIXME:?
344                           'content'  => preg_replace('/[ \t\r]*\n/', "\n",
345                                                      chop($text))
346                           );
347         SavePage($dbi, $pageinfo, sprintf(_("plain file %s"),$filename),
348                  $basename);
349     }
350 }
351
352 function LoadZip ($dbi, $zipfile, $files = false, $exclude = false) {
353     $zip = new ZipReader($zipfile);
354     while (list ($fn, $data, $attrib) = $zip->readFile()) {
355         // FIXME: basename("filewithnoslashes") seems to return garbage sometimes.
356         $fn = basename("/dummy/" . $fn);
357         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
358
359             PrintXML(array(HTML::dt(LinkExistingWikiWord($fn)),
360                            HTML::dd(_("Skipping"))));
361            
362             continue;
363         }
364        
365         LoadFile($dbi, $fn, $data, $attrib['mtime']);
366     }
367 }
368
369 function LoadDir ($dbi, $dirname, $files = false, $exclude = false)
370 {
371     $handle = opendir($dir = $dirname);
372     while ($fn = readdir($handle)) {
373         if ($fn[0] == '.' || filetype("$dir/$fn") != 'file')
374             continue;
375             
376         if ( ($files && !in_array($fn, $files)) || ($exclude && in_array($fn, $exclude)) ) {
377
378             PrintXML(array(HTML::dt(LinkExistingWikiWord($fn)),
379                            HTML::dd(_("Skipping"))));
380             continue;
381         }
382             
383         LoadFile($dbi, "$dir/$fn");
384     }
385     closedir($handle);
386 }
387
388 function IsZipFile ($filename_or_fd)
389 {
390     // See if it looks like zip file
391     if (is_string($filename_or_fd))
392         {
393             $fd    = fopen($filename_or_fd, "rb");
394             $magic = fread($fd, 4);
395             fclose($fd);
396         }
397     else
398         {
399             $fpos  = ftell($filename_or_fd);
400             $magic = fread($filename_or_fd, 4);
401             fseek($filename_or_fd, $fpos);
402         }
403     
404     return $magic == ZIP_LOCHEAD_MAGIC || $magic == ZIP_CENTHEAD_MAGIC;
405 }
406
407
408 function LoadAny ($dbi, $file_or_dir, $files = false, $exclude = false)
409 {
410     $type = filetype($file_or_dir);
411     if ($type == 'link') {
412         // For symbolic links, use stat() to determine
413         // the type of the underlying file.
414         list(,,$mode) = stat($file_or_dir);
415         $type = ($mode >> 12) & 017;
416         if ($type == 010)
417             $type = 'file';
418         elseif ($type == 004)
419             $type = 'dir';
420     }
421     
422     if ($type == 'dir') {
423         LoadDir($dbi, $file_or_dir, $files, $exclude);
424     }
425     else if ($type != 'file' && !preg_match('/^(http|ftp):/', $file_or_dir))
426     {
427         ExitWiki( sprintf(_("Bad file type: %s"),$type) );
428     }
429     else if (IsZipFile($file_or_dir)) {
430         LoadZip($dbi, $file_or_dir, $files, $exclude);
431     }
432     else /* if (!$files || in_array(basename($file_or_dir), $files)) */
433     {
434         LoadFile($dbi, $file_or_dir);
435     }
436 }
437
438 function LoadFileOrDir ($dbi, $request)
439 {
440     $source = $request->getArg('source');
441     StartLoadDump( sprintf(_("Loading '%s'"),$source) );
442     echo "<dl>\n";
443     LoadAny($dbi, $source/*, false, array(gettext("RecentChanges"))*/);
444     echo "</dl>\n";
445     EndLoadDump();
446 }
447
448 function SetupWiki ($dbi)
449 {
450     global $GenericPages, $LANG, $user;
451     
452     //FIXME: This is a hack (err, "interim solution")
453     // This is a bogo-bogo-login:  Login without
454     // saving login information in session state.
455     // This avoids logging in the unsuspecting
456     // visitor as "The PhpWiki programming team".
457     //
458     // This really needs to be cleaned up...
459     // (I'm working on it.)
460     $user->_userid = _("The PhpWiki programming team");
461     $user->_level = WIKIAUTH_BOGO;
462     
463     StartLoadDump(_("Loading up virgin wiki"));
464     echo "<dl>\n";
465     
466     LoadAny($dbi, FindLocalizedFile(WIKI_PGSRC)/*, false, $ignore*/);
467     if ($LANG != "C")
468         LoadAny($dbi, FindFile(DEFAULT_WIKI_PGSRC),
469                 $GenericPages/*, $ignore*/);
470     
471     echo "</dl>\n";
472     EndLoadDump();
473 }
474
475 function LoadPostFile ($dbi, $request)
476 {
477     $upload = $request->getUploadedFile('file');
478     
479     if (!$upload)
480         // FIXME: better message?
481         ExitWiki(_("No uploaded file to upload?"));
482     
483     // Dump http headers.
484     StartLoadDump( sprintf(_("Uploading %s"),$upload->getName()) );
485     echo "<dl>\n";
486     
487     $fd = $upload->open();
488     if (IsZipFile($fd))
489         LoadZip($dbi, $fd, false, array(_("RecentChanges")));
490     else
491         Loadfile($dbi, $upload->getName(), $upload->getContents());
492     
493     echo "</dl>\n";
494     EndLoadDump();
495 }
496
497 // For emacs users
498 // Local Variables:
499 // mode: php
500 // tab-width: 8
501 // c-basic-offset: 4
502 // c-hanging-comment-ender-p: nil
503 // indent-tabs-mode: nil
504 // End:   
505 ?>