Kill WebDAV entirely; now only the PUT and OPTIONS are left.
[pr0n] / perl / Sesse / pr0n / Upload.pm
1 package Sesse::pr0n::Upload;
2 use strict;
3 use warnings;
4
5 use Sesse::pr0n::Common qw(error dberror);
6 use Digest::SHA;
7 use MIME::Base64;
8
9 sub handler {
10         my $r = shift;
11         my $dbh = Sesse::pr0n::Common::get_dbh();
12
13         my $res = Plack::Response->new(200);
14         my $io = IO::String->new;
15
16         my ($user,$takenby) = Sesse::pr0n::Common::check_access($r);
17         return Sesse::pr0n::Common::generate_401($r) if (!defined($user));
18
19         # Just "ping, are you alive"
20         if ($r->method eq "OPTIONS") {
21                 $res->content_type('text/plain; charset="utf-8"');
22                 return $res;
23         }
24         
25         if ($r->method eq "PUT") {
26                 if ($r->path_info !~ m#^/upload/([a-zA-Z0-9-]+)/(autorename/)?(.{1,250})$#) {
27                         $res->status(403);
28                         $res->content_type('text/plain; charset=utf-8');
29                         $res->body("No access");
30                         return $res;
31                 }
32                 
33                 my ($event, $autorename, $filename) = ($1, $2, $3);
34                 my $size = $r->header('content-length');
35                 my $orig_filename = $filename;
36
37                 # Remove evil characters
38                 if ($filename =~ /[^a-zA-Z0-9._()-]/) {
39                         if (defined($autorename) && $autorename eq "autorename/") {
40                                 $filename =~ tr/a-zA-Z0-9.()-/_/c;
41                         } else {
42                                 $res->status(403);
43                                 $res->content_type('text/plain; charset=utf-8');
44                                 $res->body("Illegal characters in filename");
45                                 return $res;
46                         }
47                 }
48                 
49                 # Get the new ID
50                 my $ref = $dbh->selectrow_hashref("SELECT NEXTVAL('imageid_seq') AS id;");
51                 my $newid = $ref->{'id'};
52                 if (!defined($newid)) {
53                         return dberror($r, "Couldn't get new ID");
54                 }
55                 
56                 # Autorename if we need to
57                 $ref = $dbh->selectrow_hashref("SELECT COUNT(*) AS numfiles FROM images WHERE vhost=? AND event=? AND filename=?",
58                                                undef, Sesse::pr0n::Common::get_server_name($r), $event, $filename)
59                         or return dberror($r, "Couldn't check for existing files");
60                 if ($ref->{'numfiles'} > 0) {
61                         if (defined($autorename) && $autorename eq "autorename/") {
62                                 Sesse::pr0n::Common::log_info($r, "Renaming $filename to $newid.jpeg");
63                                 $filename = "$newid.jpeg";
64                         } else {
65                                 $res->status(403);
66                                 $res->content_type('text/plain; charset=utf-8');
67                                 $res->body("File $filename already exists in event $event, cannot overwrite");
68                                 return $res;
69                         }
70                 }
71                 
72                 {
73                         # Enable transactions and error raising temporarily
74                         local $dbh->{AutoCommit} = 0;
75                         local $dbh->{RaiseError} = 1;
76                         my $fname;
77
78                         # Try to insert this new file
79                         eval {
80                                 $dbh->do('INSERT INTO images (id,vhost,event,uploadedby,takenby,filename) VALUES (?,?,?,?,?,?)',
81                                         undef, $newid, Sesse::pr0n::Common::get_server_name($r), $event, $user, $takenby, $filename);
82                                 Sesse::pr0n::Common::purge_cache($r, $res, "/$event/");
83
84                                 # Now save the file to disk
85                                 Sesse::pr0n::Common::ensure_disk_location_exists($r, $newid);   
86                                 $fname = Sesse::pr0n::Common::get_disk_location($r, $newid);
87
88                                 open NEWFILE, ">", $fname
89                                         or die "$fname: $!";
90                                 print NEWFILE $r->content;
91                                 close NEWFILE or die "close($fname): $!";
92                                 
93                                 # Orient stuff correctly
94                                 system("/usr/bin/exifautotran", $fname) == 0
95                                         or die "/usr/bin/exifautotran: $!";
96
97                                 # Make cache while we're at it.
98                                 # FIXME: Ideally we'd want to ensure cache of -1x-1 here as well (for NEFs), but that would
99                                 # preclude mipmapping in its current form.
100                                 Sesse::pr0n::Common::ensure_cached($r, $filename, $newid, undef, undef, 320, 256);
101                                 
102                                 # OK, we got this far, commit
103                                 $dbh->commit;
104
105                                 Sesse::pr0n::Common::log_info($r, "Successfully wrote $event/$filename to $fname");
106                         };
107                         if ($@) {
108                                 # Some error occurred, rollback and bomb out
109                                 $dbh->rollback;
110                                 unlink($fname);
111                                 return error($r, "Transaction aborted because $@");
112                         }
113                 }
114
115                 $res->content_type('text/plain; charset="utf-8"');
116                 $res->status(201);
117                 $res->body("OK");
118                 return $res;
119         }
120
121         $res->content_type('text/plain; charset=utf-8');
122         Sesse::pr0n::Common::log_error($r, "unknown method " . $r->method);
123         $res->status(500);
124         $res->body("Unknown method");
125         return $res;
126 }
127
128 1;
129
130