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