Daniel Brötzmann pushed to branch newcontrol at gajim / gajim
Commits:
-
c671abf2
by wurstsalat at 2022-07-15T18:32:47+02:00
5 changed files:
- gajim/common/const.py
- gajim/common/modules/muc.py
- gajim/common/structs.py
- gajim/gtk/chat_function_page.py
- gajim/gtk/chat_stack.py
Changes:
| ... | ... | @@ -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
|
| ... | ... | @@ -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)
|
| ... | ... | @@ -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:
|
| ... | ... | @@ -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 |
| ... | ... | @@ -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 |