| 1 | # Movable Type (r) Open Source (C) 2001-2008 Six Apart, Ltd. |
|---|
| 2 | # This program is distributed under the terms of the |
|---|
| 3 | # GNU General Public License, version 2. |
|---|
| 4 | # |
|---|
| 5 | # $Id$ |
|---|
| 6 | |
|---|
| 7 | package MT::FileMgr::Local; |
|---|
| 8 | use strict; |
|---|
| 9 | |
|---|
| 10 | use MT::FileMgr; |
|---|
| 11 | @MT::FileMgr::Local::ISA = qw( MT::FileMgr ); |
|---|
| 12 | |
|---|
| 13 | use Symbol; |
|---|
| 14 | use Fcntl qw( :DEFAULT :flock ); |
|---|
| 15 | |
|---|
| 16 | sub get_data { |
|---|
| 17 | my $fmgr = shift; |
|---|
| 18 | my($from, $type) = @_; |
|---|
| 19 | my($fh); |
|---|
| 20 | if (!$fmgr->is_handle($from)) { |
|---|
| 21 | $fh = gensym(); |
|---|
| 22 | open $fh, $from |
|---|
| 23 | or return $fmgr->error(MT->translate( |
|---|
| 24 | "Opening local file '[_1]' failed: [_2]", $from, "$!" )); |
|---|
| 25 | } else { |
|---|
| 26 | $fh = $from; |
|---|
| 27 | } |
|---|
| 28 | if ($type && $type eq 'upload') { |
|---|
| 29 | binmode($fh); |
|---|
| 30 | } |
|---|
| 31 | my($data); |
|---|
| 32 | { local $/; $data = <$fh>; } |
|---|
| 33 | close $fh if !$fmgr->is_handle($from); |
|---|
| 34 | $data; |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | ## $type is either 'upload' or 'output' |
|---|
| 38 | sub put { |
|---|
| 39 | my $fmgr = shift; |
|---|
| 40 | my($from, $to, $type) = @_; |
|---|
| 41 | my $rv; |
|---|
| 42 | if (!$fmgr->is_handle($from)) { |
|---|
| 43 | my $fh = gensym(); |
|---|
| 44 | open $fh, $from |
|---|
| 45 | or return $fmgr->error(MT->translate( |
|---|
| 46 | "Opening local file '[_1]' failed: [_2]", $from, "$!")); |
|---|
| 47 | $rv = _write_file($fmgr, $fh, $to, $type); |
|---|
| 48 | close $fh; |
|---|
| 49 | } else { |
|---|
| 50 | $rv = _write_file($fmgr, $from, $to, $type); |
|---|
| 51 | } |
|---|
| 52 | $rv; |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | *put_data = \&_write_file; |
|---|
| 56 | |
|---|
| 57 | sub _write_file { |
|---|
| 58 | my $fmgr = shift; |
|---|
| 59 | my($from, $to, $type) = @_; |
|---|
| 60 | local *FH; |
|---|
| 61 | my($umask, $perms); |
|---|
| 62 | my $cfg = MT->config; |
|---|
| 63 | if ($type && $type eq 'upload') { |
|---|
| 64 | $umask = $cfg->UploadUmask; |
|---|
| 65 | $perms = $cfg->UploadPerms; |
|---|
| 66 | } else { |
|---|
| 67 | $umask = $cfg->HTMLUmask; |
|---|
| 68 | $perms = $cfg->HTMLPerms; |
|---|
| 69 | } |
|---|
| 70 | my $old = umask(oct $umask); |
|---|
| 71 | sysopen FH, $to, O_RDWR|O_CREAT|O_TRUNC, oct $perms |
|---|
| 72 | or return $fmgr->error(MT->translate( |
|---|
| 73 | "Opening local file '[_1]' failed: [_2]", $to, "$!")); |
|---|
| 74 | if ($type && $type eq 'upload') { |
|---|
| 75 | binmode(FH); |
|---|
| 76 | binmode($from) if $fmgr->is_handle($from); |
|---|
| 77 | } |
|---|
| 78 | ## Lock file unless NoLocking specified. |
|---|
| 79 | flock FH, LOCK_EX unless $cfg->NoLocking; |
|---|
| 80 | seek FH, 0, 0; |
|---|
| 81 | truncate FH, 0; |
|---|
| 82 | my $bytes = 0; |
|---|
| 83 | if ($fmgr->is_handle($from)) { |
|---|
| 84 | while (my $len = read $from, my($block), 8192) { |
|---|
| 85 | print FH $block; |
|---|
| 86 | $bytes += $len; |
|---|
| 87 | } |
|---|
| 88 | } else { |
|---|
| 89 | print FH $from; |
|---|
| 90 | $bytes = length($from); |
|---|
| 91 | } |
|---|
| 92 | close FH; |
|---|
| 93 | umask($old); |
|---|
| 94 | $bytes; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | sub exists { -e $_[1] } |
|---|
| 98 | |
|---|
| 99 | sub can_write { -w $_[1] } |
|---|
| 100 | |
|---|
| 101 | sub mkpath { |
|---|
| 102 | my $fmgr = shift; |
|---|
| 103 | my($path) = @_; |
|---|
| 104 | require File::Path; |
|---|
| 105 | my $umask = oct MT->config->DirUmask; |
|---|
| 106 | my $old = umask($umask); |
|---|
| 107 | eval { File::Path::mkpath([$path], 0, 0777) }; |
|---|
| 108 | return $fmgr->error($@) if $@; |
|---|
| 109 | umask($old); |
|---|
| 110 | 1; |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | sub rename { |
|---|
| 114 | my $fmgr = shift; |
|---|
| 115 | my($from, $to) = @_; |
|---|
| 116 | rename $from, $to |
|---|
| 117 | or return $fmgr->error(MT->translate( |
|---|
| 118 | "Renaming '[_1]' to '[_2]' failed: [_3]", $from, $to, "$!")); |
|---|
| 119 | 1; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | sub file_mod_time { |
|---|
| 123 | my $fmgr = shift; |
|---|
| 124 | my ($file) = @_; |
|---|
| 125 | if (-e $file) { |
|---|
| 126 | return (stat($file))[9]; # modification timestamp |
|---|
| 127 | } |
|---|
| 128 | return undef; |
|---|
| 129 | } |
|---|
| 130 | |
|---|
| 131 | sub content_is_updated { |
|---|
| 132 | my $fmgr = shift; |
|---|
| 133 | my($file, $content) = @_; |
|---|
| 134 | return 1 unless -e $file; |
|---|
| 135 | ## If the system has Digest::MD5, compare MD5 hashes; otherwise |
|---|
| 136 | ## read in the file and compare the strings. |
|---|
| 137 | my $fh = gensym(); |
|---|
| 138 | open $fh, $file or return 1; |
|---|
| 139 | if (eval { require Digest::MD5; 1 }) { |
|---|
| 140 | my $ctx = Digest::MD5->new; |
|---|
| 141 | $ctx->addfile($fh); |
|---|
| 142 | close $fh; |
|---|
| 143 | my $data; |
|---|
| 144 | if ($] >= 5.007003) { |
|---|
| 145 | require Encode; |
|---|
| 146 | $data = $$content; |
|---|
| 147 | Encode::_utf8_off($data); |
|---|
| 148 | } elsif ($] >= 5.006001) { |
|---|
| 149 | $data = pack 'C0A*', $$content; |
|---|
| 150 | } else { |
|---|
| 151 | $data = $$content; |
|---|
| 152 | } |
|---|
| 153 | return $ctx->digest ne Digest::MD5::md5($data); |
|---|
| 154 | } else { |
|---|
| 155 | my $data; |
|---|
| 156 | binmode $fh; |
|---|
| 157 | while (read $fh, my($chunk), 8192) { |
|---|
| 158 | $data .= $chunk; |
|---|
| 159 | } |
|---|
| 160 | close $fh; |
|---|
| 161 | return $$content ne $data; |
|---|
| 162 | } |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | sub delete { |
|---|
| 166 | my $fmgr = shift; |
|---|
| 167 | my ($file) = @_; |
|---|
| 168 | |
|---|
| 169 | return 1 unless -e $file or -l $file; |
|---|
| 170 | unlink $file |
|---|
| 171 | or return $fmgr->error(MT->translate( |
|---|
| 172 | "Deleting '[_1]' failed: [_2]", $file, "$!")); |
|---|
| 173 | 1; |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | 1; |
|---|
| 177 | __END__ |
|---|
| 178 | |
|---|
| 179 | =head1 NAME |
|---|
| 180 | |
|---|
| 181 | MT::FileMgr::Local |
|---|
| 182 | |
|---|
| 183 | =head1 AUTHOR & COPYRIGHT |
|---|
| 184 | |
|---|
| 185 | Please see L<MT/AUTHOR & COPYRIGHT>. |
|---|
| 186 | |
|---|
| 187 | =cut |
|---|