datatypes.py 7.91 KB
Newer Older
1

Dan Pascu's avatar
Dan Pascu committed
2
"""Definitions for datatypes used in configuration extensions."""
3 4

import os
5
import re
Adrian Georgescu's avatar
Adrian Georgescu committed
6 7
from urllib.request import pathname2url, url2pathname
from urllib.parse import urlparse
8

9
from application.python.types import MarkerType
10
from sipsimple.configuration.datatypes import Hostname, List
11
from sipsimple.configuration.settings import SIPSimpleSettings
12 13 14 15

from blink.resources import ApplicationData


16
__all__ = ['ApplicationDataPath', 'DefaultPath', 'SoundFile', 'CustomSoundFile', 'HTTPURL', 'FileURL', 'IconDescriptor', 'PresenceState', 'PresenceStateList', 'GraphTimeScale', 'File']
Dan Pascu's avatar
Dan Pascu committed
17 18


Adrian Georgescu's avatar
Adrian Georgescu committed
19
class ApplicationDataPath(str):
20 21 22 23
    def __new__(cls, path):
        path = os.path.normpath(path)
        if path.startswith(ApplicationData.directory+os.path.sep):
            path = path[len(ApplicationData.directory+os.path.sep):]
Adrian Georgescu's avatar
Adrian Georgescu committed
24
        return str.__new__(cls, path)
25 26 27 28 29 30 31 32 33 34

    @property
    def normalized(self):
        return ApplicationData.get(self)


class SoundFile(object):
    def __init__(self, path, volume=100):
        self.path = path
        self.volume = int(volume)
35
        if not (0 <= self.volume <= 100):
36 37 38
            raise ValueError('illegal volume level: %d' % self.volume)

    def __getstate__(self):
Adrian Georgescu's avatar
Adrian Georgescu committed
39
        return '%s,%s' % (self.__dict__['path'], self.volume)
40 41 42

    def __setstate__(self, state):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
43
            path, volume = state.rsplit(',', 1)
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
        except ValueError:
            self.__init__(state)
        else:
            self.__init__(path, volume)

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.path, self.volume)

    def _get_path(self):
        return ApplicationData.get(self.__dict__['path'])
    def _set_path(self, path):
        path = os.path.normpath(path)
        if path.startswith(ApplicationData.directory+os.path.sep):
            path = path[len(ApplicationData.directory+os.path.sep):]
        self.__dict__['path'] = path
    path = property(_get_path, _set_path)
    del _get_path, _set_path


Adrian Georgescu's avatar
Adrian Georgescu committed
63
class DefaultPath(metaclass=MarkerType): pass
64

Dan Pascu's avatar
Dan Pascu committed
65

66
class CustomSoundFile(object): # check if this data type is still needed -Dan
67 68 69
    def __init__(self, path=DefaultPath, volume=100):
        self.path = path
        self.volume = int(volume)
Dan Pascu's avatar
Dan Pascu committed
70
        if not (0 <= self.volume <= 100):
71 72 73 74
            raise ValueError('illegal volume level: %d' % self.volume)

    def __getstate__(self):
        if self.path is DefaultPath:
Adrian Georgescu's avatar
Adrian Georgescu committed
75
            return 'default'
76
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
77
            return 'file:%s,%s' % (self.__dict__['path'], self.volume)
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

    def __setstate__(self, state):
        match = re.match(r'^(?P<type>default|file:)(?P<path>.+?)?(,(?P<volume>\d+))?$', state)
        if match is None:
            raise ValueError('illegal value: %r' % state)
        data = match.groupdict()
        if data.pop('type') == 'default':
            data['path'] = DefaultPath
        data['volume'] = data['volume'] or 100
        self.__init__(**data)

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.path, self.volume)

    def _get_path(self):
        path = self.__dict__['path']
        return path if path is DefaultPath else ApplicationData.get(path)
    def _set_path(self, path):
        if path is not DefaultPath:
            path = os.path.normpath(path)
            if path.startswith(ApplicationData.directory+os.path.sep):
                path = path[len(ApplicationData.directory+os.path.sep):]
        self.__dict__['path'] = path
    path = property(_get_path, _set_path)
    del _get_path, _set_path


Adrian Georgescu's avatar
Adrian Georgescu committed
105
class HTTPURL(str):
106
    def __new__(cls, value):
Adrian Georgescu's avatar
Adrian Georgescu committed
107
        value = str(value)
108
        url = urlparse(value)
Adrian Georgescu's avatar
Adrian Georgescu committed
109
        if url.scheme not in ('http', 'https'):
110 111 112 113 114 115 116
            raise ValueError("illegal HTTP URL scheme (http and https only): %s" % url.scheme)
        Hostname(url.hostname)
        if url.port is not None and not (0 < url.port < 65536):
            raise ValueError("illegal port value: %d" % url.port)
        return value


