imip-agent

tests/test_handle.py

1321:d8a75a959d5c
2017-10-15 Paul Boddie Merged add-fallback-tzid-to-objects back into this branch. client-editing-simplification
     1 #!/usr/bin/env python     2      3 """     4 A handler to help with testing.     5      6 Copyright (C) 2014, 2015, 2016, 2017 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 from os.path import split    29 import sys    30     31 class TestClient(ClientForObject):    32     33     """    34     A content handler for use in testing, as opposed to operating within the    35     mail processing pipeline.    36     """    37     38     # Action methods.    39     40     def handle_request(self, action, start=None, end=None, recurrenceid=None):    41     42         """    43         Process the current request for the current user. Return whether the    44         given 'action' was taken.    45     46         If 'start' and 'end' are specified, they will be used in any    47         counter-proposal.    48     49         Where 'recurrenceid' is specified and refers to a new recurrence, the    50         action will apply only to this new recurrence.    51         """    52     53         have_new_recurrence = self.obj.get_recurrenceid() != recurrenceid    54     55         if have_new_recurrence:    56             self.obj["RECURRENCE-ID"] = [(recurrenceid, {})]    57             self.obj.remove_all(["RDATE", "RRULE"])    58     59         # Reply only on behalf of this user.    60     61         if action in ("accept", "decline"):    62             attendee_attr = self.update_participation(action == "accept" and "ACCEPTED" or "DECLINED")    63             method = "REPLY"    64     65         elif action == "counter":    66             attendee_attr = self.obj.get_value_map("ATTENDEE").get(self.user)    67             method = "COUNTER"    68     69         # Nothing else is supported.    70     71         else:    72             return None    73     74         # For counter-proposals or new recurrences, set a new main period for    75         # the event.    76     77         if action == "counter" or have_new_recurrence:    78             period = self.obj.get_main_period()    79     80             # Use the existing or configured time zone for the specified    81             # datetimes.    82     83             start = to_timezone(get_datetime(start), period.tzid)    84             end = to_timezone(get_datetime(end), period.tzid)    85             period = RecurringPeriod(start, end, period.tzid, period.origin, period.get_start_attr(), period.get_end_attr())    86             self.obj.set_period(period)    87     88         # Where no attendees remain, no message is generated.    89     90         if not attendee_attr:    91             return None    92     93         # NOTE: This is a simpler form of the code in imipweb.client.    94     95         self.update_sender_attr(attendee_attr)    96         self.obj["ATTENDEE"] = [(self.user, attendee_attr)]    97         self.update_dtstamp()    98         self.update_sequence()    99    100         parts = [self.obj.to_part(method)]   101         recipients = self.get_recipients()   102    103         message = self.make_message(parts, recipients, bcc_sender=True)   104         return message.as_string()   105    106 # A simple main program that attempts to handle a stored request, writing the   107 # response message to standard output.   108    109 if __name__ == "__main__":   110     progname = split(sys.argv[0])[-1]   111    112     try:   113         action, store_type, store_dir, journal_dir, preferences_dir, user = sys.argv[1:7]   114         if len(sys.argv) >= 10:   115             start, end = sys.argv[7:9]   116             i = 9   117         else:   118             start, end = None, None   119             i = 7   120         uid, recurrenceid = (sys.argv[i:i+2] + [None] * 2)[:2]   121     except ValueError:   122         print >>sys.stderr, """\   123 Usage: %s <action> <store type> <store directory> <journal directory>   124        <preferences directory> <user URI> [ <start> <end> ]   125        <uid> <recurrence-id>   126    127 Need 'accept', 'counter' or 'decline', a store type, a store directory, a   128 journal directory, a preferences directory, user URI, any counter-proposal or   129 new recurrence datetimes (see below), plus the appropriate event UID and   130 RECURRENCE-ID (if a recurrence is involved).   131    132 The RECURRENCE-ID must be in exactly the form employed by the store, not a   133 different but equivalent representation, if the identifier is to refer to an   134 existing recurrence.   135    136 Alternatively, omit the UID and RECURRENCE-ID and provide event-only details on   137 standard input to force the script to handle an event not already present in the   138 store. The event details must be a fragment of a calendar file, not an entire   139 calendar file, nor a mail message containing calendar data.   140    141 If 'counter' has been indicated, alternative start and end datetimes are also   142 required. If a specific recurrence is being separated from an event, such   143 datetimes are also required in order to set the main period of the recurrence.   144 """   145         sys.exit(1)   146    147     store = get_store(store_type, store_dir)   148     journal = get_journal(store_type, journal_dir)   149    150     if uid is not None:   151         obj = store.get_event(user, uid, recurrenceid)   152    153         # Permit new recurrences by getting the parent object.   154    155         if not obj:   156             obj = store.get_event(user, uid)   157    158         if not obj:   159             print >>sys.stderr, "No such event:", uid, recurrenceid   160             sys.exit(1)   161     else:   162         obj = Object(parse_object(sys.stdin, "utf-8"))   163    164     handler = TestClient(obj, user, Messenger(), store, None, journal, preferences_dir)   165     response = handler.handle_request(action, start, end, recurrenceid)   166    167     if response:   168         if uid is not None:   169             store.dequeue_request(user, uid, recurrenceid)   170         print response   171     else:   172         sys.exit(1)   173    174 # vim: tabstop=4 expandtab shiftwidth=4