[Dovecot] Marking messages read / retaining date with Sieve
I'm trying to mark several hundred thousand messages as read as they are delivered via dovecot-lda(1). (I'm importing some mail from another format for migration purposes.)
I've been able to do this with Sieve, but it has the side effect that the messages' received and saved dates are set to the current date, and Apple Mail (at least) uses one of these to display the message date. Without the Sieve filter in place, the dates are retained based on the From_ line as I would like.
Does anyone know of any way I can either
retain the date when delivering using Sieve?
systematically mark a specific set of messages as read *after* delivery instead, perhaps with doveadm(1)? (even marking *everything* read would work in this particular case. I couldn't find any way to set flags with doveadm)
Thanks,
--Bret
At 1PM -0500 on 30/01/13 you (Bret Martin) wrote:
I'm trying to mark several hundred thousand messages as read as they are delivered via dovecot-lda(1). (I'm importing some mail from another format for migration purposes.)
I've been able to do this with Sieve, but it has the side effect that the messages' received and saved dates are set to the current date, and Apple Mail (at least) uses one of these to display the message date. Without the Sieve filter in place, the dates are retained based on the From_ line as I would like.
Does anyone know of any way I can either
retain the date when delivering using Sieve?
systematically mark a specific set of messages as read *after* delivery instead, perhaps with doveadm(1)? (even marking *everything* read would work in this particular case. I couldn't find any way to set flags with doveadm)
I would do this by scripting IMAP access. Perl's Mail::IMAPClient has explicit support for running dovecot/imap in preauth mode, so you don't even have to authenticate. Of course, you need a Dovecot user account with access to all the relevant messages.
Ben
On Jan 30, 2013, at 6:56 PM, Ben Morrow <ben@morrow.me.uk> wrote:
I would do this by scripting IMAP access. Perl's Mail::IMAPClient has explicit support for running dovecot/imap in preauth mode, so you don't even have to authenticate. Of course, you need a Dovecot user account with access to all the relevant messages.
Thanks so much for your help! This sounds like a great option to me.
For "explicit" support, I'm having a lot of trouble finding out how to have Mail::IMAPClient invoke /usr/lib/dovecot/imap instead of connecting over the network -- could you provide any pointers?
--Bret
At 7PM -0500 on 30/01/13 you (Bret Martin) wrote:
On Jan 30, 2013, at 6:56 PM, Ben Morrow <ben@morrow.me.uk> wrote:
I would do this by scripting IMAP access. Perl's Mail::IMAPClient has explicit support for running dovecot/imap in preauth mode, so you don't even have to authenticate. Of course, you need a Dovecot user account with access to all the relevant messages.
Thanks so much for your help! This sounds like a great option to me.
For "explicit" support, I'm having a lot of trouble finding out how to have Mail::IMAPClient invoke /usr/lib/dovecot/imap instead of connecting over the network -- could you provide any pointers?
Ack, sorry, I was misremembering: it's Net::IMAP::Simple that has explicit support (note that the dovecot invocation example in the perldoc is for dovecot 1.x, for 2.x you just run $prefix/libexec/dovecot/imap directly).
Mail::IMAPClient (which I usually prefer) will work with a preauthenticated socket, but you need to create a socketpair explicitly, fork and exec dovecot/imap with one end of the pair on STDIN/STDOUT, then pass the other end to Mail::IMAPClient->new as the Socket parameter. Something like this (I've left out error checking)
use Socket;
socketpair my $DOVE, my $CLIENT, AF_UNIX, SOCK_STREAM, PF_UNSPEC;
unless (fork) {
open STDIN, "<&", $CLIENT;
open STDOUT, ">&", $CLIENT;
exec "/usr/local/libexec/dovecot/imap";
}
close $CLIENT;
my $IMAP = Mail::IMAPClient->new(
Socket => $DOVE,
# other options
);
# don't forget to wait for the imap process when you've finished
Ben
On Jan 30, 2013, at 8:39 PM, Ben Morrow <ben@morrow.me.uk> wrote: [...]
Mail::IMAPClient (which I usually prefer) will work with a preauthenticated socket, but you need to create a socketpair explicitly, fork and exec dovecot/imap with one end of the pair on STDIN/STDOUT, then pass the other end to Mail::IMAPClient->new as the Socket parameter. Something like this (I've left out error checking) [...]
Thanks again for your help!
To close the loop on this, I ended up doing the following, although it seems to fail on mailboxes with large numbers of messages (on the order of 10,000 or so; I didn't test carefully enough to find the exact number)
use Mail::IMAPClient;
use Socket;
use strict;
socketpair( my $dovecot, my $client, AF_UNIX, SOCK_STREAM, PF_UNSPEC );
unless ( fork() ) {
open( STDIN, '<&', $client );
open( STDOUT, '>&', $client );
exec( '/usr/lib/dovecot/imap' );
}
close( $client );
my $imap = Mail::IMAPClient->new( Socket => $dovecot );
foreach my $folder( sort $imap->folders() ) {
print( "$folder\n" );
$imap->select( $folder );
$imap->set_flag( 'Seen', $imap->search( 'ALL' ) );
}
For the moment the failure on large folders was easier to handle by just doing those folders manually, although I'm curious if anyone knows the reason that might fail.
--Bret
At 3PM -0500 on 12/02/13 you (Bret Martin) wrote:
To close the loop on this, I ended up doing the following, although it seems to fail on mailboxes with large numbers of messages (on the order of 10,000 or so; I didn't test carefully enough to find the exact number)
use Mail::IMAPClient; use Socket; use strict; socketpair( my $dovecot, my $client, AF_UNIX, SOCK_STREAM, PF_UNSPEC ); unless ( fork() ) { open( STDIN, '<&', $client ); open( STDOUT, '>&', $client ); exec( '/usr/lib/dovecot/imap' ); } close( $client ); my $imap = Mail::IMAPClient->new( Socket => $dovecot ); foreach my $folder( sort $imap->folders() ) { print( "$folder\n" ); $imap->select( $folder ); $imap->set_flag( 'Seen', $imap->search( 'ALL' ) ); }
You're not specifying Ranges in the ->new call, and you're calling ->search in list context; either of these will force ->search to return a complete list of message numbers, which you are then trying to pass back in a single IMAP command. You want to either loop through the messages or set Ranges and call ->search in scalar context to get an (object which stringifies to an) IMAP range specification, which will be much shorter than the whole list.
Ben
participants (2)
-
Ben Morrow
-
Bret Martin