#!/usr/bin/env python
# mailbox2ics.py
"""Convert the contents of an imap mailbox to an ICS file.
This program scans an IMAP mailbox, reads in any messages with ICS
files attached, and merges them into a single ICS file as output.
"""
# Import system modules
import imaplib
import email
import getpass
import optparse
import sys
# Import Local modules
from icalendar import Calendar, Event
# Module
def main():
# Set up our options
option_parser = optparse.OptionParser(
usage='usage: %prog [options] hostname username mailbox [mailbox...]'
)
option_parser.add_option('-p', '--password', dest='password',
default='',
help='Password for username',
)
option_parser.add_option('--port', dest='port',
help='Port for IMAP server',
type="int",
)
option_parser.add_option('-v', '--verbose',
dest="verbose",
action="store_true",
default=True,
help='Show progress',
)
option_parser.add_option('-q', '--quiet',
dest="verbose",
action="store_false",
help='Do not show progress',
)
option_parser.add_option('-o', '--output', dest="output",
help="Output file",
default=None,
)
(options, args) = option_parser.parse_args()
if len(args) < 3:
option_parser.print_help()
print >>sys.stderr, '\nERROR: Please specify a username, hostname, and mailbox.'
return 1
hostname = args[0]
username = args[1]
mailboxes = args[2:]
# Make sure we have the credentials to login to the IMAP server.
password = options.password or getpass.getpass(stream=sys.stderr)
# Initialize a calendar to hold the merged data
merged_calendar = Calendar()
merged_calendar.add('prodid', '-//mailbox2ics//doughellmann.com//')
merged_calendar.add('calscale', 'GREGORIAN')
if options.verbose:
print >>sys.stderr, 'Logging in to "%s" as %s' % (hostname, username)
# Connect to the mail server
if options.port is not None:
mail_server = imaplib.IMAP4_SSL(hostname, options.port)
else:
mail_server = imaplib.IMAP4_SSL(hostname)
(typ, [login_response]) = mail_server.login(username, password)
try:
# Process the mailboxes
for mailbox in mailboxes:
if options.verbose: print >>sys.stderr, 'Scanning %s ...' % mailbox
(typ, [num_messages]) = mail_server.select(mailbox)
if typ == 'NO':
raise RuntimeError('Could not find mailbox %s: %s' %
(mailbox, num_messages))
num_messages = int(num_messages)
if not num_messages:
if options.verbose: print >>sys.stderr, ' empty'
continue
# Find all messages
(typ, [message_ids]) = mail_server.search(None, 'ALL')
for num in message_ids.split():
# Get a Message object
typ, message_parts = mail_server.fetch(num, '(RFC822)')
msg = email.message_from_string(message_parts[0][1])
# Look for calendar attachments
for part in msg.walk():
if part.get_content_type() == 'text/calendar':
# Parse the calendar attachment
ics_text = part.get_payload(decode=1)
importing = Calendar.from_string(ics_text)
# Add events from the calendar to our merge calendar
for event in importing.subcomponents:
if event.name != 'VEVENT':
continue
if options.verbose:
print >>sys.stderr, 'Found: %s' % event['SUMMARY']
merged_calendar.add_component(event)
finally:
# Disconnect from the IMAP server
if mail_server.state != 'AUTH':
mail_server.close()
mail_server.logout()
# Dump the merged calendar to our output destination
if options.output:
output = open(options.output, 'wt')
try:
output.write(str(merged_calendar))
finally:
output.close()
else:
print str(merged_calendar)
return 0
if __name__ == '__main__':
try:
exit_code = main()
except Exception, err:
print >>sys.stderr, 'ERROR: %s' % str(err)
exit_code = 1
sys.exit(exit_code)