#!/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)