Table of Contents
Curl - Perform imap queries using Curl
If you have an IMAP or IMAPS server you can “read your email” using Curl. To get started you'll first of all need to know:
- Your mail-server address.
- Your mail login.
- Your mail password.
Listing Folders
The initial example will connect us to the IMAP server at imap.example.com, using username bobby and password tables:
curl --url "imap://mail.example.com/" --user "bobby:tables"
If you leave out the password from the --user argument, curl will prompt you for the password before it preforms the request. This way you're not leaking your password to your shell history or the process list.
Curl also supports .netrc lookup of user:password with -n, if the --user argument is left off.
returns
.. .. * LIST (\HasNoChildren) "/" xen-users * LIST (\HasNoChildren) "/" mentors-debian-org * LIST (\HasNoChildren) "/" fairshare * LIST (\HasNoChildren) "/" INBOX
Here we see that we've connected, and received a list of folders, including “xen-users”, “fairshare”, and the “INBOX”.
If your mail-server is running over SSL then instead of using imap:// you should set the schema to imaps://, it may be that you're using a self-signed certificate in that case you'd add --insecure to avoid checking the certificate trust-chain.
This would look like so:
curl --insecure --url "imaps://mail.example.com/" --user "bobby:tables"
Discovering Messages
With the previous example we looked at listing mailboxes. What if we wanted to actually view a message?
To fetch a message we need the identifier of the message to fetch - so we need to find out how many messages exist, as message-IDs are sequential.
To see how many messages exist in the folder “People-Steve” we'd run this:
curl --insecure \ --url "imaps://mail.example.com/" \ --user "bobby:tables" \ --request "EXAMINE People-Steve"
returns:
* OK [PERMANENTFLAGS ()] Read-only mailbox. * 9465 EXISTS * 3266 RECENT * OK [UIDVALIDITY 1373146046] UIDs valid * OK [UIDNEXT 9468] Predicted next UID
This tells us that there are 9465 messages. So we can probably assume that fetching a message with each of these IDs will work: 1, 2, 3, … 9465, & 9465.
Fetching A Single Message
Fetching a message is simple if you know both the folder from which it comes and the ID of the message you wish to retrieve.
We've already shown that the folder “People-Steve” contains nearly ten-thousand messages, so this next example will show us fetching the message with ID 512:
curl --insecure \ --url "imaps://mail.example.com/People-Steve;UID=512" \ --user "bobby:tables"
returns:
.. X-HELO: mail.cs.helsinki.fi X-REMOTE-IP: 128.214.9.1 X-REMOTE-HOST: courier.cs.helsinki.fi .. .. I just accidentally someone! (see facebook) ..
Fetching the message includes both the headers and the body. If you want to fetch just the headers, or a subset, things are a little fiddlier due to URL-encoding. This example gets the headers “to”, “from”, “date”, and “subject”:
curl --insecure \ --url "imaps://mail.example.com/People-Steve;UID=512;SECTION=HEADER.FIELDS%20(DATE%20FROM%20TO%20SUBJECT)" \ --user "bobby:tables"
returns:
Date: Sat, 14 Apr 2012 14:13:41 +0300 (EEST) From: Steve Kemp <steve@steve.org.uk> To: Steve Kemp <steve@steve.org.uk> Subject: Re: Fancy a cake?
We could have avoided the use of URL-encoding and instead sent a custom-request, like so:
curl --insecure --verbose \ --url "imaps://imap.example.com/People-Steve" \ --user "bobby:tables" \ --request "fetch 512 BODY.PEEK[HEADER.FIELDS (Subject)]"
returns:
.. Subject: Re: Fancy a cake? ..
The reason for avoiding custom-requests where possible is that when you're using the curl API programatically you'll discover that responses are not decoded - which is a known issue.
In the example above we'd have received zero output unless/until we added the --verbose flag. Precisely because curl has received the output from the IMAP-server but not decoded it and presented it to us.
Simple Shell Scripts
As a quick hack the following shell-script will dump the subject of the first ten messages in the given mailbox:
#!/bin/sh # Dump the subject of the first ten messages in the folder. for id in `seq 1 10` ; do echo "Message ${id}" curl --insecure \ --url "imaps://mail.example.com/People-Steve;UID=${id};SECTION=HEADER.FIELDS%20(SUBJECT)" \ --user "bobby:tables" done
This takes no account of the maximum message-ID. You could just keep going indefinitely, to dump all subjects and stop on error:
#!/bin/sh # Dump the subject of all messages in the folder. id=1 while true ; do echo "Message ${id}" curl --insecure \ --url "imaps://mail.example.com/People-Steve;UID=${id};SECTION=HEADER.FIELDS%20(SUBJECT)" \ --user "bobby:tables" || exit id=`expr $id + 1` done
CurlMail Script
#!/bin/bash # /usr/local/bin/curlmail # http://crystalfaeries.net/posix/bin/curlmail # celeste crystalfaery 2016-02-24 22:27:10+00:00 # https://debian-administration.org/article/726/Performing_IMAP_queries_via_curl # read(only) IMAPS INBOX (using ~/.netrc for password) # (machine example.org login user password my_password) # 1st argument is machine name (else talk to self if not specified) # 2..n argument(s) (optional) are message number(s) to view # e.g.: curlmail example.org 3 1 4 help=10 # this line number of file -1 # NOTE: remove the "--insecure" argument to curl if server(s) have real certificate vs self-signed certificate # NOTE: we do not sanitize the server argument, so as-is don't make this into a cgi function cleanup { if [ -e /tmp/$$ ]; then rm /tmp/$$ # cleanup our temporary file fi } trap cleanup INT TERM # cleanup our temporary file case $# in 0) exec $0 `hostname` # talk to myself exit -1 # exec doesn't return, right? ;; 1) case "$1" in -v) tail -n +4 $0 | head -n 1 ;; --version) tail -n +4 $0 | head -n 1 ;; -h) head -n $help $0 | tail -n +6 ;; --help) head -n $help $0 | tail -n +6 ;; *) let uid=0 while let uid=$uid+1 do # iterate available messages echo -n "${uid}: " >> /tmp/$$ chmod 600 /tmp/$$ curl -# --insecure -n --url \ "imaps://${1}/INBOX;UID=${uid};SECTION=HEADER.FIELDS%20(DATE%20FROM%20TO%20SUBJECT)" >> /tmp/$$ 2>/dev/null || break done sed 's/\n\n/\n/g' < /tmp/$$ | less rm /tmp/$$ ;; esac exit ;; *) server="$1" shift while [ $# != 0 ] do # iterate requested messages message=`echo "$1" | tr -d -c '[:digit:]'` # sanitize input shift rm /tmp/$$ 2>/dev/null echo -n "${message}: " >> /tmp/$$ chmod 600 /tmp/$$ curl -# --insecure -n --url "imaps://${server}/INBOX;UID=${message}" >> /tmp/$$ 2>/dev/null sed 's/\n\n/\n/g' < /tmp/$$ | less rm /tmp/$$ done exit ;; esac