plan9port email

This article describes how to work with email on Slack9 using the plan9port environment.

Most of the tools included in plan9port came, obviously, from Plan9, but even if they’re old, there have been maintained to keep them working on current systems.

The instructions to set up email on Plan9 can be read on the wiki

To read email from acme, we need to set up:

Install plan9port

To install plan9port I do:

I do not install plan9port system wide, but you can follow the standard location and install it in /usr/local/plan9 or where you want.

To install upas utilities, we need to manually build them:

Environment

On Plan9, services posted an entry into the srv filesystem so others could easily mount their filesystem from them and use they services. On Plan9Port libraries and programs expect a $NAMESPACE varaible to point to that location.

Also, on Plan9 there is the network database, which in plan9port is located in $PLAN9/ndb. On the current plan9port implementation, the ndb is not generally used, and programs expect some hostnames to work with their default settings.

On my system, I prepare my environment on my $HOME/.profileTo read my current profile, take a loot at my dot files

file like this:

Please read [XDG_RUNTIME_DIR] documentation, as that folder is deleted when the user logs out.

Then reload your user profile either by logging out and logging in or by loading your profile on your current session:

Setting up secstored

You can skip this section and go directly to the section abut mailfs below if you do not plan to use secstore to persist your secrets on disk.

It serves files using the secstore protocol. It allows to securely store and retrieve files from an encrypted data store on disk.

Secstored starts when the new_p9p_session function executes when your profile is loaded.

Secstored programs use by default the host called secstore as its server name, so we add that to our /etc/hosts file:

To use it, we need to create an user into secstore with the secuser command:

Files stored in secstore are saved in $PLAN9/secstore/store/$USER/ by default, filenames can be read, but the contents are encrypted.

There are no files yet on our secstore:

Then we need to store our credentials in file called factotum using secstore.

To start, we need to upload an empty factotum fie to secstore, so the script ipso works correctly.

Other rough edges of the script is the way select where to store the temporary files. I use a modified version which uses $XDG_RUNTIME_DIR as a base.

#!/home/your_user_here/.plan9/bin/rc

. 9.rc
name = secstore
get = secstoreget
put = secstoreput
edit = no
load = no
flush = no

fn secstoreget{
    secstore -i -g $1 <_password
}

fn secstoreput{
    secstore -i -p $1 <_password
}

fn aesget{
    if(! ~ $1 /*){
        echo >[1=2] ipso: aescbc requires fully qualified pathname
        exit usage
    }
    aescbc -i -d < $1 > `{basename $1} <[3] _password
}

fn aesput{
    aescbc -i -e > $1 < `{basename $1} <[3] _password
}

fn editedfiles{
    if(~ $get aesget){
        for(i in $files)
            if(ls -tr | sed '1,/^_timestamp$/d' | grep -s '^'^`{basename $i}^'$')
                echo $i
    }
    if not
        ls -tr | sed '1,/^_timestamp$/d'
}

while(~ $1 -*){
    switch($1){
    case -a
        name = aescbc
        get = aesget
        put = aesput
    case -f
        flush = yes
    case -e
        edit = yes
    case -l
        load = yes
    case *
        echo >[2=1] 'usage: ipso [-a -f -e -l] [-s] [file ...]'
        exit usage
    }
    shift
}

if(~ $flush no && ~ $edit no && ~ $load no){
    edit = yes
    if(~ factotum $*){
        load = yes
        flush = yes
    }
}

if(~ $flush yes && ~ $edit no && ~ $load no){
    echo flushing old keys
    echo delkey | 9p write factotum/ctl
    exit 0
}

if(~ $get aesget && ~ $#* 0){
    echo >[2=1] ipso: must specify a fully qualified file name for aescbc '(-a)'
    exit usage
}

user=`{whoami}
cd $XDG_RUNTIME_DIR || exit $status
mkdir -p ipso.$user
chmod 700 ipso.$user || exit $status
cd ipso.$user
dir=`{pwd}
dir=$"dir

fn sigexit {
    rm -rf $dir
}

if ( ~ $edit yes ) echo '
    Warning: The editor will display the secret contents of
    your '$name' files in the clear, and they will
    be stored temporarily in '^$dir^'
    in the clear, along with your password.
'

# get password and remember it
readcons -s $name^' password' >_password

# get list of files
if(~ $#* 0){
    if(! secstore -G . -i < _password > _listing){
        echo 'secstore read failed - bad password?'
        sleep 2
        exit password
    }
    files=`{sed 's/[    ]+.*//' _listing}
}
if not
    files = $*

# copy the files to local ramfs
for(i in $files){
    if(! $get $i){
        echo $name ' read failed - bad password?'
        sleep 2
        exit password
    }
}
sleep 2; date > _timestamp  # so we can find which files have been edited.

# edit the files
if(~ $edit yes){
    B `{for(i in $files) basename $i}
    readcons 'type enter when finished editing' >/dev/null
}
if(~ $flush yes ){
    echo flushing old keys
    echo delkey | 9p write factotum/ctl
}
if(~ $load yes){
    echo loading factotum keys
    if (~ factotum $files) cat factotum | 9p write -l factotum/ctl
}

# copy the files back
for(i in `{editedfiles}){
    prompt='copy '''^`{basename $i}^''' back? [y/n/x]'
    switch(`{readcons $prompt}){
    case [yY]*
        if(! $put $i){
            echo $name ' read failed - bad password?'
            sleep 2
            exit password
        }
        echo ''''$i'''' copied to $name
        if(~ $i factotum && ! ~ $load yes){ # do not do it twice
            cat $i | 9p write -l factotum/ctl
        }
    case [xXqQ]*
        exit
    case [nN]* *
        echo ''''$i'''' skipped
    }
}

exit ''

To execute ipso we first need to open a plan9port editor like acme.

Then we need to fill the acme window with the contents of our authentication information:

key proto=pass role=client server=imap.mail_provider.com service=imap [email protected]_provider.com !password=my_imap_password

We need to save the file, close it, and press enter on the console where ipso is waiting.

When we finish the process, we will have a file in $PLAN9/secstore/store/$USER/ called factotum whose contents we cannot read.

Setting up factotum

factotum is the authentication agent that will identify us to the remote servers. When started, if we have configured a secstore hostname properly, it will try to load keys from a file called factotum stored in the secstore server. We can also use ipso to load our keys using the -l option.

After that, we can read the list of entries loaded into `factotum with the following command:

We can store credentials for more services in `factotum

Accesing an imap inbox with mailfs

If we have followed the secstore and factotum set up, mailfs will not ask us for a password, but if we don’t, mailfs will ask us for a password and will fill factotum with our log in information. This information will not be persisted, so the next time we access our imap service, we are going to need to type our imap password again.

Now that we can identify ourselves to our imap service, we can instruct mailfs to connect to our server:

This has created a socket in $NAMESPACE called mail which is serving a 9P connection to email clients like acme Mail and ned.

If you want to explore the filesystem interface using regular Linux tool you can use the 9pfuse tool to mount the mail socket into a folder like $HOME/mail:

And here at last, my inbox as presented by ned:

ned is a very neat program to work with email. It is easy to deal with multiple emails, and with attachments. For example:

  • : g/9fans/ s 9fans will save all emails with the 9fans word in the headers to the 9fans folder
  • : g/tuhs/ h will list all emails with tuhs in the headers
  • : 2,7 d will delete emails two to seven
  • : y will execute the commands againts the mail box, writting the changes the commands made

Sending email

This set up will use a SMTP service from your email provider. For it to work we need to configure the $HOME/mail/pipefrom file.

This script will be in charge to send the email as if it were the system mailer:

Our mailer will use upas/smtp to send our email, and it will use factotum credentials to authenticate against the smtp server of our provider. We can use other mailers such as msmtp, but it wont use factotum to authenticate. Some mailers might be able to integrate with secstore using scripts to get the secrets from it.

Also, acme Mail will complete our From address like our [email protected]$HOSTNAME or using the upasname variable, if we want to change this, which is almost always, we need to create a file in $HOME/mail/headers containing the From line of our emails:

Or we need to export out upasname variable as you can see on the next section.

Multiple accounts

Let’s say you have, like me, multiple email accounts, one for work and one for personal stuff for example.

Reading email is as simple as:

Now we have two sockets under $NAMESPACE: work and personal. To read email with nedPlease note the nedmail option -S is only present in plan9port, it does not appear in then Plan9 man page, and nedmail does not have a man page on plan9port.

we need to indicate it the name of the mailbox we want to read:

When launching acme Mail we need to do something simmilar:

To send emails we need to change our pipefrom script to select which server to use:

As you can see, the upasname variable is used when launching acme Mail to know not only the From line, but also to know to which server is going to send our email.

Todo

  • Update ipso to work with an empty secstore
  • Replying to an email from ned does not work, becasue upas/marshal fails due to mailer being incorrectly set up.
  • How to work with calendar attachments, accepting meetings, remainders and tasks, proposing new dates for a meeting using Google Calender or a Caldav server.
  • How to work with PGP email signatures, validating remote signatures, signing and encrypting our emails.