File Coverage

blib/lib/Apache2/OneTimeDownload.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             =head1 NAME
2            
3             Apache2::OneTimeDownload - Tolerant mechanism for expiring downloads
4            
5             =head2 DESCRIPTION
6            
7             Allows you to distribute files that expire a given time after
8             the first download
9            
10             =head2 SYNOPSIS
11            
12             In your Apache config:
13            
14             PerlModule Apache2::OneTimeDownload
15            
16            
17             PerlHandler Apache2::OneTimeDownload
18             SetHandler perl-script
19             PerlSetVar OneTimeDb /home/sheriff/download_access.db
20             PerlSetVar OneTimeWindow 3600
21             PerlSetVar OneTimeDownloadDirectory /home/sheriff/downloads/
22            
23            
24             Example authorize.pl...
25            
26             #!/usr/bin/perl
27            
28             use Apache2::OneTimeDownload;
29            
30             my $file = $ARGV[0];
31             my $comment = $ARGV[1-];
32             my $db = '/opt/secret/access.db'
33            
34             print Apache2::OneTimeDownload::authorize( $db, $comment, $file );
35            
36             and then:
37            
38             % authorize.pl TopSecret.pdf Given out on IRC...
39             2c61de78edd612cf79c0d73a3c7c94fb
40            
41             Which might mean:
42            
43             http://www.sheriff.com/download/2c61de78edd612cf79c0d73a3c7c94fb
44            
45             =head1 CONFIG
46            
47             =head2 OneTimeDb
48            
49             The location of the DB file where key->file mappings will be kept
50            
51             =head2 OneTimeWindow
52            
53             The amount of time after a download you wish the file to remain
54             before it expires. An hour is a good sized window...
55            
56             =head2 OneTimeDownloadDirectory
57            
58             The directory from which you're serving your file downloads -
59             probably not one that's accessible from the web...
60            
61             =head1 &authorize
62            
63             Adds a key to the db. Accepts these arguments, in this order:
64            
65             =head2 db name
66            
67             Absolute path of the database
68            
69             =head2 comment
70            
71             Plain text comments about the file
72            
73             =head2 file
74            
75             Location of the file - this has the download directory
76             stuck at the beginning of it when it comes to download
77             time...
78            
79             =head2 expires
80            
81             Time in seconds until the file expires before anyone
82             has downloaded it. Defaults to a week.
83            
84             =head1 AUTHOR
85            
86             Stephan Jauernick -- C
87             Pete Sergeant -- C
88            
89             =head1 COPYRIGHT
90             Copyright for this Version:
91             Copyright 2009 B.
92             Original Copyright:
93             Copyright 2004 B.
94            
95             This program is free software; you can redistribute it and/or modify it under
96             the same terms as Perl itself.
97            
98             =cut
99            
100             require 5;
101            
102             package Apache2::OneTimeDownload;
103            
104 1     1   5178 use vars qw($VERSION);
  1         2  
  1         38  
105            
106 1     1   5 use strict;
  1         2  
  1         28  
107            
108 1     1   1766 use Apache2::RequestRec ();
  0            
  0            
109             use Apache2::RequestIO ();
110             use Apache2::SubRequest ();
111             use Apache2::Const -compile => qw(:common);
112             use APR::Table;
113             use MLDBM qw(DB_File);
114             use Digest::MD5 qw(md5_hex);
115            
116             $VERSION = '1.01';
117            
118             sub handler {
119            
120             my $r = shift;
121            
122             # Read in the key we're using
123             my ($key) = $r->unparsed_uri =~ /([a-f0-9]{32})/ or return Apache2::Const::NOT_FOUND;
124            
125            
126             # Load our database
127            
128             my $db_name = $r->dir_config("OneTimeDb")
129             or die "Database not specified in OneTimeDb!";
130            
131             my %db; tie %db, "MLDBM", $db_name
132             or die "Couldn't open database $db_name: $!";
133            
134             # Does the key exist?
135            
136             return Apache2::Const::FORBIDDEN if !exists $db{$key};
137            
138             my $file = $db{$key};
139            
140             # Has the object expired?
141            
142             my $time_until_expiry = $file->{expires} - time();
143            
144             unless ($time_until_expiry > 0) {
145            
146             return Apache2::Const::FORBIDDEN;
147            
148             }
149            
150             # Does the object need it's expiry date shortened?
151            
152             my $window = $r->dir_config("OneTimeWindow") || 3600;
153            
154             if ( $time_until_expiry > $window ) {
155            
156             $file->{downloaded} = 1;
157             $file->{count} = $file->{count} + 1;
158             $file->{expires} = ( time() + ( $window - 1 ) );
159             $db{$key} = $file;
160            
161             }
162            
163             my $file_path = $r->dir_config("OneTimeDownloadDirectory") . $file->{file};
164            
165             untie %db;
166            
167             # Return the file
168             my $filesize= -s $file_path;
169             my $subr = $r->lookup_file( $file_path );
170             return Apache2::Const::NOT_FOUND unless (defined($subr));
171            
172             $subr->headers_in->set( 'Range' => $r->headers_in->get('Range') ) if (defined($r->headers_in->get('Range')));
173            
174             $r->headers_out->set("Accept-Ranges" => "bytes");
175             $r->headers_out->set("Content-Length" => $filesize);
176             $r->headers_out->set("Content-Disposition" => "attachment; filename=".$file->{file});
177            
178             return $subr->run();
179            
180             }
181            
182            
183             sub authorize {
184            
185             my ($db_name, $comments, $file, $expiry) = @_;
186            
187             my $key = md5_hex(time().{}.rand().$$);
188            
189             my %db; tie %db, "MLDBM", $db_name or die "Couldn't open database: $!";
190            
191             $db{$key} = {
192            
193             comments => $comments,
194             expires => $expiry || ( time() + 604800 ),
195             file => $file,
196             downloaded => 0,
197             count => 0
198            
199             };
200            
201             untie %db;
202             return $key;
203            
204             }
205            
206            
207            
208             1;