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

Comparing syncmail/folder.pm (file contents):
Revision 1.4 by root, Sun Oct 28 20:52:24 2001 UTC vs.
Revision 1.6 by root, Mon Oct 29 02:36:32 2001 UTC

23 $mid = $digest->hexdigest; 23 $mid = $digest->hexdigest;
24 }; 24 };
25 } 25 }
26} 26}
27 27
28sub flushfh { 28sub fflush {
29 my $oldfh = select $_[0]; 29 my $oldfh = select $_[0];
30 $| = 1; 30 $| = 1; $| = 0;
31 select $oldfh; 31 select $oldfh;
32} 32}
33 33
34# rename a file and fsync the directory 34# rename a file and fsync the directory
35sub replace { 35sub replace {
62sub dirty { 62sub dirty {
63 $_[0]{dirty} = 1; 63 $_[0]{dirty} = 1;
64} 64}
65 65
66sub DESTROY { 66sub DESTROY {
67 $_[0]->write_mdif; 67 #$_[0]->write_mdif; # do NOT!
68} 68}
69 69
70# parse_mbox(mbox-file-path, callback) 70# parse_mbox(mbox-file-path, callback)
71# callback gets called with \$header and \$body, 71# callback gets called with \$header and \$body,
72# $header includes the mbox From_ line without 72# $header includes the mbox From_ line without
190 print $fh "[DIFF $_->[0]]\n"; 190 print $fh "[DIFF $_->[0]]\n";
191 print $fh "+$_\n" for @{$_->[1]}; 191 print $fh "+$_\n" for @{$_->[1]};
192 print $fh "-$_\n" for @{$_->[2]}; 192 print $fh "-$_\n" for @{$_->[2]};
193 } 193 }
194 194
195 flushfh $fh; 195 fflush($fh);
196 File::Sync::fsync($fh); 196 File::Sync::fsync($fh);
197 close $fh 197 close $fh
198 or die "$path~: unable to create updated .mdif: $!"; 198 or die "$path~: unable to create updated .mdif: $!";
199 199
200 replace("$path~", $path); 200 replace("$path~", $path);
226 226
227 push @{$self->{diff}}, [ 227 push @{$self->{diff}}, [
228 $self->{ctime}, 228 $self->{ctime},
229 \@add, \@del, 229 \@add, \@del,
230 ] if @add || @del; 230 ] if @add || @del;
231}
232
233sub check {
234 my $self = shift;
235 my $path = $self->{path};
236 my $conf = $self->conf_path;
237 my $guard = $::lockdisk->guard;
238
239 slog 3, "checking $path\n";
240
241 stat $path
242 or die "$path: $!";
243
244 my ($fsize, $mtime) = (stat _)[7, 9];
245
246 if (open my $fh, "<", $conf) {
247 my %conf;
248 <$fh>; # skip initial comment
249 <$fh> eq "[SYNCMAIL]\n"
250 or die "$conf: format error";
251 while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) {
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} 231}
288 232
289sub open { 233sub open {
290 my ($self, $rw) = @_; 234 my ($self, $rw) = @_;
291 235
297 0666 241 0666
298 or die "$self->{path}: $!"; 242 or die "$self->{path}: $!";
299 0 == setlkw(fileno $self->{fh}, $rw ? 2 : 1) 243 0 == setlkw(fileno $self->{fh}, $rw ? 2 : 1)
300 or die "$self->{path}: $!"; 244 or die "$self->{path}: $!";
301 245
246 $self->check;
302 } 247 }
303} 248}
304 249
305sub close { 250sub close {
306 my $self = shift; 251 my $self = shift;
307 252
253 if ($self->{rw} && $self->{fh}) {
308 flushfh $self->{fh}; 254 fflush($self->{fh});
309 File::Sync::fsync($self->{fh}); 255 File::Sync::fsync($self->{fh});
256 }
257
258 $self->write_mdif;
259
310 delete $self->{fh}; 260 delete $self->{fh};
261}
262
263sub check {
264 my $self = shift;
265 my $conf = $self->conf_path;
266 my $guard = $::lockdisk->guard;
267
268 slog 3, "checking $self->{path}\n";
269
270 my ($fsize, $mtime) = (stat $self->{fh})[7, 9];
271
272 if ($self->{idx}) {
273 return 1 if $fsize == $self->{fsize}
274 && $mtime == $self->{mtime};
275 } else {
276 if (open my $fh, "<", $conf) {
277 my %conf;
278 <$fh>; # skip initial comment
279 <$fh> eq "[SYNCMAIL]\n"
280 or die "$conf: format error";
281 while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) {
282 $conf{$1} = $2;
283 }
284 return 1 if $fsize == $conf{fsize}
285 && $mtime == $conf{mtime};
286
287 $conf{mtime} <= $mtime
288 or die "$self->{path}: folder older than mdif";
289 }
290 }
291
292 slog 2, "updating $self->{path}\n";
293
294 my @idx;
295
296 seek $self->{fh}, 0, SEEK_SET;
297
298 parse_mbox $self->{fh}, sub {
299 my ($offs, $head, $body) = @_;
300 push @idx, [$offs, hash($$head, "\n\n", $$body)];
301 } or die "$self->{path}: no valid mbox file";
302
303 $self->read_mdif;
304
305 $self->{version} ||= MDIFVERSION;
306 $self->{ctime} = time;
307
308 $self->gendiff($self->{idx}, \@idx);
309
310 $self->{fsize} = $fsize;
311 $self->{mtime} = $mtime;
312 $self->{idx} = \@idx;
313
314 $self->dirty;
315 $self->write_mdif;#d#
316}
317
318sub inventory {
319 hash sort map { $_->[1] } @{$_[0]{idx}};
320}
321
322sub iidx {
323 my $self = shift;
324
325 $self->{iidx} ||= do {
326 my %iidx;
327 my $idx = $self->{idx};
328
329 push @$idx, [$self->{fsize}];
330 my $ofs = 0;
331 for (0 .. @$idx - 2) {
332 $iidx{$idx->[$_][1]} = [$idx->[$_][0], $idx->[$_+1][0] - $idx->[$_][0]];
333 }
334 pop @$idx, [$self->{fsize}];
335
336 \%iidx;
337 };
338}
339
340sub exists {
341 $_[0]->iidx unless $_[0]{iidx};
342 return $_[0]{iidx}{$_[1]};
343}
344
345sub fetch {
346 my ($self, $hash) = @_;
347
348 $self->iidx unless $self->{iidx};
349
350 my $mail;
351
352 my $msg = $self->{iidx}{$hash}
353 or die "$hash: no such message in $self->{path}";
354
355 print STDERR "$self->{fh}, $msg->[0], SEEK_SET\n";#d#
356
357 seek $self->{fh}, $msg->[0], SEEK_SET
358 or die "$self->{path}: $!";
359
360 print STDERR "$msg->[1] == read $self->{fh}, $msg->[1]\n";#d#
361
362 $msg->[1] == read $self->{fh}, $mail, $msg->[1]
363 or die "$self->{path}: $!";
364
365 $mail =~ /^From \S/
366 or die "$self->{path}: mail folder corrupted";
367
368 $mail;
311} 369}
312 370
313# begin updating folder 371# begin updating folder
314sub begin_update { 372sub begin_update {
315 my $self = shift; 373 my $self = shift;
316 374
317 $self->{oidx} = $self->{idx}; 375 $self->{oidx} = $self->{idx};
318
319} 376}
320 377
321sub delete { 378sub delete {
322 my $self = shift; 379 my $self = shift;
323 my $temp = "$self->{path}~"; 380 my $temp = "$self->{path}~";
324 381
382 $self->iidx unless $self->{iidx};
383
325 if (@_) { 384 for (@_) {
385 if (exists $self->{iidx}{$_}) { # at least one message exists
326 my $guard = $::lockdisk->guard; 386 my $guard = $::lockdisk->guard;
327 my %del; @del{@_} = (); 387 my %del; @del{@_} = ();
388 my @nidx;
389 my $dofs = 0;
328 390
329 open my $fh, ">", $temp 391 open my $fh, "+>", $temp
330 or die "$temp: $!"; 392 or die "$temp: $!";
331 393
332 my $nidx; 394 eval {
333 my $idx = delete $self->{idx}; 395 0 == setlkw(fileno $fh, 2)
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}: $!"; 396 or die "$self->{path}~: $!";
350 397
398 $self->{fsize} = 0; # we virtually truncated the file
399
400 for (@{delete $self->{idx}}) {
401 my $hash = $_->[1];
402 my $buf;
403
404 unless (exists $del{$hash}) {
405 my ($ofs, $len) = @{$self->{iidx}{$hash}};
406
407 $len or die;
408
409 seek $self->{fh}, $ofs, SEEK_SET
410 or die "$self->{path}: $!";
411
351 $len == read $self->{fh}, $buf, $len 412 $len == read $self->{fh}, $buf, $len
352 or die "$self->{path}: $!"; 413 or die "$self->{path}: $!";
353 414
354 $buf =~ /^From \S/ 415 $buf =~ /^From \S/
355 or die "$self->{path}: corrupted mail folder"; 416 or die "$self->{path}: corrupted mail folder";
356 417
418 print $fh $buf
419 or die "$self->{path}: $!";
420
421 push @nidx, [$dofs, $hash];
422 $self->{iidx}{$hash}[0] = $dofs;
423 $dofs += $len;
424
357 &::give unless ++$ecnt & 255; 425 &::give unless ++$ecnt & 255;
358 } else { 426 } else {
359 slog 0, "skipping $idx->[$_][1]\n"; 427 delete $self->{iidx}{$hash};
428 slog 0, "skipping/deleting $hash\n";
429 }
430 }
431 };
432
433 if ($@) {
434 close $fh;
435 unlink $temp;
436 die;
360 } 437 }
438
439 fflush($fh);
440 File::Sync::fsync($fh);
441 replace $temp, $self->{path};
442
443 $self->{fh} = $fh;
444 $self->{rw} = 1;
445
446 delete $self->{iidx};
447
448 $self->{idx} = \@nidx;
449 $self->{fsize} = $dofs;
450
451 return;
361 } 452 }
453 }
454}
362 455
363 File::Sync::fsync($fh); 456sub append {
364 close $fh; 457 my ($self, $hash, $mail) = @_;
365 458
366 # replace $temp, $self->{path} 459 if (length $mail) {
460 $self->open(1);
461
462 seek $self->{fh}, $self->{fsize}, SEEK_SET
463 or die "$self->{path}: $!";
464
465 print {$self->{fh}} $mail
466 or die "$self->{path}: $!";
467
468 push @{$self->{idx}}, [$self->{fsize}, $hash];
469 $self->{fsize} += length $mail;
367 } 470 }
368} 471}
369 472
370sub end_update { 473sub end_update {
371}
372
373sub append {
374 my $self = shift; 474 my $self = shift;
375 475
376 &update; 476 $self->gendiff((delete $self->{oidx}), $self->{idx});
377 #$self->open(1); 477
478 fflush($self->{fh});
479 File::Sync::fsync($self->{fh});
480
481 stat $self->{fh}
482 or die "$self->{path}: $!";
483
484 $self->{fsize} = (stat _)[7];
485 $self->{mtime} = (stat _)[9];
486
487 $self->dirty;
378} 488}
379 489
3801; 4901;
381 491
382__DATA__ 492__DATA__

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines