-
-
Save sbeyer/8230267 to your computer and use it in GitHub Desktop.
A Python script that (re-)sends all messages in mbox files with From/To/Cc/Bcc as specified in the messages headers
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 python | |
"""\ | |
A command-line utility that can (re)send all messages in an mbox file | |
with options for controlling the rate at which they are sent, etc. | |
""" | |
# This script is based on a gist on github, see | |
# https://gist.github.com/sbeyer/8230267 | |
# | |
# For the origin of the script see | |
# https://github.com/wojdyr/fityk/wiki/MigrationToGoogleGroups | |
import sys | |
import os | |
import time | |
import mailbox | |
import email | |
import smtplib | |
from copy import copy | |
from optparse import OptionParser, make_option | |
from getpass import getpass | |
# Set some defaults | |
defBcc = [] | |
defChunkSize = 100 | |
defChunkDelay = 30.0 | |
defUser = None | |
defHost = 'localhost' | |
defPort = 25 | |
defCount = -1 | |
defStart = -1 | |
# define the command line options | |
option_list = [ | |
make_option('--add-bcc', action='append', metavar='ADDRESSES', dest='bccAddresses', default=defBcc, | |
help='Address that every mail should be sent to, additionally. May be repeated.'), | |
make_option('--host', dest='host', default=defHost, | |
help='Hostname where SMTP server is running. [default: %s]' % defHost), | |
make_option('--port', type='int', dest='port', default=defPort, | |
help='Port number to use for connecting to SMTP server. [default: %d]' % defPort), | |
make_option('--user', dest='user', default=defUser, | |
help='Username for SMTP server. (Will prompt for password.)'), | |
make_option('--chunk', type='int', dest='chunkSize', default=defChunkSize, | |
help='How many messages to send in each batch before pausing. [default: %d]' % defChunkSize), | |
make_option('--pause', type='float', dest='chunkDelay', default=defChunkDelay, | |
help='How many seconds to delay between chunks. [default: %f]' % defChunkDelay), | |
make_option('--count', type='int', dest='count', default=defCount, | |
help='How many messages to send before exiting the tool. [default: all messages in mbox]'), | |
make_option('--start', type='int', dest='start', default=defStart, | |
help='Which message number to start with. [default: where the tool left off the last time, or zero]'), | |
] | |
#--------------------------------------------------------------------------- | |
def get_hwm(hwmfile): | |
if not os.path.exists(hwmfile): | |
return -1 | |
hwm = int(open(hwmfile).read()) | |
return hwm | |
def set_hwm(hwmfile, count): | |
f = open(hwmfile, 'w') | |
f.write(str(count)) | |
f.close() | |
def addresses(string): | |
if string: | |
return [x[1] for x in email.utils.getaddresses([string])] | |
return [] | |
def main(args): | |
if sys.version_info < (2,5): | |
print('Python 2.5 or better is required.') | |
sys.exit(1) | |
# Parse the command line args | |
parser = OptionParser( | |
usage='%prog [options] mbox_file(s)', | |
description=__doc__, | |
version='%prog 20140103', | |
option_list=option_list) | |
options, arguments = parser.parse_args(args) | |
if not arguments: | |
parser.error('At least one mbox file is required') | |
smtpPassword = None | |
if options.user: | |
smtpPassword = getpass() # implies using TLS | |
# process the mbox file(s) | |
for mboxfile in arguments: | |
print('Opening %s...' % mboxfile) | |
mbox = mailbox.mbox(mboxfile) | |
totalInMbox = len(mbox) | |
print('Total messages in mbox: %d' % totalInMbox) | |
hwmfile = mboxfile + '.hwm' | |
print('Storing last message processed in %s' % hwmfile) | |
start = get_hwm(hwmfile) | |
if options.start != -1: | |
start = options.start | |
start += 1 | |
print('Starting with message #%d' % start) | |
totalSent = 0 | |
current = start | |
# Outer loop continues until either the whole mbox or options.count | |
# messages have been sent, | |
while (current < totalInMbox and | |
(totalSent < options.count or options.count == -1)): | |
# Inner loop works one chunkSize number of messages at a time, | |
# pausing and reconnecting to the SMTP server for each chunk. | |
print('Connecting to SMTP(%s, %d)' % (options.host, options.port)) | |
smtp = smtplib.SMTP(options.host, options.port) | |
if options.user: # use TLS | |
print('Logging in as %s' % options.user) | |
smtp.ehlo() | |
smtp.starttls() | |
smtp.ehlo() | |
smtp.login(options.user, smtpPassword) | |
chunkSent = 0 | |
while chunkSent < options.chunkSize: | |
msg = mbox[current] | |
print('Processing message %d: %s' % (current, msg['Subject'])) | |
try: | |
# Here is where we actually send the message | |
fromAddress = addresses(msg['From'])[0] | |
toAddresses = copy(options.bccAddresses) | |
toAddresses += addresses(msg['To']) | |
toAddresses += addresses(msg['Cc']) | |
toAddresses += addresses(msg['Bcc']) | |
smtp.sendmail(fromAddress, toAddresses, msg.as_string()) | |
set_hwm(hwmfile, current) # set new 'high water mark' | |
current += 1 | |
totalSent += 1 | |
chunkSent += 1 | |
if (current >= totalInMbox or | |
(totalSent >= options.count and options.count != -1)): | |
break | |
except smtplib.SMTPServerDisconnected as e: | |
print('Error: %s' % str(e)) | |
del smtp | |
print('Pausing for %f seconds...' % options.chunkDelay) | |
time.sleep(options.chunkDelay) | |
print('') | |
break | |
else: | |
smtp.quit() | |
del smtp | |
print('Pausing for %f seconds...' % options.chunkDelay) | |
time.sleep(options.chunkDelay) | |
print('') | |
print('Goodbye') | |
#--------------------------------------------------------------------------- | |
if __name__ == '__main__': | |
main(sys.argv[1:]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment