Daniel Brötzmann pushed to branch newcontrol at gajim / gajim

Commits:

5 changed files:

Changes:

  • gajim/common/const.py
    ... ... @@ -246,6 +246,7 @@ class MUCJoinedState(Enum):
    246 246
         CREATING = 'creating'
    
    247 247
         CAPTCHA_REQUEST = 'captcha in progress'
    
    248 248
         CAPTCHA_FAILED = 'captcha failed'
    
    249
    +    PASSWORD_REQUEST = 'password required'
    
    249 250
     
    
    250 251
         def __str__(self):
    
    251 252
             return self.name
    
    ... ... @@ -274,6 +275,10 @@ def is_captcha_request(self):
    274 275
         def is_captcha_failed(self):
    
    275 276
             return self == MUCJoinedState.CAPTCHA_FAILED
    
    276 277
     
    
    278
    +    @property
    
    279
    +    def is_password_request(self):
    
    280
    +        return self == MUCJoinedState.PASSWORD_REQUEST
    
    281
    +
    
    277 282
     
    
    278 283
     class ClientState(IntEnum):
    
    279 284
         DISCONNECTING = 0
    

  • gajim/common/modules/muc.py
    ... ... @@ -815,8 +815,11 @@ def _on_captcha_challenge(self,
    815 815
                 return
    
    816 816
     
    
    817 817
             self._log.info('Captcha challenge received from %s', properties.jid)
    
    818
    +
    
    819
    +        assert properties.captcha is not None
    
    818 820
             store_bob_data(properties.captcha.bob_data)
    
    819 821
             muc_data.captcha_id = properties.id
    
    822
    +        muc_data.captcha_form = properties.captcha.form
    
    820 823
     
    
    821 824
             self._set_muc_state(properties.jid, MUCJoinedState.CAPTCHA_REQUEST)
    
    822 825
             self._remove_rejoin_timeout(properties.jid)
    

  • gajim/common/structs.py
    ... ... @@ -31,8 +31,10 @@
    31 31
     from nbxmpp.const import Chatstate
    
    32 32
     from nbxmpp.const import PresenceShow
    
    33 33
     from nbxmpp.const import Role
    
    34
    +from nbxmpp.modules.dataforms import SimpleDataForm
    
    34 35
     from nbxmpp.modules.security_labels import SecurityLabel
    
    35 36
     from nbxmpp.protocol import JID
    
    37
    +from nbxmpp.structs import MucSubject
    
    36 38
     from nbxmpp.structs import PresenceProperties
    
    37 39
     
    
    38 40
     from gajim.common import types
    
    ... ... @@ -56,7 +58,7 @@ class MUCData:
    56 58
         def __init__(self,
    
    57 59
                      room_jid: str,
    
    58 60
                      nick: str,
    
    59
    -                 password: str,
    
    61
    +                 password: Optional[str],
    
    60 62
                      config: Optional[dict[str, Any]] = None
    
    61 63
                      ) -> None:
    
    62 64
     
    
    ... ... @@ -66,8 +68,9 @@ def __init__(self,
    66 68
             self.password = password
    
    67 69
             self.state = MUCJoinedState.NOT_JOINED
    
    68 70
             # Message id of the captcha challenge
    
    69
    -        self.captcha_id = None
    
    70
    -        self.subject = None
    
    71
    +        self.captcha_id: Optional[str] = None
    
    72
    +        self.captcha_form: Optional[SimpleDataForm] = None
    
    73
    +        self.subject: Optional[MucSubject] = None
    
    71 74
     
    
    72 75
         @property
    
    73 76
         def jid(self) -> JID:
    

  • gajim/gtk/chat_function_page.py
    ... ... @@ -31,6 +31,7 @@
    31 31
     from gajim.common.modules.contacts import GroupchatContact
    
    32 32
     from gajim.common.types import ChatContactT
    
    33 33
     
    
    34
    +from .dataform import DataFormWidget
    
    34 35
     from .groupchat_inviter import GroupChatInviter
    
    35 36
     
    
    36 37
     
    
    ... ... @@ -138,10 +139,32 @@ def set_mode(self,
    138 139
                     'destructive-action')
    
    139 140
                 self._widget = InputWidget(self._contact, mode, data)
    
    140 141
     
    
    142
    +        elif mode == 'password-request':
    
    143
    +            self._confirm_button.set_label(_('Join'))
    
    144
    +            self._confirm_button.get_style_context().add_class(
    
    145
    +                'suggested-action')
    
    146
    +            self._widget = InputWidget(self._contact, mode)
    
    147
    +
    
    148
    +        elif mode == 'captcha-request':
    
    149
    +            self._confirm_button.set_label(_('Join'))
    
    150
    +            self._confirm_button.get_style_context().add_class(
    
    151
    +                'suggested-action')
    
    152
    +            muc_data = self._client.get_module('MUC').get_muc_data(
    
    153
    +                self._contact.jid)
    
    154
    +            form = muc_data.captcha_form
    
    155
    +            options = {'no-scrolling': True,
    
    156
    +                       'entry-activates-default': True}
    
    157
    +            self._widget = DataFormWidget(form, options=options)
    
    158
    +            self._widget.set_valign(Gtk.Align.START)
    
    159
    +            self._widget.show_all()
    
    160
    +            self._widget.connect('is-valid', self._on_ready)
    
    161
    +
    
    141 162
             assert self._widget is not None
    
    142 163
             self._content_box.add(self._widget)
    
    143 164
             if isinstance(self._widget, InputWidget):
    
    144 165
                 self._widget.focus()
    
    166
    +        elif isinstance(self._widget, DataFormWidget):
    
    167
    +            self._widget.focus_first_entry()
    
    145 168
     
    
    146 169
         def _on_ready(self,
    
    147 170
                       _widget: Gtk.Widget,
    
    ... ... @@ -181,9 +204,32 @@ def _on_confirm_clicked(self, _button: Gtk.Button) -> None:
    181 204
                     self._contact.jid,
    
    182 205
                     {self._data: {'affiliation': 'outcast', 'reason': reason}})
    
    183 206
     
    
    207
    +        elif self._mode == 'password-request':
    
    208
    +            assert isinstance(self._widget, InputWidget)
    
    209
    +            password = self._widget.get_text()
    
    210
    +            assert self._client is not None
    
    211
    +            assert self._contact is not None
    
    212
    +            self._client.get_module('MUC').set_password(
    
    213
    +                self._contact.jid, password)
    
    214
    +            self._client.get_module('MUC').join(self._contact.jid)
    
    215
    +
    
    216
    +        elif self._mode == 'captcha-request':
    
    217
    +            assert isinstance(self._widget, DataFormWidget)
    
    218
    +            form_node = self._widget.get_submit_form()
    
    219
    +            assert self._client is not None
    
    220
    +            assert self._contact is not None
    
    221
    +            self._client.get_module('MUC').send_captcha(
    
    222
    +                self._contact.jid, form_node)
    
    223
    +
    
    184 224
             self.emit('finish')
    
    185 225
     
    
    186 226
         def _on_cancel_clicked(self, _button: Gtk.Button) -> None:
    
    227
    +        if self._mode == 'captcha-request':
    
    228
    +            assert self._client is not None
    
    229
    +            assert self._contact is not None
    
    230
    +            self._client.get_module('MUC').cancel_captcha(
    
    231
    +                self._contact.jid)
    
    232
    +
    
    187 233
             self.emit('finish')
    
    188 234
     
    
    189 235
         def _invite(self, invited_jid: JID) -> None:
    
    ... ... @@ -236,12 +282,20 @@ def __init__(self,
    236 282
                 sub_label.set_text(_('Enter your new nickname'))
    
    237 283
                 assert isinstance(self._contact, GroupchatContact)
    
    238 284
                 self._entry.set_text(self._contact.nickname or '')
    
    285
    +
    
    239 286
             elif mode == 'kick':
    
    240 287
                 heading_label.set_text(_('Kick %s') % data)
    
    241 288
                 sub_label.set_text(_('Reason (optional)'))
    
    289
    +
    
    242 290
             elif mode == 'ban':
    
    243 291
                 heading_label.set_text(_('Ban %s') % data)
    
    244 292
                 sub_label.set_text(_('Reason (optional)'))
    
    293
    +
    
    294
    +        elif mode == 'password-request':
    
    295
    +            heading_label.set_text(_('Password Required'))
    
    296
    +            sub_label.set_text(_('Enter a password to join this chat'))
    
    297
    +            self._entry.set_input_purpose(Gtk.InputPurpose.PASSWORD)
    
    298
    +
    
    245 299
             self.connect('destroy', self._on_destroy)
    
    246 300
             self.show_all()
    
    247 301
     
    

  • gajim/gtk/chat_stack.py
    ... ... @@ -27,7 +27,8 @@
    27 27
     from nbxmpp.protocol import JID
    
    28 28
     from nbxmpp.structs import PresenceProperties
    
    29 29
     
    
    30
    -from gajim.common import app, helpers
    
    30
    +from gajim.common import app
    
    31
    +from gajim.common import helpers
    
    31 32
     from gajim.common.events import MucDiscoUpdate
    
    32 33
     from gajim.common.i18n import _
    
    33 34
     from gajim.common.const import CallType
    
    ... ... @@ -112,6 +113,19 @@ def show_chat(self, account: str, jid: JID) -> None:
    112 113
             else:
    
    113 114
                 self._update_chat_actions(self._current_contact)
    
    114 115
     
    
    116
    +        if isinstance(self._current_contact, GroupchatContact):
    
    117
    +            muc_data = client.get_module('MUC').get_muc_data(
    
    118
    +                str(self._current_contact.jid))
    
    119
    +            if muc_data is not None:
    
    120
    +                if muc_data.state.is_captcha_request:
    
    121
    +                    self._chat_function_page.set_mode(
    
    122
    +                        self._current_contact, 'captcha-request')
    
    123
    +                    return
    
    124
    +                if muc_data.state.is_password_request:
    
    125
    +                    self._chat_function_page.set_mode(
    
    126
    +                        self._current_contact, 'password-request')
    
    127
    +                    return
    
    128
    +
    
    115 129
             self.set_transition_type(Gtk.StackTransitionType.NONE)
    
    116 130
             self.set_visible_child_name('controls')
    
    117 131
     
    
    ... ... @@ -399,6 +413,7 @@ def _show_chat_function_page(self,
    399 413
             self.set_visible_child_name('function')
    
    400 414
     
    
    401 415
         def _on_function_finished(self, _function_page: ChatFunctionPage) -> None:
    
    416
    +        # TODO: close control on password/captcha cancel
    
    402 417
             self.set_visible_child_name('controls')
    
    403 418
             self.set_transition_type(Gtk.StackTransitionType.NONE)
    
    404 419