|
| 1 | +# See bottom of file for default license and copyright information |
| 2 | + |
| 3 | +=begin TML |
| 4 | +
|
| 5 | +---+ package Foswiki::Plugins::PubLinkFixupPlugin |
| 6 | +
|
| 7 | +This plugin performs pub link fixup of the generated HTML page. If Foswiki is configured with a |
| 8 | +non-utf-8 ={Store}{Encoding}=, then links to /pub files will be generated with the incorrect encoding. |
| 9 | +
|
| 10 | +Even on non-utf-8 sites, Foswiki operates fully with UNICODE and utf-8 encoding in the core and on |
| 11 | +the web interface. /pub attachment links will be generated assuming the filesnames are utf-8 encoded. |
| 12 | +This plugin provides a completePageHandler that finds utf-8 encoded links to /pub attachments and |
| 13 | +re-encodes them to the {Store}{Encoding}. |
| 14 | +
|
| 15 | +This is __not__ a complete fix to the issue. It is still strongly recommended that sites convert |
| 16 | +their Store to utf-8 to avoid these types of encoding issues. |
| 17 | +
|
| 18 | +=cut |
| 19 | + |
| 20 | +# change the package name!!! |
| 21 | +package Foswiki::Plugins::PubLinkFixupPlugin; |
| 22 | + |
| 23 | +# Always use strict to enforce variable scoping |
| 24 | +use strict; |
| 25 | +use warnings; |
| 26 | + |
| 27 | +use Foswiki (); |
| 28 | +use Foswiki::Func (); # The plugins API |
| 29 | +use Foswiki::Plugins (); # For the API version |
| 30 | +use Foswiki::Store (); |
| 31 | + |
| 32 | +our $VERSION = '1.00'; |
| 33 | +our $RELEASE = '14 Sep 2015'; |
| 34 | + |
| 35 | +# One line description of the module |
| 36 | +our $SHORTDESCRIPTION = |
| 37 | + 'For non-utf-8 sites, fix up Pub links to use correct encoding.'; |
| 38 | + |
| 39 | +our $NO_PREFS_IN_TOPIC = 1; |
| 40 | + |
| 41 | +=begin TML |
| 42 | +
|
| 43 | +---++ initPlugin($topic, $web, $user) -> $boolean |
| 44 | + * =$topic= - the name of the topic in the current CGI query |
| 45 | + * =$web= - the name of the web in the current CGI query |
| 46 | + * =$user= - the login name of the user |
| 47 | + * =$installWeb= - the name of the web the plugin topic is in |
| 48 | + (usually the same as =$Foswiki::cfg{SystemWebName}=) |
| 49 | +
|
| 50 | +=cut |
| 51 | + |
| 52 | +sub initPlugin { |
| 53 | + my ( $topic, $web, $user, $installWeb ) = @_; |
| 54 | + |
| 55 | + # check for Plugins.pm versions |
| 56 | + if ( $Foswiki::Plugins::VERSION < 2.3 ) { |
| 57 | + Foswiki::Func::writeWarning( 'Version mismatch between ', |
| 58 | + __PACKAGE__, ' and Plugins.pm' ); |
| 59 | + return 0; |
| 60 | + } |
| 61 | + |
| 62 | + # If site is running utf-8 store, then this plugin is not needed. |
| 63 | + # Just undefine the completePageHandler. |
| 64 | + unless ( $Foswiki::cfg{Store}{Encoding} |
| 65 | + && $Foswiki::cfg{Store}{Encoding} ne 'utf-8' ) |
| 66 | + { |
| 67 | + undef *completePageHandler; |
| 68 | + Foswiki::Func::writeDebug( |
| 69 | + 'PubLinkFixupPlugin disabled - utf-8 store detected') |
| 70 | + if ( $Foswiki::cfg{Plugins}{PubLinkFixupPlugin}{Debug} ); |
| 71 | + } |
| 72 | + |
| 73 | + # Plugin correctly initialized |
| 74 | + return 1; |
| 75 | +} |
| 76 | + |
| 77 | +=begin TML |
| 78 | +
|
| 79 | +---++ completePageHandler($html, $httpHeaders) |
| 80 | +
|
| 81 | +This handler is called on the ingredients of every page that is |
| 82 | +output by the standard CGI scripts. It is designed primarily for use by |
| 83 | +cache and security plugins. |
| 84 | + * =$html= - the body of the page (normally <html>..$lt;/html>) |
| 85 | + * =$httpHeaders= - the HTTP headers. Note that the headers do not contain |
| 86 | + a =Content-length=. That will be computed and added immediately before |
| 87 | + the page is actually written. This is a string, which must end in \n\n. |
| 88 | +
|
| 89 | +*Since:* Foswiki::Plugins::VERSION 2.0 |
| 90 | +
|
| 91 | +=cut |
| 92 | + |
| 93 | +sub completePageHandler { |
| 94 | + |
| 95 | + # my( $html, $httpHeaders ) = @_; |
| 96 | + |
| 97 | + if ( $Foswiki::cfg{Store}{Encoding} |
| 98 | + && $Foswiki::cfg{Store}{Encoding} ne 'utf-8' ) |
| 99 | + { |
| 100 | + $_[0] =~ |
| 101 | +s#(<(?:a|link) .*?href=(["'])(?:$Foswiki::cfg{DefaultUrlHost})?($Foswiki::cfg{PubUrlPath}/?.*?)\2.*?/?>)#_reEncodePubLink($1, $3)#ge; |
| 102 | + $_[0] =~ |
| 103 | +s#(<(?:audio|iframe|img|script|source|track|video) .*?src=(["'])(?:$Foswiki::cfg{DefaultUrlHost})?($Foswiki::cfg{PubUrlPath}/?.*?)\2.*?/?>)#_reEncodePubLink($1, $3)#ge; |
| 104 | + $_[0] =~ |
| 105 | +s#(<object .*?data=(["'])(?:$Foswiki::cfg{DefaultUrlHost})?($Foswiki::cfg{PubUrlPath}/?.*?)\2.*?/?>)#_reEncodePubLink($1, $3)#ge; |
| 106 | + } |
| 107 | +} |
| 108 | + |
| 109 | +=begin TML |
| 110 | +---++ private _reEncodePubLink( $wholeLink, $url ) |
| 111 | +
|
| 112 | +This routine is called for each pub link found in the complete page. |
| 113 | +It takes the href/src location from the link, re-encodes it into the |
| 114 | +{Store}{Encoding} and then replaces it back into the whole link. |
| 115 | +
|
| 116 | +=cut |
| 117 | + |
| 118 | +sub _reEncodePubLink { |
| 119 | + my ( $wholeLink, $url ) = @_; |
| 120 | + |
| 121 | + my $origLink = $wholeLink; # For debug printing |
| 122 | + |
| 123 | + # Extract just the path component, truncating any querystring |
| 124 | + my $qPos = index( $url, '?' ); |
| 125 | + if ( $qPos >= 0 ) { |
| 126 | + $url = substr( $url, 0, $qPos ); |
| 127 | + } |
| 128 | + |
| 129 | + # Decode the path back to utf-8 |
| 130 | + my $decoded = Foswiki::urlDecode($url); |
| 131 | + |
| 132 | + # something didn't work right, undo the decode and keep going |
| 133 | + if ( index( $decoded, chr(0xFFFD) ) > 0 ) { |
| 134 | + Foswiki::Func::writeDebug("Warning: Decode failed for ($decoded)") |
| 135 | + if ( $Foswiki::cfg{Plugins}{PubLinkFixupPlugin}{Debug} ); |
| 136 | + $decoded = $url; |
| 137 | + } |
| 138 | + |
| 139 | + # if ascii, just return unmodified |
| 140 | + return $wholeLink if $decoded !~ m/[^[:ascii:]]+/; |
| 141 | + |
| 142 | + # Extract out the file system path for further checking |
| 143 | + ( my $storePath ) = $decoded =~ m/^$Foswiki::cfg{PubUrlPath}(\/.*)$/; |
| 144 | + return $wholeLink unless $storePath; #Nothing to check? |
| 145 | + |
| 146 | + # If file exists with utf-8 encoding, do nothing |
| 147 | + my $tmpPath = "$Foswiki::cfg{PubDir}$storePath"; |
| 148 | + return $wholeLink |
| 149 | + if ( -e Encode::encode( 'utf-8', $tmpPath, Encode::FB_WARN ) ); |
| 150 | + |
| 151 | + # re-encode the decoded URL into the {Store}{Encoding} |
| 152 | + my $text = Foswiki::Store::encode($decoded); |
| 153 | + |
| 154 | + ($storePath) = $text =~ m/^$Foswiki::cfg{PubUrlPath}(\/.*)$/; |
| 155 | + return $wholeLink unless $storePath; #Nothing to check? |
| 156 | + |
| 157 | + # if the file doesn't exist, then either oddball encoding, or |
| 158 | + # maybe a real broken link. Just return unchanged. |
| 159 | + return $wholeLink unless ( -e $Foswiki::cfg{PubDir} . $storePath ); |
| 160 | + |
| 161 | + # Entity-encode non-ASCII high character and other restricted characters. |
| 162 | + $text =~ s{([^0-9a-zA-Z-_.:~!*#/])}{sprintf('%%%02x',ord($1))}ge; |
| 163 | + |
| 164 | + # Replace the urlpath in the link. |
| 165 | + $wholeLink =~ s/\Q$url\E/$text/; |
| 166 | + |
| 167 | + if ( $origLink ne $wholeLink |
| 168 | + && $Foswiki::cfg{Plugins}{PubLinkFixupPlugin}{Debug} ) |
| 169 | + { |
| 170 | + Foswiki::Func::writeDebug( <<DETAILS ); |
| 171 | +PubLinkFixupPlugin - |
| 172 | +REWRITING: $origLink |
| 173 | + TO: $wholeLink |
| 174 | +DETAILS |
| 175 | + |
| 176 | + return $wholeLink; |
| 177 | + |
| 178 | + } |
| 179 | +} |
| 180 | + |
| 181 | +1; |
| 182 | + |
| 183 | +__END__ |
| 184 | +Foswiki - The Free and Open Source Wiki, http://foswiki.org/ |
| 185 | +
|
| 186 | +Copyright (C) 2008-2013 Foswiki Contributors. Foswiki Contributors |
| 187 | +are listed in the AUTHORS file in the root of this distribution. |
| 188 | +NOTE: Please extend that file, not this notice. |
| 189 | +
|
| 190 | +This program is free software; you can redistribute it and/or |
| 191 | +modify it under the terms of the GNU General Public License |
| 192 | +as published by the Free Software Foundation; either version 2 |
| 193 | +of the License, or (at your option) any later version. For |
| 194 | +more details read LICENSE in the root of this distribution. |
| 195 | +
|
| 196 | +This program is distributed in the hope that it will be useful, |
| 197 | +but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 198 | +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| 199 | +
|
| 200 | +As per the GPL, removal of this notice is prohibited. |
0 commit comments