po(cansave): check PO validity using new isvalidpo function
authorintrigeri <intrigeri@boum.org>
Thu, 1 Jan 2009 22:10:16 +0000 (23:10 +0100)
committerintrigeri <intrigeri@boum.org>
Thu, 1 Jan 2009 22:12:08 +0000 (23:12 +0100)
... because Po4a's parser does not care about malformed PO data.
Use gettext's msgfmt program instead.

Signed-off-by: intrigeri <intrigeri@boum.org>
IkiWiki/Plugin/po.pm
doc/plugins/po.mdwn

index 35e7c13fffa1622e2060f3a03c8a07b9c9b8ca44..8f11a5f5ab3902e4d5a02e96f28d23d5e5cec5c0 100644 (file)
@@ -388,11 +388,12 @@ sub cansave ($$$$) {
        my ($page, $content, $cgi, $session) = (shift, shift, shift, shift);
 
        if (istranslation($page)) {
-               if (defined po_to_markup($page, $content, "nonfatal")) {
+               my $res = isvalidpo($content);
+               if ($res) {
                        return undef;
                }
                else {
-                       return "Could not parse this page's content; is this valid gettext?";
+                       return "$res";
                }
        }
        return undef;
@@ -942,6 +943,48 @@ sub po_to_markup ($$;$) {
        return $content;
 }
 
+# returns a SuccessReason or FailReason object
+sub isvalidpo ($) {
+       my $content = shift;
+
+       # NB: we don't use po_to_markup here, since Po4a parser does
+       # not mind invalid PO content
+       $content = '' unless defined $content;
+       $content = decode_utf8(encode_utf8($content));
+
+       # There are incompatibilities between some File::Temp versions
+       # (including 0.18, bundled with Lenny's perl-modules package)
+       # and others (e.g. 0.20, previously present in the archive as
+       # a standalone package): under certain circumstances, some
+       # return a relative filename, whereas others return an absolute one;
+       # we here use this module in a way that is at least compatible
+       # with 0.18 and 0.20. Beware, hit'n'run refactorers!
+       my $infile = new File::Temp(TEMPLATE => "ikiwiki-po-isvalidpo.XXXXXXXXXX",
+                                   DIR => File::Spec->tmpdir,
+                                   UNLINK => 1)->filename;
+
+       sub failure ($) {
+               my $msg = '[po/isvalidpo:'.$page.'] ' . shift;
+               unlink $infile;
+               return IkiWiki::FailReason->new("$msg");
+       }
+
+       writefile(basename($infile), File::Spec->tmpdir, $content)
+               or return failure("failed to write $infile");
+
+       my $res = (system("msgfmt", "--check", $infile) == 0);
+
+       # Unlinking should happen automatically, thanks to File::Temp,
+       # but it does not work here, probably because of the way writefile()
+       # and Locale::Po4a::write() work.
+       unlink $infile;
+
+       if ($res) {
+           return IkiWiki::SuccessReason->new("valid gettext data");
+       }
+       return IkiWiki::FailReason->new("invalid gettext data");
+}
+
 # ,----
 # | PageSpec's
 # `----
index c150092bfbbe93f87a2b444cc43444c3a93f50cb..a32d66af1f699c131a7d48e49ecc9148724f62f3 100644 (file)
@@ -399,6 +399,10 @@ use in our case, I suggest we define `ENV{COLUMNS}` before loading
 `refreshpofiles()` runs this external program. A po4a developer
 answered he does "not expect any security issues from it".
 
+### msgfmt
+
+`isvalidpo()` runs this external program. Its security should be checked.
+
 ### Fuzzing input
 
 I was not able to find any public information about gettext or po4a