#! /usr/bin/env python ######################################################################## # COPYRIGHT_BEGIN # # mical # Minimal iCalendar utilities # # Copyright (C) 2007, David Arnold # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # COPYRIGHT_END ######################################################################## # $ZeroXOne: mical/micreply,v 1.8 2007/06/06 00:48:10 d Exp $ ######################################################################## # RFC-2446 # Section 3.2.3 deals with REPLY import datetime, smtplib, sys, os, vobject from email.MIMEText import MIMEText from email.MIMEMultipart import MIMEMultipart from optparse import OptionParser from mical import VERSION PROGRAM = os.path.basename(sys.argv[0]) ######################################################################## def usage(msg=None): if msg: print msg print "Usage: %s [-i index] action" % PROGRAM return def prefixes(s): """Return list of possible prefixes for the supplied string.""" l = [] for i in range(len(s)+1): l.append(s[:i]) return l def reply(request, answer): # Get my email address try: f = open("%s/.mical/me" % os.environ["HOME"]) me = f.read().strip() f.close() except IOError: usage("mical not initialized. Please run micsetup") sys.exit(1) # Get required event properties organizer = request.vevent.organizer.value.split(":")[1] uid = request.vevent.uid.value sequence = request.vevent.sequence.value summary = request.vevent.summary.value start = request.vevent.dtstart.value if hasattr(request.vevent, "dtend"): end = request.vevent.dtend.value elif hasattr(request.vevent, "duration"): duration = request.vevent.duration.value end = start + duration if hasattr(request.vevent, "location"): location = request.vevent.location.value else: location = "Unspecified" # Create reply iCalendar object reply = vobject.iCalendar() reply.add('method') reply.method.value = "REPLY" reply.add('vevent') reply.vevent.add('attendee') reply.vevent.attendee.value = "MAILTO:" + me reply.vevent.attendee.params["PARTSTAT"] = [answer.upper()] reply.vevent.add('organizer') reply.vevent.organizer.value = "MAILTO:" + organizer reply.vevent.add('uid') reply.vevent.uid.value = uid reply.vevent.add('sequence') reply.vevent.sequence.value = sequence reply.vevent.add('dtstamp') reply.vevent.dtstamp.value = datetime.datetime.now() # Ensure Outlook prints the reply nicely reply.vevent.add('location') reply.vevent.location.value = location reply.vevent.add('dtstart') reply.vevent.dtstart.value = start if hasattr(request.vevent, "dtend"): reply.vevent.add('dtend') reply.vevent.dtend.value = end elif hasattr(request.vevent, "duration"): reply.vevent.add('duration') reply.vevent.duration.value = duration # Create reply email text #text = "I have %s your invitation to '%s'\r\n" % (answer.lower(), summary) # Create reply email message = MIMEText(reply.serialize(), "calendar; method=REPLY") message['Subject'] = summary message['From'] = me message['Cc'] = me message['To'] = organizer message['X-Mailer'] = PROGRAM + " v" + VERSION # Send email mailer = smtplib.SMTP() mailer.connect() mailer.sendmail(me, [organizer], message.as_string()) mailer.close() # Print action print answer.capitalize() + ": " + summary return def main(): # Parse arguments parser = OptionParser(description="Send an email reply to a specified " "iCalendar meeting invitation, accepting or " "declining as specified. Without a saved initation " "index number, read the invitation text from the " "standard input device.", usage="%prog [-i index] accepted|declined", version=PROGRAM + "-" + VERSION) parser.add_option("-i", "--index", dest="index", help="Index of saved event", metavar="NUM") (options, args) = parser.parse_args() if len(args) != 1: usage("Illegal options") sys.exit(1) event_id = options.index action = args[0] if event_id: # Find event matching supplied identifier try: events = os.listdir("%s/.mical" % os.environ["HOME"]) except OSError: usage("No saved events found") sys.exit(1) if event_id not in events: usage("No such event: %s" % event_id) sys.exit(1) # Open event file f = open("%s/.mical/%s" % (os.environ["HOME"], event_id)) else: f = sys.stdin # Read event data request = vobject.readOne(f) f.close() # Perform requested action if action.lower() in prefixes("accepted"): reply(request, "accepted") elif action.lower() in prefixes("declined"): reply(request, "declined") else: usage("Unrecognised action: %s" % action) sys.exit(1) if __name__ == "__main__": main() ########################################################################