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.5 by root, Mon Oct 29 00:37:41 2001 UTC

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 flushfh($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 flushfh($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 seek $self->{fh}, $msg->[0], SEEK_SET
356 or die "$self->{path}: $!";
357
358 $msg->[1] == read $self->{fh}, $mail, $msg->[1]
359 or die "$self->{path}: $!";
360
361 $mail =~ /^From \S/
362 or die "$self->{path}: mail folder corrupted";
363
364 $mail;
311} 365}
312 366
313# begin updating folder 367# begin updating folder
314sub begin_update { 368sub begin_update {
315 my $self = shift; 369 my $self = shift;
316 370
317 $self->{oidx} = $self->{idx}; 371 $self->{oidx} = $self->{idx};
318
319} 372}
320 373
321sub delete { 374sub delete {
322 my $self = shift; 375 my $self = shift;
323 my $temp = "$self->{path}~"; 376 my $temp = "$self->{path}~";
324 377
378 $self->iidx unless $self->{iidx};
379
325 if (@_) { 380 for (@_) {
381 if (exists $self->{iidx}{$_}) { # at least one message exists
326 my $guard = $::lockdisk->guard; 382 my $guard = $::lockdisk->guard;
327 my %del; @del{@_} = (); 383 my %del; @del{@_} = ();
384 my @nidx;
328 385
329 open my $fh, ">", $temp 386 open my $fh, ">", $temp
330 or die "$temp: $!"; 387 or die "$temp: $!";
331 388
332 my $nidx; 389 eval {
333 my $idx = delete $self->{idx}; 390 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}: $!"; 391 or die "$self->{path}~: $!";
350 392
393 $self->{fsize} = 0; # we virtually truncated the file
394
395 my $dofs = 0;
396 for (@{delete $self->{idx}}) {
397 my $hash = $_->[1];
398 my $buf;
399
400 unless (exists $del{$hash}) {
401 my ($ofs, $len) = @{$self->{iidx}{$hash}};
402
403 $len or die;
404
405 seek $self->{fh}, $ofs, SEEK_SET
406 or die "$self->{path}: $!";
407
351 $len == read $self->{fh}, $buf, $len 408 $len == read $self->{fh}, $buf, $len
352 or die "$self->{path}: $!"; 409 or die "$self->{path}: $!";
353 410
354 $buf =~ /^From \S/ 411 $buf =~ /^From \S/
355 or die "$self->{path}: corrupted mail folder"; 412 or die "$self->{path}: corrupted mail folder";
356 413
414 print $fh $buf
415 or die "$self->{path}: $!";
416
417 push @nidx, [$dofs, $hash];
418 $self->{iidx}{$hash}[0] = $dofs;
419 $dofs += $len;
420
357 &::give unless ++$ecnt & 255; 421 &::give unless ++$ecnt & 255;
358 } else { 422 } else {
359 slog 0, "skipping $idx->[$_][1]\n"; 423 delete $self->{iidx}{$hash};
424 slog 0, "skipping/deleting $hash\n";
425 }
426 }
427 };
428
429 if ($@) {
430 close $fh;
431 unlink $temp;
432 die;
360 } 433 }
434
435 File::Sync::fsync($fh);
436 replace $temp, $self->{path};
437
438 $self->{fh} = $fh;
439 $self->{rw} = 1;
440
441 delete $self->{iidx};
442
443 $self->{idx} = \@nidx;
444 $self->{fsize} = $ofs;
445
446 return;
361 } 447 }
448 }
449}
362 450
363 File::Sync::fsync($fh); 451sub append {
364 close $fh; 452 my ($self, $hash, $mail) = @_;
365 453
366 # replace $temp, $self->{path} 454 if (length $mail) {
455 $self->open(1);
456
457 seek $self->{fh}, $self->{fsize}, SEEK_SET
458 or die "$self->{path}: $!";
459
460 print {$self->{fh}} $mail
461 or die "$self->{path}: $!";
462
463 push @{$self->{idx}}, [$self->{fsize}, $hash];
464 $self->{fsize} += length $mail;
367 } 465 }
368} 466}
369 467
370sub end_update { 468sub end_update {
371}
372
373sub append {
374 my $self = shift; 469 my $self = shift;
375 470
376 &update; 471 $self->gendiff((delete $self->{oidx}, $self->{idx}));
377 #$self->open(1); 472
473 flushfh($self->{fh});
474 File::Sync::fsync($self->{fh});
475
476 stat $self->{fh}
477 or die "$self->{path}: $!";
478
479 $self->{fsize} = (stat _)[7];
480 $self->{mtime} = (stat _)[9];
481
482 $self->dirty;
378} 483}
379 484
3801; 4851;
381 486
382__DATA__ 487__DATA__

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines