Alpine + IMAP offline na macOS

Posted on Tue 04 February 2020 in Hacks (wannabe), Linux, Macbook • 4 min read

Postanowiłem przestać się męczyć środowiskiem graficznym i powrócić do źródeł. Moim pierwszym programem do poczty był Pine i o nim będzie mowa. A raczej o jego nowszym bracie: Alpine.

Instalacja i konfiguracja na macOS-ie jest prościutka. Będę ją rozbudowywał i dostosowywał w miarę używania. Na początek postanowiłem skończyć z bezpośrednim dostępem do skrzynek na serwerze. Alpine będzie czytał pocztę z lokalnych folderów, które z kolei będą synchronizowane w obie strony - między moim laptopem, a serwerem - za pomocą narzędzia: offlineimap.

Instalacja offlineimap

Zatem najpierw zainstaluję offlineimap-a za pomocą Homebrew:

brew install offlineimap

Konfiguracja kilku kont

Przykładowa konfiguracja dwóch kont (foo i bar) w .offlineimaprc:

[general]
accounts = foo,bar
# The pre-defined options are:
# Blinkenlights -- A fancy (terminal) interface
# TTYUI         -- a text-based (terminal) interface
# Basic         -- Noninteractive interface suitable for cron'ing
# Quiet         -- Noninteractive interface, generates no output
#                  except for errors.
# MachineUI     -- Interactive interface suitable for machine
ui = quiet

#####   FOO   ####################
[Account foo]
localrepository = Local_foo
remoterepository = Remote_foo
# Jak często sprawdzać pocztę (minuty)
autorefresh = 1
# Zrób 10 szybkich synchronizacji zanim zrobisz pełną
quick = 10
[Repository Local_foo]
type = Maildir
localfolders = ~/POCZTA/FOO
# kasuj na serwerze co skasuje lokalnie
sync_deletes = yes
[Repository Remote_foo]
type = IMAP
remotehost = mail.foo.pl
remoteuser = trompka@foo.pl
starttls = yes
sslcacertfile = /usr/local/etc/openssl/cert.pem

#####   BAR    ####################
[Account bar]
localrepository = Local_bar
remoterepository = Remote_bar
autorefresh = 1
quick = 10
[Repository Local_bar]
type = Maildir
localfolders = ~/POCZTA/BAR
sync_deletes = yes
[Repository Remote_bar]
type = IMAP
remotehost = mail.bar.com
remoteuser = trompka
starttls = yes
cert_fingerprint = 4375fcdalefh952z3b307f84350dupaec103ea62b32zbita

#####   BAZ    ####################
[Account baz]
localrepository = Local_baz
remoterepository = Remote_baz
...

Jak widać z powyższego, poczta jest pobierana z serwera i zachowywana w podfolderach w ~/POCZTA.

Skrypty dla Launchd uruchamiające agentów offlineimapa

W folderze ~/Library/LaunchAgents/ utworzyłem plisty dla każdego konta. Przykładowo dla konta FOO plik wygląda tak:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>local.alpine.foo</string>
    <key>OnDemand</key>
    <true/>
    <key>ProgramArguments</key>
    <array>
        <string>~/bin/offlineimap</string>
        <string>-a foo</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <dict>
        <key>NetworkState</key>
        <true/>
    </dict>
    <key>StandardInPath</key>
    <string>/tmp/offlineimap-foo.stdin</string>
    <key>StandardOutPath</key>
    <string>/tmp/offlineimap-foo.stdout</string>
    <key>StandardErrorPath</key>
    <string>/tmp/offlineimap-foo.stderr</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>~/bin:/bin:/usr/bin:/usr/local/bin</string>
    </dict>
</dict>
</plist>
Uwaga: bardzo ważne jest by dodać odpowiednie ścieżki do zmiennych środowiskowych.
Dyrektywy StandardInPath, StandardOutPath i StandardErrorPath służą mi do sprawdzania czy offlineimap działa bo bywa, że się wysypuje, np. gdy wyłączę sieć przy jakichś eksperymentach. Bywało, że Launchd uruchamiał mi kilka kopii offlineimapa i po jakimś czasie zarzynał system. Jest to szybki hack i nie mam czasu robić czegoś znośniejszego.

Launchd-em ładuję jeszcze dodatkowy skrypt, którego plist jest poniżej:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>local.poczta-ok</string>
    <key>Program</key>
        <string>~/bin/alpine_czy_poczta_ok.sh</string>
    <key>RunAtLoad</key>
    <true/>
    <key>StartInterval</key>
    <integer>60</integer>
    <key>StandardInPath</key>
    <string>/tmp/poczta-ok.stdin</string>
    <key>StandardOutPath</key>
    <string>/tmp/poczta-ok.stdout</string>
    <key>StandardErrorPath</key>
    <string>/tmp/poczta-ok.stderr</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>~/homebrew/bin:/bin:/usr/bin:/usr/local/bin</string>
    </dict>
</dict>
</plist>

A sam skrypt wygląda tak:

#!/bin/bash

SERVERS=(foo bar)

