| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::DistSync; # $Id: DistSync.pm 25 2017-08-29 09:21:01Z abalama $ | 
| 2 | 1 |  |  | 1 |  | 51020 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 46 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | =head1 NAME | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | App::DistSync - Utility synchronization of the mirror distribution-sites | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 VERSION | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | Version 1.05 | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | use App::DistSync; | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | my $ds = new App::DistSync( | 
| 17 |  |  |  |  |  |  | dir => "/var/www/www.example.com/dist", | 
| 18 |  |  |  |  |  |  | pid => $$, | 
| 19 |  |  |  |  |  |  | ); | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | $ds->init or die ("Initialization error"); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | $ds->sync or die ("Sync error"); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | Utility synchronization of the mirror distribution-sites | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | =head2 METHODS | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | =over 8 | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | =item new | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | my $ds = new App::DistSync( | 
| 36 |  |  |  |  |  |  | dir => "/var/www/www.example.com/dist", | 
| 37 |  |  |  |  |  |  | pid => $$, | 
| 38 |  |  |  |  |  |  | ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | Returns the object | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =item init | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | $ds->init or die ("Initialization error"); | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | Initializing the mirror in the specified directory | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =item sync | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | $ds->sync or die ("Sync error"); | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | Synchronization of the specified directory with the remote resources (mirrors) | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | =back | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | =head2 SHARED FUNCTIONS | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | =over 8 | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | =item fdelete | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | my $status = fdelete( $file ); | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | Deleting a file if it exists | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | =item fetch | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | my $struct = fetch( $URI_STRING, "path/to/file.txt", "/tmp/file.txt" ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | Fetching file from remote resource by URI and filename. | 
| 71 |  |  |  |  |  |  | The result will be written to the specified file. For example: "/tmp/file.txt" | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | Function returns structure, contains: | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | { | 
| 76 |  |  |  |  |  |  | status  => 1,         # Status. 0 - Errors; 1 - OK | 
| 77 |  |  |  |  |  |  | mtime   => 123456789, # Last-Modified in ctime format or 0 in case of errors | 
| 78 |  |  |  |  |  |  | size    => 123,       # Content-length | 
| 79 |  |  |  |  |  |  | code    => 200,       # HTTP Status code | 
| 80 |  |  |  |  |  |  | }; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | =item touch | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | my $status = touch( $file ); | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | Makes files exist, with current timestamp. | 
| 87 |  |  |  |  |  |  | See original in L | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | =back | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =head1 HISTORY | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | See C file | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | =head1 DEPENDENCIES | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | L | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =head1 TO DO | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | See C file | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | =head1 BUGS | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | * none noted | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | C, L, L | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | =head1 AUTHOR | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | Serz Minus (Lepenkov Sergey) L Eminus@mail333.comE | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | Copyright (C) 1998-2014 D&D Corporation. All Rights Reserved | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =head1 LICENSE | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | This program is free software: you can redistribute it and/or modify | 
| 122 |  |  |  |  |  |  | it under the terms of the GNU General Public License as published by | 
| 123 |  |  |  |  |  |  | the Free Software Foundation, either version 3 of the License, or | 
| 124 |  |  |  |  |  |  | (at your option) any later version. | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | This program is distributed in the hope that it will be useful, | 
| 127 |  |  |  |  |  |  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 128 |  |  |  |  |  |  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
| 129 |  |  |  |  |  |  | GNU General Public License for more details. | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | See C file | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =cut | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 1 |  |  | 1 |  | 5 | use vars qw/$VERSION/; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 66 |  | 
| 136 |  |  |  |  |  |  | $VERSION = '1.05'; | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 1 |  |  | 1 |  | 6 | use Carp; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 49 |  | 
| 139 | 1 |  |  | 1 |  | 5 | use File::Basename; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 64 |  | 
| 140 | 1 |  |  | 1 |  | 289 | use File::Copy qw/ mv /; | 
|  | 1 |  |  |  |  | 3410 |  | 
|  | 1 |  |  |  |  | 63 |  | 
| 141 | 1 |  |  | 1 |  | 10 | use File::Spec; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 34 |  | 
| 142 | 1 |  |  | 1 |  | 7 | use File::Find; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 79 |  | 
| 143 | 1 |  |  | 1 |  | 9 | use File::Path; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 55 |  | 
| 144 | 1 |  |  | 1 |  | 365 | use YAML::Tiny; | 
|  | 1 |  |  |  |  | 4499 |  | 
|  | 1 |  |  |  |  | 62 |  | 
| 145 | 1 |  |  | 1 |  | 429 | use URI; | 
|  | 1 |  |  |  |  | 3612 |  | 
|  | 1 |  |  |  |  | 51 |  | 
| 146 | 1 |  |  | 1 |  | 337 | use LWP::Simple qw/$ua head mirror/; | 
|  | 1 |  |  |  |  | 50012 |  | 
|  | 1 |  |  |  |  | 14 |  | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 1 |  |  | 1 |  | 265 | use base qw/Exporter/; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 662 |  | 
| 149 |  |  |  |  |  |  | our @EXPORT = qw/ | 
| 150 |  |  |  |  |  |  | debug | 
| 151 |  |  |  |  |  |  | /; # Auto | 
| 152 |  |  |  |  |  |  | our @EXPORT_OK = qw/ | 
| 153 |  |  |  |  |  |  | debug | 
| 154 |  |  |  |  |  |  | touch | 
| 155 |  |  |  |  |  |  | fdelete | 
| 156 |  |  |  |  |  |  | read_yaml | 
| 157 |  |  |  |  |  |  | write_yaml | 
| 158 |  |  |  |  |  |  | maniread | 
| 159 |  |  |  |  |  |  | maniwrite | 
| 160 |  |  |  |  |  |  | fetch | 
| 161 |  |  |  |  |  |  | /; # Manual | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | use constant { | 
| 164 |  |  |  |  |  |  | TEMPFILE    => sprintf("distsync_%s.tmp", $$), | 
| 165 |  |  |  |  |  |  | TIMEOUT     => 30, | 
| 166 |  |  |  |  |  |  | METAFILE    => 'META', | 
| 167 |  |  |  |  |  |  | MANIFEST    => 'MANIFEST', | 
| 168 |  |  |  |  |  |  | MANISKIP    => 'MANIFEST.SKIP', | 
| 169 |  |  |  |  |  |  | MANITEMP    => 'MANIFEST.TEMP', | 
| 170 |  |  |  |  |  |  | MANILOCK    => 'MANIFEST.LOCK', | 
| 171 |  |  |  |  |  |  | MANIDEL     => 'MANIFEST.DEL', | 
| 172 |  |  |  |  |  |  | MIRRORS     => 'MIRRORS', | 
| 173 |  |  |  |  |  |  | README      => 'README', | 
| 174 |  |  |  |  |  |  | SKIPFILES   => [qw/ | 
| 175 |  |  |  |  |  |  | META | 
| 176 |  |  |  |  |  |  | MANIFEST | 
| 177 |  |  |  |  |  |  | MANIFEST.SKIP | 
| 178 |  |  |  |  |  |  | MANIFEST.LOCK | 
| 179 |  |  |  |  |  |  | MANIFEST.TEMP | 
| 180 |  |  |  |  |  |  | MANIFEST.DEL | 
| 181 |  |  |  |  |  |  | MIRRORS | 
| 182 |  |  |  |  |  |  | README | 
| 183 |  |  |  |  |  |  | /], | 
| 184 |  |  |  |  |  |  | SKIPMODE    => 1, | 
| 185 |  |  |  |  |  |  | LIMIT       => '+1m', # '+1m' Limit gt and lt | 
| 186 |  |  |  |  |  |  | EXPIRE      => '+3d', # '+3d' For deleting | 
| 187 |  |  |  |  |  |  | FREEZE      => '+1d', # '+1d' For META test | 
| 188 |  |  |  |  |  |  | QRTYPES => { | 
| 189 | 0 |  |  |  |  |  | ''  => sub { qr{$_[0]} }, | 
| 190 | 0 |  |  |  |  |  | x   => sub { qr{$_[0]}x }, | 
| 191 | 0 |  |  |  |  |  | i   => sub { qr{$_[0]}i }, | 
| 192 | 0 |  |  |  |  |  | s   => sub { qr{$_[0]}s }, | 
| 193 | 0 |  |  |  |  |  | m   => sub { qr{$_[0]}m }, | 
| 194 | 0 |  |  |  |  |  | ix  => sub { qr{$_[0]}ix }, | 
| 195 | 0 |  |  |  |  |  | sx  => sub { qr{$_[0]}sx }, | 
| 196 | 0 |  |  |  |  |  | mx  => sub { qr{$_[0]}mx }, | 
| 197 | 0 |  |  |  |  |  | si  => sub { qr{$_[0]}si }, | 
| 198 | 0 |  |  |  |  |  | mi  => sub { qr{$_[0]}mi }, | 
| 199 | 0 |  |  |  |  |  | ms  => sub { qr{$_[0]}sm }, | 
| 200 | 0 |  |  |  |  |  | six => sub { qr{$_[0]}six }, | 
| 201 | 0 |  |  |  |  |  | mix => sub { qr{$_[0]}mix }, | 
| 202 | 0 |  |  |  |  |  | msx => sub { qr{$_[0]}msx }, | 
| 203 | 0 |  |  |  |  |  | msi => sub { qr{$_[0]}msi }, | 
| 204 | 0 |  |  |  |  |  | msix => sub { qr{$_[0]}msix }, | 
| 205 |  |  |  |  |  |  | }, | 
| 206 | 1 |  |  | 1 |  | 12 | }; | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 8231 |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | our $DEBUG = 0; | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | # Methods | 
| 211 |  |  |  |  |  |  | sub new { | 
| 212 | 0 |  |  | 0 | 1 |  | my $class = shift; | 
| 213 | 0 |  |  |  |  |  | my %props = @_; | 
| 214 |  |  |  |  |  |  |  | 
| 215 | 0 |  |  |  |  |  | $props{stamp} = time; | 
| 216 | 0 |  | 0 |  |  |  | $props{pid} ||= $$; | 
| 217 | 0 |  | 0 |  |  |  | $props{timeout} //= TIMEOUT; | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | # Directories check | 
| 220 | 0 |  |  |  |  |  | my $dir = $props{dir}; | 
| 221 | 0 | 0 | 0 |  |  |  | carp("Can't select directory") && return unless defined $dir; | 
| 222 | 0 |  |  |  |  |  | $props{file_meta}       = File::Spec->catfile($dir, METAFILE); | 
| 223 | 0 |  |  |  |  |  | $props{file_manifest}   = File::Spec->catfile($dir, MANIFEST); | 
| 224 | 0 |  |  |  |  |  | $props{file_maniskip}   = File::Spec->catfile($dir, MANISKIP); | 
| 225 | 0 |  |  |  |  |  | $props{file_manilock}   = File::Spec->catfile($dir, MANILOCK); | 
| 226 | 0 |  |  |  |  |  | $props{file_manitemp}   = File::Spec->catfile($dir, MANITEMP); | 
| 227 | 0 |  |  |  |  |  | $props{file_manidel}    = File::Spec->catfile($dir, MANIDEL); | 
| 228 | 0 |  |  |  |  |  | $props{file_mirrors}    = File::Spec->catfile($dir, MIRRORS); | 
| 229 | 0 |  |  |  |  |  | $props{file_readme}     = File::Spec->catfile($dir, README); | 
| 230 | 0 |  |  |  |  |  | $props{file_temp}       = File::Spec->catfile(File::Spec->tmpdir(), TEMPFILE); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # Read META file as YAML | 
| 233 | 0 |  |  |  |  |  | my $meta = read_yaml($props{file_meta}); | 
| 234 | 0 |  |  |  |  |  | $props{meta} = $meta; | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | # Create current static dates | 
| 237 |  |  |  |  |  |  | $props{mtime_manifest} = (-e $props{file_manifest}) && -s $props{file_manifest} | 
| 238 | 0 | 0 | 0 |  |  |  | ? (stat($props{file_manifest}))[9] | 
| 239 |  |  |  |  |  |  | : 0; | 
| 240 |  |  |  |  |  |  | $props{mtime_manidel}  = (-e $props{file_manidel}) && -s $props{file_manidel} | 
| 241 | 0 | 0 | 0 |  |  |  | ? (stat($props{file_manidel}))[9] | 
| 242 |  |  |  |  |  |  | : 0; | 
| 243 |  |  |  |  |  |  | $props{mtime_mirrors}  = (-e $props{file_mirrors}) && -s $props{file_mirrors} | 
| 244 | 0 | 0 | 0 |  |  |  | ? (stat($props{file_mirrors}))[9] | 
| 245 |  |  |  |  |  |  | : 0; | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | # Read MANIFEST, MANIFEST.SKIP, MANIFEST.DEL files | 
| 248 | 0 |  |  |  |  |  | $props{manifest} = maniread($props{file_manifest}); | 
| 249 | 0 |  |  |  |  |  | $props{maniskip} = maniread($props{file_maniskip}, SKIPMODE); | 
| 250 | 0 |  |  |  |  |  | $props{manidel}  = maniread($props{file_manidel}); | 
| 251 | 0 |  |  |  |  |  | $props{mirrors}  = maniread($props{file_mirrors}); | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # TimeOut | 
| 254 | 0 |  |  |  |  |  | my $to = $props{timeout}; | 
| 255 | 0 | 0 | 0 |  |  |  | if ($to && $to =~ /^[0-9]{1,11}$/) { | 
| 256 | 0 |  |  |  |  |  | $ua->timeout($to); | 
| 257 |  |  |  |  |  |  | } else { | 
| 258 | 0 |  |  |  |  |  | croak(sprintf("Can't use specified timeout value: %s", $to)); | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 0 |  |  |  |  |  | my $self = bless({%props}, $class); | 
| 262 | 0 |  |  |  |  |  | return $self; | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  | sub init { # Initialization | 
| 265 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 266 | 0 |  |  |  |  |  | my $stamp = scalar(localtime($self->{stamp})); | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | # MANIFEST.SKIP | 
| 269 | 0 | 0 |  |  |  |  | touch($self->{file_maniskip}) or return 0; | 
| 270 | 0 | 0 | 0 |  |  |  | if (-e $self->{file_maniskip} && -z $self->{file_maniskip}) { | 
| 271 | 0 | 0 |  |  |  |  | open FILE, ">", $self->{file_maniskip} or return 0; | 
| 272 | 0 |  |  |  |  |  | printf FILE join("\n", | 
| 273 |  |  |  |  |  |  | "# Generated on %s", | 
| 274 |  |  |  |  |  |  | "# List of files that should not be synchronized", | 
| 275 |  |  |  |  |  |  | "#", | 
| 276 |  |  |  |  |  |  | "# Format of file:", | 
| 277 |  |  |  |  |  |  | "#", | 
| 278 |  |  |  |  |  |  | "# dir1/dir2/.../dirn/foo.txt        any comment, for example blah-blah-blah", | 
| 279 |  |  |  |  |  |  | "# bar.txt                           any comment, for example blah-blah-blah", | 
| 280 |  |  |  |  |  |  | "# baz.txt", | 
| 281 |  |  |  |  |  |  | "# 'spaced dir1/foo.txt'             any comment, for example blah-blah-blah", | 
| 282 |  |  |  |  |  |  | "# 'spaced dir1/foo.txt'             any comment, for example blah-blah-blah", | 
| 283 |  |  |  |  |  |  | "# !!perl/regexp (?i-xsm:\\.bak\$)     avoid all bak files", | 
| 284 |  |  |  |  |  |  | "#", | 
| 285 |  |  |  |  |  |  | "# See also MANIFEST.SKIP file of ExtUtils::Manifest v1.68 or later", | 
| 286 |  |  |  |  |  |  | "#", | 
| 287 |  |  |  |  |  |  | "", | 
| 288 |  |  |  |  |  |  | "# Avoid version control files.", | 
| 289 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\bRCS\\b)", | 
| 290 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\bCVS\\b)", | 
| 291 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\bSCCS\\b)", | 
| 292 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:,v\$)", | 
| 293 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.svn\\b)", | 
| 294 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.git\\b)", | 
| 295 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.gitignore\\b)", | 
| 296 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\b_darcs\\b)", | 
| 297 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.cvsignore\$)", | 
| 298 |  |  |  |  |  |  | "", | 
| 299 |  |  |  |  |  |  | "# Avoid temp and backup files.", | 
| 300 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:~\$)", | 
| 301 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\.(old|bak|tmp|rej)\$)", | 
| 302 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\#\$)", | 
| 303 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\b\\.#)", | 
| 304 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\.#)", | 
| 305 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\..*\\.sw.?\$)", | 
| 306 |  |  |  |  |  |  | "", | 
| 307 |  |  |  |  |  |  | "# Avoid prove files", | 
| 308 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.prove\$)", | 
| 309 |  |  |  |  |  |  | "", | 
| 310 |  |  |  |  |  |  | "# Avoid MYMETA files", | 
| 311 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:^MYMETA\\.)", | 
| 312 |  |  |  |  |  |  | "", | 
| 313 |  |  |  |  |  |  | "# Avoid Apache and building files", | 
| 314 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.ht.+\$)", | 
| 315 |  |  |  |  |  |  | "!!perl/regexp (?i-xsm:\\B\\.exists\$)", | 
| 316 |  |  |  |  |  |  | "\n", | 
| 317 |  |  |  |  |  |  | ), $stamp; | 
| 318 | 0 |  |  |  |  |  | close FILE; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | # MANIFEST.DEL | 
| 322 | 0 | 0 |  |  |  |  | touch($self->{file_manidel}) or return 0; | 
| 323 | 0 | 0 | 0 |  |  |  | if (-e $self->{file_manidel} && -z $self->{file_manidel}) { | 
| 324 | 0 | 0 |  |  |  |  | open FILE, ">", $self->{file_manidel} or return 0; | 
| 325 | 0 |  |  |  |  |  | printf FILE join("\n", | 
| 326 |  |  |  |  |  |  | "# Generated on %s", | 
| 327 |  |  |  |  |  |  | "# List of files that must be deleted. By default, the files will be", | 
| 328 |  |  |  |  |  |  | "# deleted after 3 days.", | 
| 329 |  |  |  |  |  |  | "#", | 
| 330 |  |  |  |  |  |  | "# Format of file:", | 
| 331 |  |  |  |  |  |  | "#", | 
| 332 |  |  |  |  |  |  | "# dir1/dir2/.../dirn/foo.txt        1d", | 
| 333 |  |  |  |  |  |  | "# bar.txt                           2M", | 
| 334 |  |  |  |  |  |  | "# baz.txt", | 
| 335 |  |  |  |  |  |  | "# 'spaced dir1/foo.txt'             1m", | 
| 336 |  |  |  |  |  |  | "# 'spaced dir1/foo.txt'             2y", | 
| 337 |  |  |  |  |  |  | "#", | 
| 338 |  |  |  |  |  |  | "\n", | 
| 339 |  |  |  |  |  |  | ), $stamp; | 
| 340 | 0 |  |  |  |  |  | close FILE; | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | # MIRRORS | 
| 344 | 0 | 0 |  |  |  |  | touch($self->{file_mirrors}) or return 0; | 
| 345 | 0 | 0 | 0 |  |  |  | if (-e $self->{file_mirrors} && -z $self->{file_mirrors}) { | 
| 346 | 0 | 0 |  |  |  |  | open FILE, ">", $self->{file_mirrors} or return 0; | 
| 347 | 0 |  |  |  |  |  | printf FILE join("\n", | 
| 348 |  |  |  |  |  |  | "# Generated on %s", | 
| 349 |  |  |  |  |  |  | "# List of addresses (URIs) of remote storage (mirrors).", | 
| 350 |  |  |  |  |  |  | "# Must be specified at least two mirrors", | 
| 351 |  |  |  |  |  |  | "#", | 
| 352 |  |  |  |  |  |  | "# Format of file:", | 
| 353 |  |  |  |  |  |  | "#", | 
| 354 |  |  |  |  |  |  | "# http://www.example.com/dir1       any comment, for example blah-blah-blah", | 
| 355 |  |  |  |  |  |  | "# http://www.example.com/dir2       any comment, for example blah-blah-blah", | 
| 356 |  |  |  |  |  |  | "# 'http://www.example.com/dir2'     any comment, for example blah-blah-blah", | 
| 357 |  |  |  |  |  |  | "#", | 
| 358 |  |  |  |  |  |  | "\n", | 
| 359 |  |  |  |  |  |  | ), $stamp; | 
| 360 | 0 |  |  |  |  |  | close FILE; | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | # README | 
| 364 | 0 | 0 |  |  |  |  | touch($self->{file_readme}) or return 0; | 
| 365 | 0 | 0 | 0 |  |  |  | if (-e $self->{file_readme} && -z $self->{file_readme}) { | 
| 366 | 0 | 0 |  |  |  |  | open FILE, ">", $self->{file_readme} or return 0; | 
| 367 |  |  |  |  |  |  | printf FILE join("\n", | 
| 368 |  |  |  |  |  |  | "# This file contains information about the resource (mirror) in the free form.", | 
| 369 |  |  |  |  |  |  | "#", | 
| 370 |  |  |  |  |  |  | "# Initialization date  : %s", | 
| 371 |  |  |  |  |  |  | "# Resource's directory : %s", | 
| 372 |  |  |  |  |  |  | "#", | 
| 373 |  |  |  |  |  |  | "\n", | 
| 374 | 0 |  |  |  |  |  | ), $stamp, $self->{dir}; | 
| 375 | 0 |  |  |  |  |  | close FILE; | 
| 376 |  |  |  |  |  |  | } | 
| 377 |  |  |  |  |  |  |  | 
| 378 | 0 |  |  |  |  |  | return 1; | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  | sub sync { # Synchronization. Main proccess | 
| 381 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 382 | 0 |  |  |  |  |  | my $status = 0; # Статус операции для META | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | # Создаем список исключений на базе прочитанного ранее SKIP + системные файлы | 
| 385 | 0 |  |  |  |  |  | my @skip_keys = @{(SKIPFILES)}; | 
|  | 0 |  |  |  |  |  |  | 
| 386 | 0 | 0 |  |  |  |  | push @skip_keys, keys %{($self->{maniskip})} if ref($self->{maniskip}) eq 'HASH'; | 
|  | 0 |  |  |  |  |  |  | 
| 387 | 0 |  |  |  |  |  | my %skips; for (@skip_keys) {$skips{$_} = _qrreconstruct($_)} | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper(\%skips)) && return 0; | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | # Удяляем файлы перечисленные в .DEL | 
| 391 | 0 |  |  |  |  |  | debug("Deleting of declared files"); | 
| 392 | 0 |  |  |  |  |  | my $dellist = $self->{manidel}; | 
| 393 | 0 |  |  |  |  |  | my $expire = _expire(0); | 
| 394 | 0 | 0 | 0 |  |  |  | if ($dellist && ref($dellist) eq 'HASH') { | 
| 395 | 0 |  |  |  |  |  | foreach (values %$dellist) { | 
| 396 | 0 |  | 0 |  |  |  | my $dt = _expire($_->[0] || 0); | 
| 397 | 0 |  |  |  |  |  | $_ = [$dt]; | 
| 398 | 0 | 0 |  |  |  |  | $expire = $dt if $dt > $expire; | 
| 399 |  |  |  |  |  |  | } | 
| 400 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($dellist)); | 
| 401 |  |  |  |  |  |  | } | 
| 402 | 0 | 0 |  |  |  |  | $expire = _expire(EXPIRE) unless $expire > 0; | 
| 403 | 0 |  |  |  |  |  | debug(sprintf("Expires at %s", scalar(localtime(time + $expire)))); | 
| 404 | 0 |  |  |  |  |  | my $delfile = $self->{file_manidel}; | 
| 405 | 0 |  |  |  |  |  | my $deltime = $self->{mtime_manidel}; | 
| 406 | 0 | 0 | 0 |  |  |  | if ($deltime && (time - $deltime) > $expire) { | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | # Удаляем файлы физически, если они есть физически и их нет в SKIP файле! | 
| 409 | 0 |  |  |  |  |  | foreach my $k (keys %$dellist) { | 
| 410 | 0 | 0 |  |  |  |  | if (_skipcheck(\%skips, $k)) { # Файл есть в списке исклюений | 
| 411 | 0 |  |  |  |  |  | debug(sprintf("> [SKIPPED] %s", $k)); | 
| 412 |  |  |  |  |  |  | } else { | 
| 413 | 0 |  |  |  |  |  | my $f = File::Spec->canonpath(File::Spec->catfile($self->{dir}, $k)); | 
| 414 | 0 | 0 |  |  |  |  | if (-e $f) { | 
| 415 | 0 |  |  |  |  |  | fdelete($f); | 
| 416 | 0 |  |  |  |  |  | debug(sprintf("> [DELETED] %s", $k)); | 
| 417 |  |  |  |  |  |  | } else { | 
| 418 | 0 |  |  |  |  |  | debug(sprintf("> [MISSING] %s (%s)", $k, $f)); | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 0 |  |  |  |  |  | fdelete($delfile); # Удаляем файл MANIFEST.DEL | 
| 424 | 0 |  |  |  |  |  | touch($delfile); # Создаем новый файл MANIFEST.DEL | 
| 425 |  |  |  |  |  |  | } else { | 
| 426 | 0 | 0 |  |  |  |  | if ($deltime) { | 
| 427 | 0 |  |  |  |  |  | debug(sprintf("Deleting is skipped. File %s\n\tcreated\t%s;\n\tnow\t%s;\n\texpires\t%s", | 
| 428 |  |  |  |  |  |  | MANIDEL, | 
| 429 |  |  |  |  |  |  | scalar(localtime($deltime)), | 
| 430 |  |  |  |  |  |  | scalar(localtime(time)), | 
| 431 |  |  |  |  |  |  | scalar(localtime($deltime + $expire)), | 
| 432 |  |  |  |  |  |  | )); | 
| 433 |  |  |  |  |  |  | } else { | 
| 434 | 0 |  |  |  |  |  | debug(sprintf("Deleting is skipped. Missing file %s",  MANIDEL)) | 
| 435 |  |  |  |  |  |  | } | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | # Добавляем в список исключений на базе прочитанного ранее SKIP - DEL файлы | 
| 439 | 0 | 0 |  |  |  |  | my @del_keys = keys %$dellist if ref($dellist) eq 'HASH'; | 
| 440 | 0 |  |  |  |  |  | for (@del_keys) {$skips{$_} = _qrreconstruct($_)} | 
|  | 0 |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | ################ | 
| 443 |  |  |  |  |  |  | # Синхронизация | 
| 444 |  |  |  |  |  |  | ################ | 
| 445 | 0 |  |  |  |  |  | my %sync_list;      # Синхронизационный список | 
| 446 |  |  |  |  |  |  | my %delete_list;    # Список на удаление | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | # Чтение MIRRORS и принятие решения - делать синхронизацию или нет | 
| 449 | 0 |  |  |  |  |  | debug("Synchronization"); | 
| 450 | 0 |  |  |  |  |  | my $mirror_list = $self->{mirrors}; | 
| 451 | 0 | 0 |  |  |  |  | my @mirrors = sort {$a cmp $b} keys %$mirror_list if ref($mirror_list) eq 'HASH'; | 
|  | 0 |  |  |  |  |  |  | 
| 452 | 0 | 0 |  |  |  |  | if (@mirrors) { | 
| 453 | 0 |  |  |  |  |  | foreach my $url (@mirrors) { | 
| 454 | 0 |  |  |  |  |  | debug(sprintf("\nRESOURCE %s",$url)); | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | # Получение .LOCK файла, пропуск если он имеется | 
| 457 | 0 |  |  |  |  |  | debug(sprintf("Fetching %s file", MANILOCK)); | 
| 458 | 0 |  |  |  |  |  | my $fetch_lock = fetch($url, MANILOCK, $self->{file_manitemp}); | 
| 459 | 0 | 0 |  |  |  |  | if ($fetch_lock->{status}) { | 
| 460 | 0 | 0 |  |  |  |  | if ($self->check_lock($self->{file_manitemp})) { | 
| 461 | 0 |  |  |  |  |  | $self->{uri} = $url; | 
| 462 | 0 |  |  |  |  |  | debug("> [SKIPPED] Current resource SHOULD NOT update itself"); | 
| 463 |  |  |  |  |  |  | } else { | 
| 464 | 0 |  |  |  |  |  | debug("> [SKIPPED] Remote resource is in a state of updating. Please wait"); | 
| 465 |  |  |  |  |  |  | } | 
| 466 | 0 |  |  |  |  |  | next; | 
| 467 |  |  |  |  |  |  | } | 
| 468 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($fetch_data)); | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | # Получение удаленного META и анализ его на status = 1. Иначе, пропуск данного ресурса | 
| 471 | 0 |  |  |  |  |  | debug(sprintf("Fetching %s file", METAFILE)); | 
| 472 | 0 |  |  |  |  |  | my $fetch_meta = fetch($url, METAFILE, $self->{file_manitemp}); | 
| 473 | 0 | 0 |  |  |  |  | if ($fetch_meta->{status}) { | 
| 474 | 0 |  |  |  |  |  | my $remote_meta = read_yaml($self->{file_manitemp}); | 
| 475 | 0 | 0 | 0 |  |  |  | if ($remote_meta && ((ref($remote_meta) eq 'ARRAY') || ref($remote_meta) eq 'YAML::Tiny')) { | 
|  |  | 0 | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 476 | 0 |  | 0 |  |  |  | $remote_meta = $remote_meta->[0] || {}; | 
| 477 |  |  |  |  |  |  | } elsif ($remote_meta && ref($remote_meta) eq 'HASH') { | 
| 478 |  |  |  |  |  |  | # OK | 
| 479 |  |  |  |  |  |  | } else { | 
| 480 | 0 |  |  |  |  |  | debug(Data::Dumper::Dumper(ref($remote_meta),$remote_meta)); | 
| 481 | 0 |  |  |  |  |  | debug("> [SKIPPED] Remote resource is unreadable. Please contact the administrator of this resource"); | 
| 482 | 0 |  |  |  |  |  | next; | 
| 483 |  |  |  |  |  |  | } | 
| 484 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($remote_meta)); | 
| 485 | 0 | 0 | 0 |  |  |  | if ($remote_meta && $remote_meta->{status}) { | 
| 486 | 0 |  | 0 |  |  |  | my $remote_uri  = $remote_meta->{uri} || 'localhost'; | 
| 487 | 0 |  | 0 |  |  |  | my $remote_date = $fetch_meta->{mtime} || 0; | 
| 488 | 0 | 0 |  |  |  |  | my $remote_ok = (time - $remote_date) > _expire(FREEZE) ? 0 : 1; | 
| 489 |  |  |  |  |  |  | debug(sprintf("REMOTE RESOURCE:" | 
| 490 |  |  |  |  |  |  | ."\n\tResource:\t%s" | 
| 491 |  |  |  |  |  |  | ."\n\tDate:\t\t%s" | 
| 492 |  |  |  |  |  |  | ."\n\tModified:\t%s" | 
| 493 |  |  |  |  |  |  | ."\n\tStatus:\t\t%s", | 
| 494 |  |  |  |  |  |  | $remote_uri, | 
| 495 | 0 | 0 |  |  |  |  | defined $remote_meta->{date} ? $remote_meta->{date} : 'UNKNOWN', | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | $remote_date ? scalar(localtime($remote_date)) : 'UNKNOWN', | 
| 497 |  |  |  |  |  |  | $remote_ok ? "OK" : "EXPIRED" | 
| 498 |  |  |  |  |  |  | )); | 
| 499 | 0 | 0 |  |  |  |  | unless ($remote_ok) { | 
| 500 | 0 | 0 |  |  |  |  | debug(sprintf("> [SKIPPED] Remote resource is expired. Last updated: %s", | 
| 501 |  |  |  |  |  |  | $remote_date ? scalar(localtime($remote_date)) : 'UNKNOWN' | 
| 502 |  |  |  |  |  |  | )); | 
| 503 |  |  |  |  |  |  | next | 
| 504 | 0 |  |  |  |  |  | } | 
| 505 |  |  |  |  |  |  | } else { | 
| 506 | 0 |  |  |  |  |  | debug("> [SKIPPED] Remote resource is in negative state. Please contact the administrator of this resource"); | 
| 507 | 0 |  |  |  |  |  | next; | 
| 508 |  |  |  |  |  |  | } | 
| 509 |  |  |  |  |  |  | } | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | # Получение удаленного MANIFEST | 
| 512 | 0 |  |  |  |  |  | debug(sprintf("Fetching %s file", MANIFEST)); | 
| 513 | 0 |  |  |  |  |  | my $fetch_mani = fetch($url, MANIFEST, $self->{file_manitemp}); | 
| 514 | 0 | 0 |  |  |  |  | if ($fetch_mani->{status}) { | 
| 515 |  |  |  |  |  |  | # Читаем файл в отдельную структуру | 
| 516 | 0 |  |  |  |  |  | my $remote_manifest = maniread($self->{file_manitemp}); | 
| 517 | 0 |  |  |  |  |  | my $local_manifest = $self->{manifest}; | 
| 518 | 0 |  |  |  |  |  | my %mtmp; | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | # Два списка объединяются во временную структуру | 
| 521 | 0 |  |  |  |  |  | foreach my $k (keys(%$local_manifest), keys(%$remote_manifest)) { | 
| 522 | 0 | 0 |  |  |  |  | if ($mtmp{$k}) { | 
| 523 | 0 |  | 0 |  |  |  | my $mt_l = $local_manifest->{$k}[0] || 0; | 
| 524 | 0 |  | 0 |  |  |  | my $mt_r = $remote_manifest->{$k}[0] || 0; | 
| 525 | 0 | 0 | 0 |  |  |  | $mtmp{$k}++ if $mt_l && $mt_r && $mt_l == $mt_r; | 
|  |  |  | 0 |  |  |  |  | 
| 526 |  |  |  |  |  |  | } else { | 
| 527 | 0 |  |  |  |  |  | $mtmp{$k} = 1 | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($mt_l,$mt_r)); | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | # Полуаем разницумоих и удаленных файлов | 
| 533 |  |  |  |  |  |  | # [<] Есть строка в левом файле | 
| 534 |  |  |  |  |  |  | # [>] есть строка в правом файле | 
| 535 |  |  |  |  |  |  | # [{] Более "свежий" в левом файле | 
| 536 |  |  |  |  |  |  | # [}] Более "свежий" в првом файле | 
| 537 |  |  |  |  |  |  | # [~] Отличаются размеры файлов в строке. Просто вывод информации об этом, | 
| 538 |  |  |  |  |  |  | #     т.к. более приоритетными являются даты модификации и наличие. | 
| 539 |  |  |  |  |  |  | # | 
| 540 |  |  |  |  |  |  | # Сравнение делается так: | 
| 541 |  |  |  |  |  |  | # пробегамся по полученному хэшу и смотрим где инкремент равен 1! | 
| 542 |  |  |  |  |  |  | # Там где 1 - значит данный файл есть в одном из файлов, в каком? если | 
| 543 |  |  |  |  |  |  | # в левом, помечается что в левом, иначе в правом | 
| 544 | 0 |  |  |  |  |  | foreach my $k (keys %mtmp) { | 
| 545 | 0 | 0 | 0 |  |  |  | next unless $mtmp{$k} && $mtmp{$k} == 1; | 
| 546 | 0 | 0 | 0 |  |  |  | if ($local_manifest->{$k} && $remote_manifest->{$k}) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 547 | 0 |  | 0 |  |  |  | my $mt_l = $local_manifest->{$k}[0] || 0; | 
| 548 | 0 |  | 0 |  |  |  | my $mt_r = $remote_manifest->{$k}[0] || 0; | 
| 549 | 0 | 0 | 0 |  |  |  | if (($mt_l > $mt_r) && ($mt_l - $mt_r) > _expire(LIMIT)) { | 
| 550 |  |  |  |  |  |  | # debug(sprintf("> [{] %s", $k)); | 
| 551 | 0 | 0 | 0 |  |  |  | } if (($mt_l < $mt_r) && ($mt_r - $mt_l) > _expire(LIMIT)) { | 
| 552 | 0 |  |  |  |  |  | debug(sprintf("> [}] %s (LOC: %s < RMT: %s)", $k, | 
| 553 |  |  |  |  |  |  | scalar(localtime($mt_l)), | 
| 554 |  |  |  |  |  |  | scalar(localtime($mt_r)), | 
| 555 |  |  |  |  |  |  | )); | 
| 556 |  |  |  |  |  |  | # Скачиваем т.к. там свежее | 
| 557 | 0 | 0 |  |  |  |  | unless (_skipcheck(\%skips, $k)) { | 
| 558 | 0 |  | 0 |  |  |  | my $ar = $sync_list{$k} || []; | 
| 559 |  |  |  |  |  |  | push @$ar, { | 
| 560 |  |  |  |  |  |  | uri     => $url, | 
| 561 |  |  |  |  |  |  | mtime   => $remote_manifest->{$k}[0], | 
| 562 | 0 |  |  |  |  |  | size   => $remote_manifest->{$k}[1], | 
| 563 |  |  |  |  |  |  | }; | 
| 564 | 0 |  |  |  |  |  | $sync_list{$k} = $ar; | 
| 565 |  |  |  |  |  |  | } | 
| 566 |  |  |  |  |  |  | } else { | 
| 567 |  |  |  |  |  |  | #debug(sprintf("> [=] %s", $k)); | 
| 568 |  |  |  |  |  |  | } | 
| 569 |  |  |  |  |  |  | } elsif ($local_manifest->{$k}) { | 
| 570 |  |  |  |  |  |  | # debug(sprintf("> [<] %s", $k)); | 
| 571 |  |  |  |  |  |  | } elsif ($remote_manifest->{$k}) { | 
| 572 | 0 |  |  |  |  |  | debug(sprintf("> [>] %s", $k)); | 
| 573 |  |  |  |  |  |  | # Скачиваем, т.к. у нас такого нет | 
| 574 | 0 | 0 |  |  |  |  | unless (_skipcheck(\%skips, $k)) { | 
| 575 | 0 |  | 0 |  |  |  | my $ar = $sync_list{$k} || []; | 
| 576 |  |  |  |  |  |  | push @$ar, { | 
| 577 |  |  |  |  |  |  | uri     => $url, | 
| 578 |  |  |  |  |  |  | mtime   => $remote_manifest->{$k}[0], | 
| 579 | 0 |  |  |  |  |  | size    => $remote_manifest->{$k}[1], | 
| 580 |  |  |  |  |  |  | }; | 
| 581 | 0 |  |  |  |  |  | $sync_list{$k} = $ar; | 
| 582 |  |  |  |  |  |  | } | 
| 583 |  |  |  |  |  |  | } else { | 
| 584 | 0 |  |  |  |  |  | debug(sprintf("> [!] %s", $k)); | 
| 585 |  |  |  |  |  |  | } | 
| 586 |  |  |  |  |  |  | } | 
| 587 | 0 |  |  |  |  |  | $status = 1; # Удалось связаться с ресурсом, значит он доступен | 
| 588 |  |  |  |  |  |  | } else { | 
| 589 |  |  |  |  |  |  | debug(sprintf("> [MISSING] File %s not fetched. Status code: %s", | 
| 590 |  |  |  |  |  |  | MANIFEST, | 
| 591 | 0 |  | 0 |  |  |  | $fetch_mani->{code} || 'UNDEFINED', | 
| 592 |  |  |  |  |  |  | )); | 
| 593 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($fetch_mani)); | 
| 594 | 0 |  |  |  |  |  | next; | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | # Пробегаемся по MIRRORS удаленным файлам и добавляем его к общему списку на обновление | 
| 598 | 0 |  |  |  |  |  | debug(sprintf("Fetching %s file", MIRRORS)); | 
| 599 | 0 |  |  |  |  |  | my $fetch_mirr = fetch($url, MIRRORS, $self->{file_manitemp}); | 
| 600 | 0 | 0 | 0 |  |  |  | if ($fetch_mirr->{status} && ((-z $self->{file_mirrors}) || $fetch_mirr->{mtime} > $self->{mtime_mirrors})) { | 
|  |  |  | 0 |  |  |  |  | 
| 601 |  |  |  |  |  |  | # Читаем файл в отдельную структуру | 
| 602 | 0 |  |  |  |  |  | my $remote_mirr = maniread($self->{file_manitemp}); | 
| 603 |  |  |  |  |  |  | # Добаляем файл на скачку, если там есть два или более зеркал | 
| 604 | 0 |  | 0 |  |  |  | my $mcnt = scalar(keys %$remote_mirr) || 0; | 
| 605 | 0 | 0 | 0 |  |  |  | if ($mcnt && $mcnt > 1) { | 
| 606 | 0 |  |  |  |  |  | my $k = MIRRORS; | 
| 607 | 0 |  | 0 |  |  |  | my $ar = $sync_list{$k} || []; | 
| 608 |  |  |  |  |  |  | push @$ar, { | 
| 609 |  |  |  |  |  |  | uri     => $url, | 
| 610 |  |  |  |  |  |  | mtime   => $fetch_mirr->{mtime}, | 
| 611 |  |  |  |  |  |  | size    => $fetch_mirr->{size}, | 
| 612 | 0 |  |  |  |  |  | }; | 
| 613 | 0 |  |  |  |  |  | $sync_list{$k} = $ar; | 
| 614 |  |  |  |  |  |  | } else { | 
| 615 | 0 |  |  |  |  |  | debug(sprintf("> [SKIPPED] File %s on %s contains too few mirrors", MIRRORS, $url)); | 
| 616 |  |  |  |  |  |  | } | 
| 617 |  |  |  |  |  |  | } | 
| 618 |  |  |  |  |  |  |  | 
| 619 |  |  |  |  |  |  | # Пробегаемся по .DEL удаленным файлам и получаем список для принудительного удаления | 
| 620 | 0 |  |  |  |  |  | debug(sprintf("Fetching %s file", MANIDEL)); | 
| 621 | 0 |  |  |  |  |  | my $fetch_dir = fetch($url, MANIDEL, $self->{file_manitemp}); | 
| 622 | 0 | 0 |  |  |  |  | if ($fetch_dir->{status}) { | 
| 623 |  |  |  |  |  |  | # Читаем файл в отдельную структуру | 
| 624 | 0 |  |  |  |  |  | my $remote_manidel = maniread($self->{file_manitemp}); | 
| 625 | 0 |  |  |  |  |  | foreach my $k (keys %$remote_manidel) { | 
| 626 | 0 | 0 |  |  |  |  | unless (_skipcheck(\%skips, $k)) { | 
| 627 | 0 | 0 |  |  |  |  | $delete_list{$k} ? ($delete_list{$k}++) : ($delete_list{$k} = 1) | 
| 628 |  |  |  |  |  |  | } | 
| 629 |  |  |  |  |  |  | } | 
| 630 |  |  |  |  |  |  | } | 
| 631 |  |  |  |  |  |  | } continue { | 
| 632 | 0 |  |  |  |  |  | fdelete($self->{file_manitemp}); | 
| 633 |  |  |  |  |  |  | } | 
| 634 |  |  |  |  |  |  | } else { | 
| 635 | 0 |  |  |  |  |  | carp(sprintf("File %s is empty", MIRRORS)); | 
| 636 | 0 |  |  |  |  |  | $status = 1; # Факт невозможности получить зеркала не является признаком того что ресурс | 
| 637 |  |  |  |  |  |  | # отработал с ошибками | 
| 638 |  |  |  |  |  |  | } | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | # Удаляем принудительно файлы полученного списка | 
| 641 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper(\%delete_list)); | 
| 642 | 0 |  |  |  |  |  | debug("Deleting files"); | 
| 643 | 0 |  |  |  |  |  | foreach my $k (keys %delete_list) { | 
| 644 | 0 |  |  |  |  |  | my $f = File::Spec->canonpath(File::Spec->catfile($self->{dir}, $k)); | 
| 645 | 0 | 0 |  |  |  |  | if (-e $f) { | 
| 646 | 0 |  |  |  |  |  | fdelete($f); | 
| 647 | 0 |  |  |  |  |  | debug(sprintf("> [DELETED] %s", $k)); | 
| 648 |  |  |  |  |  |  | } else { | 
| 649 | 0 |  |  |  |  |  | debug(sprintf("> [MISSING] %s (%s)", $k, $f)); | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  | } | 
| 652 |  |  |  |  |  |  |  | 
| 653 |  |  |  |  |  |  | # Проходим по sync_list и скачиваем файлы, но которых НЕТ в списке на удаление | 
| 654 | 0 |  |  |  |  |  | debug("Downloading files"); | 
| 655 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper(\%sync_list)); | 
| 656 | 0 |  |  |  |  |  | my $total = 0; | 
| 657 | 0 |  |  |  |  |  | my $cnt = 0; | 
| 658 | 0 |  |  |  |  |  | my $all = scalar(keys %sync_list); | 
| 659 | 0 |  |  |  |  |  | foreach my $k (sort {lc $a cmp lc $b} keys %sync_list) {$cnt++; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 660 | 0 |  |  |  |  |  | debug(sprintf("%03d/%03d %s", $cnt, $all, $k)); | 
| 661 | 0 |  |  |  |  |  | my $list = $sync_list{$k}; | 
| 662 | 0 | 0 | 0 |  |  |  | if ($list && ref($list) eq 'ARRAY') { | 
| 663 | 0 |  | 0 |  |  |  | my $mt_l = $self->{manifest}{$k}[0] || 0; | 
| 664 | 0 |  |  |  |  |  | my $dwldd = 0; | 
| 665 | 0 |  |  |  |  |  | my $skipped = 0; | 
| 666 | 0 |  | 0 |  |  |  | foreach my $job (sort {($b->{mtime} || 0)  <=> ($a->{mtime} || 0)} @$list) { | 
|  | 0 |  | 0 |  |  |  |  | 
| 667 | 0 | 0 |  |  |  |  | last if $dwldd; # Выход, если скачали! | 
| 668 | 0 |  |  |  |  |  | my $mt_r = $job->{mtime}; | 
| 669 | 0 |  |  |  |  |  | my $url  = $job->{uri}; | 
| 670 | 0 |  |  |  |  |  | my $size = $job->{size}; | 
| 671 | 0 | 0 |  |  |  |  | unless ($url) { | 
| 672 | 0 |  |  |  |  |  | debug(sprintf("\t[SKIPPED] No URI")); | 
| 673 | 0 |  |  |  |  |  | next; | 
| 674 |  |  |  |  |  |  | } | 
| 675 | 0 | 0 |  |  |  |  | unless ($size) { | 
| 676 | 0 |  |  |  |  |  | debug(sprintf("\t[SKIPPED] No data, %s", $url)); | 
| 677 | 0 |  |  |  |  |  | next; | 
| 678 |  |  |  |  |  |  | } | 
| 679 | 0 | 0 | 0 |  |  |  | unless ($mt_r || !$mt_l) { | 
| 680 | 0 |  |  |  |  |  | debug(sprintf("\t[SKIPPED] The remote file have undefined modified date, %s", $url)); | 
| 681 | 0 |  |  |  |  |  | next; | 
| 682 |  |  |  |  |  |  | } | 
| 683 | 0 | 0 |  |  |  |  | if ($mt_l >= $mt_r) { | 
| 684 | 0 |  |  |  |  |  | debug(sprintf("\t[SKIPPED] File is up to date, %s", $url)); | 
| 685 | 0 |  |  |  |  |  | $skipped = 1; | 
| 686 | 0 |  |  |  |  |  | next; | 
| 687 |  |  |  |  |  |  | } | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  | # Все проверки прошли, скачиваем | 
| 690 | 0 |  |  |  |  |  | my $fetch_file = fetch($url, $k, $self->{file_temp}); | 
| 691 | 0 | 0 |  |  |  |  | if ($fetch_file->{status}) { | 
| 692 | 0 |  | 0 |  |  |  | my $size_fact = $fetch_file->{size} || 0; | 
| 693 | 0 | 0 | 0 |  |  |  | if ($size_fact && $size_fact == $size) { | 
| 694 | 0 |  |  |  |  |  | debug(sprintf("\t[  OK   ] Received %d bytes, %s", $size_fact, $url)); | 
| 695 | 0 |  |  |  |  |  | $total += $size_fact; | 
| 696 | 0 |  |  |  |  |  | $dwldd = 1; | 
| 697 |  |  |  |  |  |  | } else { | 
| 698 | 0 |  |  |  |  |  | debug(sprintf("\t[ ERROR ] Can't fetch file [%s], %s", | 
| 699 |  |  |  |  |  |  | $url | 
| 700 |  |  |  |  |  |  | )); | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  | } else { | 
| 703 |  |  |  |  |  |  | debug(sprintf("\t[ ERROR ] Can't fetch file [%s], %s", | 
| 704 | 0 | 0 |  |  |  |  | $fetch_file->{code} ? $fetch_file->{code} : 'UNDEFINED', | 
| 705 |  |  |  |  |  |  | $url | 
| 706 |  |  |  |  |  |  | )); | 
| 707 |  |  |  |  |  |  | } | 
| 708 |  |  |  |  |  |  |  | 
| 709 |  |  |  |  |  |  | } | 
| 710 |  |  |  |  |  |  |  | 
| 711 | 0 | 0 |  |  |  |  | if ($dwldd) { # Файл скачен и лежит во временном файле | 
| 712 |  |  |  |  |  |  | # Откуда : $self->{file_temp} | 
| 713 |  |  |  |  |  |  | # Куда   : $k | 
| 714 | 0 |  |  |  |  |  | my $src = $self->{file_temp}; | 
| 715 | 0 |  |  |  |  |  | my $dst = File::Spec->canonpath(File::Spec->catfile($self->{dir}, $k)); | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | # Создаем директорию азначения | 
| 718 | 0 |  |  |  |  |  | my $dir = dirname($dst); # See File::Basename | 
| 719 | 0 |  |  |  |  |  | my $mkerr; | 
| 720 | 0 |  |  |  |  |  | mkpath($dir, { | 
| 721 |  |  |  |  |  |  | verbose => 1, | 
| 722 |  |  |  |  |  |  | mode => 0777, | 
| 723 |  |  |  |  |  |  | error => \$mkerr, | 
| 724 |  |  |  |  |  |  | }); | 
| 725 | 0 | 0 | 0 |  |  |  | if ($mkerr && (ref($mkerr) eq 'ARRAY') && @$mkerr) { | 
|  |  |  | 0 |  |  |  |  | 
| 726 | 0 |  |  |  |  |  | foreach my $e (@$mkerr) { | 
| 727 | 0 | 0 | 0 |  |  |  | next unless $e && ref($e) eq 'HASH'; | 
| 728 | 0 |  |  |  |  |  | while (my ($_k, $_v) = each %$e) { | 
| 729 | 0 |  |  |  |  |  | carp(sprintf("%s: %s", $_k, $_v)); | 
| 730 |  |  |  |  |  |  | } | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($mkerr)); | 
| 733 |  |  |  |  |  |  | } | 
| 734 |  |  |  |  |  |  | #debug(sprintf("--> %s >>> %s", $src, $dst)); | 
| 735 |  |  |  |  |  |  | #debug(sprintf("--> %s >>> %s", $dst, $dir)); | 
| 736 |  |  |  |  |  |  |  | 
| 737 |  |  |  |  |  |  | # Переносим файлы по назначению | 
| 738 | 0 |  |  |  |  |  | fdelete($dst); | 
| 739 | 0 | 0 |  |  |  |  | unless (mv($src, $dst)) { | 
| 740 | 0 |  |  |  |  |  | debug(sprintf("\t[ ERROR ] Can't move file %s to %s", $src, $dst)); | 
| 741 | 0 |  |  |  |  |  | carp($!); | 
| 742 |  |  |  |  |  |  | } | 
| 743 |  |  |  |  |  |  | } else { | 
| 744 | 0 | 0 |  |  |  |  | debug(sprintf("\t[FAILED ] Can't fetch file %s", $k)) unless $skipped; | 
| 745 |  |  |  |  |  |  | } | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | #debug($mt_l); | 
| 748 |  |  |  |  |  |  | } else { | 
| 749 | 0 |  |  |  |  |  | debug(sprintf("\t[SKIPPED] Nothing to do for %s", $k)); | 
| 750 |  |  |  |  |  |  | } | 
| 751 |  |  |  |  |  |  | } | 
| 752 | 0 |  |  |  |  |  | debug(sprintf("Received %d bytes", $total)); | 
| 753 |  |  |  |  |  |  |  | 
| 754 |  |  |  |  |  |  | # Формируем новый MANIFEST | 
| 755 | 0 |  |  |  |  |  | debug("Creating new manifest"); | 
| 756 | 0 |  |  |  |  |  | my $new_manifest = manifind($self->{dir}); | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | # Отбираем файлы исключая исключения | 
| 759 | 0 |  |  |  |  |  | foreach my $k (keys %$new_manifest) { | 
| 760 | 0 |  |  |  |  |  | my $nskip = _skipcheck(\%skips, $k); | 
| 761 | 0 | 0 |  |  |  |  | delete $new_manifest->{$k} if $nskip; | 
| 762 | 0 | 0 |  |  |  |  | debug(sprintf("> [%s] %s", $nskip ? "SKIPPED" : " ADDED ", $k)); | 
| 763 |  |  |  |  |  |  | } | 
| 764 |  |  |  |  |  |  | #debug(Data::Dumper::Dumper($new_manifest)); | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | # Пишем сам файл | 
| 767 | 0 |  |  |  |  |  | debug("Saving manifest to file ".MANIFEST); | 
| 768 | 0 | 0 |  |  |  |  | return 0 unless maniwrite($self->{file_manifest}, $new_manifest); | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | # Формируем новый META | 
| 771 | 0 |  |  |  |  |  | debug("Creating new META file"); | 
| 772 |  |  |  |  |  |  | my $new_meta = { | 
| 773 |  |  |  |  |  |  | last_start  => $self->{stamp}, | 
| 774 |  |  |  |  |  |  | last_finish => time, | 
| 775 |  |  |  |  |  |  | last_pid    => $self->{pid}, | 
| 776 | 0 |  | 0 |  |  |  | uri         => $self->{uri} || 'localhost', | 
| 777 |  |  |  |  |  |  | date        => scalar(localtime(time)), | 
| 778 |  |  |  |  |  |  | status      => 1, # $status, | 
| 779 |  |  |  |  |  |  | # статус META выставляется только по факту успешного формирования итоговой структуры | 
| 780 |  |  |  |  |  |  | # катаклога. Это изменение отличает мета-файл от только что инициализированного. | 
| 781 |  |  |  |  |  |  | # Внесенные изменения см. #468 | 
| 782 |  |  |  |  |  |  | }; | 
| 783 | 0 | 0 |  |  |  |  | return 0 unless write_yaml($self->{file_meta}, $new_meta); | 
| 784 |  |  |  |  |  |  |  | 
| 785 | 0 |  |  |  |  |  | return $status; | 
| 786 |  |  |  |  |  |  | } | 
| 787 |  |  |  |  |  |  | sub check_lock { # Проверка факта, что файл является собственным | 
| 788 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 789 | 0 |  |  |  |  |  | my $file = shift; | 
| 790 | 0 | 0 | 0 |  |  |  | return 0 unless $file && -e $file; | 
| 791 |  |  |  |  |  |  |  | 
| 792 | 0 |  |  |  |  |  | local *RD_LOCK_FILE; | 
| 793 | 0 | 0 |  |  |  |  | unless (open(RD_LOCK_FILE, "<", $file)) { | 
| 794 | 0 |  |  |  |  |  | carp(sprintf("Can't open file %s to read: %s", $file, $!)); | 
| 795 | 0 |  |  |  |  |  | return 0; | 
| 796 |  |  |  |  |  |  | } | 
| 797 |  |  |  |  |  |  |  | 
| 798 | 0 |  |  |  |  |  | my $l; | 
| 799 | 0 | 0 |  |  |  |  | chomp($l = ); $l = "" unless defined $l; | 
|  | 0 |  |  |  |  |  |  | 
| 800 | 0 | 0 |  |  |  |  | unless (close RD_LOCK_FILE) { | 
| 801 | 0 |  |  |  |  |  | carp(sprintf("Can't close file %s: %s", $file, $!)); | 
| 802 | 0 |  |  |  |  |  | return 0; | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 | 0 |  |  |  |  |  | my ($r_pid, $r_stamp, $r_name) = split(/#/, $l); | 
| 806 | 0 | 0 | 0 |  |  |  | if ($r_pid && ($r_pid =~ /^[0-9]{1,11}$/) && kill(0, $r_pid)) { | 
|  |  |  | 0 |  |  |  |  | 
| 807 | 0 | 0 |  |  |  |  | return 1 if $self->{pid} == $r_pid; | 
| 808 |  |  |  |  |  |  | } | 
| 809 | 0 |  |  |  |  |  | return 0; | 
| 810 |  |  |  |  |  |  | } | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | # Functions | 
| 813 |  |  |  |  |  |  | sub debug { | 
| 814 | 0 | 0 |  | 0 | 0 |  | print STDOUT @_ ? @_ : '',"\n" if $DEBUG; | 
|  |  | 0 |  |  |  |  |  | 
| 815 | 0 |  |  |  |  |  | 1; | 
| 816 |  |  |  |  |  |  | } | 
| 817 |  |  |  |  |  |  | sub touch { | 
| 818 | 0 |  |  | 0 | 1 |  | my $file = shift; | 
| 819 | 0 | 0 |  |  |  |  | return 0 unless defined $file; | 
| 820 | 0 |  |  |  |  |  | local *FILE; | 
| 821 | 0 | 0 |  |  |  |  | unless (open(FILE, ">>", $file)) { | 
| 822 | 0 |  |  |  |  |  | carp(sprintf("Can't write file %s: %s",$file, $!)); | 
| 823 | 0 |  |  |  |  |  | return 0; | 
| 824 |  |  |  |  |  |  | } | 
| 825 | 0 | 0 |  |  |  |  | unless (close(FILE)) { | 
| 826 | 0 |  |  |  |  |  | carp(sprintf("Can't close file %s: %s",$file, $!)); | 
| 827 | 0 |  |  |  |  |  | return 0; | 
| 828 |  |  |  |  |  |  | } | 
| 829 | 0 |  |  |  |  |  | my $t = time; | 
| 830 | 0 | 0 |  |  |  |  | unless (utime($t,$t,$file)) { | 
| 831 | 0 |  |  |  |  |  | carp(sprintf("Can't touch file %s: %s",$file, $!)); | 
| 832 | 0 |  |  |  |  |  | return 0; | 
| 833 |  |  |  |  |  |  | } | 
| 834 | 0 |  |  |  |  |  | return 1; | 
| 835 |  |  |  |  |  |  | } | 
| 836 |  |  |  |  |  |  | sub fdelete { | 
| 837 | 0 |  |  | 0 | 1 |  | my $file = shift; | 
| 838 | 0 | 0 | 0 |  |  |  | return 0 unless defined $file && -e $file; | 
| 839 | 0 | 0 |  |  |  |  | unless (unlink($file)) { | 
| 840 | 0 |  |  |  |  |  | carp(sprintf("Can't delete file %s: %s",$file, $!)) ; | 
| 841 | 0 |  |  |  |  |  | return 0; | 
| 842 |  |  |  |  |  |  | } | 
| 843 | 0 |  |  |  |  |  | return 1; | 
| 844 |  |  |  |  |  |  | } | 
| 845 |  |  |  |  |  |  | sub read_yaml { | 
| 846 | 0 |  |  | 0 | 0 |  | my $file = shift; | 
| 847 | 0 | 0 |  |  |  |  | return [] unless defined $file; | 
| 848 | 0 | 0 | 0 |  |  |  | return [] unless (-e $file) && -r $file; | 
| 849 | 0 |  |  |  |  |  | my $yaml = new YAML::Tiny; | 
| 850 | 0 |  |  |  |  |  | my $data = $yaml->read($file); | 
| 851 | 0 | 0 |  |  |  |  | return [] unless $data; | 
| 852 | 0 |  |  |  |  |  | return $data; | 
| 853 |  |  |  |  |  |  | } | 
| 854 |  |  |  |  |  |  | sub write_yaml { | 
| 855 | 0 |  |  | 0 | 0 |  | my $file = shift; | 
| 856 | 0 |  |  |  |  |  | my $data = shift; | 
| 857 | 0 | 0 |  |  |  |  | return 0 unless defined $file; | 
| 858 | 0 | 0 |  |  |  |  | return 0 unless defined $data; | 
| 859 | 0 |  |  |  |  |  | my $yaml = new YAML::Tiny( $data ); | 
| 860 | 0 |  |  |  |  |  | $yaml->write( $file ); | 
| 861 | 0 |  |  |  |  |  | return 1; | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  | sub maniread { # Reading data from MANEFEST, MIRRORS and MANEFEST.* files | 
| 864 |  |  |  |  |  |  | # Original see Ext::Utils::maniread | 
| 865 | 0 |  |  | 0 | 0 |  | my $mfile = shift; | 
| 866 | 0 |  |  |  |  |  | my $skipflag = shift; | 
| 867 |  |  |  |  |  |  |  | 
| 868 | 0 |  |  |  |  |  | my $read = {}; | 
| 869 | 0 | 0 | 0 |  |  |  | return $read unless defined($mfile) && (-e $mfile) && (-r $mfile) && (-s $mfile); | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 870 | 0 |  |  |  |  |  | local *M; | 
| 871 | 0 | 0 |  |  |  |  | unless (open M, "<", $mfile){ | 
| 872 | 0 |  |  |  |  |  | carp("Problem opening $mfile: $!"); | 
| 873 | 0 |  |  |  |  |  | return $read; | 
| 874 |  |  |  |  |  |  | } | 
| 875 | 0 |  |  |  |  |  | local $_; | 
| 876 | 0 |  |  |  |  |  | while (){ | 
| 877 | 0 |  |  |  |  |  | chomp; | 
| 878 | 0 | 0 |  |  |  |  | next if /^\s*#/; | 
| 879 | 0 |  |  |  |  |  | my($file, $args); | 
| 880 |  |  |  |  |  |  |  | 
| 881 | 0 | 0 | 0 |  |  |  | if ($skipflag && $_ =~ /^\s*\!\!perl\/regexp\s*/i) { # Working in SkipMode | 
| 882 |  |  |  |  |  |  | #s/\r//; | 
| 883 |  |  |  |  |  |  | #$_ =~ qr{^\s*\!\!perl\/regexp\s*(?:(?:'([^\\']*(?:\\.[^\\']*)*)')|([^#\s]\S*))?(?:(?:\s*)|(?:\s+(.*?)\s*))$}; | 
| 884 |  |  |  |  |  |  | #$args = $3; | 
| 885 |  |  |  |  |  |  | #my $file = $2; | 
| 886 |  |  |  |  |  |  | #if ( defined($1) ) { | 
| 887 |  |  |  |  |  |  | #    $file = $1; | 
| 888 |  |  |  |  |  |  | #    $file =~ s/\\(['\\])/$1/g; | 
| 889 |  |  |  |  |  |  | #} | 
| 890 | 0 | 0 |  |  |  |  | unless (($file, $args) = /^'(\\[\\']|.+)+'\s*(.*)/) { | 
| 891 | 0 |  |  |  |  |  | ($file, $args) = /^(^\s*\!\!perl\/regexp\s*\S+)\s*(.*)/; | 
| 892 |  |  |  |  |  |  | } | 
| 893 |  |  |  |  |  |  | } else { | 
| 894 |  |  |  |  |  |  | # filename may contain spaces if enclosed in '' | 
| 895 |  |  |  |  |  |  | # (in which case, \\ and \' are escapes) | 
| 896 | 0 | 0 |  |  |  |  | if (($file, $args) = /^'(\\[\\']|.+)+'\s*(.*)/) { | 
| 897 | 0 |  |  |  |  |  | $file =~ s/\\([\\'])/$1/g; | 
| 898 |  |  |  |  |  |  | } else { | 
| 899 | 0 |  |  |  |  |  | ($file, $args) = /^(\S+)\s*(.*)/; | 
| 900 |  |  |  |  |  |  | } | 
| 901 |  |  |  |  |  |  | } | 
| 902 | 0 | 0 |  |  |  |  | next unless $file; | 
| 903 | 0 | 0 |  |  |  |  | $read->{$file} = [defined $args ? split(/\s+/,$args) : ""]; | 
| 904 |  |  |  |  |  |  | } | 
| 905 | 0 |  |  |  |  |  | close M; | 
| 906 | 0 |  |  |  |  |  | return $read; | 
| 907 |  |  |  |  |  |  | } | 
| 908 |  |  |  |  |  |  | sub manifind { | 
| 909 | 0 |  |  | 0 | 0 |  | my $dir = shift; | 
| 910 | 0 | 0 | 0 |  |  |  | carp("Can't specified directory") && return {} unless defined($dir) && -e $dir; | 
|  |  |  | 0 |  |  |  |  | 
| 911 |  |  |  |  |  |  |  | 
| 912 | 0 |  |  |  |  |  | my $found = {}; | 
| 913 | 0 |  |  |  |  |  | my $base = File::Spec->canonpath($dir); | 
| 914 |  |  |  |  |  |  | #my ($volume,$sdirs,$sfile) = File::Spec->splitpath( $base ); | 
| 915 |  |  |  |  |  |  |  | 
| 916 |  |  |  |  |  |  | my $wanted = sub { | 
| 917 | 0 |  |  | 0 |  |  | my $path = File::Spec->canonpath($_); | 
| 918 | 0 |  |  |  |  |  | my $name = File::Spec->abs2rel( $path, $base ); | 
| 919 | 0 |  |  |  |  |  | my $fdir = File::Spec->canonpath($File::Find::dir); | 
| 920 | 0 | 0 |  |  |  |  | return if -d $_; | 
| 921 |  |  |  |  |  |  |  | 
| 922 | 0 |  |  |  |  |  | my $key = join("/", File::Spec->splitdir(File::Spec->catfile($name))); | 
| 923 | 0 |  | 0 |  |  |  | $found->{$key} = { | 
|  |  |  | 0 |  |  |  |  | 
| 924 |  |  |  |  |  |  | mtime   => (stat($_))[9] || 0, | 
| 925 |  |  |  |  |  |  | size    => (-s $_) || 0, | 
| 926 |  |  |  |  |  |  | dir     => $fdir, | 
| 927 |  |  |  |  |  |  | path    => $path, | 
| 928 |  |  |  |  |  |  | file    => File::Spec->abs2rel( $path, $fdir ), | 
| 929 |  |  |  |  |  |  | }; | 
| 930 | 0 |  |  |  |  |  | }; | 
| 931 |  |  |  |  |  |  |  | 
| 932 |  |  |  |  |  |  | # We have to use "$File::Find::dir/$_" in preprocess, because | 
| 933 |  |  |  |  |  |  | # $File::Find::name is unavailable. | 
| 934 |  |  |  |  |  |  | # Also, it's okay to use / here, because MANIFEST files use Unix-style | 
| 935 |  |  |  |  |  |  | # paths. | 
| 936 | 0 |  |  |  |  |  | find({ | 
| 937 |  |  |  |  |  |  | wanted      => $wanted, | 
| 938 |  |  |  |  |  |  | no_chdir    => 1, | 
| 939 |  |  |  |  |  |  | }, $dir); | 
| 940 |  |  |  |  |  |  |  | 
| 941 | 0 |  |  |  |  |  | return $found; | 
| 942 |  |  |  |  |  |  | } | 
| 943 |  |  |  |  |  |  | sub maniwrite { | 
| 944 | 0 |  |  | 0 | 0 |  | my $file = shift; | 
| 945 | 0 |  |  |  |  |  | my $mani = shift; | 
| 946 | 0 | 0 | 0 |  |  |  | carp("Can't specified file") && return 0 unless defined($file); | 
| 947 | 0 | 0 | 0 |  |  |  | carp("Can't specified manifest-hash") && return 0 unless defined($mani) && ref($mani) eq 'HASH'; | 
|  |  |  | 0 |  |  |  |  | 
| 948 | 0 |  |  |  |  |  | my $file_bak = $file.".bak"; | 
| 949 |  |  |  |  |  |  |  | 
| 950 | 0 |  |  |  |  |  | rename $file, $file_bak; | 
| 951 | 0 |  |  |  |  |  | local *M; | 
| 952 |  |  |  |  |  |  |  | 
| 953 | 0 | 0 |  |  |  |  | unless (open M, ">", $file){ | 
| 954 | 0 |  |  |  |  |  | carp("Can't open file $file: $!"); | 
| 955 | 0 |  |  |  |  |  | rename $file_bak, $file; | 
| 956 | 0 |  |  |  |  |  | return 0; | 
| 957 |  |  |  |  |  |  | } | 
| 958 |  |  |  |  |  |  |  | 
| 959 |  |  |  |  |  |  | # Stamp | 
| 960 | 0 |  |  |  |  |  | print  M "###########################################\n"; | 
| 961 | 0 |  |  |  |  |  | printf M "# File created at %s\n", scalar(localtime(time())); | 
| 962 | 0 |  |  |  |  |  | print  M "# Please, do NOT edit this file directly!!\n"; | 
| 963 | 0 |  |  |  |  |  | print  M "###########################################\n\n"; | 
| 964 |  |  |  |  |  |  |  | 
| 965 | 0 |  |  |  |  |  | foreach my $f (sort { lc $a cmp lc $b } keys %$mani) { | 
|  | 0 |  |  |  |  |  |  | 
| 966 | 0 |  |  |  |  |  | my $d = $mani->{$f}; | 
| 967 |  |  |  |  |  |  | my $text = sprintf("%s\t%s\t%s", | 
| 968 |  |  |  |  |  |  | $d->{mtime} || 0, | 
| 969 |  |  |  |  |  |  | $d->{size} || 0, | 
| 970 | 0 | 0 | 0 |  |  |  | $d->{mtime} ? scalar(localtime($d->{mtime})) : 'UNKNOWN', | 
|  |  |  | 0 |  |  |  |  | 
| 971 |  |  |  |  |  |  | ); | 
| 972 | 0 |  |  |  |  |  | my $tabs = (8 - (length($f)+1)/8); | 
| 973 | 0 | 0 |  |  |  |  | $tabs = 1 if $tabs < 1; | 
| 974 | 0 | 0 |  |  |  |  | $tabs = 0 unless $text; | 
| 975 | 0 | 0 |  |  |  |  | if ($f =~ /\s/) { | 
| 976 | 0 |  |  |  |  |  | $f =~ s/([\\'])/\\$1/g; | 
| 977 | 0 |  |  |  |  |  | $f = "'$f'"; | 
| 978 |  |  |  |  |  |  | } | 
| 979 | 0 |  |  |  |  |  | print M $f, "\t" x $tabs, $text, "\n"; | 
| 980 |  |  |  |  |  |  | } | 
| 981 | 0 |  |  |  |  |  | close M; | 
| 982 |  |  |  |  |  |  |  | 
| 983 | 0 |  |  |  |  |  | unlink $file_bak; | 
| 984 |  |  |  |  |  |  |  | 
| 985 | 0 |  |  |  |  |  | return 1; | 
| 986 |  |  |  |  |  |  | } | 
| 987 |  |  |  |  |  |  | sub fetch($$$) { # Returns structire | 
| 988 | 0 |  |  | 0 | 1 |  | my $url = shift; | 
| 989 | 0 |  |  |  |  |  | my $obj = shift; | 
| 990 | 0 |  |  |  |  |  | my $file = shift; | 
| 991 |  |  |  |  |  |  |  | 
| 992 | 0 |  |  |  |  |  | my $ret = { | 
| 993 |  |  |  |  |  |  | status  => 0, # Status | 
| 994 |  |  |  |  |  |  | mtime   => 0, # Last-Modified in ctime format or 0 | 
| 995 |  |  |  |  |  |  | size    => 0, # tContent-length | 
| 996 |  |  |  |  |  |  | code    => 0, # Status code | 
| 997 |  |  |  |  |  |  | }; | 
| 998 |  |  |  |  |  |  |  | 
| 999 |  |  |  |  |  |  | # Форирование URI | 
| 1000 | 0 |  |  |  |  |  | my $uri = new URI($url); | 
| 1001 | 0 |  |  |  |  |  | my $curpath = $uri->path(); | 
| 1002 | 0 | 0 |  |  |  |  | my $newpath = $curpath . (defined $obj ? "/$obj" : ''); $newpath =~ s/\/{2,}/\//; | 
|  | 0 |  |  |  |  |  |  | 
| 1003 | 0 |  |  |  |  |  | $uri->path($newpath); | 
| 1004 | 0 |  |  |  |  |  | $ret->{uri} = $uri->as_string; | 
| 1005 |  |  |  |  |  |  |  | 
| 1006 |  |  |  |  |  |  | # Проверка на файл | 
| 1007 | 0 | 0 |  |  |  |  | unless (defined $file) { | 
| 1008 | 0 |  |  |  |  |  | carp(sprintf("File to store is not defined")); | 
| 1009 | 0 |  |  |  |  |  | return $ret; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | # Первоначальный запрос на существование | 
| 1013 | 0 |  |  |  |  |  | my ($content_type, $document_length, $modified_time, $expires, $server) = head($uri); | 
| 1014 | 0 | 0 |  |  |  |  | debug(sprintf("HEAD Response:" | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1015 |  |  |  |  |  |  | ."\n\tContent-type:\t%s" | 
| 1016 |  |  |  |  |  |  | ."\n\tContent-length:\t%s" | 
| 1017 |  |  |  |  |  |  | ."\n\tModified:\t%s" | 
| 1018 |  |  |  |  |  |  | ."\n\tServer:\t\t%s", | 
| 1019 |  |  |  |  |  |  | defined $content_type ? $content_type : '', | 
| 1020 |  |  |  |  |  |  | defined $document_length ? $document_length : 0, | 
| 1021 |  |  |  |  |  |  | defined $modified_time ? scalar(localtime($modified_time)) : '', | 
| 1022 |  |  |  |  |  |  | defined $server ? $server : '' | 
| 1023 |  |  |  |  |  |  | )); | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | # Анализ. Если всё плохо, выход | 
| 1026 | 0 | 0 |  |  |  |  | if ($document_length) { | 
| 1027 | 0 |  |  |  |  |  | $ret->{size} = $document_length; | 
| 1028 |  |  |  |  |  |  | } else { | 
| 1029 | 0 |  |  |  |  |  | return $ret; | 
| 1030 |  |  |  |  |  |  | } | 
| 1031 | 0 | 0 |  |  |  |  | if ($modified_time) { | 
| 1032 | 0 |  |  |  |  |  | $ret->{mtime} = $modified_time; | 
| 1033 |  |  |  |  |  |  | } else { | 
| 1034 | 0 |  |  |  |  |  | carp(sprintf("Can't fetch resource %s. Header Last-Modified not returned", $uri->as_string)); | 
| 1035 | 0 |  |  |  |  |  | return $ret; | 
| 1036 |  |  |  |  |  |  | } | 
| 1037 |  |  |  |  |  |  |  | 
| 1038 |  |  |  |  |  |  | # Принимаем файл | 
| 1039 | 0 |  |  |  |  |  | fdelete($file); | 
| 1040 | 0 |  |  |  |  |  | my $code = mirror($uri, $file); | 
| 1041 | 0 |  |  |  |  |  | $ret->{code} = $code; | 
| 1042 | 0 | 0 | 0 |  |  |  | if (($code >= 200) && ($code < 400)) { | 
| 1043 | 0 | 0 | 0 |  |  |  | if (-e $file && -s $file) { | 
| 1044 | 0 |  |  |  |  |  | $ret->{status} = 1; | 
| 1045 |  |  |  |  |  |  | } | 
| 1046 |  |  |  |  |  |  | } | 
| 1047 |  |  |  |  |  |  |  | 
| 1048 | 0 |  |  |  |  |  | return $ret; | 
| 1049 |  |  |  |  |  |  | } | 
| 1050 |  |  |  |  |  |  | sub _expire { # Перевод в expires | 
| 1051 | 0 |  | 0 | 0 |  |  | my $str = shift || 0; | 
| 1052 |  |  |  |  |  |  |  | 
| 1053 | 0 | 0 |  |  |  |  | return 0 unless defined $str; | 
| 1054 | 0 | 0 |  |  |  |  | return $1 if $str =~ m/^[-+]?(\d+)$/; | 
| 1055 |  |  |  |  |  |  |  | 
| 1056 | 0 |  |  |  |  |  | my %_map = ( | 
| 1057 |  |  |  |  |  |  | s       => 1, | 
| 1058 |  |  |  |  |  |  | m       => 60, | 
| 1059 |  |  |  |  |  |  | h       => 3600, | 
| 1060 |  |  |  |  |  |  | d       => 86400, | 
| 1061 |  |  |  |  |  |  | w       => 604800, | 
| 1062 |  |  |  |  |  |  | M       => 2592000, | 
| 1063 |  |  |  |  |  |  | y       => 31536000 | 
| 1064 |  |  |  |  |  |  | ); | 
| 1065 |  |  |  |  |  |  |  | 
| 1066 | 0 |  |  |  |  |  | my ($koef, $d) = $str =~ m/^([+-]?\d+)([smhdwMy])$/; | 
| 1067 | 0 | 0 | 0 |  |  |  | unless ( defined($koef) && defined($d) ) { | 
| 1068 | 0 |  |  |  |  |  | carp "expire(): couldn't parse '$str' into \$koef and \$d parts. Possible invalid syntax"; | 
| 1069 | 0 |  |  |  |  |  | return 0; | 
| 1070 |  |  |  |  |  |  | } | 
| 1071 | 0 |  |  |  |  |  | return $koef * $_map{ $d }; | 
| 1072 |  |  |  |  |  |  | } | 
| 1073 |  |  |  |  |  |  | sub _qrreconstruct { | 
| 1074 |  |  |  |  |  |  | # Возвращает регулярное выражение (QR-строку) | 
| 1075 |  |  |  |  |  |  | # Функция позаимствованая из YAML::Type::regexp пакета YAML::Types, немного переделанная для | 
| 1076 |  |  |  |  |  |  | # адаптации нужд!! | 
| 1077 |  |  |  |  |  |  | # На вход подается примерно следующее: | 
| 1078 |  |  |  |  |  |  | #    !!perl/regexp (?i-xsm:^\s*(error|fault|no)) | 
| 1079 |  |  |  |  |  |  | # это является регуляркой вида: | 
| 1080 |  |  |  |  |  |  | #    qr/^\s*(error|fault|no)/i | 
| 1081 | 0 |  |  | 0 |  |  | my $node = shift; | 
| 1082 | 0 | 0 |  |  |  |  | return undef unless defined $node; | 
| 1083 | 0 | 0 |  |  |  |  | return $node unless $node =~ /^\s*\!\!perl\/regexp\s*/i; | 
| 1084 | 0 |  |  |  |  |  | $node =~ s/\s*\!\!perl\/regexp\s*//i; | 
| 1085 | 0 | 0 |  |  |  |  | return qr{$node} unless $node =~ /^\(\?([\^\-xism]*):(.*)\)\z/s; | 
| 1086 | 0 |  |  |  |  |  | my ($flags, $re) = ($1, $2); | 
| 1087 | 0 |  |  |  |  |  | $flags =~ s/-.*//; | 
| 1088 | 0 |  |  |  |  |  | $flags =~ s/^\^//; | 
| 1089 | 0 |  | 0 | 0 |  |  | my $sub = QRTYPES->{$flags} || sub { qr{$_[0]} }; | 
|  | 0 |  |  |  |  |  |  | 
| 1090 | 0 |  |  |  |  |  | return $sub->($re); | 
| 1091 |  |  |  |  |  |  | } | 
| 1092 |  |  |  |  |  |  | sub _skipcheck { | 
| 1093 | 0 |  |  | 0 |  |  | my $sl = shift; # Link to %skip | 
| 1094 | 0 |  |  |  |  |  | my $st = shift; # Test string | 
| 1095 | 0 | 0 | 0 |  |  |  | return 0 unless $sl && defined($st) && ref($sl) eq 'HASH'; | 
|  |  |  | 0 |  |  |  |  | 
| 1096 | 0 | 0 | 0 |  |  |  | return 1 if exists $sl->{$st} && defined $sl->{$st}; # Исключение нашли! Т.к. нашлось прямое соответствие | 
| 1097 |  |  |  |  |  |  |  | 
| 1098 |  |  |  |  |  |  | # Пробегаемся по всем значениям и ищем среди них только регулярки | 
| 1099 | 0 | 0 |  |  |  |  | if (grep {(ref($_) eq 'Regexp') && $st =~ $_} values %$sl) { | 
|  | 0 | 0 |  |  |  |  |  | 
| 1100 | 0 |  |  |  |  |  | $sl->{$st} = 1; # Для очередной проверки данные проверки будут уже излишними. Оптимизация производительности | 
| 1101 | 0 |  |  |  |  |  | return 1 | 
| 1102 |  |  |  |  |  |  | } | 
| 1103 |  |  |  |  |  |  |  | 
| 1104 | 0 |  |  |  |  |  | return 0; # Not Found | 
| 1105 |  |  |  |  |  |  | } | 
| 1106 |  |  |  |  |  |  | 1; | 
| 1107 |  |  |  |  |  |  | __END__ |