Skip to content

Instantly share code, notes, and snippets.

@s3rgeym
Created April 29, 2024 06:39
Show Gist options
  • Save s3rgeym/248790fc607bcb04a6091c3a135d343d to your computer and use it in GitHub Desktop.
Save s3rgeym/248790fc607bcb04a6091c3a135d343d to your computer and use it in GitHub Desktop.
Dumps all emails via IMAP
#!/usr/bin/env python3
import argparse
import functools
import imaplib
import subprocess
import sys
import time
import zipfile
print_err = functools.partial(print, file=sys.stderr, flush=True)
def process_mailbox(M, mailbox, zip_file):
print_err(f"process {mailbox!r}")
rv, messages = M.search(None, "ALL")
if rv != "OK":
print_err("messages not found")
return
# print_err(messages)
message_nums = messages[0].decode().split()
print_err("total messages:", len(message_nums))
for num in message_nums:
time.sleep(0.1)
rv, data = M.fetch(num, "(RFC822)")
if rv != "OK":
print_err("can't fetch", num)
continue
filename = f"./{mailbox}/{num}.eml"
print_err(f"add to archive: {filename!r}")
zip_file.writestr(filename, data[0][1])
# Мне было лень разбираться как декодировать UTF-7 этот
def decode_mailbox(s):
try:
return subprocess.check_output(
"tr '&' + | iconv -f UTF-7 -t UTF-8",
shell=True,
input=s.encode(),
).decode()
except: # noqa: E722
return s
def quote_if_needed(s):
return f'"{s}"' if any(c.isspace() for c in s) else s
def main():
parser = argparse.ArgumentParser(description="Dumps all emails via IMAP")
parser.add_argument("-H", "--host", required=True)
parser.add_argument("-p", "--port", type=int, default=993)
parser.add_argument("-u", "--username", required=True)
parser.add_argument("-P", "--password", required=True)
parser.add_argument("-o", "--output", type=argparse.FileType("wb"), default="-")
args = parser.parse_args()
M = imaplib.IMAP4_SSL(args.host, args.port)
M.login(args.username, args.password)
# ('OK', [b'(\\HasNoChildren \\Inbox) "/" "INBOX"', b'(\\HasNoChildren \\Sent) "/" "Sent Messages"', b'(\\HasNoChildren \\Drafts) "/" "Drafts"', b'(\\HasNoChildren \\Trash) "/" "Deleted Messages"', b'(\\HasNoChildren \\Junk) "/" "Junk"', b'(\\HasNoChildren) "/" "&sLSsjMT0ulTHfNVo-"', b'(\\HasNoChildren) "/" "&zK2tbAC3rLDIHA-"', b'(\\HasNoChildren) "/" "&znTTmA-"', b'(\\HasNoChildren) "/" "SNS"', b'(\\HasNoChildren) "/" "&1QS4XLqowVg-"'])
rv, mailboxes = M.list()
assert rv == "OK"
mailboxes = [item.decode().split('"')[-2] for item in mailboxes]
print_err(mailboxes)
with zipfile.ZipFile(
args.output, "w", compression=zipfile.ZIP_DEFLATED
) as zip_file:
for mailbox in mailboxes:
# Бросит ошибку если имя содержит пробелы
# Вроде декодированное имя выбрать нельзя
rv, _ = M.select(quote_if_needed(mailbox))
if mailbox.startswith("&"):
mailbox = decode_mailbox(mailbox)
if rv != "OK":
print_err(f"can't select mailbox: {mailbox}")
continue
try:
process_mailbox(M, mailbox, zip_file)
finally:
M.close()
M.logout()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment