imip-agent

Annotated imip_store.py

98:ac41c8e7b3ad
2014-10-30 Paul Boddie Added Postfix support for inspecting outgoing messages, fixing LMTP access for the special user, and making sure that files can be read/written by groups.
paul@2 1
#!/usr/bin/env python
paul@2 2
paul@30 3
from datetime import datetime
paul@68 4
from imiptools.config import STORE_DIR, PUBLISH_DIR
paul@15 5
from os.path import abspath, commonprefix, exists, join, split
paul@98 6
from os import chmod, makedirs
paul@15 7
from vCalendar import iterwrite
paul@2 8
paul@2 9
def check_dir(base, dir):
paul@2 10
    return commonprefix([base, abspath(dir)]) == base
paul@2 11
paul@30 12
def make_calendar(fragment, method=None):
paul@30 13
    return ("VCALENDAR", {},
paul@30 14
            (method and [("METHOD", {}, method)] or []) +
paul@30 15
            [("VERSION", {}, "2.0")] +
paul@30 16
            fragment
paul@30 17
           )
paul@30 18
paul@26 19
def to_stream(out, fragment, encoding="utf-8"):
paul@26 20
    iterwrite(out, encoding=encoding).append(fragment)
paul@15 21
paul@30 22
class FileBase:
paul@2 23
paul@30 24
    "Basic filesystem operations."
paul@2 25
paul@30 26
    def __init__(self, store_dir=STORE_DIR):
paul@30 27
        self.store_dir = store_dir
paul@2 28
        if not exists(self.store_dir):
paul@2 29
            makedirs(self.store_dir)
paul@2 30
paul@15 31
    def get_file_object(self, base, *parts):
paul@15 32
        pathname = join(base, *parts)
paul@15 33
        return check_dir(base, pathname) and pathname or None
paul@2 34
paul@52 35
    def get_object_in_store(self, *parts):
paul@52 36
paul@52 37
        """
paul@52 38
        Return the name of any valid object stored within a hierarchy specified
paul@52 39
        by the given 'parts'.
paul@52 40
        """
paul@52 41
paul@52 42
        parent = expected = self.store_dir
paul@15 43
paul@52 44
        for part in parts:
paul@52 45
            filename = self.get_file_object(expected, part)
paul@52 46
            if not filename:
paul@52 47
                return False
paul@52 48
            parent = expected
paul@52 49
            expected = filename
paul@15 50
paul@52 51
        if not exists(parent):
paul@52 52
            makedirs(parent)
paul@15 53
paul@50 54
        return filename
paul@50 55
paul@50 56
class FileStore(FileBase):
paul@50 57
paul@50 58
    "A file store of tabular free/busy data and objects."
paul@50 59
paul@50 60
    def get_event(self, user, uid):
paul@50 61
paul@50 62
        "Get the event for the given 'user' with the given 'uid'."
paul@50 63
paul@52 64
        filename = self.get_object_in_store(user, uid)
paul@50 65
        if not filename or not exists(filename):
paul@50 66
            return None
paul@50 67
paul@50 68
        return exists(filename) and open(filename) or None
paul@50 69
paul@50 70
    def set_event(self, user, uid, node):
paul@50 71
paul@50 72
        "Set an event for 'user' having the given 'uid' and 'node'."
paul@50 73
paul@52 74
        filename = self.get_object_in_store(user, uid)
paul@50 75
        if not filename:
paul@50 76
            return False
paul@50 77
paul@15 78
        f = open(filename, "w")
paul@15 79
        try:
paul@26 80
            to_stream(f, node)
paul@15 81
        finally:
paul@15 82
            f.close()
paul@98 83
            chmod(filename, 0660)
paul@15 84
paul@15 85
        return True
paul@15 86
paul@15 87
    def get_freebusy(self, user):
paul@15 88
paul@15 89
        "Get free/busy details for the given 'user'."
paul@15 90
paul@52 91
        filename = self.get_object_in_store(user, "freebusy")
paul@15 92
        if not filename or not exists(filename):
paul@2 93
            return None
paul@2 94
paul@2 95
        f = open(filename)
paul@2 96
        try:
paul@2 97
            l = []
paul@2 98
            for line in f.readlines():
paul@17 99
                l.append(tuple(line.strip().split("\t")))
paul@2 100
            return l
paul@2 101
        finally:
paul@2 102
            f.close()