Adrian Georgescu's avatar
Adrian Georgescu committed
117
class FileURL(str):
118 119
    def __new__(cls, value):
        if not value.startswith('file:'):
Adrian Georgescu's avatar
Adrian Georgescu committed
120
            value = 'file:' + pathname2url(os.path.abspath(value))
Adrian Georgescu's avatar
Adrian Georgescu committed
121
        return str.__new__(cls, value)
122 123


Adrian Georgescu's avatar
Adrian Georgescu committed
124
class ParsedURL(str):
125 126 127
    fragment = property(lambda self: self.__parsed__.fragment)
    netloc   = property(lambda self: self.__parsed__.netloc)
    params   = property(lambda self: self.__parsed__.params)
128
    path     = property(lambda self: self.__parsed__.path if self.scheme != 'file' else url2pathname(self.__parsed__.path))
129 130 131 132 133 134 135
    query    = property(lambda self: self.__parsed__.query)
    scheme   = property(lambda self: self.__parsed__.scheme)

    def __init__(self, value):
        self.__parsed__ = urlparse(self)


136 137
class IconDescriptor(object):
    def __init__(self, url, etag=None):
138
        self.url = ParsedURL(url)
139 140 141 142
        self.etag = etag

    def __getstate__(self):
        if self.etag is None:
Adrian Georgescu's avatar
Adrian Georgescu committed
143
            return str(self.url)
144
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
145
            return '%s,%s' % (self.url, self.etag)
146 147 148

    def __setstate__(self, state):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
149
            url, etag = state.rsplit(',', 1)
150 151 152 153 154 155 156
        except ValueError:
            self.__init__(state)
        else:
            self.__init__(url, etag)

    def __eq__(self, other):
        if isinstance(other, IconDescriptor):
157
            return self.url == other.url and self.etag == other.etag
158 159 160 161 162 163 164 165 166 167
        return NotImplemented

    def __ne__(self, other):
        equal = self.__eq__(other)
        return NotImplemented if equal is NotImplemented else not equal

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.url, self.etag)


168 169
class PresenceState(object):
    def __init__(self, state, note=None):
Adrian Georgescu's avatar
Adrian Georgescu committed
170
        self.state = str(state)
171 172 173 174
        self.note = note

    def __getstate__(self):
        if not self.note:
Adrian Georgescu's avatar
Adrian Georgescu committed
175
            return str(self.state)
176
        else:
Adrian Georgescu's avatar
Adrian Georgescu committed
177
            return '%s,%s' % (self.state, self.note)
178 179 180

    def __setstate__(self, data):
        try:
Adrian Georgescu's avatar
Adrian Georgescu committed
181
            state, note = data.split(',', 1)
182 183 184 185 186 187 188
        except ValueError:
            self.__init__(data)
        else:
            self.__init__(state, note)

    def __eq__(self, other):
        if isinstance(other, PresenceState):
189
            return self.state == other.state and self.note == other.note
190 191 192 193 194 195 196 197 198 199 200 201 202 203
        return NotImplemented

    def __ne__(self, other):
        equal = self.__eq__(other)
        return NotImplemented if equal is NotImplemented else not equal

    def __repr__(self):
        return '%s(%r, %r)' % (self.__class__.__name__, self.state, self.note)


class PresenceStateList(List):
    type = PresenceState


Dan Pascu's avatar
Dan Pascu committed
204 205 206 207 208 209 210 211 212 213 214
class GraphTimeScale(int):
    min_value = 2
    max_value = 4

    def __new__(cls, value):
        value = int(value)
        if not (cls.min_value <= value <= cls.max_value):
            raise ValueError("expected an integer number between %d and %d, found %d" % (cls.min_value, cls.max_value, value))
        return value


215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
class File(object):
    def __init__(self, name, size, sender, hash, id, until=None, url=None, type=None):
        self.name = os.path.join(SIPSimpleSettings().file_transfer.directory.normalized, name)
        if type is not None and type.startswith('image/'):
            self.name = os.path.join(ApplicationData.get('transfer_images'), id, name)
        self.original_name = self.name
        self.size = size
        self.contact = sender
        self.hash = hash
        self.id = id
        self.until = until
        self.url = url
        self.type = type

    @property
    def encrypted(self):
        return self.original_name.endswith('.asc')

    @property
    def decrypted_filename(self):
        if self.name.endswith('.asc'):
            return self.name.rsplit('.', 1)[0]
        return self.name

    @property
    def already_exists(self):
        if os.path.exists(self.decrypted_filename):
            return True

        for number in range(self.decrypted_filename.count("_")):
            if os.path.exists(self.decrypted_filename.replace('_', ' ', number)):
                self.name = self.decrypted_filename.replace('_', ' ', number)
                return True

        return False