imip-agent

imiptools/handlers/__init__.py

838:40f53e26c74e
2015-10-15 Paul Boddie Added support for adding suggested attendees from counter-proposals.
     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 get_results(self):    94         return self.results    95     96     def get_outgoing_methods(self):    97         return self.outgoing_methods    98     99     # Logic, filtering and access to calendar structures and other data.   100    101     def filter_by_senders(self, mapping):   102    103         """   104         Return a list of items from 'mapping' filtered using sender information.   105         """   106    107         if self.senders:   108    109             # Get a mapping from senders to identities.   110    111             identities = get_sender_identities(mapping)   112    113             # Find the senders that are valid.   114    115             senders = map(get_address, identities)   116             valid = self.senders.intersection(senders)   117    118             # Return the true identities.   119    120             return reduce(lambda a, b: a + b, [identities[get_uri(address)] for address in valid], [])   121         else:   122             return mapping   123    124     def filter_by_recipient(self, mapping):   125    126         """   127         Return a list of items from 'mapping' filtered using recipient   128         information.   129         """   130    131         if self.recipient:   132             addresses = set(map(get_address, mapping))   133             return map(get_uri, addresses.intersection([self.recipient]))   134         else:   135             return mapping   136    137     def require_organiser(self, from_organiser=True):   138    139         """   140         Return the organiser for the current object, filtered for the sender or   141         recipient of interest. Return None if no identities are eligible.   142    143         The organiser identity is normalized.   144         """   145    146         organiser, organiser_attr = organiser_item = uri_item(self.obj.get_item("ORGANIZER"))   147    148         if not organiser:   149             return None   150    151         # Only provide details for an organiser who sent/receives the message.   152    153         organiser_filter_fn = from_organiser and self.filter_by_senders or self.filter_by_recipient   154    155         if not organiser_filter_fn(dict([organiser_item])):   156             return None   157    158         # Test against any previously-received organiser details.   159    160         if not self.is_recognised_organiser(organiser):   161             replacement = self.get_organiser_replacement()   162    163             # Allow any organiser as a replacement where indicated.   164    165             if replacement == "any":   166                 pass   167    168             # Allow any recognised attendee as a replacement where indicated.   169    170             elif replacement != "attendee" or not self.is_recognised_attendee(organiser):   171                 return None   172    173         return organiser_item   174    175     def require_attendees(self, from_organiser=True):   176    177         """   178         Return the attendees for the current object, filtered for the sender or   179         recipient of interest. Return None if no identities are eligible.   180    181         The attendee identities are normalized.   182         """   183    184         attendee_map = uri_dict(self.obj.get_value_map("ATTENDEE"))   185    186         # Only provide details for attendees who sent/receive the message.   187    188         attendee_filter_fn = from_organiser and self.filter_by_recipient or self.filter_by_senders   189    190         attendees = {}   191         for attendee in attendee_filter_fn(attendee_map):   192             if attendee:   193                 attendees[attendee] = attendee_map[attendee]   194    195         return attendees   196    197     def require_organiser_and_attendees(self, from_organiser=True):   198    199         """   200         Return the organiser and attendees for the current object, filtered for   201         the recipient of interest. Return None if no identities are eligible.   202    203         Organiser and attendee identities are normalized.   204         """   205    206         organiser_item = self.require_organiser(from_organiser)   207         attendees = self.require_attendees(from_organiser)   208    209         if not attendees or not organiser_item:   210             return None   211    212         return organiser_item, attendees   213    214 # vim: tabstop=4 expandtab shiftwidth=4