diff options
author | Guilhem Moulin <guilhem@fripost.org> | 2016-03-05 18:36:07 +0100 |
---|---|---|
committer | Guilhem Moulin <guilhem@fripost.org> | 2016-03-05 18:36:07 +0100 |
commit | e51c8899d67e5d86a868e1adced55a6c72113daa (patch) | |
tree | cfd847f6de7bc1ad727e6b704e12ecaaf22e6e42 /pullimap | |
parent | 836fd409e942eb715198198caacac1e64f997365 (diff) |
pullimap: add support for IMAP IDLE (RFC 2177).
Diffstat (limited to 'pullimap')
-rwxr-xr-x | pullimap | 81 |
1 files changed, 48 insertions, 33 deletions
@@ -47,7 +47,7 @@ sub usage(;$) { exit $rv; } -usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q debug help|h/); +usage(1) unless GetOptions(\%CONFIG, qw/config=s quiet|q debug help|h idle:i/); usage(0) if $CONFIG{help}; usage(1) unless $#ARGV == 0 and $ARGV[0] ne '_'; @@ -225,10 +225,47 @@ sub smtp_send(@) { # the remote mailbox # my $IMAP = Net::IMAP::InterIMAP::->new( %$CONF, %CONFIG{qw/quiet debug/}, 'logger-fd' => $LOGGER_FD ); + +# use BODY.PEEK[] so if something gets wrong, unpulled messages +# won't be marked as \Seen in the mailbox +my $ATTRS = join ' ', qw/ENVELOPE INTERNALDATE BODY.PEEK[]/; + +# Pull new messages from IMAP and deliver them to SMTP, then update the +# statefile +sub pull(;$) { + my $ignore = shift // []; + my @uid; + + # invariant: we're at pos 8 + 4*(1+$#ignore + 1+$#uids) in the statefile + $IMAP->pull_new_messages($ATTRS, sub($) { + my $mail = shift; + return unless exists $mail->{RFC822}; # not for us + + my $uid = $mail->{UID}; + my $from = first { defined $_ and @$_ } @{$mail->{ENVELOPE}}[2,3,4]; + $from = (defined $from and @$from) ? $from->[0]->[2].'@'.$from->[0]->[3] : ''; + print STDERR "($MAILBOX): UID $uid from <$from> ($mail->{INTERNALDATE})\n" unless $CONFIG{quiet}; + + sendmail($from, $mail->{RFC822}); + + push @uid, $uid; + writeUID($uid); + }, @$ignore); + + # now that everything has been deliverd, mark @ignore and @uid as \Seen + $IMAP->silent_store(compact_set(@$ignore, @uid), '+', '\Seen') if @$ignore or @uid; + + # update the statefile + sysseek($STATE, 4, SEEK_SET) // die "Can't seek: $!"; + my ($uidnext) = $IMAP->get_cache('UIDNEXT'); + writeUID($uidnext); + truncate($STATE, 8) // die "Can't truncate"; +} + do { my $uidvalidity = readUID(); my $uidnext = readUID(); - my @ignore; + my $ignore = []; $IMAP->set_cache($MAILBOX, UIDVALIDITY => $uidvalidity, UIDNEXT => $uidnext); $IMAP->select($MAILBOX); @@ -249,37 +286,15 @@ do { # have already been delivered, but the process exited before the # statefile was updated while (defined (my $uid = readUID())) { - push @ignore, $uid; + push @$ignore, $uid; } } - - # use BODY.PEEK[] so if something gets wrong, unpulled messages - # won't be marked as \Seen in the mailbox - my $attrs = join ' ', qw/ENVELOPE INTERNALDATE BODY.PEEK[]/; - my @uid; - - # invariant: we're at pos 8 + 4*(1+$#ignore + 1+$#uids) - $IMAP->pull_new_messages($attrs, sub($) { - my $mail = shift; - return unless exists $mail->{RFC822}; # not for us - - my $uid = $mail->{UID}; - my $from = first { defined $_ and @$_ } @{$mail->{ENVELOPE}}[2,3,4]; - $from = (defined $from and @$from) ? $from->[0]->[2].'@'.$from->[0]->[3] : ''; - print STDERR "($MAILBOX): UID $uid from <$from> ($mail->{INTERNALDATE})\n" unless $CONFIG{quiet}; - - sendmail($from, $mail->{RFC822}); - - push @uid, $uid; - writeUID($uid); - }, @ignore); - - # now that everything has been deliverd, mark @ignore and @uid as \Seen - $IMAP->silent_store(compact_set(@ignore, @uid), '+', '\Seen') if @ignore or @uid; - - # update the statefile - sysseek($STATE, 4, SEEK_SET) // die "Can't seek: $!"; - ($uidnext) = $IMAP->get_cache('UIDNEXT'); - writeUID($uidnext); - truncate($STATE, 8) // die "Can't truncate"; + pull($ignore); }; +exit 0 unless defined $CONFIG{idle}; + +$CONFIG{idle} = 1740 if defined $CONFIG{idle} and $CONFIG{idle} == 0; # 29 mins +while(1) { + my $r = $IMAP->idle($CONFIG{idle}, sub() { $IMAP->has_new_mails($MAILBOX) }); + pull() if $r; +} |