More work on new indexing system

This commit is contained in:
Jamie Cameron
2007-07-20 01:25:04 +00:00
parent 16e2625bfd
commit e71c7a2640
2 changed files with 289 additions and 205 deletions

View File

@@ -121,6 +121,22 @@ close(MAIL);
return @rv;
}
# idlist_mails(user|file)
# Returns a list of IDs in some mbox
sub idlist_mails
{
my %index;
&build_dbm_index($_[0], \%index);
local @rv;
while(my ($k, $v) = each %index) {
if ($k eq int($k)) {
local ($pos, $line, $subject, $sender, $mid) = split(/\0/, $v);
push(@rv, $pos." ".$k." ".$line." ".$mid);
}
}
return @rv;
}
# search_mail(user, field, match)
# Returns an array of messages matching some search
sub search_mail
@@ -185,7 +201,8 @@ foreach $i (@possible) {
$mail->{'line'} = $startline;
$mail->{'eline'} = $startline + $mail->{'lines'} - 1;
$mail->{'idx'} = $i;
$mail->{'id'} = "$pos $i";
$mail->{'id'} = $pos." ".$i." ".$startline." ".
substr($mail->{'header'}->{'message-id'}, 0, 255);
push(@rv, $mail) if ($possible_certain ||
&mail_matches($_[1], $_[2], $mail));
}
@@ -195,7 +212,7 @@ return @rv;
# build_dbm_index(user|file, &index)
# Returns a reference to a DBM hash that indexes the given mail file.
# Hash contains keys 0, 1, 2 .. each of which has a value containing the
# position of the mail in the file, line number, subject and sender.
# position of the mail in the file, line number, subject, sender and message ID.
# Special key lastchange = time index was last updated
# mailcount = number of messages in index
# version = index format version
@@ -272,7 +289,7 @@ if (!@st ||
$nidx[3] = $1;
$index->{$istart-1} = join("\0", @nidx);
}
elsif ($doingheaders && /^Message-ID\s*(.{0,255})/i) {
elsif ($doingheaders && /^Message-ID:\s*(.{0,255})/i) {
$nidx[4] = $1;
$index->{$istart-1} = join("\0", @nidx);
}
@@ -1528,6 +1545,14 @@ foreach $f (@files) {
return @rv;
}
# idlist_maildir(file)
# Returns a list of files in a maildir, which form the IDs
sub idlist_maildir
{
local ($file) = @_;
return map { substr($_, length($file)+1) } &get_maildir_files($file);
}
# select_maildir(file, &ids, headersonly)
# Returns a list of messages with the given IDs, from a maildir directory
sub select_maildir
@@ -1836,21 +1861,33 @@ foreach $f (@files) {
return @rv;
}
# select_mhdir(file, &indexes, headersonly)
# idlist_mhdir(file)
# Returns a list of files in an MH directory, which are the IDs
sub idlist_mhdir
{
opendir(DIR, $file);
local @files = grep { /^\d+$/ } readdir(DIR);
closedir(DIR);
return @files;
}
# select_mhdir(file, &ids, headersonly)
# Returns a list of messages with the given indexes, from an mhdir directory
sub select_mhdir
{
local ($file, $ids, $headersonly) = @_;
local @rv;
opendir(DIR, $_[0]);
local @files = map { "$_[0]/$_" }
opendir(DIR, $file);
local @files = map { "$file/$_" }
sort { $a <=> $b }
grep { /^\d+$/ } readdir(DIR);
closedir(DIR);
foreach my $i (@{$_[1]}) {
local $mail = &read_mail_file($files[$i], $_[2]);
$mail->{'idx'} = $i;
$mail->{'id'} = $files[$i];
$mail->{'id'} = substr($mail->{'id'}, length($_[0])+1);
foreach my $i (@$ids) {
local $mail = &read_mail_file("$file/$i", $headersonly);
if ($mail) {
$mail->{'idx'} = &indexof("$file/$i", @files);
$mail->{'id'} = $i;
}
push(@rv, $mail);
}
return @rv;

View File

@@ -190,7 +190,7 @@ elsif ($_[2]->{'type'} == 4) {
return @mail;
}
elsif ($_[2]->{'type'} == 5) {
# Just all of the constituent folders
# A composite folder, which combined two or more others.
local @mail;
# Work out exactly how big the total is
@@ -219,7 +219,6 @@ elsif ($_[2]->{'type'} == 5) {
local $sm;
foreach $sm (@submail) {
if ($sm) {
&push_index($sm, $sf, $sm->{'idx'}+$pos);
# ID is the original folder and ID
$sm->{'id'} = $sfn."\t".$sm->{'id'};
}
@@ -235,67 +234,32 @@ elsif ($_[2]->{'type'} == 6) {
local $mems = $folder->{'members'};
local ($start, $end) = &compute_start_end($_[0], $_[1], scalar(@$mems));
# Work out which folders we need, and how much
local (%frange, %namemap);
local $i;
for($i=$start; $i<=$end; $i++) {
# Build a map from sub-folder names to IDs in them
local (%wantmap, %namemap);
for(my $i=$start; $i<=$end; $i++) {
local $sf = $mems->[$i]->[0];
local $sid = $mems->[$i]->[1];
local $sfn = &folder_name($sf);
local $idx = $mems->[$i]->[1];
if ($frange{$sfn}) {
$frange{$sfn}->[0] = $idx if ($idx < $frange{$sfn}->[0]);
$frange{$sfn}->[1] = $idx if ($idx > $frange{$sfn}->[1]);
}
else {
$frange{$sfn} = [ $idx, $idx ];
}
$namemap{$sfn} = $sf;
push(@{$wantmap{$sfn}}, [ $sid, $i ]);
}
# Get all the needed folders
local %sfs;
local $sfn;
foreach $sfn (keys %frange) {
local $sf = $namemap{$sfn};
local @submail = &mailbox_list_mails($frange{$sfn}->[0],
$frange{$sfn}->[1],
$sf, $_[3]);
$sfs{$sfn} = \@submail;
}
# Construct the results
# For each sub-folder, get the IDs we need, and put them into the
# return array at the right place
local @mail = map { undef } (0 .. @$mems-1);
local $need_save = 0;
local @newmems = @$mems;
for($i=$start; $i<=$end && $i<=@$mems; $i++) {
local $sf = $mems->[$i]->[0];
local $sfn = &folder_name($sf);
local $idx = $mems->[$i]->[1];
$mail[$i] = $sfs{$sfn}->[$idx];
if ($mems->[$i]->[2] &&
$mail[$i]->{'header'}->{'message-id'} ne $mems->[$i]->[2]) {
# Argh .. index is wrong! Find message by ID
local $real = &find_by_message_id($sf,$mems->[$i]->[2]);
if ($real) {
$mems->[$i]->[1] = $real->{'idx'};
$mail[$i] = $real;
$need_save++;
}
else {
# Doesn't exist! Lose from index..
@newmems = grep { $_ ne $mems->[$i] } @newmems;
$need_save++;
next;
foreach my $sfn (keys %wantmap) {
local $sf = $namemap{$sfn};
local @wantids = map { $_->[0] } @{$wantmap{$sfn}};
local @wantidxs = map { $_->[1] } @{$wantmap{$sfn}};
local @sfmail = &mailbox_select_mails($sf, \@wantids, $_[3]);
for(my $i=0; $i<@sfmail; $i++) {
$mail[$wantidxs[$i]] = $sfmail[$i];
if ($sfmail[$i]) {
$sfmail[$i]->{'idx'} = $wantidxs[$i];
$sfmail[$i]->{'id'} =
$sfn."\t".$sfmail[$i]->{'id'};
}
}
# ID is sub-folder name and original ID
$mail[$i]->{'id'} = $sfn."\t".$mail->{'id'};
&push_index($mail[$i], $sf, $i);
}
if ($need_save) {
$_[2]->{'members'} = \@newmems;
&save_folder($_[2]);
}
return @mail;
}
@@ -437,128 +401,92 @@ elsif ($folder->{'type'} == 4) {
return ();
}
elsif ($irv[0] == 0) { &error($irv[1]); }
elsif ($irv[0] == 3) { &error(&text('save_emailbox', $irv[1])); }
elsif ($irv[0] == 3) { &error(&text('save_emailbox', $irv[1]));}
elsif ($irv[0] == 2) { &error(&text('save_elogin2', $irv[1])); }
}
local $h = $irv[1];
local $count = $irv[2];
return () if (!$count);
# Fetch each mail by index
# XXX need to fetch by ID
# XXX need to set idx
local @rv;
# Build map from IDs to original order, as UID FETCH doesn't return
# mail in the order we asked for!
local %wantpos;
for(my $i=0; $i<@$ids; $i++) {
$wantpos{$ids->[$i]} = $i;
}
# Fetch each mail by ID
local @rv = map { undef } @$ids;
local $wanted = $headersonly ? "(RFC822.SIZE UID RFC822.HEADER)"
: "(UID RFC822)";
local @idxrv = &imap_command($h,
"FETCH ".join(",", map { $_+1 } @$indexes)." $wanted");
local $i = 0;
"UID FETCH ".join(",", @$ids)." $wanted");
foreach my $idxrv (@{idxrv->[1]}) {
local $mail = &parse_imap_mail($idxrv);
if ($mail) {
$mail->{'idx'} = $indexes->[$i++];
push(@rv, $mail);
$mail->{'idx'} = $mail->{'imapidx'};
$rv[$wantpos{$mail->{'id'}}] = $mail;
}
}
return @rv;
}
elsif ($folder->{'type'} == 5) {
# Composite folder .. need to convert each index to a position
# in a sub-folder
# XXX could be faster
my $pos = 0;
my @ranges;
foreach my $sf (@{$folder->{'subfolders'}}) {
my $len = &mailbox_folder_size($sf);
push(@ranges, [ $pos, $pos+$len, $sf ]);
$pos += $len;
}
local @rv;
foreach my $i (@$indexes) {
# Find out which sub-folder this index is in
foreach my $r (@ranges) {
if ($i >= $r->[0] && $i < $r->[1]) {
# Found it!
local ($mail) = &mailbox_select_mails(
$r->[2], [ $i - $r->[0] ],
$headersonly);
&push_index($mail, $r->[2], $i);
push(@rv, $mail);
last;
}
}
}
return @rv;
}
elsif ($folder->{'type'} == 6) {
# Virtual folder .. translate each index to sub-folder index
# XXX could be faster
local $mems = $folder->{'members'};
local @rv;
local $need_save = 0;
local @newmems = @$mems;
elsif ($folder->{'type'} == 5 || $folder->{'type'} == 6) {
# Virtual or composite folder .. for each ID, work out the folder and
# build a map from folders to ID lists
print STDERR "selecting ",scalar(@$ids)," ids\n";
# Work out how many different sub-folders we have
local $sf;
foreach my $i (@$indexes) {
$sf = $mems->[$i]->[0];
$sfcount{&folder_name($sf)} = 1;
}
if ((keys %sfcount) == 1) {
# Just one, so we can do one big select
local (@sfindexes, @sfmems);
foreach my $i (@$indexes) {
push(@sfindexes, $mems->[$i]->[1]);
push(@sfmems, $i);
}
local $i = 0;
foreach my $mail (&mailbox_select_mails($sf, \@sfindexes,
$headersonly)) {
$mail->{'mem'} = $mems->[$sfmems[$i]];
&push_index($mail, $sf, $sfmems[$i]);
push(@rv, $mail);
$i++;
}
}
elsif ((keys %sfcount) > 0) {
# Several sub-folders, so we need to select from each
foreach my $i (@$indexes) {
local $sf = $mems->[$i]->[0];
local $idx = $mems->[$i]->[1];
local ($mail) = &mailbox_select_mails($sf, [ $idx ],
$headersonly);
$mail->{'mem'} = $mems->[$i];
&push_index($mail, $sf, $i);
push(@rv, $mail);
}
# Build a map from sub-folder names to IDs in them
my $i = 0;
my %wantmap;
foreach my $id (@$ids) {
local ($sfn, $sid) = split(/\t+/, $id, 2);
push(@{$wantmap{$sfn}}, [ $sid, $i ]);
$i++;
}
# Fix up any mistmatches between indexes and message IDs
foreach $mail (@rv) {
local $mem = $mail->{'mem'};
if ($mem->[2] &&
$mail->{'header'}->{'message-id'} ne $mem->[2]) {
# Message ID mismatch! Fix it..
local $real = &find_by_message_id(
$mail->{'subfolder'}, $mem->[2]);
if ($real) {
$mem->[1] = $real->{'idx'};
$mail = $real;
$need_save++;
}
else {
# Doesn't exist! Lose from index..
@newmems = grep { $_ ne $mem } @newmems;
$need_save++;
# Build map from sub-folder names to IDs
my (%namemap, @allids);
if ($folder->{'type'} == 6) {
# For a virtual folder, we need to find all sub-folders
foreach my $m (@{$folder->{'members'}}) {
local $sfn = &folder_name($m->[0]);
$namemap{$sfn} = $m->[0];
push(@allids, $sfn."\t".$m->[1]);
}
}
else {
# For a composite, they are simply listed
foreach my $sf (@{$folder->{'subfolders'}}) {
local $sfn = &folder_name($sf);
$namemap{$sfn} = $sf;
}
@allids = &mailbox_idlist($folder);
}
# For each sub-folder, get the IDs we need, and put them into the
# return array at the right place
local @mail = map { undef } @$ids;
foreach my $sfn (keys %wantmap) {
local $sf = $namemap{$sfn};
local @wantids = map { $_->[0] } @{$wantmap{$sfn}};
local @wantidxs = map { $_->[1] } @{$wantmap{$sfn}};
local @sfmail = &mailbox_select_mails($sf, \@wantids,
$headersonly);
for(my $i=0; $i<@sfmail; $i++) {
$mail[$wantidxs[$i]] = $sfmail[$i];
if ($sfmail[$i]) {
$sfmail[$i]->{'id'} =
$sfn."\t".$sfmail[$i]->{'id'};
$sfmail[$i]->{'idx'} = &indexof(
$sfmail[$i]->{'id'}, @allids);
if ($sfmail[$i]->{'idx'} < 0) {
print STDERR "looking for ",$sfmail[$i]->{'id'}," in ",join(" ", @allids),"\n";
}
}
}
}
if ($need_save) {
$folder->{'members'} = \@newmems;
&save_folder($folder);
}
return @rv;
return @mail;
}
}
@@ -585,6 +513,90 @@ if ($mail) {
return $mail;
}
# mailbox_idlist(&folder)
# Returns a list of IDs of messages in some folder
sub mailbox_idlist
{
local ($folder) = @_;
if ($folder->{'type'} == 0) {
# mbox, for which IDs are mail positions
return &idlist_mails($folder->{'file'});
}
elsif ($folder->{'type'} == 1) {
# maildir, for which IDs are filenames
return &idlist_maildir($folder->{'file'});
}
elsif ($folder->{'type'} == 2) {
# pop3, for which IDs are uidls
local @rv = &pop3_login($folder);
if ($rv[0] != 1) {
# Failed to connect or login
if ($rv[0] == 0) { &error($rv[1]); }
else { &error(&text('save_elogin', $rv[1])); }
}
local $h = $rv[1];
local @uidl = &pop3_uidl($h);
return @uidl;
}
elsif ($folder->{'type'} == 3) {
# MH directory, for which IDs are file numbers
return &idlist_mhdir($folder->{'file'});
}
elsif ($folder->{'type'} == 4) {
# IMAP, for which IDs are IMAP UIDs
local @rv = &imap_login($folder);
if ($rv[0] != 1) {
# Something went wrong
if ($rv[0] == 0) { &error($rv[1]); }
elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
elsif ($rv[0] == 2) { &error(&text('save_elogin2', $rv[1])); }
}
local $h = $rv[1];
local $count = $rv[2];
return () if (!$count);
@rv = &imap_command($h, "FETCH 1:$count UID");
local @uids;
foreach my $uid (@{$rv[1]}) {
if ($uid =~ /UID\s+(\d+)/) {
push(@uids, $1);
}
}
return @uids;
}
elsif ($folder->{'type'} == 5) {
# Composite, IDs come from sub-folders
local @rv;
foreach my $sf (@{$folder->{'subfolders'}}) {
local $sfn = &folder_name($sf);
push(@rv, map { $sfn."\t".$_ } &mailbox_idlist($sf));
}
return @rv;
}
elsif ($folder->{'type'} == 6) {
# Virtual, IDs come from sub-folders (where they exist)
my (%wantmap, %namemap);
foreach my $m (@{$folder->{'members'}}) {
local $sf = $m->[0];
local $sid = $m->[1];
local $sfn = &folder_name($sf);
push(@{$wantmap{$sfn}}, $sid);
$namemap{$sfn} = $sf;
}
local @rv;
foreach my $sfn (keys %wantmap) {
local %wantids = map { $_, 1 } @{$wantmap{$sfn}};
local $sf = $namemap{$sfn};
foreach my $sfid (&mailbox_idlist($sf)) {
if ($wantids{$sfid}) {
push(@rv, $sfn."\t".$sfid);
}
}
}
return @rv;
}
}
# compute_start_end(start, end, count)
# Given start and end indexes (which may be negative or undef), returns the
# real mail file indexes.
@@ -632,7 +644,7 @@ if (!$field || !$folder->{'sortable'}) {
# Get a sorted list of IDs, and then find the real emails within the range
local @sorter = &build_sorted_ids($folder, $field, $dir);
($start, $end) = &compute_start_end($start, $end, scalar(@sorter));
print STDERR "for $folder->{'file'} sorter = ",join(" ", @sorter),"\n";
print STDERR "for ",&folder_name($folder)," sorter = ",scalar(@sorter)," = ",join(" ", @sorter),"\n";
print STDERR "start = $start end = $end\n";
local @rv = map { undef } (0 .. scalar(@sorter)-1);
local @wantids = map { $sorter[$_] } ($start .. $end);
@@ -759,22 +771,18 @@ local $ifile = &folder_new_sort_index_file($folder);
&open_dbm_db($index, $ifile, 0600);
if ($index->{'lastchange'} < $folder->{'lastchange'} ||
!$folder->{'lastchange'}) {
# The mail file is newer than the index .. add messages not in the index
# to it.
local $realcount = $folder->{'type'} == 6 ?
scalar(@{$folder->{'members'}}) :
&mailbox_folder_size($folder);
local $indexcount = int($index->{'mailcount'});
if ($realcount < $indexcount) {
# Mail size has decreased! Need total rebuild
$indexcount = 0;
%$index = ( );
}
local @mails;
if ($realcount) {
@mails = &mailbox_list_mails($indexcount, $realcount-1,
$folder, 1);
# The mail file has changed .. get IDs and update the index with any
# that are missing
local @ids = &mailbox_idlist($folder);
# Find IDs that are new
foreach my $id (@ids) {
if (!defined($index->{$id."_size"})) {
push(@newids, $id);
}
}
local @mails = scalar(@newids) ?
&mailbox_select_mails($folder, \@newids, 1) : ( );
foreach my $mail (@mails) {
foreach my $f (@index_fields) {
if ($f eq "date") {
@@ -809,8 +817,22 @@ if ($index->{'lastchange'} < $folder->{'lastchange'} ||
}
}
}
$index->{'lastchange'} = time();
$index->{'mailcount'} = $realcount;
print STDERR "added ",scalar(@mail)," messages to index\n";
# Remove IDs that no longer exist
local %ids = map { $_, 1 } @ids;
local $dc = 0;
while(my ($k, $v) = each %$index) {
if ($k =~ /^(.*)_([^_]+)$/ && !$ids{$1}) {
delete($index->{$k});
$dc++ if ($2 eq "size");
}
}
print STDERR "deleted $dc mesages from index\n";
# Record index update time
$index->{'lastchange'} = $folder->{'lastchange'} || time();
$index->{'mailcount'} = scalar(@ids);
}
return 1;
}
@@ -931,7 +953,6 @@ elsif ($_[2]->{'type'} == 3) {
}
elsif ($_[2]->{'type'} == 4) {
# Use IMAP's remote search feature
# XXX broken!
local @rv = &imap_login($_[2]);
if ($rv[0] == 0) { &error($rv[1]); }
elsif ($rv[0] == 3) { &error(&text('save_emailbox', $rv[1])); }
@@ -978,7 +999,7 @@ elsif ($_[2]->{'type'} == 4) {
next if ($realidx < $rv[3]-$_[3]->{'latest'});
}
local @rv = &imap_command($h,
"FETCH $idx (RFC822.SIZE RFC822.HEADER)");
"FETCH $idx (RFC822.SIZE UID RFC822.HEADER)");
&error(&text('save_esearch', $rv[3])) if (!$rv[0]);
local $mail = &parse_imap_mail($rv[1]->[0]);
if ($mail) {
@@ -1087,7 +1108,7 @@ elsif ($f->{'type'} == 4) {
local $m;
foreach $m (@_) {
@rv = &imap_command($h, "STORE ".($m->{'idx'}+1).
@rv = &imap_command($h, "UID STORE ".$m->{'id'}.
" +FLAGS (\\Deleted)");
&error(&text('save_edelete', $rv[3])) if (!$rv[0]);
}
@@ -1143,19 +1164,27 @@ elsif ($f->{'type'} == 6) {
# Update folder index to remove indexes for deleted messages
if ($f->{'sortable'}) {
local %index;
&build_new_sort_index($f, undef, \%index);
foreach my $m (@_) {
foreach my $if (@index_fields) {
delete($index{$m->{'id'}."_".$if});
}
}
$index{'mailcount'} -= scalar(@_);
$index{'lastchange'} = time();
dbmclose(%index);
&remove_new_sort_index($f, \@_);
}
}
# remove_new_sort_index(&folder, &mails)
# Remove entries for some email from a folder's sort index
sub remove_new_sort_index
{
local ($f, $mails) = @_;
local %index;
&build_new_sort_index($f, undef, \%index);
foreach my $m (@$mails) {
foreach my $if (@index_fields) {
delete($index{$m->{'id'}."_".$if});
}
}
$index{'mailcount'} -= scalar(@_);
$index{'lastchange'} = time();
dbmclose(%index);
}
# mailbox_empty_folder(&folder)
# Remove the entire contents of a mail folder
sub mailbox_empty_folder
@@ -1285,6 +1314,7 @@ local $dst = shift(@_);
local $now = time();
local $hn = &get_system_hostname();
&create_folder_maildir($dst);
local $fix_index;
if (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 1) {
# Can just move mail files
local $dd = $dst->{'file'};
@@ -1293,6 +1323,7 @@ if (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 1) {
rename($m->{'file'}, "$dd/cur/$now.$$.$hn");
$now++;
}
$fix_index = 1;
}
elsif (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 3) {
# Can move and rename to MH numbering
@@ -1302,6 +1333,7 @@ elsif (($src->{'type'} == 1 || $src->{'type'} == 3) && $dst->{'type'} == 3) {
rename($m->{'file'}, "$dd/$num");
$num++;
}
$fix_index = 1;
}
else {
# Append to new folder file, or create in folder directory
@@ -1312,10 +1344,9 @@ else {
&mailbox_delete_mail($src, @_);
}
# Force re-generation of source folder index
if ($src->{'sortable'}) {
&delete_new_sort_index($src);
# XXX could be faster
# Update folder index to remove indexes for deleted messages
if ($src->{'sortable'} && $fix_index) {
&remove_new_sort_index($src, \@_);
}
}
@@ -1325,7 +1356,7 @@ sub mailbox_move_folder
{
return undef if (&is_readonly_mode());
local ($src, $dst) = @_;
if ($src->{'type'} == $dst->{'type'}) {
if ($src->{'type'} == $dst->{'type'} && !$src->{'remote'}) {
# Can just move the file or dir
system("rm -rf ".quotemeta($dst->{'file'}));
system("mv ".quotemeta($src->{'file'})." ".quotemeta($dst->{'file'}));
@@ -1535,7 +1566,7 @@ elsif ($_[0]->{'type'} == 6 && !$_[1]) {
# exist in the parent folders
my $rv = 0;
foreach my $msg (@{$_[0]->{'members'}}) {
if (!$msg->[2] || &find_by_message_id($msg->[0], $msg->[2])) {
if (&mailbox_get_mail($msg->[0], $msg->[1])) {
$rv++;
}
}
@@ -1686,6 +1717,7 @@ local @rv;
local $id = $$."-".$imap_command_count++;
if ($c) {
print $h "$id $c\r\n";
print STDERR "imap command $id $c\n";
}
while(1) {
local $l = <$h>;
@@ -1799,18 +1831,20 @@ return $_[0]->{'remote'} ? undef : $_[0]->{'file'};
# Parses a response from the IMAP server into a standard mail structure
sub parse_imap_mail
{
local ($imap) = @_;
# Extract the actual mail part
local $mail = { };
local $realsize;
local $imap = $_[0];
if ($imap =~ /RFC822.SIZE\s+(\d+)/) {
$realsize = $1;
}
if ($imap =~ /UID\s+(\d+)/) {
$mail->{'id'} = $1;
}
$imap =~ s/^\*\s+\d+\s+FETCH.*\{(\d+)\}\r?\n// || return undef;
local $size = $1;
$imap =~ s/^\*\s+(\d+)\s+FETCH.*\{(\d+)\}\r?\n// || return undef;
$mail->{'imapidx'} = $1;
local $size = $2;
local @lines = split(/\n/, substr($imap, 0, $size));
# Parse the headers
@@ -2408,6 +2442,19 @@ foreach my $folder (@$folders) {
}
$folder->{'sortable'} = 1;
}
elsif ($folder->{'type'} == 5) {
# For a composite folder, the date is that of the newest
# sub-folder, OR the folder file itself
local @st = stat($folder->{'folderfile'});
$folder->{'lastchange'} = $st[9];
&set_folder_lastmodified($folder->{'subfolders'});
foreach my $sf (@{$folder->{'subfolders'}}) {
$folder->{'lastchange'} = $sf->{'lastchange'}
if ($sf->{'lastchange'} >
$folder->{'lastchange'});
}
$folder->{'sortable'} = 1;
}
elsif ($folder->{'type'} == 6) {
# For a virtual folder, the date is that of the newest
# sub-folder, OR the folder file itself