Menu

#11 IMAP IDLE-capable sync daemon?

1.2
requested
nobody
None
unknown
5
2023-04-07
2017-05-04
Ryan Lue
No

Are there any plans to create (or is there any interest in creating) a daemon to trigger mbsync whenever there are changes to either remote or local mailboxes? I understand that most users simply create a cronjob to pull new mail at fixed intervals, but there are cases in which it's useful to see new messages as soon as they appear on the server (e.g., when receiving a 2FA authorization code from a bank login).

imapnotify does something like this, but it can go at least 15 minutes without detecting a dropped connection, and doesn't double-check that it missed any new messages within that window. I had considered writing my own script to do this, but it appears it would require a degree of technical competency I do not yet possess (and anyway, the only language I'm really good at is Ruby).

The daemon I am imagining would trigger a sync:

  • at startup, once connected to Internet
  • upon changes to the local mailbox, detected via inotify
  • upon changes to the remote mailbox, detected via IMAP IDLE
  • any time it might have missed an IMAP IDLE notification (wake from sleep / reconnect to Internet after dropped connection)

That means it would have to be able to detect system network changes (ideally this would happen passively, via a system notification, rather than periodic pinging to an external server), which was where my ability to tackle this problem really fell apart.

Discussion

  • Rolf

    Rolf - 2019-02-10

    I can't find anything relevant in the links given by Oswald except the announcement by Ryan of his nifty little tool.

    sourceforge.net/p/isync/mailman/message/35841876/

     
    • Ryan Lue

      Ryan Lue - 2019-02-10

      Sorry to disappoint, but I mever reallt got it to work and haven’t taken a second look at it in years.

      Maybe it’s time to dust it off and give it another shot?

       

      Last edit: Oswald Buddenhagen 2019-02-22
  • Rolf

    Rolf - 2019-02-22

    Sure, it would be awesome to have continuous, yet low-resource IMAP synchronization. I know absolutely no ruby.

    I found another similar tool, but it uses python and getmail (which I cannot use because it does not support to leave messages on the server together with IDLE).

     
  • Rolf

    Rolf - 2019-02-22

    I will look into the option of using one of the biff options to initiate an mbsync

     
  • Rolf

    Rolf - 2019-02-22

    I am using "gnubiff -n --nogui" on a headless machine to watch the IMAP server via IDLE and have it call mbsync to sync new mails as they arrive. I still need to look into systemd to have this run as a service to restarted automatically when it crashes.

     
  • Doron Behar

    Doron Behar - 2023-04-07

    Every once in a while I'm delving into the code and trying to implement this feature myself. It's hard though.. I'm not experienced enough in C. Here's my attempt so far:

    diff --git i/src/drv_imap.c w/src/drv_imap.c
    index ad95e3d..4c1727e 100644
    --- i/src/drv_imap.c
    +++ w/src/drv_imap.c
    @@ -288,7 +288,8 @@ enum CAPABILITY {
        NAMESPACE,
        UTF8_ACCEPT,
        UTF8_ONLY,
    -   COMPRESS_DEFLATE
    +   COMPRESS_DEFLATE,
    +   IDLE
     };
    
     static const struct {
    @@ -311,6 +312,7 @@ static const struct {
        { "UTF8=ACCEPT", 11 },
        { "UTF8=ONLY", 9 },
        { "COMPRESS=DEFLATE", 16 },
    +   { "IDLE", 4 },
     };
    
     #define RESP_OK       0
    diff --git i/src/main.c w/src/main.c
    index 8581b0c..b703491 100644
    --- i/src/main.c
    +++ w/src/main.c
    @@ -50,6 +50,7 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
     "  -e, --ext-exit  return extended exit code\n"
     "  -V, --verbose       display what is happening\n"
     "  -q, --quiet     don't display progress counters\n"
    +"  -d, --daemon        Run in daemon mode\n"
     "  -v, --version       display version\n"
     "  -h, --help      display this help message\n"
     "\nIf neither --pull nor --push are specified, both are active.\n"
    @@ -212,6 +213,8 @@ main( int argc, char **argv )
                        usage( 0 );
                    } else if (!strcmp( opt, "version" )) {
                        version();
    +               } else if (!strcmp( opt, "daemon" )) {
    +                   mvars->daemon = 1;
                    } else if (!strcmp( opt, "quiet" )) {
                        if (Verbosity > VERYQUIET)
                            Verbosity--;
    @@ -396,6 +399,7 @@ main( int argc, char **argv )
            case 'n':
            case 'o':
            case 'd':
    +           mvars->daemon = 1;
            case 'g':
            case 'f':
            case 'N':
    @@ -576,10 +580,14 @@ main( int argc, char **argv )
        sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
        sigaction( SIGCHLD, &sa, NULL );
    
    -   if (mvars->list_stores)
    -       list_stores( mvars, argv + oind );
    -   else
    -       sync_chans( mvars, argv + oind );
    +   if (!mvars->daemon) {
    +       if (mvars->list_stores)
    +           list_stores( mvars, argv + oind );
    +       else
    +           sync_chans( mvars, argv + oind );
    +   } else {
    +       isync_daemon(mvars, argv + oind);
    +   }
        return mvars->ret;
     }
    
    diff --git i/src/main_p.h w/src/main_p.h
    index 6aa5fe2..1f80d5d 100644
    --- i/src/main_p.h
    +++ w/src/main_p.h
    @@ -16,10 +16,12 @@ typedef struct {
        int all;
        int list;
        int list_stores;
    +   int daemon;
        int ops[2];
     } core_vars_t;
    
     void sync_chans( core_vars_t *cvars, char **argv );
    +void isync_daemon( core_vars_t *cvars, char **argv );
     void list_stores( core_vars_t *cvars, char **argv );
     void cleanup_mainloop( void );
    
    diff --git i/src/main_sync.c w/src/main_sync.c
    index 226e324..f7b8374 100644
    --- i/src/main_sync.c
    +++ w/src/main_sync.c
    @@ -336,6 +336,18 @@ typedef struct {
    
     static void do_sync_chans( main_vars_t *lvars );
    
    +void
    +isync_daemon( core_vars_t *cvars, char **argv )
    +{
    +   if (!channels) {
    +       fputs( "No channels defined. Try 'man " EXE "'\n", stderr );
    +       cvars->ret = 1;
    +       return;
    +   }
    +   // TODO: implement
    +   cvars->ret = 0;
    +}
    +
     void
     sync_chans( core_vars_t *cvars, char **argv )
     {
    
     
    • Oswald Buddenhagen

      yeah. well. the problem isn't creating something, it's creating something serious, with sensible error handling, and good scalability. you can find a lot of related discussion when you're serious about it.

       

Log in to post a comment.