paul@2 103
paul@15 104
    def set_freebusy(self, user, freebusy):
paul@15 105
paul@15 106
        "For the given 'user', set 'freebusy' details."
paul@15 107
paul@52 108
        filename = self.get_object_in_store(user, "freebusy")
paul@15 109
        if not filename:
paul@15 110
            return False
paul@15 111
paul@15 112
        f = open(filename, "w")
paul@15 113
        try:
paul@15 114
            for item in freebusy:
paul@15 115
                f.write("\t".join(item) + "\n")
paul@15 116
        finally:
paul@15 117
            f.close()
paul@98 118
            chmod(filename, 0660)
paul@15 119
paul@15 120
        return True
paul@15 121
paul@66 122
    def get_requests(self, user):
paul@66 123
paul@66 124
        "Get requests for the given 'user'."
paul@66 125
paul@66 126
        filename = self.get_object_in_store(user, "requests")
paul@81 127
        if not filename or not exists(filename):
paul@66 128
            return None
paul@66 129
paul@66 130
        f = open(filename)
paul@66 131
        try:
paul@66 132
            return [line.strip() for line in f.readlines()]
paul@66 133
        finally:
paul@66 134
            f.close()
paul@66 135
paul@66 136
    def set_requests(self, user, requests):
paul@55 137
paul@66 138
        "For the given 'user', set the list of queued 'requests'."
paul@66 139
paul@66 140
        filename = self.get_object_in_store(user, "requests")
paul@66 141
        if not filename:
paul@66 142
            return False
paul@66 143
paul@66 144
        f = open(filename, "w")
paul@66 145
        try:
paul@66 146
            for request in requests:
paul@66 147
                print >>f, request
paul@66 148
        finally:
paul@66 149
            f.close()
paul@98 150
            chmod(filename, 0660)
paul@66 151
paul@66 152
        return True
paul@66 153
paul@66 154
    def set_request(self, user, request):
paul@66 155
paul@66 156
        "For the given 'user', set the queued 'request'."
paul@55 157
paul@55 158
        filename = self.get_object_in_store(user, "requests")
paul@55 159
        if not filename:
paul@55 160
            return False
paul@55 161
paul@55 162
        f = open(filename, "a")
paul@55 163
        try:
paul@66 164
            print >>f, request
paul@55 165
        finally:
paul@55 166
            f.close()
paul@98 167
            chmod(filename, 0660)
paul@55 168
paul@55 169
        return True
paul@55 170
paul@66 171
    def queue_request(self, user, uid):
paul@66 172
paul@66 173
        "Queue a request for 'user' having the given 'uid'."
paul@66 174
paul@81 175
        requests = self.get_requests(user) or []
paul@66 176
paul@66 177
        if uid not in requests:
paul@66 178
            return self.set_request(user, uid)
paul@66 179
paul@66 180
        return False
paul@66 181
paul@30 182
class FilePublisher(FileBase):
paul@30 183
paul@30 184
    "A publisher of objects."
paul@30 185
paul@30 186
    def __init__(self, store_dir=PUBLISH_DIR):
paul@30 187
        FileBase.__init__(self, store_dir)
paul@30 188
paul@30 189
    def set_freebusy(self, user, freebusy):
paul@30 190
paul@30 191
        "For the given 'user', set 'freebusy' details."
paul@30 192
paul@52 193
        filename = self.get_object_in_store(user, "freebusy")
paul@30 194
        if not filename:
paul@30 195
            return False
paul@30 196
paul@30 197
        record = []
paul@30 198
        rwrite = record.append
paul@30 199
paul@30 200
        rwrite(("ORGANIZER", {}, user))
paul@30 201
        rwrite(("UID", {}, user))
paul@30 202
        rwrite(("DTSTAMP", {}, datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")))
paul@30 203
paul@30 204
        for start, end, uid in freebusy:
paul@30 205
            rwrite(("FREEBUSY", {"FBTYPE" : "BUSY"}, "/".join([start, end])))
paul@30 206
paul@30 207
        f = open(filename, "w")
paul@30 208
        try:
paul@30 209
            to_stream(f, make_calendar([("VFREEBUSY", {}, record)], "PUBLISH"))
paul@30 210
        finally:
paul@30 211
            f.close()
paul@98 212
            chmod(filename, 0660)
paul@30 213
paul@30 214
        return True
paul@30 215
paul@2 216
# vim: tabstop=4 expandtab shiftwidth=4