Daniel Brötzmann pushed to branch fix-event-storage at gajim / gajim

Commits:

4 changed files:

Changes:

  • gajim/common/preview_helpers.py
    ... ... @@ -154,7 +154,12 @@ def create_thumbnail(data: bytes,
    154 154
                          mime_type: str
    
    155 155
                          ) -> Optional[bytes]:
    
    156 156
     
    
    157
    -    thumbnail = create_thumbnail_with_pil(data, size)
    
    157
    +    try:
    
    158
    +        thumbnail = create_thumbnail_with_pil(data, size)
    
    159
    +    except (Image.DecompressionBombError, Image.DecompressionBombWarning):
    
    160
    +        # Don't try to process image further
    
    161
    +        return None
    
    162
    +
    
    158 163
         if thumbnail is not None:
    
    159 164
             return thumbnail
    
    160 165
         return create_thumbnail_with_pixbuf(data, size, mime_type)
    
    ... ... @@ -210,6 +215,10 @@ def create_thumbnail_with_pil(data: bytes, size: int) -> Optional[bytes]:
    210 215
         output_file = BytesIO()
    
    211 216
         try:
    
    212 217
             image = Image.open(input_file)
    
    218
    +    except (Image.DecompressionBombError,
    
    219
    +            Image.DecompressionBombWarning) as error:
    
    220
    +        log.warning('Decompression bomb detected: %s', error)
    
    221
    +        raise
    
    213 222
         except Exception as error:
    
    214 223
             log.warning('making pil thumbnail failed: %s', error)
    
    215 224
             log.warning('fallback to pixbuf')
    

  • gajim/common/storage/events.py
    ... ... @@ -13,12 +13,12 @@
    13 13
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14 14
     
    
    15 15
     from __future__ import annotations
    
    16
    -import dataclasses
    
    17 16
     
    
    18 17
     from typing import Any
    
    19
    -from typing import Iterator
    
    20 18
     from typing import NamedTuple
    
    21 19
     
    
    20
    +import dataclasses
    
    21
    +
    
    22 22
     import json
    
    23 23
     import sqlite3
    
    24 24
     import logging
    
    ... ... @@ -116,18 +116,26 @@ def store(self,
    116 116
         def load(self,
    
    117 117
                  contact: ChatContactT,
    
    118 118
                  before: bool,
    
    119
    -             timestamp: float
    
    120
    -             ) -> Iterator[EventRow]:
    
    119
    +             timestamp: float,
    
    120
    +             n_lines: int,
    
    121
    +             ) -> list[events.ApplicationEvent]:
    
    121 122
     
    
    122
    -        time_operator = '<' if before else '>'
    
    123
    +        if before:
    
    124
    +            time_order = 'AND timestamp < ? ORDER BY timestamp DESC'
    
    125
    +        else:
    
    126
    +            time_order = 'AND timestamp > ? ORDER BY timestamp ASC'
    
    123 127
     
    
    124 128
             insert_sql = '''
    
    125 129
                 SELECT account, jid, event, timestamp, data as "data [JSON]"
    
    126
    -            FROM events WHERE account=? AND jid=? AND timestamp %s ?
    
    127
    -            ''' % time_operator
    
    130
    +            FROM events WHERE account=? AND jid=? {time_order}
    
    131
    +            LIMIT ?'''.format(time_order=time_order)
    
    128 132
     
    
    133
    +        event_list: list[events.ApplicationEvent] = []
    
    129 134
             for row in self._con.execute(insert_sql, (contact.account,
    
    130 135
                                                       contact.jid,
    
    131
    -                                                  timestamp)).fetchall():
    
    136
    +                                                  timestamp,
    
    137
    +                                                  n_lines)).fetchall():
    
    132 138
                 event_class = EVENT_CLASSES[row.event]
    
    133
    -            yield event_class(**row.data, timestamp=row.timestamp)
    139
    +            event_list.append(event_class(**row.data,
    
    140
    +                                          timestamp=row.timestamp))
    
    141
    +        return event_list

  • gajim/gtk/control.py
    ... ... @@ -56,18 +56,18 @@
    56 56
     from gajim.gui.groupchat_roster import GroupchatRoster
    
    57 57
     from gajim.gui.groupchat_state import GroupchatState
    
    58 58
     
    
    59
    -log = logging.getLogger('gajim.gui.control')
    
    60
    -
    
    59
    +HistoryRowT = Union[events.ApplicationEvent, ConversationRow]
    
    61 60
     
    
    62 61
     REQUEST_LINES_COUNT = 20
    
    63 62
     
    
    63
    +log = logging.getLogger('gajim.gui.control')
    
    64
    +
    
    64 65
     
    
    65 66
     class ChatControl(EventHelper):
    
    66 67
         def __init__(self) -> None:
    
    67 68
             EventHelper.__init__(self)
    
    68 69
     
    
    69 70
             self.handlers: dict[int, Any] = {}
    
    70
    -
    
    71 71
             self._contact = None
    
    72 72
             self._client = None
    
    73 73
     
    
    ... ... @@ -600,13 +600,7 @@ def scroll_to_message(self, log_line_id: int, timestamp: float) -> None:
    600 600
                 log_line_id)
    
    601 601
             GLib.idle_add(self._scrolled_view.block_signals, False)
    
    602 602
     
    
    603
    -    def _request_history(self,
    
    604
    -                         _scrolled: Gtk.ScrolledWindow,
    
    605
    -                         before: bool
    
    606
    -                         ) -> None:
    
    607
    -
    
    608
    -        self._scrolled_view.block_signals(True)
    
    609
    -
    
    603
    +    def _request_messages(self, before: bool) -> list[ConversationRow]:
    
    610 604
             if before:
    
    611 605
                 row = self._scrolled_view.get_first_message_row()
    
    612 606
             else:
    
    ... ... @@ -617,62 +611,104 @@ def _request_history(self,
    617 611
             else:
    
    618 612
                 timestamp = row.db_timestamp
    
    619 613
     
    
    614
    +        return app.storage.archive.get_conversation_before_after(
    
    615
    +            self.contact.account,
    
    616
    +            self.contact.jid,
    
    617
    +            before,
    
    618
    +            timestamp,
    
    619
    +            REQUEST_LINES_COUNT)
    
    620
    +
    
    621
    +    def _request_events(self, before: bool) -> list[events.ApplicationEvent]:
    
    622
    +        if before:
    
    623
    +            row = self._scrolled_view.get_first_event_row()
    
    624
    +        else:
    
    625
    +            row = self._scrolled_view.get_last_event_row()
    
    626
    +
    
    627
    +        if row is None:
    
    628
    +            timestamp = time.time()
    
    629
    +        else:
    
    630
    +            timestamp = row.db_timestamp
    
    631
    +
    
    620 632
             assert self._contact is not None
    
    621
    -        for event in app.storage.events.load(self._contact, before, timestamp):
    
    622
    -            if isinstance(event, events.MUCUserJoined):
    
    623
    -                self._process_muc_user_joined(event)
    
    633
    +        return app.storage.events.load(self._contact,
    
    634
    +                                       before,
    
    635
    +                                       timestamp,
    
    636
    +                                       REQUEST_LINES_COUNT)
    
    624 637
     
    
    625
    -            elif isinstance(event, events.MUCUserLeft):
    
    626
    -                self._process_muc_user_left(event)
    
    638
    +    def _request_history(self,
    
    639
    +                         _scrolled: Gtk.ScrolledWindow,
    
    640
    +                         before: bool
    
    641
    +                         ) -> None:
    
    627 642
     
    
    628
    -            elif isinstance(event, events.MUCNicknameChanged):
    
    629
    -                self._process_muc_nickname_changed(event)
    
    643
    +        self._scrolled_view.block_signals(True)
    
    630 644
     
    
    631
    -            elif isinstance(event, events.MUCRoomKicked):
    
    632
    -                self._process_muc_room_kicked(event)
    
    645
    +        rows: list[HistoryRowT] = []
    
    633 646
     
    
    634
    -            elif isinstance(event, events.MUCUserAffiliationChanged):
    
    635
    -                self._process_muc_user_affiliation_changed(event)
    
    647
    +        messages = self._request_messages(before)
    
    648
    +        event_rows = self._request_events(before)
    
    649
    +        rows = self._sort_request_rows(messages, event_rows, before)
    
    636 650
     
    
    637
    -            elif isinstance(event, events.MUCUserRoleChanged):
    
    638
    -                self._process_muc_user_role_changed(event)
    
    651
    +        assert self._contact is not None
    
    652
    +        for row in rows:
    
    653
    +            if not isinstance(row, events.ApplicationEvent):
    
    654
    +                self.add_messages([row])
    
    639 655
     
    
    640
    -            elif isinstance(event, events.MUCUserStatusShowChanged):
    
    641
    -                self._process_muc_user_status_show_changed(event)
    
    656
    +            elif isinstance(row, events.MUCUserJoined):
    
    657
    +                self._process_muc_user_joined(row)
    
    642 658
     
    
    643
    -            elif isinstance(event, events.MUCRoomConfigChanged):
    
    644
    -                self._process_muc_room_config_changed(event)
    
    659
    +            elif isinstance(row, events.MUCUserLeft):
    
    660
    +                self._process_muc_user_left(row)
    
    645 661
     
    
    646
    -            elif isinstance(event, events.MUCRoomConfigFinished):
    
    647
    -                self._process_muc_room_config_finished(event)
    
    662
    +            elif isinstance(row, events.MUCNicknameChanged):
    
    663
    +                self._process_muc_nickname_changed(row)
    
    648 664
     
    
    649
    -            elif isinstance(event, events.MUCRoomPresenceError):
    
    650
    -                self._process_muc_room_presence_error(event)
    
    665
    +            elif isinstance(row, events.MUCRoomKicked):
    
    666
    +                self._process_muc_room_kicked(row)
    
    651 667
     
    
    652
    -            elif isinstance(event, events.MUCRoomDestroyed):
    
    653
    -                self._process_muc_room_destroyed(event)
    
    654
    -            else:
    
    655
    -                raise ValueError('Unknown event: %s' % event)
    
    668
    +            elif isinstance(row, events.MUCUserAffiliationChanged):
    
    669
    +                self._process_muc_user_affiliation_changed(row)
    
    656 670
     
    
    657
    -        messages = app.storage.archive.get_conversation_before_after(
    
    658
    -            self.contact.account,
    
    659
    -            self.contact.jid,
    
    660
    -            before,
    
    661
    -            timestamp,
    
    662
    -            REQUEST_LINES_COUNT)
    
    671
    +            elif isinstance(row, events.MUCUserRoleChanged):
    
    672
    +                self._process_muc_user_role_changed(row)
    
    663 673
     
    
    664
    -        if not messages:
    
    665
    -            self._scrolled_view.set_history_complete(before, True)
    
    666
    -            self._scrolled_view.block_signals(False)
    
    667
    -            return
    
    674
    +            elif isinstance(row, events.MUCUserStatusShowChanged):
    
    675
    +                self._process_muc_user_status_show_changed(row)
    
    676
    +
    
    677
    +            elif isinstance(row, events.MUCRoomConfigChanged):
    
    678
    +                self._process_muc_room_config_changed(row)
    
    679
    +
    
    680
    +            elif isinstance(row, events.MUCRoomConfigFinished):
    
    681
    +                self._process_muc_room_config_finished(row)
    
    668 682
     
    
    669
    -        self.add_messages(messages)
    
    683
    +            elif isinstance(row, events.MUCRoomPresenceError):
    
    684
    +                self._process_muc_room_presence_error(row)
    
    670 685
     
    
    671
    -        if len(messages) < REQUEST_LINES_COUNT:
    
    686
    +            elif isinstance(row, events.MUCRoomDestroyed):
    
    687
    +                self._process_muc_room_destroyed(row)
    
    688
    +
    
    689
    +            else:
    
    690
    +                raise ValueError('Unknown event: %s' % type(row))
    
    691
    +
    
    692
    +        if len(rows) < REQUEST_LINES_COUNT:
    
    672 693
                 self._scrolled_view.set_history_complete(before, True)
    
    673 694
     
    
    674 695
             self._scrolled_view.block_signals(False)
    
    675 696
     
    
    697
    +    @staticmethod
    
    698
    +    def _sort_request_rows(messages: list[ConversationRow],
    
    699
    +                           event_rows: list[events.ApplicationEvent],
    
    700
    +                           before: bool
    
    701
    +                           ) -> list[HistoryRowT]:
    
    702
    +
    
    703
    +        def sort_func(obj: HistoryRowT) -> float:
    
    704
    +            if isinstance(obj, events.ApplicationEvent):
    
    705
    +                return obj.timestamp  # pyright: ignore
    
    706
    +            return obj.time
    
    707
    +
    
    708
    +        rows = messages + event_rows
    
    709
    +        rows.sort(key=sort_func, reverse=before)
    
    710
    +        return rows
    
    711
    +
    
    676 712
         def add_messages(self, messages: list[ConversationRow]):
    
    677 713
             for msg in messages:
    
    678 714
                 if msg.kind in (KindConstant.FILE_TRANSFER_INCOMING,
    

  • gajim/gtk/conversation/view.py
    ... ... @@ -390,6 +390,20 @@ def get_last_message_row(self) -> Optional[MessageRow]:
    390 390
                     return row
    
    391 391
             return None
    
    392 392
     
    
    393
    +    def get_first_event_row(self) -> Optional[Union[InfoMessage, MUCJoinLeft]]:
    
    394
    +        for row in self._list_box.get_children():
    
    395
    +            if isinstance(row, (InfoMessage, MUCJoinLeft)):
    
    396
    +                return row
    
    397
    +        return None
    
    398
    +
    
    399
    +    def get_last_event_row(self) -> Optional[Union[InfoMessage, MUCJoinLeft]]:
    
    400
    +        children = self._list_box.get_children()
    
    401
    +        children.reverse()
    
    402
    +        for row in children:
    
    403
    +            if isinstance(row, (InfoMessage, MUCJoinLeft)):
    
    404
    +                return row
    
    405
    +        return None
    
    406
    +
    
    393 407
         @staticmethod
    
    394 408
         def _sort_func(row1: BaseRow, row2: BaseRow) -> int:
    
    395 409
             if row1.timestamp == row2.timestamp: