ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/syncmail/folder.pm
(Generate patch)

Comparing syncmail/folder.pm (file contents):
Revision 1.3 by root, Sun Oct 28 04:00:58 2001 UTC vs.
Revision 1.4 by root, Sun Oct 28 20:52:24 2001 UTC

1package folder; 1package folder;
2 2
3BEGIN { *slog = \&::slog }; 3BEGIN { *slog = \&::slog };
4 4
5use Digest::SHA1; 5use Fcntl;
6use File::Sync ();
7
8use Inline Config => NAME => "syncmail::folder";
9use Inline C;
6 10
7use constant MDIFVERSION => 1; 11use constant MDIFVERSION => 1;
12
13BEGIN {
14 if (1) {
15 use OpenSSL ();
16 *hash = \&OpenSSL::Digest::sha1_hex;
17 } elsif (0) {
18 # use Digest::SHA1 ();
19 my $digest = new Digest::SHA1;
20 *hash = sub {
21 $digest->reset;
22 $digest->add(@_);
23 $mid = $digest->hexdigest;
24 };
25 }
26}
27
28sub flushfh {
29 my $oldfh = select $_[0];
30 $| = 1;
31 select $oldfh;
32}
33
34# rename a file and fsync the directory
35sub replace {
36 my ($src, $dst) = @_;
37 my $self = shift;
38
39 rename $src, $dst;
40
41 $dst =~ s/[^\/]*$/./;
42
43 # now sync the directory
44
45 open my $dir, "<", $dst
46 or die "$dst: $!";
47
48 File::Sync::fsync($dir);
49}
8 50
9sub new { 51sub new {
10 my $class = shift; 52 my $class = shift;
11 my %arg = @_; 53 my %arg = @_;
12 bless { 54 my $self = bless {
13 path => "$::PREFIX/$arg{name}", 55 path => "$::PREFIX/$arg{name}",
14 %arg, 56 %arg,
15 }, $class; 57 }, $class;
58 $self->open(0);
59 $self;
16} 60}
17 61
18sub dirty { 62sub dirty {
19 $_[0]{dirty} = 1; 63 $_[0]{dirty} = 1;
20} 64}
26# parse_mbox(mbox-file-path, callback) 70# parse_mbox(mbox-file-path, callback)
27# callback gets called with \$header and \$body, 71# callback gets called with \$header and \$body,
28# $header includes the mbox From_ line without 72# $header includes the mbox From_ line without
29# the leading From_ itself. 73# the leading From_ itself.
30sub parse_mbox { 74sub parse_mbox {
31 my ($path, $cb) = @_; 75 my ($fh, $cb) = @_;
32
33 open my $fh, "<", $path
34 or die "$path: $!";
35 76
36 local $/ = "\n\n"; 77 local $/ = "\n\n";
37 78
38 my ($head, $body, $offs); 79 my ($head, $body, $offs);
39 80
40 5 == read $fh, $head, 5 81 read $fh, $head, 5;
41 or return;
42 82
43 $head eq "From " 83 $head eq "From " or $head eq ""
44 or return; 84 or return;
45 85
46 $offs = 0; 86 $offs = 0;
47 while (defined ($head = <$fh>)) { 87 while (defined ($head = <$fh>)) {
48 $head =~ /^.*? [A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] \d\d:\d\d:\d\d(?: [+-]\d\d\d\d)? \d\d(?:\d\d)\n/ 88 $head =~ /^.*? [A-Z][a-z][a-z] [A-Z][a-z][a-z] [ 0-9][0-9] \d\d:\d\d:\d\d(?: [+-]\d\d\d\d)? \d\d(?:\d\d)\n/
57 $body = <$fh>; 97 $body = <$fh>;
58 #} 98 #}
59 chomp $body; 99 chomp $body;
60 $cb->($offs, \$head, \$body); 100 $cb->($offs, \$head, \$body);
61 $offs = (tell $fh) - 5; 101 $offs = (tell $fh) - 5;
62 ::give unless ++$ecnt & 1023; 102 &::give unless ++$ecnt & 255;
63 } 103 }
64 104
65 1; 105 1;
66} 106}
67 107
150 print $fh "[DIFF $_->[0]]\n"; 190 print $fh "[DIFF $_->[0]]\n";
151 print $fh "+$_\n" for @{$_->[1]}; 191 print $fh "+$_\n" for @{$_->[1]};
152 print $fh "-$_\n" for @{$_->[2]}; 192 print $fh "-$_\n" for @{$_->[2]};
153 } 193 }
154 194
195 flushfh $fh;
196 File::Sync::fsync($fh);
155 close $fh 197 close $fh
156 or die "$path~: unable to create updated .mdif: $!"; 198 or die "$path~: unable to create updated .mdif: $!";
157 199
158 rename "$path~", $path; 200 replace("$path~", $path);
159 201
160 delete $self->{dirty}; 202 delete $self->{dirty};
161} 203}
162 204
163if (1) {
164 use OpenSSL ();
165 *hash = \&OpenSSL::Digest::sha1_hex;
166} elsif (0) {
167 # use Digest::SHA1;
168 my $digest = new Digest::SHA1;
169 *hash = sub {
170 $digest->reset;
171 $digest->add(@_);
172 $mid = $digest->hexdigest;
173 };
174}
175
176sub gendiff { 205sub gendiff {
177 my ($d1, $d2) = @_; 206 my ($self, $d1, $d2) = @_;
178 207
179 my (@add, @del); 208 my (@add, @del);
180 my (%d1, %d2); 209 my (%d1, %d2);
181 210
182 for (@$d2) { 211 for (@$d2) {
193 # add msgs in d2 but not in d1 222 # add msgs in d2 but not in d1
194 for (@$d2) { 223 for (@$d2) {
195 push @add, $_->[1] unless exists $d1{$_->[1]}; 224 push @add, $_->[1] unless exists $d1{$_->[1]};
196 } 225 }
197 226
227 push @{$self->{diff}}, [
228 $self->{ctime},
198 (\@add, \@del); 229 \@add, \@del,
230 ] if @add || @del;
199} 231}
200 232
201sub check { 233sub check {
202 my $self = shift; 234 my $self = shift;
203 my $path = $self->{path}; 235 my $path = $self->{path};
204 my $conf = $self->conf_path; 236 my $conf = $self->conf_path;
205 my $guard = $::lockdisk->guard; 237 my $guard = $::lockdisk->guard;
206 238
207 slog 3, "checking $path\n"; 239 slog 3, "checking $path\n";
208 240
209 if (stat $path) { 241 stat $path
242 or die "$path: $!";
243
210 my ($fsize, $mtime) = (stat _)[7, 9]; 244 my ($fsize, $mtime) = (stat _)[7, 9];
211 245
212 if (open my $fh, "<", $conf) { 246 if (open my $fh, "<", $conf) {
213 my %conf; 247 my %conf;
214 <$fh>; # skip initial comment 248 <$fh>; # skip initial comment
215 <$fh> eq "[SYNCMAIL]\n" 249 <$fh> eq "[SYNCMAIL]\n"
216 or die "$conf: format error"; 250 or die "$conf: format error";
217 while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) { 251 while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) {
218 $conf{$1} = $2; 252 $conf{$1} = $2;
253 }
254 return 1 if $fsize == $conf{fsize}
255 && $mtime == $conf{mtime};
256
257 $conf{mtime} <= $mtime
258 or die "$path: folder older than mdif";
259 }
260
261 slog 2, "updating $path\n";
262
263 my @idx;
264
265 parse_mbox $self->{fh}, sub {
266 my ($offs, $head, $body) = @_;
267 push @idx, [$offs, hash($$head, "\0", $$body)];
268 } or die "$path: no valid mbox file";
269
270 $self->read_mdif;
271
272 $self->{version} ||= MDIFVERSION;
273 $self->{ctime} = time;
274
275 $self->gendiff($self->{idx}, \@idx);
276
277 $self->{fsize} = $fsize;
278 $self->{mtime} = $mtime;
279 $self->{idx} = \@idx;
280
281 $self->dirty;
282 $self->write_mdif;
283}
284
285sub inventory {
286 hash sort map { $_->[1] } @{$_[0]{idx}};
287}
288
289sub open {
290 my ($self, $rw) = @_;
291
292 if (!$self->{fh} || $self->{rw} != $rw) {
293 $self->close;
294 $self->{rw} = $rw;
295 sysopen $self->{fh}, $self->{path},
296 O_CREAT | ($rw ? O_RDWR : O_RDONLY),
297 0666
298 or die "$self->{path}: $!";
299 0 == setlkw(fileno $self->{fh}, $rw ? 2 : 1)
300 or die "$self->{path}: $!";
301
302 }
303}
304
305sub close {
306 my $self = shift;
307
308 flushfh $self->{fh};
309 File::Sync::fsync($self->{fh});
310 delete $self->{fh};
311}
312
313# begin updating folder
314sub begin_update {
315 my $self = shift;
316
317 $self->{oidx} = $self->{idx};
318
319}
320
321sub delete {
322 my $self = shift;
323 my $temp = "$self->{path}~";
324
325 if (@_) {
326 my $guard = $::lockdisk->guard;
327 my %del; @del{@_} = ();
328
329 open my $fh, ">", $temp
330 or die "$temp: $!";
331
332 my $nidx;
333 my $idx = delete $self->{idx};
334 push @$idx, [$self->{fsize}];
335 $self->{fsize} = 0; # we virtually truncated the file
336
337 slog 0, "XXXXXXXXXXXXXXX @_\n";#d#
338
339 my $ofs = 0;
340 for (0 .. @$idx - 2) {
341 my $buf;
342
343 unless (exists $del{$idx->[$_][1]}) {
344 my $len = $idx->[$_+1][0] - $idx->[$_][0];
345
346 slog 0, "$idx->[$_][1] $idx->[$_+1][0] - $idx->[$_][0]\n";#d#
347
348 seek $self->{fh}, $idx->[$_][0],SEEK_SET
349 or die "$self->{path}: $!";
350
351 $len == read $self->{fh}, $buf, $len
352 or die "$self->{path}: $!";
353
354 $buf =~ /^From \S/
355 or die "$self->{path}: corrupted mail folder";
356
357 &::give unless ++$ecnt & 255;
358 } else {
359 slog 0, "skipping $idx->[$_][1]\n";
219 } 360 }
220 return 1 if $fsize == $conf{fsize}
221 && $mtime == $conf{mtime};
222
223 $conf{mtime} <= $mtime
224 or die "$path: folder older than mdif";
225 } 361 }
226 362
227 slog 2, "updating $path\n"; 363 File::Sync::fsync($fh);
364 close $fh;
228 365
229 my @idx; 366 # replace $temp, $self->{path}
230
231 parse_mbox $path, sub {
232 my ($offs, $head, $body) = @_;
233 push @idx, [$offs, hash($$head, "\0", $$body)];
234 } or return ();
235
236 $self->read_mdif;
237
238 $self->{version} ||= MDIFVERSION;
239 $self->{fsize} = $fsize;
240 $self->{mtime} = $mtime;
241 $self->{ctime} = time;
242 $self->{idx} = \@idx;
243
244 my ($add, $del) = gendiff $self->{idx}, \@idx;
245 push @{$self->{diff}}, [
246 $self->{ctime},
247 $add, $del,
248 ] if @$add || @$del;
249
250 $self->dirty;
251
252 return 2;
253 } else {
254 slog 2, "$path: no longer exists\n";
255 unlink $conf;
256
257 return ();
258 } 367 }
368}
369
370sub end_update {
371}
372
373sub append {
374 my $self = shift;
375
376 &update;
377 #$self->open(1);
259} 378}
260 379
2611; 3801;
262 381
382__DATA__
383__C__
384#include <unistd.h>
385#include <fcntl.h>
386
387/* mode0 unlock, mode1 rlock, mode2 rwlock */
388int setlkw(int fd, int mode)
389{
390 struct flock l;
391
392 l.l_type = mode == 0 ? F_UNLCK
393 : mode == 1 ? F_RDLCK
394 : F_WRLCK;
395 l.l_whence = SEEK_SET;
396 l.l_start = 0;
397 l.l_len = 0;
398
399 return fcntl (fd, F_SETLKW, &l);
400}
401

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines