imip-agent

imiptools/handlers/__init__.py

886:8a3994e54ea4
2015-10-20 Paul Boddie Permit the selection of a same-day ending while still allowing time adjustments.
     1 #!/usr/bin/env python     2      3 """     4 General handler support for incoming calendar objects.     5      6 Copyright (C) 2014, 2015 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 email.mime.text import MIMEText    23 from imiptools.client import ClientForObject    24 from imiptools.config import MANAGER_PATH, MANAGER_URL    25 from imiptools.data import get_address, get_uri, get_sender_identities, \    26                            uri_dict, uri_item    27 from socket import gethostname    28     29 # References to the Web interface.    30     31 def get_manager_url():    32     url_base = MANAGER_URL or "http://%s/" % gethostname()    33     return "%s/%s" % (url_base.rstrip("/"), MANAGER_PATH.lstrip("/"))    34     35 def get_object_url(uid, recurrenceid=None):    36     return "%s/%s%s" % (    37         get_manager_url().rstrip("/"), uid,    38         recurrenceid and "/%s" % recurrenceid or ""    39         )    40     41 class Handler(ClientForObject):    42     43     "General handler support."    44     45     def __init__(self, senders=None, recipient=None, messenger=None, store=None,    46                  publisher=None, preferences_dir=None):    47     48         """    49         Initialise the handler with any specifically indicated 'senders' and    50         'recipient' of a calendar object. The object is initially undefined.    51     52         The optional 'messenger' provides a means of interacting with the mail    53         system.    54     55         The optional 'store' and 'publisher' can be specified to override the    56         default store and publisher objects.    57         """    58     59         ClientForObject.__init__(self, None, recipient and get_uri(recipient), messenger, store, publisher, preferences_dir)    60     61         self.senders = senders and set(map(get_address, senders))    62         self.recipient = recipient and get_address(recipient)    63     64         self.results = []    65         self.outgoing_methods = set()    66     67     def wrap(self, text, link=True):    68     69         "Wrap any valid message for passing to the recipient."    70     71         texts = []    72         texts.append(text)    73         if link and self.have_manager():    74             texts.append("If your mail program cannot handle this "    75                          "message, you may view the details here:\n\n%s" %    76                          get_object_url(self.uid, self.recurrenceid))    77     78         return self.add_result(None, None, MIMEText("\n".join(texts)))    79     80     # Result registration.    81     82     def add_result(self, method, outgoing_recipients, part):    83     84         """    85         Record a result having the given 'method', 'outgoing_recipients' and    86         message 'part'.    87         """    88     89         if outgoing_recipients:    90             self.outgoing_methods.add(method)    91         self.results.append((outgoing_recipients, part))    92     93     def add_results(self, methods, outgoing_recipients, parts):    94     95         """    96         Record results having the given 'methods', 'outgoing_recipients' and    97         message 'parts'.    98         """    99    100         if outgoing_recipients:   101             self.outgoing_methods.update(methods)   102         for part in parts:   103             self.results.append((outgoing_recipients, part))   104    105     def get_results(self):   106         return self.results   107    108     def get_outgoing_methods(self):   109         return self.outgoing_methods   110    111     # Logic, filtering and access to calendar structures and other data.   112    113     def filter_by_senders(self, mapping):   114    115         """   116         Return a list of items from 'mapping' filtered using sender information.   117         """   118    119         if self.senders:   120    121             # Get a mapping from senders to identities.   122    123             identities = get_sender_identities(mapping)   124    125             # Find the senders that are valid.   126    127             senders = map(get_address, identities)   128             valid = self.senders.intersection(senders)   129    130             # Return the true identities.   131    132             return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid], [])   133         else:   134             return mapping   135    136     def filter_by_recipient(self, mapping):   137    138         """   139         Return a list of items from 'mapping' filtered using recipient   140         information.   141         """   142    143         if self.recipient:   144             addresses = set(map(get_address, mapping))   145             return map(get_uri, addresses.intersection([self.recipient]))   146         else:   147             return mapping   148    149     def require_organiser(self, from_organiser=True):   150    151         """   152         Return the organiser for the current object, filtered for the sender or   153         recipient of interest. Return None if no identities are eligible.   154    155         The organiser identity is normalized.   156         """   157    158         organiser, organiser_attr = organiser_item = uri_item(self.obj.get_item("ORGANIZER"))   159    160         if not organiser:   161             return None   162    163         # Only provide details for an organiser who sent/receives the message.   164    165         organiser_filter_fn = from_organiser and self.filter_by_senders or self.filter_by_recipient   166    167         if not organiser_filter_fn(dict([organiser_item])):   168             return None   169    170         # Test against any previously-received organiser details.   171    172         if not self.is_recognised_organiser(organiser):   173             replacement = self.get_organiser_replacement()   174    175             # Allow any organiser as a replacement where indicated.   176    177             if replacement == "any":   178                 pass   179    180             # Allow any recognised attendee as a replacement where indicated.   181    182             elif replacement != "attendee" or not self.is_recognised_attendee(organiser):   183                 return None   184    185         return organiser_item   186    187     def require_attendees(self, from_organiser=True):   188    189         """   190         Return the attendees for the current object, filtered for the sender or   191         recipient of interest. Return None if no identities are eligible.   192    193         The attendee identities are normalized.   194         """   195    196         attendee_map = uri_dict(self.obj.get_value_map("ATTENDEE"))   197    198         # Only provide details for attendees who sent/receive the message.   199    200         attendee_filter_fn = from_organiser and self.filter_by_recipient or self.filter_by_senders   201    202         attendees = {}   203         for attendee in attendee_filter_fn(attendee_map):   204             if attendee:   205                 attendees[attendee] = attendee_map[attendee]   206    207         return attendees   208    209     def require_organiser_and_attendees(self, from_organiser=True):   210    211         """   212         Return the organiser and attendees for the current object, filtered for   213         the recipient of interest. Return None if no identities are eligible.   214    215         Organiser and attendee identities are normalized.   216         """   217    218         organiser_item = self.require_organiser(from_organiser)   219         attendees = self.require_attendees(from_organiser)   220    221         if not attendees or not organiser_item:   222             return None   223    224         return organiser_item, attendees   225    226 # vim: tabstop=4 expandtab shiftwidth=4