Last active
July 13, 2026 21:27
-
-
Save ernstki/e9cd74159f8246b23a665e2c9ab9cc0e to your computer and use it in GitHub Desktop.
imapfrm - Print selected headers from an IMAP inbox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| ## | |
| ## imapfrm - Print selected headers from an IMAP mailbox | |
| ## | |
| ## Uses sensible defaults that should work with DavMail[1], but you must | |
| ## define IMAP_LOGIN in the environment (or hard-code it below). | |
| ## | |
| ## The name is meant to vaguely invoke `frm` from the GNU Mailutils suite, | |
| ## and earlier historical utilities, although I made no attempt to match its | |
| ## options or output format. | |
| ## | |
| ## Author: Kevin Ernst (ernstki@mail.uc.edu) | |
| ## Assisted-by: Mistral-7B-Instruct-v0.3.Q4_0.llamafile | |
| ## Date: 13 July 2026 | |
| ## License: BSD Zero-Clause (effectively public domain) | |
| ## | |
| ## [1]: https://davmail.sourceforge.net | |
| ## | |
| import os | |
| import sys | |
| import email | |
| import email.utils | |
| import imaplib | |
| from email import policy | |
| # you *must* set this in the environment | |
| login = os.getenv('IMAP_LOGIN') | |
| # defaults suitable for DavMail | |
| passwd = os.getenv('IMAP_PASSWORD', '') | |
| host = os.getenv('IMAP_HOST', 'localhost') | |
| port = os.getenv('IMAP_PORT', 1143) | |
| mbox = os.getenv('IMAP_MAILBOX', 'inbox') | |
| wantheaders = os.getenv('IMAP_HEADERS', 'From,Subject,Date') | |
| wantheaders = wantheaders.split(',') | |
| if (port - 143) % 100 == 0: # if it ends in 143, assume no SSL | |
| imap = imaplib.IMAP4(host, port=port) | |
| else: | |
| imap = imaplib.IMAP4_SSL(host, port=port) | |
| imap.login(login, passwd) | |
| imap.select(mbox) | |
| if keywords := " ".join(sys.argv[1:]): | |
| typ, data = imap.search(None, f'SUBJECT "{keywords}"') | |
| else: | |
| typ, data = imap.search(None, 'ALL') | |
| print("\t".join(wantheaders)) | |
| for num in data[0].split(): | |
| typ, data = imap.fetch(num, '(RFC822.HEADER) FROM') | |
| raw_email = data[0][1] | |
| # policy.default seems to unwrap 'From' and 'Subject' automatically | |
| # …but it's also not the default :/ | |
| msg = email.message_from_bytes(raw_email, policy=policy.default) | |
| headers = [] | |
| for h in wantheaders: | |
| if h == 'Date' or h == 'Received': | |
| localdt = email.utils.parsedate_tz(msg.get(h)) | |
| localdt = email.utils.mktime_tz(localdt) | |
| localdt = email.utils.formatdate(localdt, localtime=True) | |
| headers.append(localdt) | |
| else: | |
| headers.append(msg.get(h)) | |
| print("\t".join(headers)) | |
| imap.close() | |
| imap.logout() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment