1 #!/usr/bin/env python 2 3 """ 4 A handler to help with testing. 5 6 Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from imiptools.client import ClientForObject 23 from imiptools.data import Object, get_address, parse_object 24 from imiptools.dates import get_datetime, to_timezone 25 from imiptools.mail import Messenger 26 from imiptools.period import RecurringPeriod 27 import imip_store 28 import sys 29 30 class TestClient(ClientForObject): 31 32 """ 33 A content handler for use in testing, as opposed to operating within the 34 mail processing pipeline. 35 """ 36 37 # Action methods. 38 39 def handle_request(self, action, start=None, end=None): 40 41 """ 42 Process the current request for the current user. Return whether the 43 given 'action' was taken. 44 45 If 'start' and 'end' are specified, they will be used in any 46 counter-proposal. 47 """ 48 49 # Reply only on behalf of this user. 50 51 if action in ("accept", "decline"): 52 attendee_attr = self.update_participation(action == "accept" and "ACCEPTED" or "DECLINED") 53 method = "REPLY" 54 55 # For counter-proposals, set a new main period for the event. 56 57 elif action == "counter": 58 attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user) 59 period = self.obj.get_main_period(self.get_tzid()) 60 61 # Use the existing or configured time zone for the specified 62 # datetimes. 63 64 start = to_timezone(get_datetime(start), period.tzid) 65 end = to_timezone(get_datetime(end), period.tzid) 66 period = RecurringPeriod(start, end, period.tzid, period.origin, period.get_start_attr(), period.get_end_attr()) 67 self.obj.set_period(period) 68 method = "COUNTER" 69 else: 70 return None 71 72 if not attendee_attr: 73 return None 74 75 # NOTE: This is a simpler form of the code in imipweb.client. 76 77 organiser = get_address(self.obj.get_value("ORGANIZER")) 78 79 self.update_sender(attendee_attr) 80 self.obj["ATTENDEE"] = [(self.user, attendee_attr)] 81 self.update_dtstamp() 82 self.update_sequence(False) 83 84 message = self.messenger.make_outgoing_message( 85 [self.obj.to_part(method)], 86 [organiser], 87 outgoing_bcc=get_address(self.user) 88 ) 89 90 return message.as_string() 91 92 # A simple main program that attempts to handle a stored request, writing the 93 # response message to standard output. 94 95 if __name__ == "__main__": 96 try: 97 action, store_dir, journal_dir, preferences_dir, user = sys.argv[1:6] 98 if action == "counter": 99 start, end = sys.argv[6:8] 100 i = 8 101 else: 102 start, end = None, None 103 i = 6 104 uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2] 105 except ValueError: 106 print >>sys.stderr, """\ 107 Need 'accept', 'counter' or 'decline', a store directory, a preferences 108 directory, user URI, any counter-proposal datetimes (see below), plus the 109 appropriate event UID and RECURRENCE-ID (if a recurrence is involved). 110 111 The RECURRENCE-ID must be in exactly the form employed by the store, not a 112 different but equivalent representation. 113 114 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on 115 standard input to force the script to handle an event not already present in the 116 store. 117 118 If 'counter' has been indicated, alternative start and end datetimes are also 119 required. 120 """ 121 sys.exit(1) 122 123 store = imip_store.FileStore(store_dir) 124 journal = imip_store.FileJournal(journal_dir) 125 126 if uid is not None: 127 fragment = store.get_event(user, uid, recurrenceid) 128 129 if not fragment: 130 print >>sys.stderr, "No such event:", uid, recurrenceid 131 sys.exit(1) 132 else: 133 fragment = parse_object(sys.stdin, "utf-8") 134 135 obj = Object(fragment) 136 handler = TestClient(obj, user, Messenger(), store, None, journal, preferences_dir) 137 response = handler.handle_request(action, start, end) 138 139 if response: 140 if uid is not None: 141 store.dequeue_request(user, uid, recurrenceid) 142 print response 143 else: 144 sys.exit(1) 145 146 # vim: tabstop=4 expandtab shiftwidth=4