for server in ${SERVERS[*]}
do
    FILE="/tmp/offlineimap-$server.stdout"
    if [ -s $FILE ]
    then
        MSG=`tail -n 1 $FILE`
        ANSWER="$(~/bin/alerter -message "$MSG" -closeLabel POKAŻ -title "`echo $server | tr '[:lower:]' '[:upper:]'`" -sound default -actions NAPRAW -timeout 10)"
    fi
    if [ "$ANSWER" == "POKAŻ" ]
    then
        ANSWER="$(~/bin/alerter -message "`tail -n 4 $FILE`" -closeLabel ZAMKNIJ -title "`echo $server | tr '[:lower:]' '[:upper:]'`" -sound default -actions NAPRAW -timeout 10)"
    fi
    if [ "$ANSWER" == "NAPRAW" ]
    then
        rm -f $FILE
        touch $FILE
    fi
    if [ "$ANSWER" == "@TIMEOUT" ]
    then
        rm -f $FILE
        touch $FILE
    fi
done

Zadaniem tego skryptu jest wykrywanie błędów offlineimapa i pokazywanie ich w postaci Notification (za pomocą alertera: https://github.com/alloy/terminal-notifier) oraz umożliwienia podstawowej ich obłsugi. Polega to na ich kasowaniu i tworzeniu. ;)

Jest jeszcze jeden skrypt, którym od czasu do czasu przeładowuję offlineimapa, nie dodałem go do obsługi bo już się praktycznie przeniosłem na Macbooka pod Debiana, a tam wszystko działa bez zarzutu. Ten skrypt to taki prymityw:

#!/bin/sh

LISTA=`ls -1 ~/Library/LaunchAgents/*.alpine.*.plist`
for i in $LISTA; do
    launchctl unload $i
done
for i in $LISTA; do
    launchctl load $i
done
launchctl unload ~/Library/LaunchAgents/local.poczta-ok.plist
launchctl load ~/Library/LaunchAgents/local.poczta-ok.plist

Instalacja Alpine

Alpine można zaisntalować z paczki, ja jednak wolałem skompilować go ze źródeł, aby wyposażyć swoją wersję w aktualne patche.

Instalacja paczki:

brew install alpine

Instalacja ze źródeł przebiega następująco:

mkdir Alpine
cd Alpine
wget http://alpine.x10host.com/alpine/release/src/alpine-2.22.tar.xz
tar zxvf alpine-2.22.tar.xz
wget http://alpine.x10host.com/alpine/patches/alpine-2.22/all.patch.gz
gunzip all.patch.gz
cd alpine-2.22
patch -p 1 < ../all.patch
xcode-select --install # instalacja "command line tools" w macOS-ie
./configure  --disable-debug --with-passfile=.pine-passfile
make
sudo make install

Warto odwiedzić stronę http://alpine.x10host.com/alpine/ i poczytać co zmieniają w Alpinie poszczególne łaty.

Konfiguracja Alpine

Mój plik jest bardzo długi, więc skupię się na najważnieszych ustawieniach, których Alpine szuka w ~/.pinerc.

Podstawową rzeczą jest podanie serwera wysyłającego pocztę:

smtp-server=mail.foo.pl:587/tls/user=trompka@foo.pl

Następną mega-ważną sprawą jest prawidłowe ustawienie ścieżek do plików poczty pobranych przez offlineimapa, u mnie każde konto ma własny podfolder w folderze ~/POCZTA, czyli dla trzech kont foo, bar i baz, wygląda to tak:

~
 |____POCZTA
      |____FOO
      |____BAR
      |____BAZ

Zatem dla konta FOO (mojego domyślnego) ścieżka do Inboxa to:

inbox-path=#md/POCZTA/FOO/inbox

Pozostałe Inboksy są w tzw. kolekcji (dodałem konto BAZ, żeby łatwiej było zrozumieć):

# Lista pozostałych Inboxów, poza głównym
incoming-folders=#md/POCZTA/BAR/inbox, #md/POCZTA/BAZ/inbox

# Lista folderów, do których trafiają zachowane emaile, pierwszy jest folderem domyślnym (w moim pzypadku: dla konta FOO)
folder-collections=FOO #md/POCZTA/FOO/[],
        BAR #md/POCZTA/BAR/[],
        BAZ #md/POCZTA/BAZ/[]

# Ścieżka względna do homedira, w której leżą foldery z moją pocztą
maildir-location=POCZTA

Jeszcze ustawienie wymuszające na Alpine, żeby po starcie wyświetlał indeks emaili w Inboksie i podświetlił najnowszy email:

initial-keystroke-list=I,
        j,
        ^v

Alpine można bardzo ściśle dopasować do własnych przyzwyczajeń ale wymaga to porządnego zapoznania się z opcjami dostępnymiu w programie, przy każdej z nich znajduje się obszerna Pomoc w języku angielskim. Naprawdę warto to przeczytać, serdecznie polecam tzw. Role pozwalające na kolorowanie emaili czy uruchamianie własnych poleceń np. na podstawie adresu nadawcy, itp.

Część dalsza nastąpi bo trzeba zsynchronizować książkę adresową...