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 from imiptools.stores import get_store, get_journal 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_type, store_dir, journal_dir, preferences_dir, user = sys.argv[1:7] 98 if action == "counter": 99 start, end = sys.argv[7:9] 100 i = 9 101 else: 102 start, end = None, None 103 i = 7 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 type, a store directory, a 108 journal directory, a preferences directory, user URI, any counter-proposal 109 datetimes (see below), plus the appropriate event UID and RECURRENCE-ID (if a 110 recurrence is involved). 111 112 The RECURRENCE-ID must be in exactly the form employed by the store, not a 113 different but equivalent representation. 114 115 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on 116 standard input to force the script to handle an event not already present in the 117 store. 118 119 If 'counter' has been indicated, alternative start and end datetimes are also 120 required. 121 """ 122 sys.exit(1) 123 124 store = get_store(store_type, store_dir) 125 journal = get_journal(store_type, journal_dir) 126 127 if uid is not None: 128 fragment = store.get_event(user, uid, recurrenceid) 129 130 if not fragment: 131 print >>sys.stderr, "No such event:", uid, recurrenceid 132 sys.exit(1) 133 else: 134 fragment = parse_object(sys.stdin, "utf-8") 135 136 obj = Object(fragment) 137 handler = TestClient(obj, user, Messenger(), store, None, journal, preferences_dir) 138 response = handler.handle_request(action, start, end) 139 140 if response: 141 if uid is not None: 142 store.dequeue_request(user, uid, recurrenceid) 143 print response 144 else: 145 sys.exit(1) 146 147 # vim: tabstop=4 expandtab shiftwidth=4