devices.py 4.81 KB
Newer Older
Philipp Hörist's avatar
Philipp Hörist committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of OMEMO Gajim Plugin.
#
# OMEMO Gajim Plugin is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# OMEMO Gajim Plugin is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OMEMO Gajim Plugin. If not, see <http://www.gnu.org/licenses/>.

from collections import defaultdict

from gajim.common import app

21
22
from omemo.backend.util import UNACKNOWLEDGED_COUNT

Philipp Hörist's avatar
Philipp Hörist committed
23
24
25
26
27
28
29
30
31

class DeviceManager:
    def __init__(self):
        self.__device_store = defaultdict(set)
        self.__muc_member_store = defaultdict(set)

        reg_id = self._storage.getLocalRegistrationId()
        if reg_id is None:
            raise ValueError('No own device found')
32
33

        self.__own_device = reg_id
Philipp Hörist's avatar
Philipp Hörist committed
34
        self.add_device(self._own_jid, self.__own_device)
Philipp Hörist's avatar
Philipp Hörist committed
35
        self._log.info('Our device id: %s', self.__own_device)
Philipp Hörist's avatar
Philipp Hörist committed
36
37

        for jid, device in self._storage.getActiveDeviceTuples():
Philipp Hörist's avatar
Philipp Hörist committed
38
            self._log.info('Load device from storage: %s - %s', jid, device)
Philipp Hörist's avatar
Philipp Hörist committed
39
40
41
            self.add_device(jid, device)

    def update_devicelist(self, jid, devicelist):
42
43
44
45
46
        for device in list(devicelist):
            if device == self.own_device:
                continue
            count = self._storage.getUnacknowledgedCount(jid, device)
            if count > UNACKNOWLEDGED_COUNT:
Philipp Hörist's avatar
Philipp Hörist committed
47
48
                self._log.warning('Ignore device because of %s unacknowledged'
                                  ' messages: %s %s', count, jid, device)
49
50
                devicelist.remove(device)

Philipp Hörist's avatar
Philipp Hörist committed
51
        self.__device_store[jid] = set(devicelist)
Philipp Hörist's avatar
Philipp Hörist committed
52
        self._log.info('Saved devices for %s', jid)
Philipp Hörist's avatar
Philipp Hörist committed
53
54
55
        self._storage.setActiveState(jid, devicelist)

    def add_muc_member(self, room_jid, jid):
Philipp Hörist's avatar
Philipp Hörist committed
56
        self._log.info('Saved MUC member %s %s', room_jid, jid)
Philipp Hörist's avatar
Philipp Hörist committed
57
58
59
        self.__muc_member_store[room_jid].add(jid)

    def remove_muc_member(self, room_jid, jid):
Philipp Hörist's avatar
Philipp Hörist committed
60
        self._log.info('Removed MUC member %s %s', room_jid, jid)
Philipp Hörist's avatar
Philipp Hörist committed
61
62
63
64
65
66
67
68
69
70
71
        self.__muc_member_store[room_jid].discard(jid)

    def get_muc_members(self, room_jid, without_self=True):
        members = set(self.__muc_member_store[room_jid])
        if without_self:
            members.discard(self._own_jid)
        return members

    def add_device(self, jid, device):
        self.__device_store[jid].add(device)

72
73
74
75
    def remove_device(self, jid, device):
        self.__device_store[jid].discard(device)
        self._storage.setInactive(jid, device)

Philipp Hörist's avatar
Philipp Hörist committed
76
77
78
    def get_devices(self, jid, without_self=False):
        devices = set(self.__device_store[jid])
        if without_self:
Philipp Hörist's avatar
Philipp Hörist committed
79
            devices.discard(self.own_device)
Philipp Hörist's avatar
Philipp Hörist committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
        return devices

    def get_devices_for_encryption(self, jid):
        devices_for_encryption = []

        if app.contacts.get_groupchat_contact(self._account, jid) is not None:
            devices_for_encryption = self._get_devices_for_muc_encryption(jid)
        else:
            devices_for_encryption = self._get_devices_for_encryption(jid)

        if not devices_for_encryption:
            raise NoDevicesFound

        devices_for_encryption += self._get_own_devices_for_encryption()
94
        return set(devices_for_encryption)
Philipp Hörist's avatar
Philipp Hörist committed
95
96
97
98
99
100
101
102
103

    def _get_devices_for_muc_encryption(self, jid):
        devices_for_encryption = []
        for jid_ in self.__muc_member_store[jid]:
            devices_for_encryption += self._get_devices_for_encryption(jid_)
        return devices_for_encryption

    def _get_own_devices_for_encryption(self):
        devices_for_encryption = []
104
        own_devices = self.get_devices(self._own_jid, without_self=True)
Philipp Hörist's avatar
Philipp Hörist committed
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
        for device in own_devices:
            if self._storage.isTrusted(self._own_jid, device):
                devices_for_encryption.append((self._own_jid, device))
        return devices_for_encryption

    def _get_devices_for_encryption(self, jid):
        devices_for_encryption = []
        devices = self.get_devices(jid)

        for device in devices:
            if self._storage.isTrusted(jid, device):
                devices_for_encryption.append((jid, device))
        return devices_for_encryption

    @property
    def own_device(self):
        return self.__own_device

    @property
    def devices_for_publish(self):
        devices = self.get_devices(self._own_jid)
        if self.own_device not in devices:
            devices.add(self.own_device)
        return devices

    @property
    def is_own_device_published(self):
        return self.own_device in self.get_devices(self._own_jid)


class NoDevicesFound(Exception):
    pass