Skip to content

Commit

Permalink
Item14958: work around bug in PDF.js
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelDaum committed Oct 20, 2020
1 parent 9ca66fd commit 6bd6bbe
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 24 deletions.
3 changes: 2 additions & 1 deletion bin/xsendfile
Expand Up @@ -27,10 +27,11 @@ BEGIN {
use Foswiki ();
use Foswiki::UI ();
$Foswiki::engine->run();

__END__
Foswiki - The Free and Open Source Wiki, http://foswiki.org/
Copyright (C) 2008-2018 Foswiki Contributors. Foswiki Contributors
Copyright (C) 2008-2020 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
Expand Down
20 changes: 19 additions & 1 deletion data/System/XSendFileContrib.txt
Expand Up @@ -5,6 +5,7 @@
%TOC%

---++ Introduction

This package implements a more efficient way to send static files using a web application
like Foswiki. A standard configuration of a Foswiki uses the =viewfile= service
to send static files over to the browser thus enforcing access rights on them. In doing
Expand Down Expand Up @@ -34,6 +35,7 @@ This basically works like this:
* otherwise it will return the response generated by the web app as usual

---++ Configuring Foswiki

<nop>%TOPIC% comes with a separate service script =.../bin/xsendfile= replacing the standard =.../bin/viewfile=. It differs from viewfile only in two respects

* any HTTP error is delivered immediately, that is without requiring Foswiki to render an error page while processing an exception.
Expand All @@ -43,8 +45,10 @@ There are two parameters that need to be adjusted according to your web server's

* =$Foswiki::cfg{XSendFileContrib}{Header}= ... the name of the HTTP header to trigger the X-Send-File feature in your web server (see below)
* =$Foswiki::cfg{XSendFileContrib}{Location}= ... the uri prefix that the web server serves the actual static file from
* =$Foswiki::cfg{XSendFileContrib}{Locations}{pdf}= ... the uri prefix for pdf files

---++ Configuring Lighttpd

(see http://redmine.lighttpd.net/projects/1/wiki/X-LIGHTTPD-send-file for lighttpd)

The X-Send-File header for Lighttpd is called *X-LIGHTTPD-send-file* and points to the file path on disk that the web server should send to the client.
Expand All @@ -69,6 +73,7 @@ $HTTP["url"] =~ "^/bin/xsendfile)" {
</verbatim>

---++ Configuring Nginx

(see http://wiki.nginx.org/XSendfile, http://wiki.nginx.org/X-accel)

In Nginx this feature is called X-Accel-Redirect. Yet the idea is the same. Requirements on the upstream web applications are almost identical
Expand All @@ -92,9 +97,18 @@ location /protected_files {
internal;
alias /path/to/foswiki/pub/;
}

location /protected_files/pdf {
internal;
alias /path/to/foswiki/pub/;

# disable Accept-Ranges header as it breaks cookie authentication with pdf.js
max_ranges 0;
}
</verbatim>

---++ Configuring Apache2

Apache itself requires an additional simple module that processes an *X-Sendfile* header.
In a classical Foswiki Apache2 installation you have to modify your web server configuration as follows:

Expand All @@ -107,13 +121,17 @@ RewriteRule ^/+pub/+(.*)$ /bin/xsendfile/$1 [L,PT]
See https://tn123.org/mod_xsendfile/ for more information on how to compile =mod_xsendfile= and how to configure apache.

---++ Installation

%$INSTALL_INSTRUCTIONS%

---++ Dependencies

%$DEPENDENCIES%

---++ Change History

%TABLE{columnwidths="7em" tablewidth="100%"}%
| 09 Oct 2020 | added ={Locations}= feature to be able to work around a bug in pdf.js not forwarding cookies in byte-range requests |
| 11 Jun 2018 | display reason why access to a file was denied |
| 12 Dec 2017 | fixed content encoding of a 403 warning; \
made file extension configurable that must be delivered in an "attachment" disposition mode |
Expand All @@ -137,7 +155,7 @@ See https://tn123.org/mod_xsendfile/ for more information on how to compile =mod

%META:FORM{name="PackageForm"}%
%META:FIELD{name="Author" title="Author" value="Michael Daum"}%
%META:FIELD{name="Copyright" title="Copyright" value="2013-2018, Michael Daum http://michaeldaumconsulting.com"}%
%META:FIELD{name="Copyright" title="Copyright" value="2013-2020, Michael Daum"}%
%META:FIELD{name="Description" title="Description" value="%25$SHORTDESCRIPTION%25"}%
%META:FIELD{name="Home" title="Home" value="https://foswiki.org/Extensions/%TOPIC%"}%
%META:FIELD{name="License" title="License" value="GPL ([[http://www.gnu.org/copyleft/gpl.html][GNU General Public License]])"}%
Expand Down
69 changes: 49 additions & 20 deletions lib/Foswiki/Contrib/XSendFileContrib.pm
@@ -1,6 +1,6 @@
# Module of Foswiki - The Free and Open Source Wiki, https://foswiki.org/
#
# Copyright (C) 2013-2018 Michael Daum http://michaeldaumconsulting.com
# Copyright (C) 2013-2020 Michael Daum http://michaeldaumconsulting.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
Expand All @@ -27,8 +27,8 @@ use File::Spec ();
use Error qw( :try );
use Foswiki::AccessControlException ();

our $VERSION = '5.22';
our $RELEASE = '11 Jun 2018';
our $VERSION = '6.00';
our $RELEASE = '20 Oct 2020';
our $SHORTDESCRIPTION = 'A viewfile replacement to send static files efficiently';
our $mimeTypeInfo;
our $mmagic;
Expand All @@ -43,13 +43,12 @@ sub _decodeUntaint {
}

sub xsendfile {

my $session = shift;
my $request = $session->{request};
my $response = $session->{response};

# remove cookie
$response->cookies([]);
#$response->cookies([]);

my $web = $session->{webName};
my $topic = $session->{topicName};
Expand All @@ -59,7 +58,6 @@ sub xsendfile {
my $pathPrefix = "";

my $headerName = $Foswiki::cfg{XSendFileContrib}{Header} || 'X-LIGHTTPD-send-file';
my $location = $Foswiki::cfg{XSendFileContrib}{Location} || $Foswiki::cfg{PubDir};
my $fileLocation;

my $filePath;
Expand Down Expand Up @@ -87,6 +85,7 @@ sub xsendfile {
unless ($web) {
$response->status(404);
$response->print("404 - no web found\n");
writeEvent("no web found");
return;
}

Expand All @@ -98,23 +97,26 @@ sub xsendfile {
$topic = _decodeUntaint(shift @path, \&Foswiki::Sandbox::validateTopicName);

# check whether this is a file already
$filePath = File::Spec->catfile("/", $Foswiki::cfg{PubDir}, $pathPrefix, $web, $topic);
if (-f $filePath) {
$foundOnDisk = 1;
$fileLocation = $location . $pathPrefix . '/' . $web . '/' . $topic;

# test for a file extension, e.g. System/WebHome.html
if ($topic =~ /^(.*)\.([^\.]+)$/) {
$fileName = $topic;
$topic = $1;
} else {
$fileName = $topic;
if (defined $web && defined $topic) {
$filePath = File::Spec->catfile("/", $Foswiki::cfg{PubDir}, $pathPrefix, $web, $topic);
if (-f $filePath) {
$foundOnDisk = 1;
$fileLocation = $pathPrefix . '/' . $web . '/' . $topic;

# test for a file extension, e.g. System/WebHome.html
if ($topic =~ /^(.*)\.([^\.]+)$/) {
$fileName = $topic;
$topic = $1;
} else {
$fileName = $topic;
}
}
}

unless ($topic) {
unless (defined $web && defined $topic) {
$response->status(404);
$response->print("404 - no topic found\n");
writeEvent("no topic found");
return;
}

Expand All @@ -133,14 +135,15 @@ sub xsendfile {
$filePath = File::Spec->catfile("/", $Foswiki::cfg{PubDir}, $pathPrefix, $web, $topic, $fileName);
if (-f $filePath) {
$foundOnDisk = 1;
$fileLocation = $location . $pathPrefix . '/' . $web . '/' . $topic . '/' . $fileName;
$fileLocation = $pathPrefix . '/' . $web . '/' . $topic . '/' . $fileName;
}
}

# not found
if (!defined($fileName) || $fileName eq '') {
$response->status(404);
$response->print("404 - no file found\n");
writeEvent("no file found");
return;
}

Expand All @@ -152,6 +155,7 @@ sub xsendfile {
unless (defined $fileName) {
$response->status(404);
$response->print("404 - file not valid\n");
writeEvent("file not valid");
return;
}

Expand All @@ -161,13 +165,15 @@ sub xsendfile {
if (!$foundOnDisk && !$topicObject->existsInStore()) {
$response->status(404);
$response->print("404 - topic $web.$topic does not exist\n");
writeEvent("topic not found");
return;
}

# not found
if (!$foundOnDisk && !$topicObject->hasAttachment($fileName)) {
$response->status(404);
$response->print("404 - attachment $fileName not found at $web.$topic\n");
writeEvent("attachment $fileName not found", $filePath);
return;
}

Expand All @@ -181,6 +187,7 @@ sub xsendfile {
$response->status(403);
$response->print("403 - access denied - $reason\n");
}
writeEvent("access denied", $filePath);
return;
}

Expand All @@ -195,6 +202,7 @@ sub xsendfile {

if ($lastModified eq $ifModifiedSince) {
$response->header(-status => 304,);
writeEvent($fileName, $filePath);
return;
}

Expand Down Expand Up @@ -226,7 +234,16 @@ sub xsendfile {
$dispositionMode = ($fileName =~ /$defaultAttachmentDispositionFiles/) ? "attachment" : "inline";
}

$fileLocation = $location . $pathPrefix . '/' . $web . '/' . $topic . '/' . $fileName unless defined $fileLocation;
$fileLocation = $pathPrefix . '/' . $web . '/' . $topic . '/' . $fileName unless defined $fileLocation;

my $location;
my ($ext) = $fileName =~ /\.(.*?)$/;

if (defined($ext) && defined($Foswiki::cfg{XSendFileContrib}{Locations}{$ext})) {
$location = $Foswiki::cfg{XSendFileContrib}{Locations}{$ext};
}
$location //= $Foswiki::cfg{XSendFileContrib}{Location} || $Foswiki::cfg{PubDir};
$fileLocation = $location . $fileLocation;

$response->header(
-status => 200,
Expand All @@ -235,11 +252,23 @@ sub xsendfile {
-last_modified => $lastModified,
$headerName => $fileLocation,
);
$response->body("");
}

writeEvent($fileName, $filePath);

return;
}

sub writeEvent {
my ($msg, $filePath) = @_;

return if exists $Foswiki::cfg{Log}{Action}{xsendfile} && !$Foswiki::cfg{Log}{Action}{xsendfile};
return if $filePath && $Foswiki::cfg{XSendFileContrib}{ExcludeFromLogging} && $filePath =~ /$Foswiki::cfg{XSendFileContrib}{ExcludeFromLogging}/;

Foswiki::Func::writeEvent("xsendfile", $msg)
}

sub checkAccess {
my ($topicObject, $fileName, $user) = @_;

Expand Down
14 changes: 13 additions & 1 deletion lib/Foswiki/Contrib/XSendFileContrib/Config.spec
Expand Up @@ -25,6 +25,13 @@ $Foswiki::cfg{XSendFileContrib}{Header} = 'none';
# as configured for an Nginx.
$Foswiki::cfg{XSendFileContrib}{Location} = '';

# **PERL**
# File-type specific locations that the http server will process internally to send protected files.
# This is a hash mapping file extensions (eg. pdf) to a different location other than the default one
# in {XSendFileContrib}{Location}. Different file types may be served differently, such as videos
# being streamed, or pdfs _not_ being streamed due to a bug in pdf.js ;)
$Foswiki::cfg{XSendFileContrib}{Locations} = {};

# **REGEX**
# Prefix of a url path to be removed before parsing the rest of it as a web.topic
# For examples thumbnails generated by ImageGalleryPlugin are stored in '/pub/images/'.
Expand All @@ -39,6 +46,12 @@ $Foswiki::cfg{XSendFileContrib}{PathPrefix} = '/(images|export/assets|export/htm
# a "save as" dialog for any content they can't display inline correctly.
$Foswiki::cfg{XSendFileContrib}{DefaultAttachmentDispositionFiles} = '(?:(?:(?:xlt|xls|csv|ppt|pps|pot|doc|dot)(x|m)?)|odc|odb|odf|odg|otg|odi|odp|otp|ods|ots|odt|odm|ott|oth|mpp|rtf|vsd)$';

# **REGEX EXPERT**
# File paths to exclude from logging. Normally, all files access sanctioned by xsendfile are
# logged to the event log. This regular expression allows to exclude some from being logged, s
# such as image thumbnails.
$Foswiki::cfg{XSendFileContrib}{ExcludeFromLogging} = '.*/igp_.*';

# **PERL**
# By default view rights of the topic are controlling the access rights to download
# all attachments on this topic. In some cases you might want to use <i>change</i>
Expand Down Expand Up @@ -67,7 +80,6 @@ $Foswiki::cfg{XSendFileContrib}{AccessRules} = [
},
{
file => "igp_.*",
requiredAccess => "",
},
];

Expand Down
2 changes: 1 addition & 1 deletion tools/virtualhosts-xsendfile
Expand Up @@ -47,7 +47,7 @@ sub doit {
__END__
Foswiki - The Free and Open Source Wiki, https://foswiki.org/
Copyright (C) 2008-2018 Foswiki Contributors. Foswiki Contributors
Copyright (C) 2008-2020 Foswiki Contributors. Foswiki Contributors
are listed in the AUTHORS file in the root of this distribution.
NOTE: Please extend that file, not this notice.
Expand Down

0 comments on commit 6bd6bbe

Please sign in to comment.