Fix issues with combining unicode srcdirs and source files.
authorJoey Hess <joey@kitenet.net>
Tue, 15 Jun 2010 20:40:37 +0000 (16:40 -0400)
committerJoey Hess <joey@kitenet.net>
Tue, 15 Jun 2010 21:13:46 +0000 (17:13 -0400)
A short story:

  Once there was a unicode string, let's call him Srcdir.

  Along came a crufy old File::Find, who went through a tree and pasted each
  of the leaves in turn onto Srcdir. But this 90's relic didn't decode the
  leaves -- despite some of them using unicode! Poor Srcdir, with these
  leaves stuck on him, tainted them with his nice unicode-ness. They didn't
  look like leaves at all, but instead garbage.

(In other words, perl's unicode support sucks mightily, and drives
us all to drink and bad storytelling. But we knew that..)

So, srcdir is not normally flagged as unicode, because typically it's pure
ascii. And in that case, things work ok; File::Find finds filenames, which
are not yet decoded to unicode, and appends them to the srcdir, and then
decode_utf8 happily converts the whole thing.

But, if the srcdir does contain utf8 characters, that breaks. Or, if a Yaml
setup file is used, Yaml::Syck's implicitunicode sets the unicode flag of
*all* strings, even those containing only ascii. In either case, srcdir
has the unicode flag set; a non-decoded filename is appended, and the flag
remains set; and decode_utf8 sees the flag and does *nothing*. The result
is that the filename is not decoded, so looks valid and gets skipped.

File::Find only sticks the directory and filenames together in no_chdir
mode .. but we need that mode for security. In order to retain the
security, and avoid the problem, I made it not pass srcdir to File::Find.
Instead, chdir to the srcdir, and pass ".". Since "." is ascii, the problem
is avoided.

Note that chdir srcdir is safe because we check for symlinks in the srcdir
path.

Note that it takes care to chdir back to the starting location. Because
the user may have specified relative paths and so staying in the srcdir
might break. A relative path could even be specifed for an underlay dir, so
it chdirs back after each.

IkiWiki/Plugin/autoindex.pm
IkiWiki/Plugin/comments.pm
IkiWiki/Render.pm
debian/changelog

index 0dd76259e0e74483f408eaec88e1d213aae45f79..5e8a9e0a32954cdbbaad64a81ead00e9ea802bc0 100644 (file)
@@ -33,18 +33,19 @@ sub genindex ($) {
 sub refresh () {
        eval q{use File::Find};
        error($@) if $@;
+       eval q{use Cwd};
+       error($@) if $@;
+       my $origdir=getcwd();
 
        my (%pages, %dirs);
        foreach my $dir ($config{srcdir}, @{$config{underlaydirs}}, $config{underlaydir}) {
-               require File::Spec;
-               $dir=File::Spec->canonpath($dir);
+               chdir($dir) || die "chdir: $!";
 
                find({
                        no_chdir => 1,
                        wanted => sub {
-                               my $file=File::Spec->canonpath(decode_utf8($_));
-                               return if $file eq $dir;
-                               $file=~s/^\Q$dir\E\/?//;
+                               my $file=decode_utf8($_);
+                               $file=~s/^\.\/?//;
                                return unless length $file;
                                if (IkiWiki::file_pruned($file)) {
                                        $File::Find::prune=1;
@@ -61,7 +62,9 @@ sub refresh () {
                                        }
                                }
                        }
-               }, $dir);
+               }, '.');
+
+               chdir($origdir) || die "chdir: $!";
        }
        
        my %deleted;
index d204a7737c4c5e1d69447404dc788d325d52b1b2..17cd99c3b92008b0ee4f35c4ca8f3aaaabe2df7d 100644 (file)
@@ -660,16 +660,22 @@ sub comments_pending () {
 
        eval q{use File::Find};
        error($@) if $@;
+       eval q{use Cwd};
+       error($@) if $@;
+       my $origdir=getcwd();
 
        my $find_comments=sub {
                my $dir=shift;
                my $extension=shift;
                return unless -d $dir;
+
+               chdir($dir) || die "chdir: $!";
+
                find({
                        no_chdir => 1,
                        wanted => sub {
                                my $file=decode_utf8($_);
-                               $file=~s/^\Q$dir\E\/?//;
+                               $file=~s/^\.\///;
                                return if ! length $file || IkiWiki::file_pruned($file)
                                        || -l $_ || -d _ || $file !~ /\Q$extension\E$/;
                                my ($f) = $file =~ /$config{wiki_file_regexp}/; # untaint
@@ -678,7 +684,9 @@ sub comments_pending () {
                                        push @ret, [$f, $dir, $ctime];
                                }
                        }
-               }, $dir);
+               }, ".");
+
+               chdir($origdir) || die "chdir: $!";
        };
        
        $find_comments->($config{srcdir}, "._comment_pending");
index f9fbc801f750419c76ca301dbcaac1ec62d32aa6..f81e373b73ad37dcdfc988f0f08a08eb2122012d 100644 (file)
@@ -292,12 +292,17 @@ sub find_src_files () {
        eval q{use File::Find};
        error($@) if $@;
 
-       my ($page, $dir, $underlay);
+       eval q{use Cwd};
+       die $@ if $@;
+       my $origdir=getcwd();
+       my $abssrcdir=Cwd::abs_path($config{srcdir});
+
+       my ($page, $underlay);
        my $helper=sub {
                my $file=decode_utf8($_);
 
                return if -l $file || -d _;
-               $file=~s/^\Q$dir\E\/?//;
+               $file=~s/^\.\///;
                return if ! length $file;
                $page = pagename($file);
                if (! exists $pagesources{$page} &&
@@ -314,7 +319,7 @@ sub find_src_files () {
        
                if ($underlay) {
                        # avoid underlaydir override attacks; see security.mdwn
-                       if (! -l "$config{srcdir}/$f" && ! -e _) {
+                       if (! -l "$abssrcdir/$f" && ! -e _) {
                                if (! $pages{$page}) {
                                        push @files, $f;
                                        $pages{$page}=1;
@@ -330,17 +335,23 @@ sub find_src_files () {
                }
        };
 
+       chdir($config{srcdir}) || die "chdir: $!";
        find({
                no_chdir => 1,
                wanted => $helper,
-       }, $dir=$config{srcdir});
+       }, '.');
+       chdir($origdir) || die "chdir: $!";
+
        $underlay=1;
        foreach (@{$config{underlaydirs}}, $config{underlaydir}) {
+               chdir($_) || die "chdir: $!";
                find({
                        no_chdir => 1,
                        wanted => $helper,
-               }, $dir=$_);
+               }, '.');
+               chdir($origdir) || die "chdir: $!";
        };
+
        return \@files, \%pages;
 }
 
index bddedeafaee8109335ba2a148ab819bb6da1aa0b..16f14b3a11c57694715b51e98942c32073906b67 100644 (file)
@@ -16,6 +16,7 @@ ikiwiki (3.20100611) UNRELEASED; urgency=low
   * editpage, comments: Fix broken links in sidebar (due to forcebaseurl).
     (Thanks, privat)
   * calendar: Tune archive_pagespec to only match pages, not other files.
+  * Fix issues with combining unicode srcdirs and source files.
 
  -- Joey Hess <joeyh@debian.org>  Fri, 11 Jun 2010 13:39:15 -0400