From ccad66f3f96f0cafc14e2db84c08edf890eaf511 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 16 Feb 2022 02:23:44 -0500 Subject: major revamp of mbsyncloop --- Makefile.hornet | 7 +- bin/mbsyncloop | 229 ++++++++++++++++++++++++++++++++++---------- config/imapnotify/tozt.conf | 5 +- mbsyncrc | 8 +- 4 files changed, 180 insertions(+), 69 deletions(-) diff --git a/Makefile.hornet b/Makefile.hornet index 29ef1e8..c5a9548 100644 --- a/Makefile.hornet +++ b/Makefile.hornet @@ -10,7 +10,6 @@ INSTALL := \ .config/msmtp/config \ .config/perspektiv/config.toml \ .mbsyncrc \ - .mbsyncloop \ .notmuch-config \ .ssh/authorized_keys \ .xbindkeysrc \ @@ -34,8 +33,7 @@ INSTALL_CUSTOM := \ $(INTO)/.cache/notmuch/hooks/post-new BUILD := \ - config/alacritty/alacritty.yml \ - mbsyncloop + config/alacritty/alacritty.yml install :: @chmod 600 config/msmtp/config @@ -66,6 +64,3 @@ config/darksky : config/darksky/api : config/darksky rbw get darksky.net api > $@ - -mbsyncloop: mbsyncrc - @sed 's/^PassCmd.*/PassCmd "head -n1 \/run\/user\/1000\/mbsyncloop"/' mbsyncrc > mbsyncloop diff --git a/bin/mbsyncloop b/bin/mbsyncloop index a127fea..82c3de1 100755 --- a/bin/mbsyncloop +++ b/bin/mbsyncloop @@ -4,74 +4,194 @@ use strict; use warnings; use File::Spec; +use JSON::PP; use POSIX 'mkfifo'; -my $password_command = "rbw get mail.tozt.net doy\@tozt.net"; -my $mbsync_config = "$ENV{HOME}/.mbsyncloop"; -my $goimapnotify_config = "$ENV{HOME}/.config/imapnotify/tozt.conf"; +my $RUN_DIR = get_run_dir(); +my ($PW_PIPE, $PW_PID, $GOIMAPNOTIFY_PID); -my ($pw_pipe, $pw_pid, $goimapnotify_pid); +$SIG{INT} = $SIG{TERM} = sub { cleanup(); exit }; +END { cleanup() } -sub cleanup { - unlink($pw_pipe) if $pw_pipe; - kill KILL => -$pw_pid if $pw_pid; - kill KILL => -$goimapnotify_pid if $goimapnotify_pid; +my $mbsync_config_file = "$ENV{HOME}/.mbsyncrc"; +my $goimapnotify_config_file = "$ENV{HOME}/.config/imapnotify/tozt.conf"; + +main($mbsync_config_file, $goimapnotify_config_file); + +sub main { + my ($mbsync_config_file, $goimapnotify_config_file) = @_; + + my $mbsync_config = slurp($mbsync_config_file); + my $goimapnotify_config = slurp($goimapnotify_config_file); + my $goimapnotify_config_data = JSON::PP::decode_json($goimapnotify_config); + + $PW_PIPE = make_pw_pipe(); + spawn_pw_proc($goimapnotify_config_data->{passwordCmd}); + + my $generated_goimapnotify_config_file = write_goimapnotify_config($goimapnotify_config_data); + my $goimapnotify_r = spawn_goimapnotify_proc($generated_goimapnotify_config_file); + + my $generated_mbsync_config_file = write_mbsync_config($mbsync_config, $goimapnotify_config_data->{boxes}); + + loop($generated_mbsync_config_file, $goimapnotify_r); } -$SIG{INT} = $SIG{TERM} = sub { cleanup; exit }; -END { cleanup } +sub slurp { + my ($file) = @_; + local $/; + open my $fh, '<', $file or die "couldn't open $file: $!"; + <$fh> +} -my $pw = `$password_command`; -if ($?) { - die "failed to fetch password: command returned $?"; +sub make_pw_pipe { + my $file = File::Spec->catfile($RUN_DIR, "mbsyncloop"); + unlink($file); + mkfifo($file, 0700) or die "couldn't create $file: $!"; + $file } -pipe(my $goimapnotify_r, my $goimapnotify_w) or die "failed to create unnamed pipe: $!"; -$goimapnotify_pid = fork; -die "fork failed: $!" unless defined $goimapnotify_pid; -if (!$goimapnotify_pid) { - setpgrp(0, 0); - close $goimapnotify_r; - while (1) { - open my $fh, '-|', 'goimapnotify', '--conf', $goimapnotify_config or die "couldn't run goimapnotify: $!"; - while (<$fh>) { - $goimapnotify_w->print("N\n"); - $goimapnotify_w->flush; +sub spawn_pw_proc { + my ($password_command) = @_; + + my $pw = fetch_password($password_command); + + $PW_PID = fork; + die "fork failed: $!" unless defined $PW_PID; + if (!$PW_PID) { + $SIG{PIPE} = 'IGNORE'; + setpgrp(0, 0); + while (1) { + open my $fh, '>', $PW_PIPE or die "couldn't open $PW_PIPE"; + $fh->print("$pw\n"); + close $fh; } } } -close $goimapnotify_w; - -$pw_pipe = make_pw_pipe(); -$pw_pid = fork; -die "fork failed: $!" unless defined $pw_pid; -if (!$pw_pid) { - $SIG{PIPE} = 'IGNORE'; - setpgrp(0, 0); - while (1) { - open my $fh, '>', $pw_pipe or die "couldn't open $pw_pipe"; - $fh->print("$pw\n"); - close $fh; + +sub fetch_password { + my ($password_command) = @_; + my $pw = `$password_command`; + die "failed to fetch password: command returned $?" if $?; + $pw +} + +sub read_mailboxes { + my ($config) = @_; + open my $fh, '-|', 'goimapnotify', '--conf', $config, '--list' + or die "couldn't run goimapnotify: $!"; + <$fh>; + map { chomp; s/^[^ ]* //r } <$fh> +} + +sub write_goimapnotify_config { + my ($goimapnotify_config_data) = @_; + + $goimapnotify_config_data->{passwordCmd} = "head -n1 '$PW_PIPE'"; + $goimapnotify_config_data->{onNewMail} = "echo new"; + + if (!$goimapnotify_config_data->{boxes}) { + my @mailboxes = read_mailboxes($goimapnotify_config_file); + if ($goimapnotify_config_data->{boxPatterns}) { + @mailboxes = grep { + my $mailbox = $_; + grep { + $mailbox =~ /$_/ + } @{ $goimapnotify_config_data->{boxPatterns} } + } @mailboxes + } + $goimapnotify_config_data->{boxes} = \@mailboxes; } + + my $filename = File::Spec->catfile($RUN_DIR, "goimapnotify.conf"); + unlink($filename); + open my $fh, '>', $filename + or die "couldn't open $filename for writing: $!"; + $fh->print(JSON::PP::encode_json($goimapnotify_config_data)); + $fh->close; + + $filename } -my $last_all = 0; -$SIG{HUP} = sub { $last_all = 0 }; -while (1) { - my $now = time; - if (($now - $last_all) >= 14 * 60) { - sync("all"); - $last_all = $now; +sub write_mbsync_config { + my ($mbsync_config, $mailboxes) = @_; + + $mbsync_config =~ s/^PassCmd .*$/PassCmd "head -n1 '$PW_PIPE'"/m; + my ($far) = $mbsync_config =~ /^IMAPStore (.*)$/m; + my ($near) = $mbsync_config =~ /^MaildirStore (.*)$/m; + + my $patterns = join "\n", map { "Pattern $_" } @$mailboxes; + my $mbsync_channels = <catfile($RUN_DIR, "mbsyncrc"); + unlink($filename); + open my $fh, '>', $filename + or die "couldn't open $filename for writing: $!"; + $fh->print($mbsync_config); + $fh->print("\n"); + $fh->print($mbsync_channels); + $fh->close; + + $filename +} + +sub spawn_goimapnotify_proc { + my ($config) = @_; + + pipe(my $goimapnotify_r, my $goimapnotify_w) + or die "failed to create unnamed pipe: $!"; + + $GOIMAPNOTIFY_PID = fork; + die "fork failed: $!" unless defined $GOIMAPNOTIFY_PID; + if (!$GOIMAPNOTIFY_PID) { + setpgrp(0, 0); + close $goimapnotify_r; + while (1) { + open my $fh, '-|', 'goimapnotify', '--conf', $config + or die "couldn't run goimapnotify: $!"; + while (<$fh>) { + $goimapnotify_w->print("N\n"); + $goimapnotify_w->flush; + } + } } - if (idle(15 * 60 - (time - $now))) { - sync("priority"); + close $goimapnotify_w; + + $goimapnotify_r +} + +sub loop { + my ($mbsync_config, $goimapnotify_r) = @_; + + my $last_all = 0; + $SIG{HUP} = sub { $last_all = 0 }; + while (1) { + my $now = time; + if (($now - $last_all) >= 14 * 60) { + sync($mbsync_config, "mbsyncloop_all"); + $last_all = $now; + } + if (idle($goimapnotify_r, 15 * 60 - (time - $now))) { + sync($mbsync_config, "mbsyncloop_priority"); + } } } sub sync { - my ($channel) = @_; + my ($config, $channel) = @_; while (1) { - my $status = system("mbsync -c '$mbsync_config' $channel"); + my $status = system("mbsync -c '$config' $channel"); if (!$status) { system("notmuch new | grep -v '^No new mail\.\$'"); last; @@ -81,7 +201,7 @@ sub sync { } sub idle { - my ($max_delay) = @_; + my ($goimapnotify_r, $max_delay) = @_; my $rin = ''; vec($rin, fileno($goimapnotify_r), 1) = 1; my $ready = select(my $rout = $rin, undef, undef, $max_delay); @@ -100,13 +220,16 @@ sub idle { return 0; } -sub make_pw_pipe { +sub get_run_dir { my $dir = "/run/user/$>"; if (!-d $dir) { $dir = File::Spec->tmpdir(); } - my $file = File::Spec->catfile($dir, "mbsyncloop"); - unlink($file); - mkfifo($file, 0700) or die "couldn't create $file: $!"; - $file + $dir +} + +sub cleanup { + unlink($PW_PIPE) if $PW_PIPE; + kill KILL => -$PW_PID if $PW_PID; + kill KILL => -$GOIMAPNOTIFY_PID if $GOIMAPNOTIFY_PID; } diff --git a/config/imapnotify/tozt.conf b/config/imapnotify/tozt.conf index acda499..fd5d3f0 100644 --- a/config/imapnotify/tozt.conf +++ b/config/imapnotify/tozt.conf @@ -3,7 +3,6 @@ "port": 993, "tls": true, "username": "doy@tozt.net", - "passwordCmd": "head -n1 /run/user/1000/mbsyncloop", - "boxes": [ "INBOX", "personal" ], - "onNewMail": "echo new" + "passwordCmd": "rbw get mail.tozt.net doy@tozt.net", + "boxPatterns": ["^(?!old\\.)"] } diff --git a/mbsyncrc b/mbsyncrc index f46e38f..6530eb9 100644 --- a/mbsyncrc +++ b/mbsyncrc @@ -18,10 +18,4 @@ Channel all Far :mail: Near :local: Sync Pull Push -Patterns * !.notmuch - -Channel priority -Far :mail: -Near :local: -Sync Pull Push -Patterns INBOX personal +Patterns * -- cgit v1.2.3-54-g00ecf