[Dovecot] dovecot.index mtime

Benjamin R. Haskell dovecot at benizi.com
Thu Nov 15 07:43:32 EET 2007


On Wed, 14 Nov 2007, Matt wrote:

>>> [...]
>>>
>>> For what it's worth, I'd look at the newest message in the ./cur folder, 
>>> rather than the oldest in the ./new. (You want the last time the user
>>
>> But most of my user POP3 and do not leave messages on server.  I just 
>> check if there is a message over 6 months old in new.

Even in POP3, dovecot moves RETR'ieved messages into ./cur as soon as 
they're read, thus changing its timestamp.


>>> [...]
>>>
>>> But, the benefit is that you can get the timestamp for the last time the 
>>> user read/deleted a message in ./cur simply by checking the change time of 
>>> the ./cur directory. (rather than going through all its files)
>>
>> Light bulb.  Wish I had thought of that.
>>

:-)


>>> [...]
>>>
>>> Should make it fast enough to run in cron.hourly, depending on your number 
>>> of users/mailboxes.
>>
>> I am going to have to try this.  I used find with -mtime to find all 
>> messages over 6 months old in all the ./new directorys.  If this way 
>> works it would be far more efficient.
>
> A flaw though.  If the account is checked daily but never receives any 
> new mail (such as the email account that goes to my fax machine and I 
> am sure there are a few others) the ctime on the cur will not be 
> updated.  It would not automagically start working again either since 
> it cannot receive new messages when suspended and thus cannot update 
> ./cur.  Someone just might email my fax machine some day rather then 
> call.
>
> This might be a good preliminary check then only then double check
> that there are messages in ./new.  Of course it makes my simple perl
> script a little more involved.  Is there a low overhead way to check
> that ./new is not empty?

Yes, that's definitely a case I didn't consider. Here's how I'd implement 
an empty-directory check in Perl:

sub empty {
     # Use 'while' instead of 'for' to avoid reading all the filenames.
     # basic idea:
     # return 1 unless directory contains something other than '.' or '..'
     # In a cron script, I think I'd treat un-openable dirs as empty ***

     my $dirname = shift;
     opendir my $d, $dirname or return 1; # *** hence the '1'
     local $_;
     !/^\.\.?$/ and return 0 while $_ = readdir $d;
     1;
}

sub oldest_msg {
     # finds oldest file in a dir and returns its ctime
     # un-openable dirs return 'now'
     my $dirname = shift;
     opendir my $d, $dirname or return time;
     my $min = time;
     for (readdir $d) { # here we need to read them all to find the oldest
         my $ctime = (stat)[10];
         next if $ctime > $min;
         $min = $ctime;
     }
     $min;
}

Then you have roughly the following logic:

my $longago = time - 86_400 * 180; # $longago = 180 days ago
if (
     (stat 'cur')[10] > $longago)     #    cur's ctime isn't long ago
     or empty('new')                  # or there are no new messages
     or oldest_msg('new') > $longago  # or the oldest isn't long ago
     ) {
     # then don't disable it
     # or switch the 'if' to 'unless', and this is where you disable it
}

# ...Perl's definitely my favorite language, if it's not obvious...


> [...]
>
> Thanks.

No prob.

Best,
Ben


More information about the dovecot mailing list