diff --git a/mailboxes/boxes-lib.pl b/mailboxes/boxes-lib.pl index 5ed00900d..6c1b61cde 100644 --- a/mailboxes/boxes-lib.pl +++ b/mailboxes/boxes-lib.pl @@ -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; diff --git a/mailboxes/folders-lib.pl b/mailboxes/folders-lib.pl index 46cf00533..e12f264ba 100644 --- a/mailboxes/folders-lib.pl +++ b/mailboxes/folders-lib.pl @@ -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