imip-agent

imip_store.py

333:aa2e2008a863
2015-02-12 Paul Boddie Always convert mail addresses to lower case.
     1 #!/usr/bin/env python     2      3 """     4 A simple filesystem-based store of calendar data.     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 datetime import datetime    23 from imiptools.config import STORE_DIR, PUBLISH_DIR    24 from imiptools.data import make_calendar, parse_object, to_stream    25 from imiptools.filesys import fix_permissions, FileBase    26 from os.path import exists, isfile, join    27 from os import listdir, remove    28 from time import sleep    29     30 class FileStore(FileBase):    31     32     "A file store of tabular free/busy data and objects."    33     34     def __init__(self, store_dir=STORE_DIR):    35         FileBase.__init__(self, store_dir)    36     37     def acquire_lock(self, user, timeout=None):    38         FileBase.acquire_lock(self, timeout, user)    39     40     def release_lock(self, user):    41         FileBase.release_lock(self, user)    42     43     def _get_object(self, user, filename):    44     45         """    46         Return the parsed object for the given 'user' having the given    47         'filename'.    48         """    49     50         self.acquire_lock(user)    51         try:    52             f = open(filename, "rb")    53             try:    54                 return parse_object(f, "utf-8")    55             finally:    56                 f.close()    57         finally:    58             self.release_lock(user)    59     60     def _set_object(self, user, filename, node):    61     62         """    63         Set an object for the given 'user' having the given 'filename', using    64         'node' to define the object.    65         """    66     67         self.acquire_lock(user)    68         try:    69             f = open(filename, "wb")    70             try:    71                 to_stream(f, node)    72             finally:    73                 f.close()    74                 fix_permissions(filename)    75         finally:    76             self.release_lock(user)    77     78         return True    79     80     def _remove_object(self, filename):    81     82         "Remove the object with the given 'filename'."    83     84         try:    85             remove(filename)    86         except OSError:    87             return False    88     89         return True    90     91     def get_events(self, user):    92     93         "Return a list of event identifiers."    94     95         filename = self.get_object_in_store(user, "objects")    96         if not filename or not exists(filename):    97             return None    98     99         return [name for name in listdir(filename) if isfile(join(filename, name))]   100    101     def get_event(self, user, uid):   102    103         "Get the event for the given 'user' with the given 'uid'."   104    105         filename = self.get_object_in_store(user, "objects", uid)   106         if not filename or not exists(filename):   107             return None   108    109         return self._get_object(user, filename)   110    111     def set_event(self, user, uid, node):   112    113         "Set an event for 'user' having the given 'uid' and 'node'."   114    115         filename = self.get_object_in_store(user, "objects", uid)   116         if not filename:   117             return False   118    119         return self._set_object(user, filename, node)   120    121     def remove_event(self, user, uid):   122    123         "Remove an event for 'user' having the given 'uid'."   124    125         filename = self.get_object_in_store(user, "objects", uid)   126         if not filename:   127             return False   128    129         return self._remove_object(filename)   130    131     def get_freebusy(self, user):   132    133         "Get free/busy details for the given 'user'."   134    135         filename = self.get_object_in_store(user, "freebusy")   136         if not filename or not exists(filename):   137             return []   138         else:   139             return self._get_freebusy(user, filename)   140    141     def get_freebusy_for_other(self, user, other):   142    143         "For the given 'user', get free/busy details for the 'other' user."   144    145         filename = self.get_object_in_store(user, "freebusy-other", other)   146         if not filename or not exists(filename):   147             return []   148         else:   149             return self._get_freebusy(user, filename)   150    151     def _get_freebusy(self, user, filename):   152    153         "For the given 'user', get the free/busy details from 'filename'."   154    155         self.acquire_lock(user)   156         try:   157             f = open(filename)   158             try:   159                 l = []   160                 for line in f.readlines():   161                     l.append(tuple(line.strip().split("\t")))   162                 return l   163             finally:   164                 f.close()   165         finally:   166             self.release_lock(user)   167    168     def set_freebusy(self, user, freebusy):   169    170         "For the given 'user', set 'freebusy' details."   171    172         filename = self.get_object_in_store(user, "freebusy")   173         if not filename:   174             return False   175    176         self._set_freebusy(user, filename, freebusy)   177         return True   178    179     def set_freebusy_for_other(self, user, freebusy, other):   180    181         "For the given 'user', set 'freebusy' details for the 'other' user."   182    183         filename = self.get_object_in_store(user, "freebusy-other", other)   184         if not filename:   185             return False   186    187         self._set_freebusy(user, filename, freebusy)   188         return True   189    190     def _set_freebusy(self, user, filename, freebusy):   191    192         """   193         For the given 'user', write to the file having the given 'filename' the   194         'freebusy' details.   195         """   196    197         self.acquire_lock(user)   198         try:   199             f = open(filename, "w")   200             try:   201                 for item in freebusy:   202                     f.write("\t".join([(value or "OPAQUE") for value in item]) + "\n")   203             finally:   204                 f.close()   205                 fix_permissions(filename)   206         finally:   207             self.release_lock(user)   208    209     def _get_requests(self, user, queue):   210    211         "Get requests for the given 'user' from the given 'queue'."   212    213         filename = self.get_object_in_store(user, queue)   214         if not filename or not exists(filename):   215             return None   216    217         self.acquire_lock(user)   218         try:   219             f = open(filename)   220             try:   221                 return [line.strip() for line in f.readlines()]   222             finally:   223                 f.close()   224         finally:   225             self.release_lock(user)   226    227     def get_requests(self, user):   228    229         "Get requests for the given 'user'."   230    231         return self._get_requests(user, "requests")   232    233     def get_cancellations(self, user):   234    235         "Get cancellations for the given 'user'."   236    237         return self._get_requests(user, "cancellations")   238    239     def _set_requests(self, user, requests, queue):   240    241         """   242         For the given 'user', set the list of queued 'requests' in the given   243         'queue'.   244         """   245    246         filename = self.get_object_in_store(user, queue)   247         if not filename:   248             return False   249    250         self.acquire_lock(user)   251         try:   252             f = open(filename, "w")   253             try:   254                 for request in requests:   255                     print >>f, request   256             finally:   257                 f.close()   258                 fix_permissions(filename)   259         finally:   260             self.release_lock(user)   261    262         return True   263    264     def set_requests(self, user, requests):   265    266         "For the given 'user', set the list of queued 'requests'."   267    268         return self._set_requests(user, requests, "requests")   269    270     def set_cancellations(self, user, cancellations):   271    272         "For the given 'user', set the list of queued 'cancellations'."   273    274         return self._set_requests(user, cancellations, "cancellations")   275    276     def _set_request(self, user, request, queue):   277    278         "For the given 'user', set the queued 'request' in the given 'queue'."   279    280         filename = self.get_object_in_store(user, queue)   281         if not filename:   282             return False   283    284         self.acquire_lock(user)   285         try:   286             f = open(filename, "a")   287             try:   288                 print >>f, request   289             finally:   290                 f.close()   291                 fix_permissions(filename)   292         finally:   293             self.release_lock(user)   294    295         return True   296    297     def set_request(self, user, request):   298    299         "For the given 'user', set the queued 'request'."   300    301         return self._set_request(user, request, "requests")   302    303     def set_cancellation(self, user, cancellation):   304    305         "For the given 'user', set the queued 'cancellation'."   306    307         return self._set_request(user, cancellation, "cancellations")   308    309     def queue_request(self, user, uid):   310    311         "Queue a request for 'user' having the given 'uid'."   312    313         requests = self.get_requests(user) or []   314    315         if uid not in requests:   316             return self.set_request(user, uid)   317    318         return False   319    320     def dequeue_request(self, user, uid):   321    322         "Dequeue a request for 'user' having the given 'uid'."   323    324         requests = self.get_requests(user) or []   325    326         try:   327             requests.remove(uid)   328             self.set_requests(user, requests)   329         except ValueError:   330             return False   331         else:   332             return True   333    334     def cancel_event(self, user, uid):   335    336         "Queue an event for cancellation for 'user' having the given 'uid'."   337    338         cancellations = self.get_cancellations(user) or []   339    340         if uid not in cancellations:   341             return self.set_cancellation(user, uid)   342    343         return False   344    345 class FilePublisher(FileBase):   346    347     "A publisher of objects."   348    349     def __init__(self, store_dir=PUBLISH_DIR):   350         FileBase.__init__(self, store_dir)   351    352     def set_freebusy(self, user, freebusy):   353    354         "For the given 'user', set 'freebusy' details."   355    356         filename = self.get_object_in_store(user, "freebusy")   357         if not filename:   358             return False   359    360         record = []   361         rwrite = record.append   362    363         rwrite(("ORGANIZER", {}, user))   364         rwrite(("UID", {}, user))   365         rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))   366    367         for start, end, uid, transp in freebusy:   368             if not transp or transp == "OPAQUE":   369                 rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))   370    371         f = open(filename, "w")   372         try:   373             to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))   374         finally:   375             f.close()   376             fix_permissions(filename)   377    378         return True   379    380 # vim: tabstop=4 expandtab shiftwidth=4