Philipp Hörist pushed to branch master at gajim / gajim

Commits:

9 changed files:

Changes:

  • gajim/common/logger.py
    ... ... @@ -1234,6 +1234,53 @@ class Logger:
    1234 1234
             self._con.executescript(sql)
    
    1235 1235
             self._timeout_commit()
    
    1236 1236
     
    
    1237
    +    def deduplicate_muc_message(self, account, jid, resource,
    
    1238
    +                                timestamp, message_id):
    
    1239
    +        """
    
    1240
    +        Check if a message is already in the `logs` table
    
    1241
    +
    
    1242
    +        :param account:     The account
    
    1243
    +
    
    1244
    +        :param jid:         The muc jid as string
    
    1245
    +
    
    1246
    +        :param resource:    The resource
    
    1247
    +
    
    1248
    +        :param timestamp:   The timestamp in UTC epoch
    
    1249
    +
    
    1250
    +        :param message_id:  The message-id
    
    1251
    +        """
    
    1252
    +
    
    1253
    +        # Add 60 seconds around the timestamp
    
    1254
    +        start_time = timestamp - 60
    
    1255
    +        end_time = timestamp + 60
    
    1256
    +
    
    1257
    +        account_id = self.get_account_id(account)
    
    1258
    +        log.debug('Search for MUC duplicate')
    
    1259
    +        log.debug('start: %s, end: %s, jid: %s, resource: %s, message-id: %s',
    
    1260
    +                  start_time, end_time, jid, resource, message_id)
    
    1261
    +
    
    1262
    +        sql = '''
    
    1263
    +            SELECT * FROM logs
    
    1264
    +            NATURAL JOIN jids WHERE
    
    1265
    +            jid = ? AND
    
    1266
    +            contact_name = ? AND
    
    1267
    +            message_id = ? AND
    
    1268
    +            account_id = ? AND
    
    1269
    +            time BETWEEN ? AND ?
    
    1270
    +            '''
    
    1271
    +
    
    1272
    +        result = self._con.execute(sql, (jid,
    
    1273
    +                                         resource,
    
    1274
    +                                         message_id,
    
    1275
    +                                         account_id,
    
    1276
    +                                         start_time,
    
    1277
    +                                         end_time)).fetchone()
    
    1278
    +
    
    1279
    +        if result is not None:
    
    1280
    +            log.debug('Found duplicate')
    
    1281
    +            return True
    
    1282
    +        return False
    
    1283
    +
    
    1237 1284
         def search_for_duplicate(self, account, jid, timestamp, msg):
    
    1238 1285
             """
    
    1239 1286
             Check if a message is already in the `logs` table
    

  • gajim/common/modules/chatstates.py
    ... ... @@ -30,7 +30,6 @@ from gi.repository import GLib
    30 30
     from gajim.common import app
    
    31 31
     from gajim.common.nec import NetworkEvent
    
    32 32
     from gajim.common.const import Chatstate as State
    
    33
    -from gajim.common.modules.misc import parse_delay
    
    34 33
     from gajim.common.modules.base import BaseModule
    
    35 34
     from gajim.common.connection_handlers_events import MessageOutgoingEvent
    
    36 35
     from gajim.common.connection_handlers_events import GcMessageOutgoingEvent
    
    ... ... @@ -53,7 +52,7 @@ def ensure_enabled(func):
    53 52
     
    
    54 53
     
    
    55 54
     def parse_chatstate(stanza: nbxmpp.Message) -> Optional[str]:
    
    56
    -    if parse_delay(stanza) is not None:
    
    55
    +    if stanza.getTag('delay', namespace=nbxmpp.NS_DELAY2) is not None:
    
    57 56
             return None
    
    58 57
     
    
    59 58
         children = stanza.getChildren()
    

  • gajim/common/modules/date_and_time.py deleted
    1
    -# This file is part of Gajim.
    
    2
    -#
    
    3
    -# Gajim is free software; you can redistribute it and/or modify
    
    4
    -# it under the terms of the GNU General Public License as published
    
    5
    -# by the Free Software Foundation; version 3 only.
    
    6
    -#
    
    7
    -# Gajim is distributed in the hope that it will be useful,
    
    8
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    -# GNU General Public License for more details.
    
    11
    -#
    
    12
    -# You should have received a copy of the GNU General Public License
    
    13
    -# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14
    -
    
    15
    -# XEP-0082: XMPP Date and Time Profiles
    
    16
    -
    
    17
    -import re
    
    18
    -import time
    
    19
    -import logging
    
    20
    -from datetime import datetime
    
    21
    -from datetime import timedelta
    
    22
    -from datetime import timezone
    
    23
    -from datetime import tzinfo
    
    24
    -
    
    25
    -log = logging.getLogger('gajim.c.m.date_and_time')
    
    26
    -
    
    27
    -PATTERN_DATETIME = re.compile(
    
    28
    -    r'([0-9]{4}-[0-9]{2}-[0-9]{2})'
    
    29
    -    r'T'
    
    30
    -    r'([0-9]{2}:[0-9]{2}:[0-9]{2})'
    
    31
    -    r'(\.[0-9]{0,6})?'
    
    32
    -    r'(?:[0-9]+)?'
    
    33
    -    r'(?:(Z)|(?:([-+][0-9]{2}):([0-9]{2})))$'
    
    34
    -)
    
    35
    -
    
    36
    -PATTERN_DELAY = re.compile(
    
    37
    -    r'([0-9]{4}-[0-9]{2}-[0-9]{2})'
    
    38
    -    r'T'
    
    39
    -    r'([0-9]{2}:[0-9]{2}:[0-9]{2})'
    
    40
    -    r'(\.[0-9]{0,6})?'
    
    41
    -    r'(?:[0-9]+)?'
    
    42
    -    r'(?:(Z)|(?:([-+][0]{2}):([0]{2})))$'
    
    43
    -)
    
    44
    -
    
    45
    -
    
    46
    -ZERO = timedelta(0)
    
    47
    -HOUR = timedelta(hours=1)
    
    48
    -SECOND = timedelta(seconds=1)
    
    49
    -
    
    50
    -STDOFFSET = timedelta(seconds=-time.timezone)
    
    51
    -if time.daylight:
    
    52
    -    DSTOFFSET = timedelta(seconds=-time.altzone)
    
    53
    -else:
    
    54
    -    DSTOFFSET = STDOFFSET
    
    55
    -
    
    56
    -DSTDIFF = DSTOFFSET - STDOFFSET
    
    57
    -
    
    58
    -
    
    59
    -class LocalTimezone(tzinfo):
    
    60
    -    '''
    
    61
    -    A class capturing the platform's idea of local time.
    
    62
    -    May result in wrong values on historical times in
    
    63
    -    timezones where UTC offset and/or the DST rules had
    
    64
    -    changed in the past.
    
    65
    -    '''
    
    66
    -    def fromutc(self, dt):
    
    67
    -        assert dt.tzinfo is self
    
    68
    -        stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND
    
    69
    -        args = time.localtime(stamp)[:6]
    
    70
    -        dst_diff = DSTDIFF // SECOND
    
    71
    -        # Detect fold
    
    72
    -        fold = (args == time.localtime(stamp - dst_diff))
    
    73
    -        return datetime(*args, microsecond=dt.microsecond,
    
    74
    -                        tzinfo=self, fold=fold)
    
    75
    -
    
    76
    -    def utcoffset(self, dt):
    
    77
    -        if self._isdst(dt):
    
    78
    -            return DSTOFFSET
    
    79
    -        return STDOFFSET
    
    80
    -
    
    81
    -    def dst(self, dt):
    
    82
    -        if self._isdst(dt):
    
    83
    -            return DSTDIFF
    
    84
    -        return ZERO
    
    85
    -
    
    86
    -    def tzname(self, dt):
    
    87
    -        return 'local'
    
    88
    -
    
    89
    -    @staticmethod
    
    90
    -    def _isdst(dt):
    
    91
    -        tt = (dt.year, dt.month, dt.day,
    
    92
    -              dt.hour, dt.minute, dt.second,
    
    93
    -              dt.weekday(), 0, 0)
    
    94
    -        stamp = time.mktime(tt)
    
    95
    -        tt = time.localtime(stamp)
    
    96
    -        return tt.tm_isdst > 0
    
    97
    -
    
    98
    -
    
    99
    -def create_tzinfo(hours=0, minutes=0, tz_string=None):
    
    100
    -    if tz_string is None:
    
    101
    -        return timezone(timedelta(hours=hours, minutes=minutes))
    
    102
    -
    
    103
    -    if tz_string.lower() == 'z':
    
    104
    -        return timezone.utc
    
    105
    -
    
    106
    -    try:
    
    107
    -        hours, minutes = map(int, tz_string.split(':'))
    
    108
    -    except Exception:
    
    109
    -        log.warning('Wrong tz string: %s', tz_string)
    
    110
    -        return
    
    111
    -
    
    112
    -    if hours not in range(-24, 24):
    
    113
    -        log.warning('Wrong tz string: %s', tz_string)
    
    114
    -        return
    
    115
    -
    
    116
    -    if minutes not in range(0, 59):
    
    117
    -        log.warning('Wrong tz string: %s', tz_string)
    
    118
    -        return
    
    119
    -
    
    120
    -    if hours in (24, -24) and minutes != 0:
    
    121
    -        log.warning('Wrong tz string: %s', tz_string)
    
    122
    -        return
    
    123
    -    return timezone(timedelta(hours=hours, minutes=minutes))
    
    124
    -
    
    125
    -
    
    126
    -def parse_datetime(timestring, check_utc=False,
    
    127
    -                   convert='utc', epoch=False):
    
    128
    -    '''
    
    129
    -    Parse a XEP-0082 DateTime Profile String
    
    130
    -
    
    131
    -    :param timestring: a XEP-0082 DateTime profile formated string
    
    132
    -
    
    133
    -    :param check_utc:  if True, returns None if timestring is not
    
    134
    -                       a timestring expressing UTC
    
    135
    -
    
    136
    -    :param convert:    convert the given timestring to utc or local time
    
    137
    -
    
    138
    -    :param epoch:      if True, returns the time in epoch
    
    139
    -
    
    140
    -    Examples:
    
    141
    -    '2017-11-05T01:41:20Z'
    
    142
    -    '2017-11-05T01:41:20.123Z'
    
    143
    -    '2017-11-05T01:41:20.123+05:00'
    
    144
    -
    
    145
    -    return a datetime or epoch
    
    146
    -    '''
    
    147
    -    if convert not in (None, 'utc', 'local'):
    
    148
    -        raise TypeError('"%s" is not a valid value for convert')
    
    149
    -    if check_utc:
    
    150
    -        match = PATTERN_DELAY.match(timestring)
    
    151
    -    else:
    
    152
    -        match = PATTERN_DATETIME.match(timestring)
    
    153
    -
    
    154
    -    if match:
    
    155
    -        timestring = ''.join(match.groups(''))
    
    156
    -        strformat = '%Y-%m-%d%H:%M:%S%z'
    
    157
    -        if match.group(3):
    
    158
    -            # Fractional second addendum to Time
    
    159
    -            strformat = '%Y-%m-%d%H:%M:%S.%f%z'
    
    160
    -        if match.group(4):
    
    161
    -            # UTC string denoted by addition of the character 'Z'
    
    162
    -            timestring = timestring[:-1] + '+0000'
    
    163
    -        try:
    
    164
    -            date_time = datetime.strptime(timestring, strformat)
    
    165
    -        except ValueError:
    
    166
    -            pass
    
    167
    -        else:
    
    168
    -            if check_utc:
    
    169
    -                if convert != 'utc':
    
    170
    -                    raise ValueError(
    
    171
    -                        'check_utc can only be used with convert="utc"')
    
    172
    -                date_time.replace(tzinfo=timezone.utc)
    
    173
    -                if epoch:
    
    174
    -                    return date_time.timestamp()
    
    175
    -                return date_time
    
    176
    -
    
    177
    -            if convert == 'utc':
    
    178
    -                date_time = date_time.astimezone(timezone.utc)
    
    179
    -                if epoch:
    
    180
    -                    return date_time.timestamp()
    
    181
    -                return date_time
    
    182
    -
    
    183
    -            if epoch:
    
    184
    -                # epoch is always UTC, use convert='utc' or check_utc=True
    
    185
    -                raise ValueError(
    
    186
    -                    'epoch not available while converting to local')
    
    187
    -
    
    188
    -            if convert == 'local':
    
    189
    -                date_time = date_time.astimezone(LocalTimezone())
    
    190
    -                return date_time
    
    191
    -
    
    192
    -            # convert=None
    
    193
    -            return date_time
    
    194
    -    return None

  • gajim/common/modules/entity_time.py
    ... ... @@ -18,12 +18,12 @@ import time
    18 18
     
    
    19 19
     import nbxmpp
    
    20 20
     from nbxmpp.structs import StanzaHandler
    
    21
    +from nbxmpp.modules.date_and_time import parse_datetime
    
    22
    +from nbxmpp.modules.date_and_time import create_tzinfo
    
    21 23
     
    
    22 24
     from gajim.common import app
    
    23 25
     from gajim.common.nec import NetworkEvent
    
    24 26
     from gajim.common.modules.base import BaseModule
    
    25
    -from gajim.common.modules.date_and_time import parse_datetime
    
    26
    -from gajim.common.modules.date_and_time import create_tzinfo
    
    27 27
     
    
    28 28
     
    
    29 29
     class EntityTime(BaseModule):
    

  • gajim/common/modules/mam.py
    ... ... @@ -29,7 +29,6 @@ from gajim.common.const import SyncThreshold
    29 29
     from gajim.common.caps_cache import muc_caps_cache
    
    30 30
     from gajim.common.helpers import get_sync_threshold
    
    31 31
     from gajim.common.helpers import AdditionalDataDict
    
    32
    -from gajim.common.modules.misc import parse_delay
    
    33 32
     from gajim.common.modules.misc import parse_oob
    
    34 33
     from gajim.common.modules.misc import parse_correction
    
    35 34
     from gajim.common.modules.util import get_eme_message
    
    ... ... @@ -138,9 +137,15 @@ class MAM(BaseModule):
    138 137
                                    'message-id: %s', stanza_id, message_id)
    
    139 138
                     raise nbxmpp.NodeProcessed
    
    140 139
     
    
    140
    +        additional_data = AdditionalDataDict()
    
    141
    +        if properties.has_user_delay:
    
    142
    +            # Record it as a user timestamp
    
    143
    +            additional_data.set_value(
    
    144
    +                'gajim', 'user_timestamp', properties.user_timestamp)
    
    145
    +
    
    141 146
             event_attrs.update(
    
    142 147
                 {'conn': self._con,
    
    143
    -             'additional_data': AdditionalDataDict(),
    
    148
    +             'additional_data': additional_data,
    
    144 149
                  'encrypted': False,
    
    145 150
                  'timestamp': properties.mam.timestamp,
    
    146 151
                  'self_message': properties.is_self_message,
    
    ... ... @@ -205,12 +210,6 @@ class MAM(BaseModule):
    205 210
                 self._log.debug(event.message.getProperties())
    
    206 211
                 return
    
    207 212
     
    
    208
    -        user_timestamp = parse_delay(event.stanza)
    
    209
    -        if user_timestamp is not None:
    
    210
    -            # Record it as a user timestamp
    
    211
    -            event.additional_data.set_value(
    
    212
    -                'gajim', 'user_timestamp', user_timestamp)
    
    213
    -
    
    214 213
             event.correct_id = parse_correction(event.message)
    
    215 214
             parse_oob(event)
    
    216 215
     
    

  • gajim/common/modules/message.py
    ... ... @@ -31,7 +31,6 @@ from gajim.common.modules.base import BaseModule
    31 31
     from gajim.common.modules.util import get_eme_message
    
    32 32
     from gajim.common.modules.security_labels import parse_securitylabel
    
    33 33
     from gajim.common.modules.user_nickname import parse_nickname
    
    34
    -from gajim.common.modules.misc import parse_delay
    
    35 34
     from gajim.common.modules.misc import parse_correction
    
    36 35
     from gajim.common.modules.misc import parse_attention
    
    37 36
     from gajim.common.modules.misc import parse_form
    
    ... ... @@ -86,18 +85,20 @@ class Message(BaseModule):
    86 85
     
    
    87 86
             type_ = properties.type
    
    88 87
     
    
    89
    -        # Check for duplicates
    
    90 88
             stanza_id, message_id = self._get_unique_id(properties)
    
    91 89
     
    
    92
    -        # Check groupchat messages for duplicates,
    
    93
    -        # We do this because of MUC History messages
    
    94
    -        if (properties.type.is_groupchat or
    
    95
    -                properties.is_self_message or
    
    96
    -                properties.is_muc_pm):
    
    97
    -            if properties.type.is_groupchat:
    
    98
    -                archive_jid = stanza.getFrom().getStripped()
    
    99
    -            else:
    
    100
    -                archive_jid = self._con.get_own_jid().getStripped()
    
    90
    +        if properties.type.is_groupchat and properties.has_server_delay:
    
    91
    +            # Only for XEP-0045 MUC History
    
    92
    +            # Dont check for message text because the message could be encrypted
    
    93
    +            if app.logger.deduplicate_muc_message(self._account,
    
    94
    +                                                  properties.jid.getBare(),
    
    95
    +                                                  properties.jid.getResource(),
    
    96
    +                                                  properties.timestamp,
    
    97
    +                                                  properties.id):
    
    98
    +                raise nbxmpp.NodeProcessed
    
    99
    +
    
    100
    +        if (properties.is_self_message or properties.is_muc_pm):
    
    101
    +            archive_jid = self._con.get_own_jid().getStripped()
    
    101 102
                 if app.logger.find_stanza_id(self._account,
    
    102 103
                                              archive_jid,
    
    103 104
                                              stanza_id,
    
    ... ... @@ -144,13 +145,19 @@ class Message(BaseModule):
    144 145
     
    
    145 146
                 session.last_receive = time.time()
    
    146 147
     
    
    148
    +        additional_data = AdditionalDataDict()
    
    149
    +
    
    150
    +        if properties.has_user_delay:
    
    151
    +            additional_data.set_value(
    
    152
    +                'gajim', 'user_timestamp', properties.user_timestamp)
    
    153
    +
    
    147 154
             event_attr = {
    
    148 155
                 'conn': self._con,
    
    149 156
                 'stanza': stanza,
    
    150 157
                 'account': self._account,
    
    151 158
                 'id_': properties.id,
    
    152 159
                 'encrypted': False,
    
    153
    -            'additional_data': AdditionalDataDict(),
    
    160
    +            'additional_data': additional_data,
    
    154 161
                 'forwarded': forwarded,
    
    155 162
                 'sent': sent,
    
    156 163
                 'fjid': fjid,
    
    ... ... @@ -164,6 +171,8 @@ class Message(BaseModule):
    164 171
                 'thread_id': thread_id,
    
    165 172
                 'session': session,
    
    166 173
                 'self_message': properties.is_self_message,
    
    174
    +            'timestamp': properties.timestamp,
    
    175
    +            'delayed': properties.user_timestamp is not None,
    
    167 176
                 'muc_pm': properties.is_muc_pm,
    
    168 177
                 'gc_control': gc_control
    
    169 178
             }
    
    ... ... @@ -197,22 +206,6 @@ class Message(BaseModule):
    197 206
             subject = event.stanza.getSubject()
    
    198 207
             groupchat = event.mtype == 'groupchat'
    
    199 208
     
    
    200
    -        # Determine timestamps
    
    201
    -        if groupchat:
    
    202
    -            delay_jid = event.jid
    
    203
    -        else:
    
    204
    -            delay_jid = self._con.get_own_jid().getDomain()
    
    205
    -        timestamp = parse_delay(event.stanza, from_=delay_jid)
    
    206
    -        if timestamp is None:
    
    207
    -            timestamp = time.time()
    
    208
    -
    
    209
    -        user_timestamp = parse_delay(event.stanza,
    
    210
    -                                     not_from=[delay_jid])
    
    211
    -
    
    212
    -        if user_timestamp is not None:
    
    213
    -            event.additional_data.set_value(
    
    214
    -                'gajim', 'user_timestamp', user_timestamp)
    
    215
    -
    
    216 209
             event_attr = {
    
    217 210
                 'popup': False,
    
    218 211
                 'msg_log_id': None,
    
    ... ... @@ -223,8 +216,6 @@ class Message(BaseModule):
    223 216
                 'user_nick': '' if event.sent else parse_nickname(event.stanza),
    
    224 217
                 'form_node': parse_form(event.stanza),
    
    225 218
                 'xhtml': parse_xhtml(event.stanza),
    
    226
    -            'timestamp': timestamp,
    
    227
    -            'delayed': user_timestamp is not None,
    
    228 219
             }
    
    229 220
             parse_oob(event)
    
    230 221
     
    

  • gajim/common/modules/misc.py
    ... ... @@ -19,54 +19,10 @@ import logging
    19 19
     import nbxmpp
    
    20 20
     
    
    21 21
     from gajim.common import app
    
    22
    -from gajim.common.modules.date_and_time import parse_datetime
    
    23 22
     
    
    24 23
     log = logging.getLogger('gajim.c.m.misc')
    
    25 24
     
    
    26 25
     
    
    27
    -# XEP-0203: Delayed Delivery
    
    28
    -
    
    29
    -def parse_delay(stanza, epoch=True, convert='utc', from_=None, not_from=None):
    
    30
    -    '''
    
    31
    -    Returns the first valid delay timestamp that matches
    
    32
    -
    
    33
    -    :param epoch:      Returns the timestamp as epoch
    
    34
    -
    
    35
    -    :param convert:    Converts the timestamp to either utc or local
    
    36
    -
    
    37
    -    :param from_:      Matches only delays that have the according
    
    38
    -                       from attr set
    
    39
    -
    
    40
    -    :param not_from:   Matches only delays that have the according
    
    41
    -                       from attr not set
    
    42
    -    '''
    
    43
    -    delays = stanza.getTags('delay', namespace=nbxmpp.NS_DELAY2)
    
    44
    -
    
    45
    -    for delay in delays:
    
    46
    -        stamp = delay.getAttr('stamp')
    
    47
    -        if stamp is None:
    
    48
    -            log.warning('Invalid timestamp received: %s', stamp)
    
    49
    -            log.warning(stanza)
    
    50
    -            continue
    
    51
    -
    
    52
    -        delay_from = delay.getAttr('from')
    
    53
    -        if from_ is not None:
    
    54
    -            if delay_from != from_:
    
    55
    -                continue
    
    56
    -        if not_from is not None:
    
    57
    -            if delay_from in not_from:
    
    58
    -                continue
    
    59
    -
    
    60
    -        timestamp = parse_datetime(stamp, check_utc=True,
    
    61
    -                                   epoch=epoch, convert=convert)
    
    62
    -        if timestamp is None:
    
    63
    -            log.warning('Invalid timestamp received: %s', stamp)
    
    64
    -            log.warning(stanza)
    
    65
    -            continue
    
    66
    -
    
    67
    -        return timestamp
    
    68
    -
    
    69
    -
    
    70 26
     # XEP-0066: Out of Band Data
    
    71 27
     
    
    72 28
     def parse_oob(event):
    

  • test/no_gui/unit/test_date_time.py deleted
    1
    -import unittest
    
    2
    -from datetime import datetime
    
    3
    -from datetime import timezone
    
    4
    -from datetime import timedelta
    
    5
    -
    
    6
    -from gajim.common.modules.date_and_time import parse_datetime
    
    7
    -from gajim.common.modules.date_and_time import LocalTimezone
    
    8
    -from gajim.common.modules.date_and_time import create_tzinfo
    
    9
    -
    
    10
    -
    
    11
    -class TestDateTime(unittest.TestCase):
    
    12
    -
    
    13
    -    def test_convert_to_utc(self):
    
    14
    -
    
    15
    -        strings = {
    
    16
    -            # Valid UTC strings and fractions
    
    17
    -            '2017-11-05T01:41:20Z': 1509846080.0,
    
    18
    -            '2017-11-05T01:41:20.123Z': 1509846080.123,
    
    19
    -            '2017-11-05T01:41:20.123123123+00:00': 1509846080.123123,
    
    20
    -            '2017-11-05T01:41:20.123123123123123-00:00': 1509846080.123123,
    
    21
    -
    
    22
    -            # Invalid strings
    
    23
    -            '2017-11-05T01:41:20Z+05:00': None,
    
    24
    -            '2017-11-05T01:41:20+0000': None,
    
    25
    -            '2017-11-05T01:41:20-0000': None,
    
    26
    -
    
    27
    -            # Valid strings with offset
    
    28
    -            '2017-11-05T01:41:20-05:00': 1509864080.0,
    
    29
    -            '2017-11-05T01:41:20+05:00': 1509828080.0,
    
    30
    -        }
    
    31
    -
    
    32
    -        strings2 = {
    
    33
    -            # Valid strings with offset
    
    34
    -            '2017-11-05T01:41:20-05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=-5)),
    
    35
    -            '2017-11-05T01:41:20+05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=5)),
    
    36
    -        }
    
    37
    -
    
    38
    -        for time_string, expected_value in strings.items():
    
    39
    -            result = parse_datetime(time_string, convert='utc', epoch=True)
    
    40
    -            self.assertEqual(result, expected_value)
    
    41
    -
    
    42
    -        for time_string, expected_value in strings2.items():
    
    43
    -            result = parse_datetime(time_string, convert='utc')
    
    44
    -            self.assertEqual(result, expected_value.astimezone(timezone.utc))
    
    45
    -
    
    46
    -    def test_convert_to_local(self):
    
    47
    -
    
    48
    -        strings = {
    
    49
    -            # Valid UTC strings and fractions
    
    50
    -            '2017-11-05T01:41:20Z': datetime(2017, 11, 5, 1, 41, 20, 0, timezone.utc),
    
    51
    -            '2017-11-05T01:41:20.123Z': datetime(2017, 11, 5, 1, 41, 20, 123000, timezone.utc),
    
    52
    -            '2017-11-05T01:41:20.123123123+00:00': datetime(2017, 11, 5, 1, 41, 20, 123123, timezone.utc),
    
    53
    -            '2017-11-05T01:41:20.123123123123123-00:00': datetime(2017, 11, 5, 1, 41, 20, 123123, timezone.utc),
    
    54
    -
    
    55
    -            # Valid strings with offset
    
    56
    -            '2017-11-05T01:41:20-05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=-5)),
    
    57
    -            '2017-11-05T01:41:20+05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=5)),
    
    58
    -        }
    
    59
    -
    
    60
    -        for time_string, expected_value in strings.items():
    
    61
    -            result = parse_datetime(time_string, convert='local')
    
    62
    -            self.assertEqual(result, expected_value.astimezone(LocalTimezone()))
    
    63
    -
    
    64
    -    def test_no_convert(self):
    
    65
    -
    
    66
    -        strings = {
    
    67
    -            # Valid UTC strings and fractions
    
    68
    -            '2017-11-05T01:41:20Z': timedelta(0),
    
    69
    -            '2017-11-05T01:41:20.123Z': timedelta(0),
    
    70
    -            '2017-11-05T01:41:20.123123123+00:00': timedelta(0),
    
    71
    -            '2017-11-05T01:41:20.123123123123123-00:00': timedelta(0),
    
    72
    -
    
    73
    -            # Valid strings with offset
    
    74
    -            '2017-11-05T01:41:20-05:00': timedelta(hours=-5),
    
    75
    -            '2017-11-05T01:41:20+05:00': timedelta(hours=5),
    
    76
    -        }
    
    77
    -
    
    78
    -        for time_string, expected_value in strings.items():
    
    79
    -            result = parse_datetime(time_string, convert=None)
    
    80
    -            self.assertEqual(result.utcoffset(), expected_value)
    
    81
    -
    
    82
    -    def test_check_utc(self):
    
    83
    -
    
    84
    -        strings = {
    
    85
    -            # Valid UTC strings and fractions
    
    86
    -            '2017-11-05T01:41:20Z': 1509846080.0,
    
    87
    -            '2017-11-05T01:41:20.123Z': 1509846080.123,
    
    88
    -            '2017-11-05T01:41:20.123123123+00:00': 1509846080.123123,
    
    89
    -            '2017-11-05T01:41:20.123123123123123-00:00': 1509846080.123123,
    
    90
    -
    
    91
    -            # Valid strings with offset
    
    92
    -            '2017-11-05T01:41:20-05:00': None,
    
    93
    -            '2017-11-05T01:41:20+05:00': None,
    
    94
    -        }
    
    95
    -
    
    96
    -        for time_string, expected_value in strings.items():
    
    97
    -            result = parse_datetime(
    
    98
    -                time_string, check_utc=True, epoch=True)
    
    99
    -            self.assertEqual(result, expected_value)

  • test/no_gui/unit/test_helpers.py deleted
    1
    -import unittest
    
    2
    -
    
    3
    -import nbxmpp
    
    4
    -
    
    5
    -from gajim.common.modules.misc import parse_delay
    
    6
    -
    
    7
    -class TestHelpers(unittest.TestCase):
    
    8
    -
    
    9
    -    def test_parse_delay(self):
    
    10
    -
    
    11
    -        node = """
    
    12
    -        <message>
    
    13
    -            <delay xmlns='urn:xmpp:delay' from='capulet.com' stamp='2002-09-10T23:08:25Z' />
    
    14
    -            <delay xmlns='urn:xmpp:delay' from='romeo.com' stamp='2010-09-10T23:08:25Z' />
    
    15
    -            <delay xmlns='urn:xmpp:delay' stamp='2015-09-10T23:08:25Z' />
    
    16
    -        </message>
    
    17
    -        """
    
    18
    -        message = nbxmpp.Node(node=node)
    
    19
    -
    
    20
    -        timestamp = parse_delay(message)
    
    21
    -        self.assertEqual(timestamp, 1031699305.0)
    
    22
    -
    
    23
    -        timestamp = parse_delay(message, from_='capulet.com')
    
    24
    -        self.assertEqual(timestamp, 1031699305.0)
    
    25
    -
    
    26
    -        timestamp = parse_delay(message, from_='romeo.com')
    
    27
    -        self.assertEqual(timestamp, 1284160105.0)
    
    28
    -
    
    29
    -        timestamp = parse_delay(message, not_from=['romeo.com'])
    
    30
    -        self.assertEqual(timestamp, 1031699305.0)
    \ No newline at end of file