--- syncmail/folder.pm 2001/10/28 20:52:24 1.4 +++ syncmail/folder.pm 2001/10/29 00:37:41 1.5 @@ -64,7 +64,7 @@ } sub DESTROY { - $_[0]->write_mdif; + #$_[0]->write_mdif; # do NOT! } # parse_mbox(mbox-file-path, callback) @@ -192,7 +192,7 @@ print $fh "-$_\n" for @{$_->[2]}; } - flushfh $fh; + flushfh($fh); File::Sync::fsync($fh); close $fh or die "$path~: unable to create updated .mdif: $!"; @@ -230,42 +230,75 @@ ] if @add || @del; } +sub open { + my ($self, $rw) = @_; + + if (!$self->{fh} || $self->{rw} != $rw) { + $self->close; + $self->{rw} = $rw; + sysopen $self->{fh}, $self->{path}, + O_CREAT | ($rw ? O_RDWR : O_RDONLY), + 0666 + or die "$self->{path}: $!"; + 0 == setlkw(fileno $self->{fh}, $rw ? 2 : 1) + or die "$self->{path}: $!"; + + $self->check; + } +} + +sub close { + my $self = shift; + + if ($self->{rw} && $self->{fh}) { + flushfh($self->{fh}); + File::Sync::fsync($self->{fh}); + } + + $self->write_mdif; + + delete $self->{fh}; +} + sub check { my $self = shift; - my $path = $self->{path}; my $conf = $self->conf_path; my $guard = $::lockdisk->guard; - slog 3, "checking $path\n"; + slog 3, "checking $self->{path}\n"; - stat $path - or die "$path: $!"; + my ($fsize, $mtime) = (stat $self->{fh})[7, 9]; - my ($fsize, $mtime) = (stat _)[7, 9]; + if ($self->{idx}) { + return 1 if $fsize == $self->{fsize} + && $mtime == $self->{mtime}; + } else { + if (open my $fh, "<", $conf) { + my %conf; + <$fh>; # skip initial comment + <$fh> eq "[SYNCMAIL]\n" + or die "$conf: format error"; + while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) { + $conf{$1} = $2; + } + return 1 if $fsize == $conf{fsize} + && $mtime == $conf{mtime}; - if (open my $fh, "<", $conf) { - my %conf; - <$fh>; # skip initial comment - <$fh> eq "[SYNCMAIL]\n" - or die "$conf: format error"; - while (<$fh> =~ /^([a-z]+)\s*=\s*(.*)$/) { - $conf{$1} = $2; + $conf{mtime} <= $mtime + or die "$self->{path}: folder older than mdif"; } - return 1 if $fsize == $conf{fsize} - && $mtime == $conf{mtime}; - - $conf{mtime} <= $mtime - or die "$path: folder older than mdif"; } - slog 2, "updating $path\n"; + slog 2, "updating $self->{path}\n"; my @idx; + seek $self->{fh}, 0, SEEK_SET; + parse_mbox $self->{fh}, sub { my ($offs, $head, $body) = @_; - push @idx, [$offs, hash($$head, "\0", $$body)]; - } or die "$path: no valid mbox file"; + push @idx, [$offs, hash($$head, "\n\n", $$body)]; + } or die "$self->{path}: no valid mbox file"; $self->read_mdif; @@ -279,35 +312,56 @@ $self->{idx} = \@idx; $self->dirty; - $self->write_mdif; + $self->write_mdif;#d# } sub inventory { hash sort map { $_->[1] } @{$_[0]{idx}}; } -sub open { - my ($self, $rw) = @_; +sub iidx { + my $self = shift; - if (!$self->{fh} || $self->{rw} != $rw) { - $self->close; - $self->{rw} = $rw; - sysopen $self->{fh}, $self->{path}, - O_CREAT | ($rw ? O_RDWR : O_RDONLY), - 0666 - or die "$self->{path}: $!"; - 0 == setlkw(fileno $self->{fh}, $rw ? 2 : 1) - or die "$self->{path}: $!"; + $self->{iidx} ||= do { + my %iidx; + my $idx = $self->{idx}; - } + push @$idx, [$self->{fsize}]; + my $ofs = 0; + for (0 .. @$idx - 2) { + $iidx{$idx->[$_][1]} = [$idx->[$_][0], $idx->[$_+1][0] - $idx->[$_][0]]; + } + pop @$idx, [$self->{fsize}]; + + \%iidx; + }; } -sub close { - my $self = shift; +sub exists { + $_[0]->iidx unless $_[0]{iidx}; + return $_[0]{iidx}{$_[1]}; +} - flushfh $self->{fh}; - File::Sync::fsync($self->{fh}); - delete $self->{fh}; +sub fetch { + my ($self, $hash) = @_; + + $self->iidx unless $self->{iidx}; + + my $mail; + + my $msg = $self->{iidx}{$hash} + or die "$hash: no such message in $self->{path}"; + + seek $self->{fh}, $msg->[0], SEEK_SET + or die "$self->{path}: $!"; + + $msg->[1] == read $self->{fh}, $mail, $msg->[1] + or die "$self->{path}: $!"; + + $mail =~ /^From \S/ + or die "$self->{path}: mail folder corrupted"; + + $mail; } # begin updating folder @@ -315,66 +369,117 @@ my $self = shift; $self->{oidx} = $self->{idx}; - } sub delete { my $self = shift; my $temp = "$self->{path}~"; - if (@_) { - my $guard = $::lockdisk->guard; - my %del; @del{@_} = (); + $self->iidx unless $self->{iidx}; - open my $fh, ">", $temp - or die "$temp: $!"; + for (@_) { + if (exists $self->{iidx}{$_}) { # at least one message exists + my $guard = $::lockdisk->guard; + my %del; @del{@_} = (); + my @nidx; - my $nidx; - my $idx = delete $self->{idx}; - push @$idx, [$self->{fsize}]; - $self->{fsize} = 0; # we virtually truncated the file + open my $fh, ">", $temp + or die "$temp: $!"; - slog 0, "XXXXXXXXXXXXXXX @_\n";#d# + eval { + 0 == setlkw(fileno $fh, 2) + or die "$self->{path}~: $!"; - my $ofs = 0; - for (0 .. @$idx - 2) { - my $buf; + $self->{fsize} = 0; # we virtually truncated the file - unless (exists $del{$idx->[$_][1]}) { - my $len = $idx->[$_+1][0] - $idx->[$_][0]; + my $dofs = 0; + for (@{delete $self->{idx}}) { + my $hash = $_->[1]; + my $buf; - slog 0, "$idx->[$_][1] $idx->[$_+1][0] - $idx->[$_][0]\n";#d# + unless (exists $del{$hash}) { + my ($ofs, $len) = @{$self->{iidx}{$hash}}; - seek $self->{fh}, $idx->[$_][0],SEEK_SET - or die "$self->{path}: $!"; + $len or die; - $len == read $self->{fh}, $buf, $len - or die "$self->{path}: $!"; + seek $self->{fh}, $ofs, SEEK_SET + or die "$self->{path}: $!"; - $buf =~ /^From \S/ - or die "$self->{path}: corrupted mail folder"; + $len == read $self->{fh}, $buf, $len + or die "$self->{path}: $!"; + + $buf =~ /^From \S/ + or die "$self->{path}: corrupted mail folder"; + + print $fh $buf + or die "$self->{path}: $!"; + + push @nidx, [$dofs, $hash]; + $self->{iidx}{$hash}[0] = $dofs; + $dofs += $len; + + &::give unless ++$ecnt & 255; + } else { + delete $self->{iidx}{$hash}; + slog 0, "skipping/deleting $hash\n"; + } + } + }; - &::give unless ++$ecnt & 255; - } else { - slog 0, "skipping $idx->[$_][1]\n"; + if ($@) { + close $fh; + unlink $temp; + die; } - } - File::Sync::fsync($fh); - close $fh; + File::Sync::fsync($fh); + replace $temp, $self->{path}; + + $self->{fh} = $fh; + $self->{rw} = 1; + + delete $self->{iidx}; - # replace $temp, $self->{path} + $self->{idx} = \@nidx; + $self->{fsize} = $ofs; + + return; + } } } -sub end_update { +sub append { + my ($self, $hash, $mail) = @_; + + if (length $mail) { + $self->open(1); + + seek $self->{fh}, $self->{fsize}, SEEK_SET + or die "$self->{path}: $!"; + + print {$self->{fh}} $mail + or die "$self->{path}: $!"; + + push @{$self->{idx}}, [$self->{fsize}, $hash]; + $self->{fsize} += length $mail; + } } -sub append { +sub end_update { my $self = shift; - &update; - #$self->open(1); + $self->gendiff((delete $self->{oidx}, $self->{idx})); + + flushfh($self->{fh}); + File::Sync::fsync($self->{fh}); + + stat $self->{fh} + or die "$self->{path}: $!"; + + $self->{fsize} = (stat _)[7]; + $self->{mtime} = (stat _)[9]; + + $self->dirty; } 1;