Remove GPG code from Gajim
Code moved into plugin
This commit is contained in:
parent
64bac1d910
commit
9d75c77982
30 changed files with 64 additions and 1009 deletions
|
@ -179,11 +179,6 @@ def on_merge_accounts(action, param):
|
||||||
app.interface.roster.setup_and_draw_roster()
|
app.interface.roster.setup_and_draw_roster()
|
||||||
|
|
||||||
|
|
||||||
def on_use_pgp_agent(action, param):
|
|
||||||
action.set_state(param)
|
|
||||||
app.config.set('use_gpg_agent', param.get_boolean())
|
|
||||||
|
|
||||||
|
|
||||||
def on_add_account(action, param):
|
def on_add_account(action, param):
|
||||||
if 'account_creation_wizard' in app.interface.instances:
|
if 'account_creation_wizard' in app.interface.instances:
|
||||||
app.interface.instances['account_creation_wizard'].window.present()
|
app.interface.instances['account_creation_wizard'].window.present()
|
||||||
|
|
|
@ -388,12 +388,6 @@ class GajimApplication(Gtk.Application):
|
||||||
act.connect('change-state', app_actions.on_merge_accounts)
|
act.connect('change-state', app_actions.on_merge_accounts)
|
||||||
self.add_action(act)
|
self.add_action(act)
|
||||||
|
|
||||||
act = Gio.SimpleAction.new_stateful(
|
|
||||||
'agent', None,
|
|
||||||
GLib.Variant.new_boolean(app.config.get('use_gpg_agent')))
|
|
||||||
act.connect('change-state', app_actions.on_use_pgp_agent)
|
|
||||||
self.add_action(act)
|
|
||||||
|
|
||||||
# General Actions
|
# General Actions
|
||||||
|
|
||||||
general_actions = [
|
general_actions = [
|
||||||
|
|
|
@ -399,12 +399,8 @@ class ChatControl(ChatControlBase):
|
||||||
'Show a list of formattings'))
|
'Show a list of formattings'))
|
||||||
else:
|
else:
|
||||||
self._formattings_button.set_sensitive(False)
|
self._formattings_button.set_sensitive(False)
|
||||||
if self.contact.supports(NS_XHTML_IM):
|
self._formattings_button.set_tooltip_text(
|
||||||
self._formattings_button.set_tooltip_text(_('Formatting is not '
|
_('This contact does not support HTML'))
|
||||||
'available so long as GPG is active'))
|
|
||||||
else:
|
|
||||||
self._formattings_button.set_tooltip_text(_('This contact does '
|
|
||||||
'not support HTML'))
|
|
||||||
|
|
||||||
# Jingle detection
|
# Jingle detection
|
||||||
if self.contact.supports(NS_JINGLE_ICE_UDP) and \
|
if self.contact.supports(NS_JINGLE_ICE_UDP) and \
|
||||||
|
@ -922,7 +918,7 @@ class ChatControl(ChatControlBase):
|
||||||
correct_id=obj.correct_id,
|
correct_id=obj.correct_id,
|
||||||
additional_data=obj.additional_data)
|
additional_data=obj.additional_data)
|
||||||
|
|
||||||
def send_message(self, message, keyID='', xhtml=None,
|
def send_message(self, message, xhtml=None,
|
||||||
process_commands=True, attention=False):
|
process_commands=True, attention=False):
|
||||||
"""
|
"""
|
||||||
Send a message to contact
|
Send a message to contact
|
||||||
|
@ -939,12 +935,8 @@ class ChatControl(ChatControlBase):
|
||||||
if message in ('', None, '\n'):
|
if message in ('', None, '\n'):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
contact = self.contact
|
|
||||||
keyID = contact.keyID
|
|
||||||
|
|
||||||
ChatControlBase.send_message(self,
|
ChatControlBase.send_message(self,
|
||||||
message,
|
message,
|
||||||
keyID,
|
|
||||||
type_='chat',
|
type_='chat',
|
||||||
xhtml=xhtml,
|
xhtml=xhtml,
|
||||||
process_commands=process_commands,
|
process_commands=process_commands,
|
||||||
|
@ -1426,9 +1418,6 @@ class ChatControl(ChatControlBase):
|
||||||
self.update_actions()
|
self.update_actions()
|
||||||
|
|
||||||
def update_status_display(self, name, uf_show, status):
|
def update_status_display(self, name, uf_show, status):
|
||||||
"""
|
|
||||||
Print the contact's status and update the status/GPG image
|
|
||||||
"""
|
|
||||||
self.update_ui()
|
self.update_ui()
|
||||||
self.parent_win.redraw_tab(self)
|
self.parent_win.redraw_tab(self)
|
||||||
|
|
||||||
|
|
|
@ -773,7 +773,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
label = labels[lname]
|
label = labels[lname]
|
||||||
return label
|
return label
|
||||||
|
|
||||||
def send_message(self, message, keyID='', type_='chat',
|
def send_message(self, message, type_='chat',
|
||||||
resource=None, xhtml=None, process_commands=True, attention=False):
|
resource=None, xhtml=None, process_commands=True, attention=False):
|
||||||
"""
|
"""
|
||||||
Send the given message to the active tab. Doesn't return None if error
|
Send the given message to the active tab. Doesn't return None if error
|
||||||
|
@ -797,7 +797,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
|
||||||
|
|
||||||
app.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
app.nec.push_outgoing_event(MessageOutgoingEvent(None,
|
||||||
account=self.account, jid=self.contact.jid, message=message,
|
account=self.account, jid=self.contact.jid, message=message,
|
||||||
keyID=keyID, type_=type_, chatstate=chatstate,
|
type_=type_, chatstate=chatstate,
|
||||||
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
|
resource=resource, user_nick=self.user_nick, xhtml=xhtml,
|
||||||
label=label, control=self, attention=attention, correct_id=correct_id,
|
label=label, control=self, attention=attention, correct_id=correct_id,
|
||||||
automatic_message=False, encryption=self.encryption))
|
automatic_message=False, encryption=self.encryption))
|
||||||
|
|
|
@ -35,7 +35,6 @@ import sys
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from distutils.version import LooseVersion as V
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
@ -191,8 +190,6 @@ caps_hash = {} # type: Dict[str, List[str]]
|
||||||
_dependencies = {
|
_dependencies = {
|
||||||
'AVAHI': False,
|
'AVAHI': False,
|
||||||
'PYBONJOUR': False,
|
'PYBONJOUR': False,
|
||||||
'PYGPG': False,
|
|
||||||
'GPG_BINARY': False,
|
|
||||||
'FARSTREAM': False,
|
'FARSTREAM': False,
|
||||||
'GEOCLUE': False,
|
'GEOCLUE': False,
|
||||||
'UPNP': False,
|
'UPNP': False,
|
||||||
|
@ -203,9 +200,6 @@ _dependencies = {
|
||||||
|
|
||||||
|
|
||||||
def is_installed(dependency):
|
def is_installed(dependency):
|
||||||
if dependency == 'GPG':
|
|
||||||
# Alias for checking python-gnupg and the GPG binary
|
|
||||||
return _dependencies['PYGPG'] and _dependencies['GPG_BINARY']
|
|
||||||
if dependency == 'ZEROCONF':
|
if dependency == 'ZEROCONF':
|
||||||
# Alias for checking zeroconf libs
|
# Alias for checking zeroconf libs
|
||||||
return _dependencies['AVAHI'] or _dependencies['PYBONJOUR']
|
return _dependencies['AVAHI'] or _dependencies['PYBONJOUR']
|
||||||
|
@ -246,40 +240,6 @@ def detect_dependencies():
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# python-gnupg
|
|
||||||
try:
|
|
||||||
import gnupg
|
|
||||||
# We need https://pypi.python.org/pypi/python-gnupg
|
|
||||||
# but https://pypi.python.org/pypi/gnupg shares the same package name.
|
|
||||||
# It cannot be used as a drop-in replacement.
|
|
||||||
# We test with a version check if python-gnupg is installed as it is
|
|
||||||
# on a much lower version number than gnupg
|
|
||||||
# Also we need at least python-gnupg 0.3.8
|
|
||||||
v_gnupg = gnupg.__version__
|
|
||||||
if V(v_gnupg) < V('0.3.8') or V(v_gnupg) > V('1.0.0'):
|
|
||||||
log('gajim').info('Gajim needs python-gnupg >= 0.3.8')
|
|
||||||
raise ImportError
|
|
||||||
_dependencies['PYGPG'] = True
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# GPG BINARY
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
def test_gpg(binary='gpg'):
|
|
||||||
if os.name == 'nt':
|
|
||||||
gpg_cmd = binary + ' -h >nul 2>&1'
|
|
||||||
else:
|
|
||||||
gpg_cmd = binary + ' -h >/dev/null 2>&1'
|
|
||||||
if subprocess.call(gpg_cmd, shell=True):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
if test_gpg(binary='gpg2'):
|
|
||||||
_dependencies['GPG_BINARY'] = 'gpg2'
|
|
||||||
elif test_gpg(binary='gpg'):
|
|
||||||
_dependencies['GPG_BINARY'] = 'gpg'
|
|
||||||
|
|
||||||
# FARSTREAM
|
# FARSTREAM
|
||||||
try:
|
try:
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
@ -354,9 +314,6 @@ def detect_dependencies():
|
||||||
|
|
||||||
log('gajim').info('Used language: %s', LANG)
|
log('gajim').info('Used language: %s', LANG)
|
||||||
|
|
||||||
def get_gpg_binary():
|
|
||||||
return _dependencies['GPG_BINARY']
|
|
||||||
|
|
||||||
def get_an_id():
|
def get_an_id():
|
||||||
return str(uuid.uuid4())
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,6 @@ class Config:
|
||||||
'time_stamp': [opt_str, '[%X] ', _('This option let you customize timestamp that is printed in conversation. For example "[%H:%M] " will show "[hour:minute] ". See python doc on strftime for full documentation: http://docs.python.org/lib/module-time.html')],
|
'time_stamp': [opt_str, '[%X] ', _('This option let you customize timestamp that is printed in conversation. For example "[%H:%M] " will show "[hour:minute] ". See python doc on strftime for full documentation: http://docs.python.org/lib/module-time.html')],
|
||||||
'before_nickname': [opt_str, '', _('Characters that are printed before the nickname in conversations')],
|
'before_nickname': [opt_str, '', _('Characters that are printed before the nickname in conversations')],
|
||||||
'after_nickname': [opt_str, ':', _('Characters that are printed after the nickname in conversations')],
|
'after_nickname': [opt_str, ':', _('Characters that are printed after the nickname in conversations')],
|
||||||
'use_gpg_agent': [opt_bool, False],
|
|
||||||
'change_roster_title': [opt_bool, True, _('Add * and [n] in roster title?')],
|
'change_roster_title': [opt_bool, True, _('Add * and [n] in roster title?')],
|
||||||
'restore_lines': [opt_int, 10, _('How many history messages should be restored when a chat tab/window is reopened?')],
|
'restore_lines': [opt_int, 10, _('How many history messages should be restored when a chat tab/window is reopened?')],
|
||||||
'restore_timeout': [opt_int, -1, _('How far back in time (minutes) history is restored. -1 means no limit.')],
|
'restore_timeout': [opt_int, -1, _('How far back in time (minutes) history is restored. -1 means no limit.')],
|
||||||
|
@ -285,7 +284,6 @@ class Config:
|
||||||
'positive_184_ack': [opt_bool, False, _('If enabled, Gajim will show an icon to show that sent message has been received by your contact')],
|
'positive_184_ack': [opt_bool, False, _('If enabled, Gajim will show an icon to show that sent message has been received by your contact')],
|
||||||
'show_avatar_in_tabs': [opt_bool, False, _('Show a mini avatar in chat window tabs and in window icon')],
|
'show_avatar_in_tabs': [opt_bool, False, _('Show a mini avatar in chat window tabs and in window icon')],
|
||||||
'use_keyring': [opt_bool, True, _('If true, Gajim will use the Systems Keyring to store account passwords.')],
|
'use_keyring': [opt_bool, True, _('If true, Gajim will use the Systems Keyring to store account passwords.')],
|
||||||
'pgp_encoding': [opt_str, '', _('Sets the encoding used by python-gnupg'), True],
|
|
||||||
'remote_commands': [opt_bool, False, _('If true, Gajim will execute XEP-0146 Commands.')],
|
'remote_commands': [opt_bool, False, _('If true, Gajim will execute XEP-0146 Commands.')],
|
||||||
'dark_theme': [opt_int, 2, _('2: System, 1: Enabled, 0: Disabled')],
|
'dark_theme': [opt_int, 2, _('2: System, 1: Enabled, 0: Disabled')],
|
||||||
'threshold_options': [opt_str, '1, 2, 4, 10, 0', _('Options in days which can be chosen in the sync threshold menu'), True],
|
'threshold_options': [opt_str, '1, 2, 4, 10, 0', _('Options in days which can be chosen in the sync threshold menu'), True],
|
||||||
|
@ -325,7 +323,6 @@ class Config:
|
||||||
'active': [opt_bool, True, _('If False, this account will be disabled and will not appear in roster window.'), True],
|
'active': [opt_bool, True, _('If False, this account will be disabled and will not appear in roster window.'), True],
|
||||||
'proxy': [opt_str, '', '', True],
|
'proxy': [opt_str, '', '', True],
|
||||||
'keyid': [opt_str, '', '', True],
|
'keyid': [opt_str, '', '', True],
|
||||||
'gpg_sign_presence': [opt_bool, True, _('If disabled, don\'t sign presences with GPG key, even if GPG is configured.')],
|
|
||||||
'keyname': [opt_str, '', '', True],
|
'keyname': [opt_str, '', '', True],
|
||||||
'allow_plaintext_connection': [opt_bool, False, _('Allow plaintext connections')],
|
'allow_plaintext_connection': [opt_bool, False, _('Allow plaintext connections')],
|
||||||
'tls_version': [opt_str, '1.2', ''],
|
'tls_version': [opt_str, '1.2', ''],
|
||||||
|
|
|
@ -55,7 +55,6 @@ from nbxmpp.const import Event
|
||||||
from gajim import common
|
from gajim import common
|
||||||
from gajim.common import helpers
|
from gajim.common import helpers
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
from gajim.common import gpg
|
|
||||||
from gajim.common import passwords
|
from gajim.common import passwords
|
||||||
from gajim.common import idle
|
from gajim.common import idle
|
||||||
from gajim.common import modules
|
from gajim.common import modules
|
||||||
|
@ -66,8 +65,6 @@ from gajim.common.nec import NetworkEvent
|
||||||
from gajim.common.contacts import GC_Contact
|
from gajim.common.contacts import GC_Contact
|
||||||
from gajim.common.connection_handlers import ConnectionHandlers
|
from gajim.common.connection_handlers import ConnectionHandlers
|
||||||
from gajim.common.connection_handlers_events import OurShowEvent
|
from gajim.common.connection_handlers_events import OurShowEvent
|
||||||
from gajim.common.connection_handlers_events import BadGPGPassphraseEvent
|
|
||||||
from gajim.common.connection_handlers_events import GPGPasswordRequiredEvent
|
|
||||||
from gajim.common.connection_handlers_events import InformationEvent
|
from gajim.common.connection_handlers_events import InformationEvent
|
||||||
from gajim.common.connection_handlers_events import StanzaMessageOutgoingEvent
|
from gajim.common.connection_handlers_events import StanzaMessageOutgoingEvent
|
||||||
from gajim.common.connection_handlers_events import GcStanzaMessageOutgoingEvent
|
from gajim.common.connection_handlers_events import GcStanzaMessageOutgoingEvent
|
||||||
|
@ -105,11 +102,6 @@ class CommonConnection:
|
||||||
self.is_zeroconf = False
|
self.is_zeroconf = False
|
||||||
self.password = None
|
self.password = None
|
||||||
self.server_resource = self._compute_resource()
|
self.server_resource = self._compute_resource()
|
||||||
self.gpg = None
|
|
||||||
self.USE_GPG = False
|
|
||||||
if app.is_installed('GPG'):
|
|
||||||
self.USE_GPG = True
|
|
||||||
self.gpg = gpg.GnuPG()
|
|
||||||
self.status = ''
|
self.status = ''
|
||||||
self.old_show = ''
|
self.old_show = ''
|
||||||
self.priority = app.get_priority(name, 'offline')
|
self.priority = app.get_priority(name, 'offline')
|
||||||
|
@ -187,43 +179,6 @@ class CommonConnection:
|
||||||
if kill_core and app.account_is_connected(self.name):
|
if kill_core and app.account_is_connected(self.name):
|
||||||
self.disconnect(reconnect=False)
|
self.disconnect(reconnect=False)
|
||||||
|
|
||||||
def test_gpg_passphrase(self, password):
|
|
||||||
"""
|
|
||||||
Returns 'ok', 'bad_pass' or 'expired'
|
|
||||||
"""
|
|
||||||
if not self.gpg:
|
|
||||||
return False
|
|
||||||
self.gpg.passphrase = password
|
|
||||||
keyID = app.config.get_per('accounts', self.name, 'keyid')
|
|
||||||
signed = self.gpg.sign('test', keyID)
|
|
||||||
self.gpg.password = None
|
|
||||||
if signed == 'KEYEXPIRED':
|
|
||||||
return 'expired'
|
|
||||||
if signed == 'BAD_PASSPHRASE':
|
|
||||||
return 'bad_pass'
|
|
||||||
return 'ok'
|
|
||||||
|
|
||||||
def get_signed_msg(self, msg, callback=None):
|
|
||||||
"""
|
|
||||||
Returns the signed message if possible or an empty string if gpg is not
|
|
||||||
used or None if waiting for passphrase
|
|
||||||
|
|
||||||
callback is the function to call when user give the passphrase
|
|
||||||
"""
|
|
||||||
signed = ''
|
|
||||||
keyID = app.config.get_per('accounts', self.name, 'keyid')
|
|
||||||
if keyID and self.USE_GPG:
|
|
||||||
if self.gpg.passphrase is None and not self.gpg.use_agent:
|
|
||||||
# We didn't set a passphrase
|
|
||||||
return None
|
|
||||||
signed = self.gpg.sign(msg, keyID)
|
|
||||||
if signed == 'BAD_PASSPHRASE':
|
|
||||||
self.USE_GPG = False
|
|
||||||
signed = ''
|
|
||||||
app.nec.push_incoming_event(BadGPGPassphraseEvent(None,
|
|
||||||
conn=self))
|
|
||||||
return signed
|
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
return app.SHOW_LIST[self.connected]
|
return app.SHOW_LIST[self.connected]
|
||||||
|
|
||||||
|
@ -432,25 +387,6 @@ class CommonConnection:
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def gpg_passphrase(self, passphrase):
|
|
||||||
if self.gpg:
|
|
||||||
if self.gpg.use_agent:
|
|
||||||
self.gpg.passphrase = None
|
|
||||||
else:
|
|
||||||
self.gpg.passphrase = passphrase
|
|
||||||
|
|
||||||
def ask_gpg_keys(self, keyID=None):
|
|
||||||
if self.gpg:
|
|
||||||
if keyID:
|
|
||||||
return self.gpg.get_key(keyID)
|
|
||||||
return self.gpg.get_keys()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def ask_gpg_secrete_keys(self):
|
|
||||||
if self.gpg:
|
|
||||||
return self.gpg.get_secret_keys()
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _event_dispatcher(self, realm, event, data):
|
def _event_dispatcher(self, realm, event, data):
|
||||||
if realm == '':
|
if realm == '':
|
||||||
if event == 'STANZA RECEIVED':
|
if event == 'STANZA RECEIVED':
|
||||||
|
@ -467,9 +403,7 @@ class CommonConnection:
|
||||||
def change_status(self, show, msg, auto=False):
|
def change_status(self, show, msg, auto=False):
|
||||||
if not msg:
|
if not msg:
|
||||||
msg = ''
|
msg = ''
|
||||||
sign_msg = False
|
|
||||||
if not auto and not show == 'offline':
|
|
||||||
sign_msg = True
|
|
||||||
if show != 'invisible':
|
if show != 'invisible':
|
||||||
# We save it only when privacy list is accepted
|
# We save it only when privacy list is accepted
|
||||||
self.status = msg
|
self.status = msg
|
||||||
|
@ -478,10 +412,7 @@ class CommonConnection:
|
||||||
# recconect before we auth to server
|
# recconect before we auth to server
|
||||||
self.old_show = show
|
self.old_show = show
|
||||||
self.server_resource = self._compute_resource()
|
self.server_resource = self._compute_resource()
|
||||||
if app.is_installed('GPG'):
|
self.connect_and_init(show, msg)
|
||||||
self.USE_GPG = True
|
|
||||||
self.gpg = gpg.GnuPG()
|
|
||||||
self.connect_and_init(show, msg, sign_msg)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if show == 'offline':
|
if show == 'offline':
|
||||||
|
@ -615,7 +546,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
app.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
app.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
||||||
show='connecting'))
|
show='connecting'))
|
||||||
self.retrycount += 1
|
self.retrycount += 1
|
||||||
self.connect_and_init(self.old_show, self.status, self.USE_GPG)
|
self.connect_and_init(self.old_show, self.status)
|
||||||
else:
|
else:
|
||||||
log.info('Reconnect successfull')
|
log.info('Reconnect successfull')
|
||||||
# reconnect succeeded
|
# reconnect succeeded
|
||||||
|
@ -784,9 +715,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
app.nec.push_incoming_event(NetworkEvent(
|
app.nec.push_incoming_event(NetworkEvent(
|
||||||
'account-not-created', conn=self, reason=reason))
|
'account-not-created', conn=self, reason=reason))
|
||||||
return
|
return
|
||||||
if app.is_installed('GPG'):
|
|
||||||
self.USE_GPG = True
|
|
||||||
self.gpg = gpg.GnuPG()
|
|
||||||
app.nec.push_incoming_event(
|
app.nec.push_incoming_event(
|
||||||
NetworkEvent('account-created',
|
NetworkEvent('account-created',
|
||||||
conn=self,
|
conn=self,
|
||||||
|
@ -1439,7 +1368,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
self.connection.send(' ')
|
self.connection.send(' ')
|
||||||
|
|
||||||
def send_invisible_presence(self, msg, signed, initial=False):
|
def send_invisible_presence(self, msg, initial=False):
|
||||||
if not app.account_is_connected(self.name):
|
if not app.account_is_connected(self.name):
|
||||||
return
|
return
|
||||||
if not self.get_module('PrivacyLists').supported:
|
if not self.get_module('PrivacyLists').supported:
|
||||||
|
@ -1461,7 +1390,6 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.get_module('PrivacyLists').set_invisible_rule(
|
self.get_module('PrivacyLists').set_invisible_rule(
|
||||||
callback=self._continue_invisible,
|
callback=self._continue_invisible,
|
||||||
msg=msg,
|
msg=msg,
|
||||||
signed=signed,
|
|
||||||
initial=initial)
|
initial=initial)
|
||||||
|
|
||||||
def _continue_invisible(self, con, iq_obj, msg, signed, initial):
|
def _continue_invisible(self, con, iq_obj, msg, signed, initial):
|
||||||
|
@ -1475,8 +1403,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
|
|
||||||
self.get_module('Presence').send_presence(
|
self.get_module('Presence').send_presence(
|
||||||
priority=priority,
|
priority=priority,
|
||||||
status=msg,
|
status=msg)
|
||||||
sign=signed)
|
|
||||||
|
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
app.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
app.nec.push_incoming_event(OurShowEvent(None, conn=self,
|
||||||
|
@ -1501,19 +1428,14 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
# Inform GUI we just signed in
|
# Inform GUI we just signed in
|
||||||
app.nec.push_incoming_event(NetworkEvent('signed-in', conn=self))
|
app.nec.push_incoming_event(NetworkEvent('signed-in', conn=self))
|
||||||
|
|
||||||
def get_signed_presence(self, msg, callback=None):
|
|
||||||
if app.config.get_per('accounts', self.name, 'gpg_sign_presence'):
|
|
||||||
return self.get_signed_msg(msg, callback)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def connect_and_auth(self):
|
def connect_and_auth(self):
|
||||||
self.on_connect_success = self._connect_success
|
self.on_connect_success = self._connect_success
|
||||||
self.on_connect_failure = self._connect_failure
|
self.on_connect_failure = self._connect_failure
|
||||||
self.connect()
|
self.connect()
|
||||||
|
|
||||||
def connect_and_init(self, show, msg, sign_msg):
|
def connect_and_init(self, show, msg):
|
||||||
self.disable_reconnect_timer()
|
self.disable_reconnect_timer()
|
||||||
self.continue_connect_info = [show, msg, sign_msg]
|
self.continue_connect_info = [show, msg]
|
||||||
self.connect_and_auth()
|
self.connect_and_auth()
|
||||||
|
|
||||||
def _discover_server(self):
|
def _discover_server(self):
|
||||||
|
@ -1564,40 +1486,16 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
app.proxy65_manager.resolve(proxy, self.connection, our_jid,
|
app.proxy65_manager.resolve(proxy, self.connection, our_jid,
|
||||||
testit=testit)
|
testit=testit)
|
||||||
|
|
||||||
def send_first_presence(self):
|
def send_first_presence(self, signed=''):
|
||||||
if self.connected > 1 and self.continue_connect_info:
|
if self.connected <= 1 or not self.continue_connect_info:
|
||||||
msg = self.continue_connect_info[1]
|
return
|
||||||
sign_msg = self.continue_connect_info[2]
|
|
||||||
signed = ''
|
|
||||||
send_first_presence = True
|
|
||||||
if sign_msg:
|
|
||||||
signed = self.get_signed_presence(msg,
|
|
||||||
self._send_first_presence)
|
|
||||||
if signed is None:
|
|
||||||
app.nec.push_incoming_event(GPGPasswordRequiredEvent(None,
|
|
||||||
conn=self, callback=self._send_first_presence))
|
|
||||||
# _send_first_presence will be called when user enter
|
|
||||||
# passphrase
|
|
||||||
send_first_presence = False
|
|
||||||
if send_first_presence:
|
|
||||||
self._send_first_presence(signed)
|
|
||||||
|
|
||||||
def _send_first_presence(self, signed=''):
|
|
||||||
show = self.continue_connect_info[0]
|
show = self.continue_connect_info[0]
|
||||||
msg = self.continue_connect_info[1]
|
msg = self.continue_connect_info[1]
|
||||||
sign_msg = self.continue_connect_info[2]
|
|
||||||
if sign_msg and not signed:
|
|
||||||
signed = self.get_signed_presence(msg)
|
|
||||||
if signed is None:
|
|
||||||
app.nec.push_incoming_event(BadGPGPassphraseEvent(None,
|
|
||||||
conn=self))
|
|
||||||
self.USE_GPG = False
|
|
||||||
signed = ''
|
|
||||||
self.connected = app.SHOW_LIST.index(show)
|
self.connected = app.SHOW_LIST.index(show)
|
||||||
sshow = helpers.get_xmpp_show(show)
|
sshow = helpers.get_xmpp_show(show)
|
||||||
# send our presence
|
# send our presence
|
||||||
if show == 'invisible':
|
if show == 'invisible':
|
||||||
self.send_invisible_presence(msg, signed, True)
|
self.send_invisible_presence(msg, True)
|
||||||
return
|
return
|
||||||
if show not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
|
if show not in ['offline', 'online', 'chat', 'away', 'xa', 'dnd']:
|
||||||
return
|
return
|
||||||
|
@ -1606,8 +1504,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.get_module('Presence').send_presence(
|
self.get_module('Presence').send_presence(
|
||||||
priority=priority,
|
priority=priority,
|
||||||
show=sshow,
|
show=sshow,
|
||||||
status=msg,
|
status=msg)
|
||||||
sign=signed)
|
|
||||||
|
|
||||||
if self.connection:
|
if self.connection:
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
|
@ -1643,18 +1540,15 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
status=msg)
|
status=msg)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
signed = self.get_signed_presence(msg)
|
|
||||||
priority = app.get_priority(self.name, sshow)
|
priority = app.get_priority(self.name, sshow)
|
||||||
self.get_module('Presence').send_presence(
|
self.get_module('Presence').send_presence(
|
||||||
jid,
|
jid,
|
||||||
priority=priority,
|
priority=priority,
|
||||||
show=sshow,
|
show=sshow,
|
||||||
status=msg,
|
status=msg)
|
||||||
sign=signed)
|
|
||||||
|
|
||||||
def _change_to_invisible(self, msg):
|
def _change_to_invisible(self, msg):
|
||||||
signed = self.get_signed_presence(msg)
|
self.send_invisible_presence(msg)
|
||||||
self.send_invisible_presence(msg, signed)
|
|
||||||
|
|
||||||
def _change_from_invisible(self):
|
def _change_from_invisible(self):
|
||||||
if self.get_module('PrivacyLists').supported:
|
if self.get_module('PrivacyLists').supported:
|
||||||
|
@ -1663,13 +1557,11 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
def _update_status(self, show, msg, idle_time=None):
|
def _update_status(self, show, msg, idle_time=None):
|
||||||
xmpp_show = helpers.get_xmpp_show(show)
|
xmpp_show = helpers.get_xmpp_show(show)
|
||||||
priority = app.get_priority(self.name, xmpp_show)
|
priority = app.get_priority(self.name, xmpp_show)
|
||||||
signed = self.get_signed_presence(msg)
|
|
||||||
|
|
||||||
self.get_module('Presence').send_presence(
|
self.get_module('Presence').send_presence(
|
||||||
priority=priority,
|
priority=priority,
|
||||||
show=xmpp_show,
|
show=xmpp_show,
|
||||||
status=msg,
|
status=msg,
|
||||||
sign=signed,
|
|
||||||
idle_time=idle_time)
|
idle_time=idle_time)
|
||||||
|
|
||||||
if self.connection:
|
if self.connection:
|
||||||
|
@ -1862,7 +1754,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
def send_gc_message(self, obj):
|
def send_gc_message(self, obj):
|
||||||
obj.stanza_id = self.connection.send(obj.msg_iq)
|
obj.stanza_id = self.connection.send(obj.msg_iq)
|
||||||
app.nec.push_incoming_event(MessageSentEvent(
|
app.nec.push_incoming_event(MessageSentEvent(
|
||||||
None, conn=self, jid=obj.jid, message=obj.message, keyID=None,
|
None, conn=self, jid=obj.jid, message=obj.message,
|
||||||
automatic_message=obj.automatic_message,
|
automatic_message=obj.automatic_message,
|
||||||
stanza_id=obj.stanza_id, additional_data=obj.additional_data))
|
stanza_id=obj.stanza_id, additional_data=obj.additional_data))
|
||||||
|
|
||||||
|
|
|
@ -153,15 +153,6 @@ class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
|
||||||
class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
|
class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'new-account-not-connected'
|
name = 'new-account-not-connected'
|
||||||
|
|
||||||
class BadGPGPassphraseEvent(nec.NetworkIncomingEvent):
|
|
||||||
name = 'bad-gpg-passphrase'
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
self.account = self.conn.name
|
|
||||||
self.use_gpg_agent = app.config.get('use_gpg_agent')
|
|
||||||
self.keyID = app.config.get_per('accounts', self.conn.name, 'keyid')
|
|
||||||
return True
|
|
||||||
|
|
||||||
class ConnectionLostEvent(nec.NetworkIncomingEvent):
|
class ConnectionLostEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'connection-lost'
|
name = 'connection-lost'
|
||||||
|
|
||||||
|
@ -170,13 +161,6 @@ class ConnectionLostEvent(nec.NetworkIncomingEvent):
|
||||||
show='offline'))
|
show='offline'))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
class GPGPasswordRequiredEvent(nec.NetworkIncomingEvent):
|
|
||||||
name = 'gpg-password-required'
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
self.keyid = app.config.get_per('accounts', self.conn.name, 'keyid')
|
|
||||||
return True
|
|
||||||
|
|
||||||
class FileRequestReceivedEvent(nec.NetworkIncomingEvent):
|
class FileRequestReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'file-request-received'
|
name = 'file-request-received'
|
||||||
|
|
||||||
|
@ -610,7 +594,6 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
|
||||||
def init(self):
|
def init(self):
|
||||||
self.additional_data = AdditionalDataDict()
|
self.additional_data = AdditionalDataDict()
|
||||||
self.message = None
|
self.message = None
|
||||||
self.keyID = None
|
|
||||||
self.type_ = 'chat'
|
self.type_ = 'chat'
|
||||||
self.kind = None
|
self.kind = None
|
||||||
self.timestamp = None
|
self.timestamp = None
|
||||||
|
|
|
@ -23,7 +23,6 @@ class OptionKind(IntEnum):
|
||||||
PRIORITY = 9
|
PRIORITY = 9
|
||||||
FILECHOOSER = 10
|
FILECHOOSER = 10
|
||||||
CHANGEPASSWORD = 11
|
CHANGEPASSWORD = 11
|
||||||
GPG = 12
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
|
|
|
@ -137,7 +137,7 @@ class Contact(CommonContact):
|
||||||
Information concerning a contact
|
Information concerning a contact
|
||||||
"""
|
"""
|
||||||
def __init__(self, jid, account, name='', groups=None, show='', status='',
|
def __init__(self, jid, account, name='', groups=None, show='', status='',
|
||||||
sub='', ask='', resource='', priority=0, keyID='', client_caps=None,
|
sub='', ask='', resource='', priority=0, client_caps=None,
|
||||||
chatstate=None, idle_time=None, avatar_sha=None, groupchat=False,
|
chatstate=None, idle_time=None, avatar_sha=None, groupchat=False,
|
||||||
is_pm_contact=False):
|
is_pm_contact=False):
|
||||||
if not isinstance(jid, str):
|
if not isinstance(jid, str):
|
||||||
|
@ -159,7 +159,6 @@ class Contact(CommonContact):
|
||||||
self.ask = ask
|
self.ask = ask
|
||||||
|
|
||||||
self.priority = priority
|
self.priority = priority
|
||||||
self.keyID = keyID
|
|
||||||
self.idle_time = idle_time
|
self.idle_time = idle_time
|
||||||
|
|
||||||
self.pep = {}
|
self.pep = {}
|
||||||
|
@ -306,7 +305,7 @@ class LegacyContactsAPI:
|
||||||
self._metacontact_manager.remove_account(account)
|
self._metacontact_manager.remove_account(account)
|
||||||
|
|
||||||
def create_contact(self, jid, account, name='', groups=None, show='',
|
def create_contact(self, jid, account, name='', groups=None, show='',
|
||||||
status='', sub='', ask='', resource='', priority=0, keyID='',
|
status='', sub='', ask='', resource='', priority=0,
|
||||||
client_caps=None, chatstate=None, idle_time=None,
|
client_caps=None, chatstate=None, idle_time=None,
|
||||||
avatar_sha=None, groupchat=False):
|
avatar_sha=None, groupchat=False):
|
||||||
if groups is None:
|
if groups is None:
|
||||||
|
@ -315,36 +314,36 @@ class LegacyContactsAPI:
|
||||||
account = self._accounts.get(account, account)
|
account = self._accounts.get(account, account)
|
||||||
return Contact(jid=jid, account=account, name=name, groups=groups,
|
return Contact(jid=jid, account=account, name=name, groups=groups,
|
||||||
show=show, status=status, sub=sub, ask=ask, resource=resource,
|
show=show, status=status, sub=sub, ask=ask, resource=resource,
|
||||||
priority=priority, keyID=keyID, client_caps=client_caps,
|
priority=priority, client_caps=client_caps,
|
||||||
chatstate=chatstate, idle_time=idle_time, avatar_sha=avatar_sha,
|
chatstate=chatstate, idle_time=idle_time, avatar_sha=avatar_sha,
|
||||||
groupchat=groupchat)
|
groupchat=groupchat)
|
||||||
|
|
||||||
def create_self_contact(self, jid, account, resource, show, status, priority,
|
def create_self_contact(self, jid, account, resource, show, status, priority,
|
||||||
name='', keyID=''):
|
name=''):
|
||||||
conn = common.app.connections[account]
|
conn = common.app.connections[account]
|
||||||
nick = name or common.app.nicks[account]
|
nick = name or common.app.nicks[account]
|
||||||
account = self._accounts.get(account, account) # Use Account object if available
|
account = self._accounts.get(account, account) # Use Account object if available
|
||||||
self_contact = self.create_contact(jid=jid, account=account,
|
self_contact = self.create_contact(jid=jid, account=account,
|
||||||
name=nick, groups=['self_contact'], show=show, status=status,
|
name=nick, groups=['self_contact'], show=show, status=status,
|
||||||
sub='both', ask='none', priority=priority, keyID=keyID,
|
sub='both', ask='none', priority=priority,
|
||||||
resource=resource)
|
resource=resource)
|
||||||
self_contact.pep = conn.pep
|
self_contact.pep = conn.pep
|
||||||
return self_contact
|
return self_contact
|
||||||
|
|
||||||
def create_not_in_roster_contact(self, jid, account, resource='', name='',
|
def create_not_in_roster_contact(self, jid, account, resource='', name='',
|
||||||
keyID='', groupchat=False):
|
groupchat=False):
|
||||||
# Use Account object if available
|
# Use Account object if available
|
||||||
account = self._accounts.get(account, account)
|
account = self._accounts.get(account, account)
|
||||||
return self.create_contact(jid=jid, account=account, resource=resource,
|
return self.create_contact(jid=jid, account=account, resource=resource,
|
||||||
name=name, groups=[_('Not in Roster')], show='not in roster',
|
name=name, groups=[_('Not in Roster')], show='not in roster',
|
||||||
status='', sub='none', keyID=keyID, groupchat=groupchat)
|
status='', sub='none', groupchat=groupchat)
|
||||||
|
|
||||||
def copy_contact(self, contact):
|
def copy_contact(self, contact):
|
||||||
return self.create_contact(contact.jid, contact.account,
|
return self.create_contact(contact.jid, contact.account,
|
||||||
name=contact.name, groups=contact.groups, show=contact.show,
|
name=contact.name, groups=contact.groups, show=contact.show,
|
||||||
status=contact.status, sub=contact.sub, ask=contact.ask,
|
status=contact.status, sub=contact.sub, ask=contact.ask,
|
||||||
resource=contact.resource, priority=contact.priority,
|
resource=contact.resource, priority=contact.priority,
|
||||||
keyID=contact.keyID, client_caps=contact.client_caps,
|
client_caps=contact.client_caps,
|
||||||
chatstate=contact.chatstate_enum,
|
chatstate=contact.chatstate_enum,
|
||||||
idle_time=contact.idle_time, avatar_sha=contact.avatar_sha)
|
idle_time=contact.idle_time, avatar_sha=contact.avatar_sha)
|
||||||
|
|
||||||
|
|
|
@ -1,146 +0,0 @@
|
||||||
# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
|
|
||||||
# Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
|
|
||||||
# Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
|
|
||||||
# Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
|
|
||||||
# Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
|
|
||||||
# Jonathan Schleifer <js-gajim AT webkeks.org>
|
|
||||||
#
|
|
||||||
# This file is part of Gajim.
|
|
||||||
#
|
|
||||||
# Gajim 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.
|
|
||||||
#
|
|
||||||
# Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import os
|
|
||||||
from gajim.common import app
|
|
||||||
|
|
||||||
if app.is_installed('GPG'):
|
|
||||||
import gnupg
|
|
||||||
|
|
||||||
class GnuPG(gnupg.GPG):
|
|
||||||
def __init__(self):
|
|
||||||
use_agent = app.config.get('use_gpg_agent')
|
|
||||||
gnupg.GPG.__init__(self, gpgbinary=app.get_gpg_binary(), use_agent=use_agent)
|
|
||||||
encoding = app.config.get('pgp_encoding')
|
|
||||||
if encoding:
|
|
||||||
self.encoding = encoding
|
|
||||||
self.decode_errors = 'replace'
|
|
||||||
self.passphrase = None
|
|
||||||
self.always_trust = [] # list of keyID to always trust
|
|
||||||
|
|
||||||
def encrypt(self, str_, recipients, always_trust=False):
|
|
||||||
trust = always_trust
|
|
||||||
if not trust:
|
|
||||||
# check if we trust all keys
|
|
||||||
trust = True
|
|
||||||
for key in recipients:
|
|
||||||
if key not in self.always_trust:
|
|
||||||
trust = False
|
|
||||||
if not trust:
|
|
||||||
# check that we'll be able to encrypt
|
|
||||||
result = super(GnuPG, self).list_keys(keys=recipients)
|
|
||||||
for key in result:
|
|
||||||
if key['trust'] not in ('f', 'u'):
|
|
||||||
if key['keyid'][-8:] not in self.always_trust:
|
|
||||||
return '', 'NOT_TRUSTED ' + key['keyid'][-8:]
|
|
||||||
trust = True
|
|
||||||
result = super(GnuPG, self).encrypt(str_.encode('utf8'), recipients,
|
|
||||||
always_trust=trust, passphrase=self.passphrase)
|
|
||||||
|
|
||||||
if result.ok:
|
|
||||||
error = ''
|
|
||||||
else:
|
|
||||||
error = result.status
|
|
||||||
|
|
||||||
return self._stripHeaderFooter(str(result)), error
|
|
||||||
|
|
||||||
def decrypt(self, str_, keyID):
|
|
||||||
data = self._addHeaderFooter(str_, 'MESSAGE')
|
|
||||||
result = super(GnuPG, self).decrypt(data.encode('utf8'),
|
|
||||||
passphrase=self.passphrase)
|
|
||||||
|
|
||||||
return result.data.decode('utf8')
|
|
||||||
|
|
||||||
def sign(self, str_, keyID):
|
|
||||||
result = super(GnuPG, self).sign(str_.encode('utf8'), keyid=keyID, detach=True,
|
|
||||||
passphrase=self.passphrase)
|
|
||||||
|
|
||||||
if result.fingerprint:
|
|
||||||
return self._stripHeaderFooter(str(result))
|
|
||||||
if hasattr(result, 'status') and result.status == 'key expired':
|
|
||||||
return 'KEYEXPIRED'
|
|
||||||
return 'BAD_PASSPHRASE'
|
|
||||||
|
|
||||||
def verify(self, str_, sign):
|
|
||||||
if str_ is None:
|
|
||||||
return ''
|
|
||||||
# Hash algorithm is not transfered in the signed presence stanza so try
|
|
||||||
# all algorithms. Text name for hash algorithms from RFC 4880 - section 9.4
|
|
||||||
hash_algorithms = ['SHA512', 'SHA384', 'SHA256', 'SHA224', 'SHA1', 'RIPEMD160']
|
|
||||||
for algo in hash_algorithms:
|
|
||||||
data = os.linesep.join(
|
|
||||||
['-----BEGIN PGP SIGNED MESSAGE-----',
|
|
||||||
'Hash: ' + algo,
|
|
||||||
'',
|
|
||||||
str_,
|
|
||||||
self._addHeaderFooter(sign, 'SIGNATURE')]
|
|
||||||
)
|
|
||||||
result = super(GnuPG, self).verify(data.encode('utf8'))
|
|
||||||
if result.valid:
|
|
||||||
return result.key_id
|
|
||||||
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def get_key(self, keyID):
|
|
||||||
return super(GnuPG, self).list_keys(keys=[keyID])
|
|
||||||
|
|
||||||
def get_keys(self, secret=False):
|
|
||||||
keys = {}
|
|
||||||
result = super(GnuPG, self).list_keys(secret=secret)
|
|
||||||
|
|
||||||
for key in result:
|
|
||||||
# Take first not empty uid
|
|
||||||
keys[key['keyid'][8:]] = [uid for uid in key['uids'] if uid][0]
|
|
||||||
return keys
|
|
||||||
|
|
||||||
def get_secret_keys(self):
|
|
||||||
return self.get_keys(True)
|
|
||||||
|
|
||||||
def _stripHeaderFooter(self, data):
|
|
||||||
"""
|
|
||||||
Remove header and footer from data
|
|
||||||
"""
|
|
||||||
if not data:
|
|
||||||
return ''
|
|
||||||
lines = data.splitlines()
|
|
||||||
while lines[0] != '':
|
|
||||||
lines.remove(lines[0])
|
|
||||||
while lines[0] == '':
|
|
||||||
lines.remove(lines[0])
|
|
||||||
i = 0
|
|
||||||
for line in lines:
|
|
||||||
if line:
|
|
||||||
if line[0] == '-':
|
|
||||||
break
|
|
||||||
i = i+1
|
|
||||||
line = '\n'.join(lines[0:i])
|
|
||||||
return line
|
|
||||||
|
|
||||||
def _addHeaderFooter(self, data, type_):
|
|
||||||
"""
|
|
||||||
Add header and footer from data
|
|
||||||
"""
|
|
||||||
out = "-----BEGIN PGP %s-----" % type_ + os.linesep
|
|
||||||
out = out + "Version: PGP" + os.linesep
|
|
||||||
out = out + os.linesep
|
|
||||||
out = out + data + os.linesep
|
|
||||||
out = out + "-----END PGP %s-----" % type_ + os.linesep
|
|
||||||
return out
|
|
|
@ -1108,54 +1108,6 @@ def get_current_show(account):
|
||||||
status = app.connections[account].connected
|
status = app.connections[account].connected
|
||||||
return app.SHOW_LIST[status]
|
return app.SHOW_LIST[status]
|
||||||
|
|
||||||
def prepare_and_validate_gpg_keyID(account, jid, keyID):
|
|
||||||
"""
|
|
||||||
Return an eight char long keyID that can be used with for GPG encryption
|
|
||||||
with this contact
|
|
||||||
|
|
||||||
If the given keyID is None, return UNKNOWN; if the key does not match the
|
|
||||||
assigned key XXXXXXXXMISMATCH is returned. If the key is trusted and not yet
|
|
||||||
assigned, assign it.
|
|
||||||
"""
|
|
||||||
if app.connections[account].USE_GPG:
|
|
||||||
if keyID and len(keyID) == 16:
|
|
||||||
keyID = keyID[8:]
|
|
||||||
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
|
|
||||||
if jid in attached_keys and keyID:
|
|
||||||
attachedkeyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
if attachedkeyID != keyID:
|
|
||||||
# Get signing subkeys for the attached key
|
|
||||||
subkeys = []
|
|
||||||
for key in app.connections[account].gpg.list_keys():
|
|
||||||
if key['keyid'][8:] == attachedkeyID:
|
|
||||||
subkeys = [subkey[0][8:] for subkey in key['subkeys'] \
|
|
||||||
if subkey[1] == 's']
|
|
||||||
break
|
|
||||||
|
|
||||||
if keyID not in subkeys:
|
|
||||||
# Mismatch! Another gpg key was expected
|
|
||||||
keyID += 'MISMATCH'
|
|
||||||
elif jid in attached_keys:
|
|
||||||
# An unsigned presence, just use the assigned key
|
|
||||||
keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
elif keyID:
|
|
||||||
full_key = app.connections[account].ask_gpg_keys(keyID=keyID)
|
|
||||||
# Assign the corresponding key, if we have it in our keyring
|
|
||||||
if full_key:
|
|
||||||
for u in app.contacts.get_contacts(account, jid):
|
|
||||||
u.keyID = keyID
|
|
||||||
keys_str = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys')
|
|
||||||
keys_str += jid + ' ' + keyID + ' '
|
|
||||||
app.config.set_per('accounts', account, 'attached_gpg_keys',
|
|
||||||
keys_str)
|
|
||||||
elif keyID is None:
|
|
||||||
keyID = 'UNKNOWN'
|
|
||||||
return keyID
|
|
||||||
|
|
||||||
def update_optional_features(account=None):
|
def update_optional_features(account=None):
|
||||||
if account is not None:
|
if account is not None:
|
||||||
accounts = [account]
|
accounts = [account]
|
||||||
|
|
|
@ -329,7 +329,7 @@ class HTTPUpload(BaseModule):
|
||||||
else:
|
else:
|
||||||
app.nec.push_outgoing_event(MessageOutgoingEvent(
|
app.nec.push_outgoing_event(MessageOutgoingEvent(
|
||||||
None, account=self._account, jid=file.contact.jid,
|
None, account=self._account, jid=file.contact.jid,
|
||||||
message=message, keyID=file.key_id, type_='chat',
|
message=message, type_='chat',
|
||||||
automatic_message=False, session=file.session))
|
automatic_message=False, session=file.session))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -353,9 +353,6 @@ class File:
|
||||||
setattr(self, key, val)
|
setattr(self, key, val)
|
||||||
self.encrypted = False
|
self.encrypted = False
|
||||||
self.contact = contact
|
self.contact = contact
|
||||||
self.key_id = None
|
|
||||||
if hasattr(contact, 'keyID'):
|
|
||||||
self.key_id = contact.keyID
|
|
||||||
self.stream = None
|
self.stream = None
|
||||||
self.path = path
|
self.path = path
|
||||||
self.put = None
|
self.put = None
|
||||||
|
|
|
@ -25,7 +25,6 @@ from gajim.common.i18n import _
|
||||||
from gajim.common.nec import NetworkEvent
|
from gajim.common.nec import NetworkEvent
|
||||||
from gajim.common.const import KindConstant
|
from gajim.common.const import KindConstant
|
||||||
from gajim.common.const import ShowConstant
|
from gajim.common.const import ShowConstant
|
||||||
from gajim.common.helpers import prepare_and_validate_gpg_keyID
|
|
||||||
from gajim.common.modules.base import BaseModule
|
from gajim.common.modules.base import BaseModule
|
||||||
|
|
||||||
|
|
||||||
|
@ -94,12 +93,6 @@ class Presence(BaseModule):
|
||||||
self._log.warning(stanza)
|
self._log.warning(stanza)
|
||||||
return
|
return
|
||||||
|
|
||||||
key_id = ''
|
|
||||||
if properties.signed is not None and self._con.USE_GPG:
|
|
||||||
key_id = self._con.gpg.verify(properties.status, properties.signed)
|
|
||||||
key_id = prepare_and_validate_gpg_keyID(
|
|
||||||
self._account, properties.jid.getBare(), key_id)
|
|
||||||
|
|
||||||
show = properties.show.value
|
show = properties.show.value
|
||||||
if properties.type.is_unavailable:
|
if properties.type.is_unavailable:
|
||||||
show = 'offline'
|
show = 'offline'
|
||||||
|
@ -107,7 +100,6 @@ class Presence(BaseModule):
|
||||||
event_attrs = {
|
event_attrs = {
|
||||||
'conn': self._con,
|
'conn': self._con,
|
||||||
'stanza': stanza,
|
'stanza': stanza,
|
||||||
'keyID': key_id,
|
|
||||||
'prio': properties.priority,
|
'prio': properties.priority,
|
||||||
'need_add_in_roster': False,
|
'need_add_in_roster': False,
|
||||||
'popup': False,
|
'popup': False,
|
||||||
|
@ -185,13 +177,6 @@ class Presence(BaseModule):
|
||||||
contact.show = event.show
|
contact.show = event.show
|
||||||
contact.status = properties.status
|
contact.status = properties.status
|
||||||
contact.priority = properties.priority
|
contact.priority = properties.priority
|
||||||
attached_keys = app.config.get_per('accounts', self._account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if jid in attached_keys:
|
|
||||||
contact.keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
else:
|
|
||||||
# Do not override assigned key
|
|
||||||
contact.keyID = event.keyID
|
|
||||||
contact.idle_time = properties.idle_timestamp
|
contact.idle_time = properties.idle_timestamp
|
||||||
|
|
||||||
event.contact = contact
|
event.contact = contact
|
||||||
|
@ -355,7 +340,7 @@ class Presence(BaseModule):
|
||||||
|
|
||||||
def get_presence(self, to=None, typ=None, priority=None,
|
def get_presence(self, to=None, typ=None, priority=None,
|
||||||
show=None, status=None, nick=None, caps=True,
|
show=None, status=None, nick=None, caps=True,
|
||||||
sign=None, idle_time=None):
|
idle_time=None):
|
||||||
if show not in ('chat', 'away', 'xa', 'dnd'):
|
if show not in ('chat', 'away', 'xa', 'dnd'):
|
||||||
# Gajim sometimes passes invalid show values here
|
# Gajim sometimes passes invalid show values here
|
||||||
# until this is fixed this is a workaround
|
# until this is fixed this is a workaround
|
||||||
|
@ -365,9 +350,6 @@ class Presence(BaseModule):
|
||||||
nick_tag = presence.setTag('nick', namespace=nbxmpp.NS_NICK)
|
nick_tag = presence.setTag('nick', namespace=nbxmpp.NS_NICK)
|
||||||
nick_tag.setData(nick)
|
nick_tag.setData(nick)
|
||||||
|
|
||||||
if sign:
|
|
||||||
presence.setTag(nbxmpp.NS_SIGNED + ' x').setData(sign)
|
|
||||||
|
|
||||||
if idle_time is not None:
|
if idle_time is not None:
|
||||||
idle_node = presence.setTag('idle', namespace=nbxmpp.NS_IDLE)
|
idle_node = presence.setTag('idle', namespace=nbxmpp.NS_IDLE)
|
||||||
idle_node.setAttr('since', idle_time)
|
idle_node.setAttr('since', idle_time)
|
||||||
|
|
|
@ -156,7 +156,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
def _on_remove_service(self, jid):
|
def _on_remove_service(self, jid):
|
||||||
self.roster.delItem(jid)
|
self.roster.delItem(jid)
|
||||||
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
|
# 'NOTIFY' (account, (jid, status, status message, resource, priority,
|
||||||
# keyID, timestamp))
|
# timestamp))
|
||||||
self._on_presence(jid, show='offline', status='')
|
self._on_presence(jid, show='offline', status='')
|
||||||
|
|
||||||
def _on_presence(self, jid, show=None, status=None):
|
def _on_presence(self, jid, show=None, status=None):
|
||||||
|
@ -169,7 +169,6 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
|
|
||||||
event_attrs = {
|
event_attrs = {
|
||||||
'conn': self,
|
'conn': self,
|
||||||
'keyID': None,
|
|
||||||
'prio': 0,
|
'prio': 0,
|
||||||
'need_add_in_roster': False,
|
'need_add_in_roster': False,
|
||||||
'popup': False,
|
'popup': False,
|
||||||
|
@ -216,13 +215,6 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
contact.show = event.show
|
contact.show = event.show
|
||||||
contact.status = event.status
|
contact.status = event.status
|
||||||
contact.priority = event.prio
|
contact.priority = event.prio
|
||||||
attached_keys = app.config.get_per('accounts', self.name,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if jid in attached_keys:
|
|
||||||
contact.keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
else:
|
|
||||||
# Do not override assigned key
|
|
||||||
contact.keyID = event.keyID
|
|
||||||
contact.idle_time = event.idle_time
|
contact.idle_time = event.idle_time
|
||||||
|
|
||||||
event.contact = contact
|
event.contact = contact
|
||||||
|
@ -363,7 +355,7 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf):
|
||||||
else:
|
else:
|
||||||
self.reannounce()
|
self.reannounce()
|
||||||
|
|
||||||
def connect_and_init(self, show, msg, sign_msg):
|
def connect_and_init(self, show, msg):
|
||||||
# to check for errors from zeroconf
|
# to check for errors from zeroconf
|
||||||
check = True
|
check = True
|
||||||
if not self.connect(show, msg):
|
if not self.connect(show, msg):
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!-- Generated with glade 3.18.3 -->
|
|
||||||
<interface>
|
|
||||||
<requires lib="gtk+" version="3.12"/>
|
|
||||||
<object class="GtkDialog" id="choose_gpg_key_dialog">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="border_width">6</property>
|
|
||||||
<property name="default_width">550</property>
|
|
||||||
<property name="default_height">300</property>
|
|
||||||
<property name="type_hint">dialog</property>
|
|
||||||
<child internal-child="vbox">
|
|
||||||
<object class="GtkBox" id="vbox33">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child internal-child="action_area">
|
|
||||||
<object class="GtkButtonBox" id="hbuttonbox14">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="layout_style">end</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="button26">
|
|
||||||
<property name="label">gtk-cancel</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="can_default">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="use_stock">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkButton" id="button27">
|
|
||||||
<property name="label">gtk-ok</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="can_default">True</property>
|
|
||||||
<property name="has_default">True</property>
|
|
||||||
<property name="receives_default">False</property>
|
|
||||||
<property name="use_stock">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="pack_type">end</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkBox" id="vbox91">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="border_width">6</property>
|
|
||||||
<property name="orientation">vertical</property>
|
|
||||||
<property name="spacing">6</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="prompt_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="xalign">0</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">False</property>
|
|
||||||
<property name="position">0</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkScrolledWindow" id="scrolledwindow20">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="shadow_type">in</property>
|
|
||||||
<child>
|
|
||||||
<object class="GtkTreeView" id="keys_treeview">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<child internal-child="selection">
|
|
||||||
<object class="GtkTreeSelection" id="treeview-selection1"/>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">2</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<action-widgets>
|
|
||||||
<action-widget response="-6">button26</action-widget>
|
|
||||||
<action-widget response="-5">button27</action-widget>
|
|
||||||
</action-widgets>
|
|
||||||
</object>
|
|
||||||
</interface>
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.20.0 -->
|
<!-- Generated with glade 3.22.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.12"/>
|
<requires lib="gtk+" version="3.12"/>
|
||||||
<object class="GtkAccelGroup" id="accelgroup1"/>
|
<object class="GtkAccelGroup" id="accelgroup1"/>
|
||||||
|
@ -100,14 +100,6 @@
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkMenuItem" id="assign_openpgp_key_menuitem">
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="label" translatable="yes">Assign Open_PGP Key...</property>
|
|
||||||
<property name="use_underline">True</property>
|
|
||||||
<signal name="activate" handler="on_assign_openpgp_key_menuitem_activate" swapped="no"/>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkMenuItem" id="add_special_notification_menuitem">
|
<object class="GtkMenuItem" id="add_special_notification_menuitem">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
|
|
@ -182,32 +182,6 @@
|
||||||
<property name="top_attach">9</property>
|
<property name="top_attach">9</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="pgp_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">end</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<property name="label" translatable="yes">OpenPGP:</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">0</property>
|
|
||||||
<property name="top_attach">10</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="pgp">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="halign">start</property>
|
|
||||||
<property name="valign">start</property>
|
|
||||||
<property name="hexpand">True</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="left_attach">1</property>
|
|
||||||
<property name="top_attach">10</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkLabel" id="sub_label">
|
<object class="GtkLabel" id="sub_label">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -300,5 +274,11 @@
|
||||||
<child>
|
<child>
|
||||||
<placeholder/>
|
<placeholder/>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
@ -284,73 +284,6 @@ class PassphraseDialog:
|
||||||
if self.cancel_handler and not self.ok:
|
if self.cancel_handler and not self.ok:
|
||||||
self.cancel_handler()
|
self.cancel_handler()
|
||||||
|
|
||||||
class ChooseGPGKeyDialog:
|
|
||||||
"""
|
|
||||||
Class for GPG key dialog
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, title_text, prompt_text, secret_keys, on_response,
|
|
||||||
selected=None, transient_for=None):
|
|
||||||
'''secret_keys : {keyID: userName, ...}'''
|
|
||||||
self.on_response = on_response
|
|
||||||
xml = get_builder('choose_gpg_key_dialog.ui')
|
|
||||||
self.window = xml.get_object('choose_gpg_key_dialog')
|
|
||||||
self.window.set_title(title_text)
|
|
||||||
self.window.set_transient_for(transient_for)
|
|
||||||
self.keys_treeview = xml.get_object('keys_treeview')
|
|
||||||
prompt_label = xml.get_object('prompt_label')
|
|
||||||
prompt_label.set_text(prompt_text)
|
|
||||||
model = Gtk.ListStore(str, str)
|
|
||||||
model.set_sort_func(1, self.sort_keys)
|
|
||||||
model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
|
|
||||||
self.keys_treeview.set_model(model)
|
|
||||||
#columns
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
self.keys_treeview.insert_column_with_attributes(-1, _('KeyID'),
|
|
||||||
renderer, text=0)
|
|
||||||
col = self.keys_treeview.get_column(0)
|
|
||||||
col.set_sort_column_id(0)
|
|
||||||
renderer = Gtk.CellRendererText()
|
|
||||||
self.keys_treeview.insert_column_with_attributes(-1, _('Contact name'),
|
|
||||||
renderer, text=1)
|
|
||||||
col = self.keys_treeview.get_column(1)
|
|
||||||
col.set_sort_column_id(1)
|
|
||||||
self.keys_treeview.set_search_column(1)
|
|
||||||
self.fill_tree(secret_keys, selected)
|
|
||||||
self.window.connect('response', self.on_dialog_response)
|
|
||||||
self.window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
|
|
||||||
self.window.show_all()
|
|
||||||
|
|
||||||
def sort_keys(self, model, iter1, iter2, data=None):
|
|
||||||
value1 = model[iter1][1]
|
|
||||||
value2 = model[iter2][1]
|
|
||||||
if value1 == _('None'):
|
|
||||||
return -1
|
|
||||||
if value2 == _('None'):
|
|
||||||
return 1
|
|
||||||
if value1 < value2:
|
|
||||||
return -1
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def on_dialog_response(self, dialog, response):
|
|
||||||
selection = self.keys_treeview.get_selection()
|
|
||||||
(model, iter_) = selection.get_selected()
|
|
||||||
if iter_ and response == Gtk.ResponseType.OK:
|
|
||||||
keyID = [model[iter_][0], model[iter_][1]]
|
|
||||||
else:
|
|
||||||
keyID = None
|
|
||||||
self.on_response(keyID)
|
|
||||||
self.window.destroy()
|
|
||||||
|
|
||||||
def fill_tree(self, list_, selected):
|
|
||||||
model = self.keys_treeview.get_model()
|
|
||||||
for keyID in list_.keys():
|
|
||||||
iter_ = model.append((keyID, list_[keyID]))
|
|
||||||
if keyID == selected:
|
|
||||||
path = model.get_path(iter_)
|
|
||||||
self.keys_treeview.set_cursor(path)
|
|
||||||
|
|
||||||
|
|
||||||
class ChangeActivityDialog:
|
class ChangeActivityDialog:
|
||||||
PAGELIST = [
|
PAGELIST = [
|
||||||
'doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
|
'doing_chores', 'drinking', 'eating', 'exercising', 'grooming',
|
||||||
|
|
|
@ -115,28 +115,21 @@ class GajimRemote:
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'send_chat_message': [
|
'send_chat_message': [
|
||||||
_('Sends new chat message to a contact in the roster. Both OpenPGP key '
|
_('Sends new chat message to a contact in the roster. Account is optional.'),
|
||||||
'and account are optional. If you want to set only \'account\', '
|
|
||||||
'without \'OpenPGP key\', just set \'OpenPGP key\' to \'\'.'),
|
|
||||||
[
|
[
|
||||||
('jid', _('JID of the contact that will receive the message'), True),
|
('jid', _('JID of the contact that will receive the message'), True),
|
||||||
(Q_('?CLI:message'), _('message contents'), True),
|
(Q_('?CLI:message'), _('message contents'), True),
|
||||||
(_('PGP key'), _('if specified, the message will be encrypted '
|
|
||||||
'using this public key'), False),
|
|
||||||
(Q_('?CLI:account'), _('if specified, the message will be sent '
|
(Q_('?CLI:account'), _('if specified, the message will be sent '
|
||||||
'using this account'), False),
|
'using this account'), False),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'send_single_message': [
|
'send_single_message': [
|
||||||
_('Sends a chat message to someone on your roster. '
|
_('Sends a chat message to someone on your roster. '
|
||||||
'Optionally with OpenPGP key and account. If you want '
|
'Account is optional.'),
|
||||||
'to only set the latter, set OpenPGP key to "".'),
|
|
||||||
[
|
[
|
||||||
('jid', _('JID of the contact that will receive the message'), True),
|
('jid', _('JID of the contact that will receive the message'), True),
|
||||||
(_('subject'), _('message subject'), True),
|
(_('subject'), _('message subject'), True),
|
||||||
(Q_('?CLI:message'), _('message contents'), True),
|
(Q_('?CLI:message'), _('message contents'), True),
|
||||||
(_('PGP key'), _('if specified, the message will be encrypted '
|
|
||||||
'using this public key'), False),
|
|
||||||
(Q_('?CLI:account'), _('if specified, the message will be sent '
|
(Q_('?CLI:account'), _('if specified, the message will be sent '
|
||||||
'using this account'), False),
|
'using this account'), False),
|
||||||
]
|
]
|
||||||
|
|
|
@ -537,8 +537,6 @@ class AccountCreationWizard:
|
||||||
config['use_custom_host'] = False
|
config['use_custom_host'] = False
|
||||||
config['custom_port'] = 0
|
config['custom_port'] = 0
|
||||||
config['custom_host'] = ''
|
config['custom_host'] = ''
|
||||||
config['keyname'] = ''
|
|
||||||
config['keyid'] = ''
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def save_account(self, login, server, savepass, password, anonymous=False):
|
def save_account(self, login, server, savepass, password, anonymous=False):
|
||||||
|
|
|
@ -183,11 +183,10 @@ class AccountsWindow(Gtk.ApplicationWindow):
|
||||||
def get_relogin_options(account):
|
def get_relogin_options(account):
|
||||||
if account == app.ZEROCONF_ACC_NAME:
|
if account == app.ZEROCONF_ACC_NAME:
|
||||||
options = ['zeroconf_first_name', 'zeroconf_last_name',
|
options = ['zeroconf_first_name', 'zeroconf_last_name',
|
||||||
'zeroconf_jabber_id', 'zeroconf_email', 'keyid']
|
'zeroconf_jabber_id', 'zeroconf_email']
|
||||||
else:
|
else:
|
||||||
options = ['client_cert', 'proxy', 'resource',
|
options = ['client_cert', 'proxy', 'resource',
|
||||||
'use_custom_host', 'custom_host', 'custom_port',
|
'use_custom_host', 'custom_host', 'custom_port']
|
||||||
'keyid']
|
|
||||||
|
|
||||||
values = []
|
values = []
|
||||||
for option in options:
|
for option in options:
|
||||||
|
@ -567,9 +566,6 @@ class PreferencesPage(GenericOptionPage):
|
||||||
options = [
|
options = [
|
||||||
Option(OptionKind.SWITCH, _('Merge Accounts'),
|
Option(OptionKind.SWITCH, _('Merge Accounts'),
|
||||||
OptionType.ACTION, 'merge'),
|
OptionType.ACTION, 'merge'),
|
||||||
|
|
||||||
Option(OptionKind.SWITCH, _('Use PGP Agent'),
|
|
||||||
OptionType.ACTION, 'agent'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
GenericOptionPage.__init__(self, None, None, options)
|
GenericOptionPage.__init__(self, None, None, options)
|
||||||
|
@ -605,9 +601,6 @@ class AccountPage(GenericOptionPage):
|
||||||
|
|
||||||
Option(OptionKind.DIALOG, _('Client Certificate'),
|
Option(OptionKind.DIALOG, _('Client Certificate'),
|
||||||
OptionType.DIALOG, props={'dialog': CertificateDialog}),
|
OptionType.DIALOG, props={'dialog': CertificateDialog}),
|
||||||
|
|
||||||
Option(OptionKind.GPG, _('OpenPGP Key'), OptionType.DIALOG,
|
|
||||||
props={'dialog': None}),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
GenericOptionPage.__init__(self, account, parent, options)
|
GenericOptionPage.__init__(self, account, parent, options)
|
||||||
|
@ -704,9 +697,6 @@ class ZeroConfPage(GenericOptionPage):
|
||||||
Option(OptionKind.SWITCH, _('Global Status'),
|
Option(OptionKind.SWITCH, _('Global Status'),
|
||||||
OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
|
OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
|
||||||
desc=_('Synchronize the status of all accounts')),
|
desc=_('Synchronize the status of all accounts')),
|
||||||
|
|
||||||
Option(OptionKind.GPG, _('OpenPGP Key'),
|
|
||||||
OptionType.DIALOG, props={'dialog': None}),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
GenericOptionPage.__init__(self, account, parent, options)
|
GenericOptionPage.__init__(self, account, parent, options)
|
||||||
|
|
|
@ -112,13 +112,6 @@ class FeaturesDialog(Gtk.Dialog):
|
||||||
_('Requires: pybonjour and bonjour SDK running (%(url)s)')
|
_('Requires: pybonjour and bonjour SDK running (%(url)s)')
|
||||||
% {'url': 'https://developer.apple.com/opensource/)'},
|
% {'url': 'https://developer.apple.com/opensource/)'},
|
||||||
None),
|
None),
|
||||||
Feature(_('OpenPGP Message Encryption'),
|
|
||||||
app.is_installed('GPG'),
|
|
||||||
_('Enables Gajim to encrypt chat messages with OpenPGP'),
|
|
||||||
_('Requires: gpg and python-gnupg (%(url)s)')
|
|
||||||
% {'url': 'https://bitbucket.org/vinay.sajip/python-gnupg'},
|
|
||||||
_('Requires: gpg.exe in your PATH environment variable'),
|
|
||||||
None),
|
|
||||||
Feature(_('RST XHTML Generator'),
|
Feature(_('RST XHTML Generator'),
|
||||||
self.docutils_available(),
|
self.docutils_available(),
|
||||||
_('Enables Gajim to generate XHTML output from RST '
|
_('Enables Gajim to generate XHTML output from RST '
|
||||||
|
|
|
@ -335,9 +335,7 @@ class RosterTooltip(StatusTable):
|
||||||
status=connection.status,
|
status=connection.status,
|
||||||
resource=connection.server_resource,
|
resource=connection.server_resource,
|
||||||
priority=connection.priority)
|
priority=connection.priority)
|
||||||
if app.connections[account].gpg:
|
|
||||||
contact.keyID = app.config.get_per(
|
|
||||||
'accounts', connection.name, 'keyid')
|
|
||||||
contacts.append(contact)
|
contacts.append(contact)
|
||||||
|
|
||||||
# Username/Account/Groupchat
|
# Username/Account/Groupchat
|
||||||
|
@ -438,17 +436,6 @@ class RosterTooltip(StatusTable):
|
||||||
self._ui.sub.show()
|
self._ui.sub.show()
|
||||||
self._ui.sub_label.show()
|
self._ui.sub_label.show()
|
||||||
|
|
||||||
if self.prim_contact.keyID:
|
|
||||||
key_id = None
|
|
||||||
if len(self.prim_contact.keyID) == 8:
|
|
||||||
key_id = self.prim_contact.keyID
|
|
||||||
elif len(self.prim_contact.keyID) == 16:
|
|
||||||
key_id = self.prim_contact.keyID[8:]
|
|
||||||
if key_id:
|
|
||||||
self._ui.pgp.set_text(key_id)
|
|
||||||
self._ui.pgp.show()
|
|
||||||
self._ui.pgp_label.show()
|
|
||||||
|
|
||||||
self._set_idle_time(contact)
|
self._set_idle_time(contact)
|
||||||
|
|
||||||
# Avatar
|
# Avatar
|
||||||
|
|
|
@ -230,9 +230,7 @@ class Interface:
|
||||||
if name in self.instances[account]['online_dialog']:
|
if name in self.instances[account]['online_dialog']:
|
||||||
# destroy handler may have already removed it
|
# destroy handler may have already removed it
|
||||||
del self.instances[account]['online_dialog'][name]
|
del self.instances[account]['online_dialog'][name]
|
||||||
for request in self.gpg_passphrase.values():
|
|
||||||
if request:
|
|
||||||
request.interrupt(account=account)
|
|
||||||
if account in self.pass_dialog:
|
if account in self.pass_dialog:
|
||||||
self.pass_dialog[account].window.destroy()
|
self.pass_dialog[account].window.destroy()
|
||||||
if obj.show == 'offline':
|
if obj.show == 'offline':
|
||||||
|
@ -306,7 +304,7 @@ class Interface:
|
||||||
|
|
||||||
def handle_event_presence(self, obj):
|
def handle_event_presence(self, obj):
|
||||||
# 'NOTIFY' (account, (jid, status, status message, resource,
|
# 'NOTIFY' (account, (jid, status, status message, resource,
|
||||||
# priority, # keyID, timestamp))
|
# priority, timestamp))
|
||||||
#
|
#
|
||||||
# Contact changed show
|
# Contact changed show
|
||||||
account = obj.conn.name
|
account = obj.conn.name
|
||||||
|
@ -390,7 +388,7 @@ class Interface:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_event_msgsent(obj):
|
def handle_event_msgsent(obj):
|
||||||
#('MSGSENT', account, (jid, msg, keyID))
|
#('MSGSENT', account, (jid, msg))
|
||||||
# do not play sound when standalone chatstate message (eg no msg)
|
# do not play sound when standalone chatstate message (eg no msg)
|
||||||
if obj.message and app.config.get_per('soundevents', 'message_sent',
|
if obj.message and app.config.get_per('soundevents', 'message_sent',
|
||||||
'enabled'):
|
'enabled'):
|
||||||
|
@ -438,16 +436,11 @@ class Interface:
|
||||||
self.roster.remove_contact_from_groups(c.jid, account,
|
self.roster.remove_contact_from_groups(c.jid, account,
|
||||||
[_('Not in Roster'), _('Observers')], update=False)
|
[_('Not in Roster'), _('Observers')], update=False)
|
||||||
else:
|
else:
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if obj.jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
|
|
||||||
name = obj.jid.split('@', 1)[0]
|
name = obj.jid.split('@', 1)[0]
|
||||||
name = name.split('%', 1)[0]
|
name = name.split('%', 1)[0]
|
||||||
contact1 = app.contacts.create_contact(jid=obj.jid,
|
contact1 = app.contacts.create_contact(jid=obj.jid,
|
||||||
account=account, name=name, groups=[], show='online',
|
account=account, name=name, groups=[], show='online',
|
||||||
status='online', ask='to', resource=obj.resource, keyID=keyID)
|
status='online', ask='to', resource=obj.resource)
|
||||||
app.contacts.add_contact(account, contact1)
|
app.contacts.add_contact(account, contact1)
|
||||||
self.roster.add_contact(obj.jid, account)
|
self.roster.add_contact(obj.jid, account)
|
||||||
InformationDialog(_('Authorization accepted'),
|
InformationDialog(_('Authorization accepted'),
|
||||||
|
@ -571,28 +564,6 @@ class Interface:
|
||||||
text,
|
text,
|
||||||
room_jid=muc)
|
room_jid=muc)
|
||||||
|
|
||||||
def forget_gpg_passphrase(self, keyid):
|
|
||||||
if keyid in self.gpg_passphrase:
|
|
||||||
del self.gpg_passphrase[keyid]
|
|
||||||
return False
|
|
||||||
|
|
||||||
def handle_event_bad_gpg_passphrase(self, obj):
|
|
||||||
#('BAD_PASSPHRASE', account, ())
|
|
||||||
if obj.use_gpg_agent:
|
|
||||||
sectext = _('You configured Gajim to use OpenPGP agent, but there '
|
|
||||||
'is no OpenPGP agent running or it returned a wrong passphrase.'
|
|
||||||
'\n')
|
|
||||||
sectext += _('You are currently connected without your OpenPGP '
|
|
||||||
'key.')
|
|
||||||
WarningDialog(_('Wrong passphrase'), sectext)
|
|
||||||
else:
|
|
||||||
account = obj.conn.name
|
|
||||||
app.notification.popup(
|
|
||||||
'warning', account, account, '', 'dialog-warning',
|
|
||||||
_('Wrong OpenPGP passphrase'),
|
|
||||||
_('You are currently connected without your OpenPGP key.'))
|
|
||||||
self.forget_gpg_passphrase(obj.keyID)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_event_client_cert_passphrase(obj):
|
def handle_event_client_cert_passphrase(obj):
|
||||||
def on_ok(passphrase, checked):
|
def on_ok(passphrase, checked):
|
||||||
|
@ -607,15 +578,6 @@ class Interface:
|
||||||
_('Enter the certificate passphrase for account %s') % \
|
_('Enter the certificate passphrase for account %s') % \
|
||||||
obj.conn.name, ok_handler=on_ok, cancel_handler=on_cancel)
|
obj.conn.name, ok_handler=on_ok, cancel_handler=on_cancel)
|
||||||
|
|
||||||
def handle_event_gpg_password_required(self, obj):
|
|
||||||
#('GPG_PASSWORD_REQUIRED', account, (callback,))
|
|
||||||
if obj.keyid in self.gpg_passphrase:
|
|
||||||
request = self.gpg_passphrase[obj.keyid]
|
|
||||||
else:
|
|
||||||
request = PassphraseRequest(obj.keyid)
|
|
||||||
self.gpg_passphrase[obj.keyid] = request
|
|
||||||
request.add_callback(obj.conn.name, obj.callback)
|
|
||||||
|
|
||||||
def handle_event_password_required(self, obj):
|
def handle_event_password_required(self, obj):
|
||||||
#('PASSWORD_REQUIRED', account, None)
|
#('PASSWORD_REQUIRED', account, None)
|
||||||
account = obj.conn.name
|
account = obj.conn.name
|
||||||
|
@ -652,14 +614,10 @@ class Interface:
|
||||||
if obj.sub == 'remove':
|
if obj.sub == 'remove':
|
||||||
return
|
return
|
||||||
# Add new contact to roster
|
# Add new contact to roster
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if obj.jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
|
|
||||||
contact = app.contacts.create_contact(jid=obj.jid,
|
contact = app.contacts.create_contact(jid=obj.jid,
|
||||||
account=account, name=obj.nickname, groups=obj.groups,
|
account=account, name=obj.nickname, groups=obj.groups,
|
||||||
show='offline', sub=obj.sub, ask=obj.ask, keyID=keyID,
|
show='offline', sub=obj.sub, ask=obj.ask,
|
||||||
avatar_sha=obj.avatar_sha)
|
avatar_sha=obj.avatar_sha)
|
||||||
app.contacts.add_contact(account, contact)
|
app.contacts.add_contact(account, contact)
|
||||||
self.roster.add_contact(obj.jid, account)
|
self.roster.add_contact(obj.jid, account)
|
||||||
|
@ -750,13 +708,8 @@ class Interface:
|
||||||
def handle_event_file_request(self, obj):
|
def handle_event_file_request(self, obj):
|
||||||
account = obj.conn.name
|
account = obj.conn.name
|
||||||
if obj.jid not in app.contacts.get_jid_list(account):
|
if obj.jid not in app.contacts.get_jid_list(account):
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if obj.jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(obj.jid) + 1]
|
|
||||||
contact = app.contacts.create_not_in_roster_contact(jid=obj.jid,
|
contact = app.contacts.create_not_in_roster_contact(jid=obj.jid,
|
||||||
account=account, keyID=keyID)
|
account=account)
|
||||||
app.contacts.add_contact(account, contact)
|
app.contacts.add_contact(account, contact)
|
||||||
self.roster.add_contact(obj.jid, account)
|
self.roster.add_contact(obj.jid, account)
|
||||||
contact = app.contacts.get_first_contact_from_jid(account, obj.jid)
|
contact = app.contacts.get_first_contact_from_jid(account, obj.jid)
|
||||||
|
@ -1287,7 +1240,6 @@ class Interface:
|
||||||
self.handlers = {
|
self.handlers = {
|
||||||
'DB_ERROR': [self.handle_event_db_error],
|
'DB_ERROR': [self.handle_event_db_error],
|
||||||
'file-send-error': [self.handle_event_file_send_error],
|
'file-send-error': [self.handle_event_file_send_error],
|
||||||
'bad-gpg-passphrase': [self.handle_event_bad_gpg_passphrase],
|
|
||||||
'bookmarks-received': [self.handle_event_bookmarks],
|
'bookmarks-received': [self.handle_event_bookmarks],
|
||||||
'client-cert-passphrase': [
|
'client-cert-passphrase': [
|
||||||
self.handle_event_client_cert_passphrase],
|
self.handle_event_client_cert_passphrase],
|
||||||
|
@ -1296,7 +1248,6 @@ class Interface:
|
||||||
'file-request-received': [self.handle_event_file_request],
|
'file-request-received': [self.handle_event_file_request],
|
||||||
'muc-invitation': [self.handle_event_gc_invitation],
|
'muc-invitation': [self.handle_event_gc_invitation],
|
||||||
'muc-decline': [self.handle_event_gc_decline],
|
'muc-decline': [self.handle_event_gc_decline],
|
||||||
'gpg-password-required': [self.handle_event_gpg_password_required],
|
|
||||||
'http-auth-received': [self.handle_event_http_auth],
|
'http-auth-received': [self.handle_event_http_auth],
|
||||||
'information': [self.handle_event_information],
|
'information': [self.handle_event_information],
|
||||||
'iq-error-received': [self.handle_event_iq_error],
|
'iq-error-received': [self.handle_event_iq_error],
|
||||||
|
@ -2326,7 +2277,6 @@ class Interface:
|
||||||
self.minimized_controls = {}
|
self.minimized_controls = {}
|
||||||
self.status_sent_to_users = {}
|
self.status_sent_to_users = {}
|
||||||
self.status_sent_to_groups = {}
|
self.status_sent_to_groups = {}
|
||||||
self.gpg_passphrase = {}
|
|
||||||
self.pass_dialog = {}
|
self.pass_dialog = {}
|
||||||
self.db_error_dialog = None
|
self.db_error_dialog = None
|
||||||
|
|
||||||
|
@ -2492,89 +2442,6 @@ class Interface:
|
||||||
self.network_state = self.network_monitor.get_network_available()
|
self.network_state = self.network_monitor.get_network_available()
|
||||||
|
|
||||||
|
|
||||||
class PassphraseRequest:
|
|
||||||
def __init__(self, keyid):
|
|
||||||
self.keyid = keyid
|
|
||||||
self.callbacks = []
|
|
||||||
self.dialog_created = False
|
|
||||||
self.dialog = None
|
|
||||||
self.passphrase = None
|
|
||||||
self.completed = False
|
|
||||||
|
|
||||||
def interrupt(self, account=None):
|
|
||||||
if account:
|
|
||||||
for (acct, cb) in self.callbacks:
|
|
||||||
if acct == account:
|
|
||||||
self.callbacks.remove((acct, cb))
|
|
||||||
else:
|
|
||||||
self.callbacks = []
|
|
||||||
if not self.callbacks:
|
|
||||||
self.dialog.window.destroy()
|
|
||||||
|
|
||||||
def run_callback(self, account, callback):
|
|
||||||
app.connections[account].gpg_passphrase(self.passphrase)
|
|
||||||
callback()
|
|
||||||
|
|
||||||
def add_callback(self, account, cb):
|
|
||||||
if self.completed:
|
|
||||||
self.run_callback(account, cb)
|
|
||||||
else:
|
|
||||||
self.callbacks.append((account, cb))
|
|
||||||
if not self.dialog_created:
|
|
||||||
self.create_dialog(account)
|
|
||||||
|
|
||||||
def complete(self, passphrase):
|
|
||||||
self.passphrase = passphrase
|
|
||||||
self.completed = True
|
|
||||||
if passphrase is not None:
|
|
||||||
GLib.timeout_add_seconds(30, app.interface.forget_gpg_passphrase,
|
|
||||||
self.keyid)
|
|
||||||
for (account, cb) in self.callbacks:
|
|
||||||
self.run_callback(account, cb)
|
|
||||||
self.callbacks = []
|
|
||||||
|
|
||||||
def create_dialog(self, account):
|
|
||||||
title = _('Passphrase Required')
|
|
||||||
second = _('Enter OpenPGP key passphrase for key %(keyid)s '
|
|
||||||
'(account %(account)s).') % {'keyid': self.keyid,
|
|
||||||
'account': account}
|
|
||||||
|
|
||||||
def _cancel():
|
|
||||||
# user cancelled, continue without GPG
|
|
||||||
self.complete(None)
|
|
||||||
|
|
||||||
def _ok(passphrase, checked, count):
|
|
||||||
result = app.connections[account].test_gpg_passphrase(passphrase)
|
|
||||||
if result == 'ok':
|
|
||||||
# passphrase is good
|
|
||||||
self.complete(passphrase)
|
|
||||||
return
|
|
||||||
|
|
||||||
if result == 'expired':
|
|
||||||
ErrorDialog(
|
|
||||||
_('OpenPGP key expired'),
|
|
||||||
_('Your OpenPGP key has expired, you will be connected to '
|
|
||||||
'%s without OpenPGP.') % account)
|
|
||||||
# Don't try to connect with GPG
|
|
||||||
app.connections[account].continue_connect_info[2] = False
|
|
||||||
self.complete(None)
|
|
||||||
return
|
|
||||||
|
|
||||||
if count < 3:
|
|
||||||
# ask again
|
|
||||||
dialogs.PassphraseDialog(
|
|
||||||
_('Wrong Passphrase'),
|
|
||||||
_('Please retype your OpenPGP passphrase or press Cancel.'),
|
|
||||||
ok_handler=(_ok, count + 1), cancel_handler=_cancel)
|
|
||||||
else:
|
|
||||||
# user failed 3 times, continue without GPG
|
|
||||||
self.complete(None)
|
|
||||||
|
|
||||||
self.dialog = dialogs.PassphraseDialog(
|
|
||||||
title, second, ok_handler=(_ok, 1), cancel_handler=_cancel)
|
|
||||||
self.dialog_created = True
|
|
||||||
|
|
||||||
|
|
||||||
class ThreadInterface:
|
class ThreadInterface:
|
||||||
def __init__(self, func, func_args=(), callback=None, callback_args=()):
|
def __init__(self, func, func_args=(), callback=None, callback_args=()):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -217,7 +217,6 @@ control=None, gc_contact=None, is_anonymous=True):
|
||||||
rename_menuitem = xml.get_object('rename_menuitem')
|
rename_menuitem = xml.get_object('rename_menuitem')
|
||||||
edit_groups_menuitem = xml.get_object('edit_groups_menuitem')
|
edit_groups_menuitem = xml.get_object('edit_groups_menuitem')
|
||||||
send_file_menuitem = xml.get_object('send_file_menuitem')
|
send_file_menuitem = xml.get_object('send_file_menuitem')
|
||||||
assign_openpgp_key_menuitem = xml.get_object('assign_openpgp_key_menuitem')
|
|
||||||
information_menuitem = xml.get_object('information_menuitem')
|
information_menuitem = xml.get_object('information_menuitem')
|
||||||
history_menuitem = xml.get_object('history_menuitem')
|
history_menuitem = xml.get_object('history_menuitem')
|
||||||
send_custom_status_menuitem = xml.get_object('send_custom_status_menuitem')
|
send_custom_status_menuitem = xml.get_object('send_custom_status_menuitem')
|
||||||
|
@ -290,16 +289,9 @@ control=None, gc_contact=None, is_anonymous=True):
|
||||||
# contact is in normal group
|
# contact is in normal group
|
||||||
edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
|
edit_groups_menuitem.connect('activate', roster.on_edit_groups, [(contact,
|
||||||
account)])
|
account)])
|
||||||
|
|
||||||
if app.connections[account].gpg:
|
|
||||||
assign_openpgp_key_menuitem.connect('activate',
|
|
||||||
roster.on_assign_pgp_key, contact, account)
|
|
||||||
else:
|
|
||||||
assign_openpgp_key_menuitem.set_sensitive(False)
|
|
||||||
else:
|
else:
|
||||||
# contact is in group 'Not in Roster'
|
# contact is in group 'Not in Roster'
|
||||||
edit_groups_menuitem.set_sensitive(False)
|
edit_groups_menuitem.set_sensitive(False)
|
||||||
assign_openpgp_key_menuitem.set_sensitive(False)
|
|
||||||
|
|
||||||
# Hide items when it's self contact row
|
# Hide items when it's self contact row
|
||||||
if our_jid:
|
if our_jid:
|
||||||
|
|
|
@ -5,8 +5,6 @@ from gajim.common.i18n import _
|
||||||
from gajim import gtkgui_helpers
|
from gajim import gtkgui_helpers
|
||||||
from gajim.common.const import OptionKind, OptionType
|
from gajim.common.const import OptionKind, OptionType
|
||||||
from gajim.common.exceptions import GajimGeneralException
|
from gajim.common.exceptions import GajimGeneralException
|
||||||
from gajim import dialogs
|
|
||||||
from gajim.gtk.dialogs import ErrorDialog
|
|
||||||
from gajim.gtk.dialogs import ChangePasswordDialog
|
from gajim.gtk.dialogs import ChangePasswordDialog
|
||||||
from gajim.gtk.util import get_image_button
|
from gajim.gtk.util import get_image_button
|
||||||
|
|
||||||
|
@ -74,7 +72,6 @@ class OptionsBox(Gtk.ListBox):
|
||||||
OptionKind.PRIORITY: PriorityOption,
|
OptionKind.PRIORITY: PriorityOption,
|
||||||
OptionKind.HOSTNAME: CutstomHostnameOption,
|
OptionKind.HOSTNAME: CutstomHostnameOption,
|
||||||
OptionKind.CHANGEPASSWORD: ChangePasswordOption,
|
OptionKind.CHANGEPASSWORD: ChangePasswordOption,
|
||||||
OptionKind.GPG: GPGOption,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if extend is not None:
|
if extend is not None:
|
||||||
|
@ -566,55 +563,3 @@ class ChangePasswordOption(DialogOption):
|
||||||
con = app.connections[self.account]
|
con = app.connections[self.account]
|
||||||
activatable = con.connected >= 2 and con.register_supported
|
activatable = con.connected >= 2 and con.register_supported
|
||||||
self.get_parent().set_activatable(activatable)
|
self.get_parent().set_activatable(activatable)
|
||||||
|
|
||||||
|
|
||||||
class GPGOption(DialogOption):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
DialogOption.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def show_dialog(self, parent):
|
|
||||||
secret_keys = app.connections[self.account].ask_gpg_secrete_keys()
|
|
||||||
secret_keys[_('None')] = _('None')
|
|
||||||
|
|
||||||
if not secret_keys:
|
|
||||||
ErrorDialog(
|
|
||||||
_('Failed to get secret keys'),
|
|
||||||
_('There is no OpenPGP secret key available.'),
|
|
||||||
transient_for=parent)
|
|
||||||
return
|
|
||||||
|
|
||||||
dialog = dialogs.ChooseGPGKeyDialog(
|
|
||||||
_('OpenPGP Key Selection'), _('Choose your OpenPGP key'),
|
|
||||||
secret_keys, self.on_key_selected, transient_for=parent)
|
|
||||||
dialog.window.connect('destroy', self.on_destroy)
|
|
||||||
|
|
||||||
def on_key_selected(self, keyID):
|
|
||||||
if keyID is None:
|
|
||||||
return
|
|
||||||
keyid_new, keyname_new = keyID
|
|
||||||
|
|
||||||
keyid = app.config.get_per('accounts', self.account, 'keyid')
|
|
||||||
|
|
||||||
if keyid_new == _('None'):
|
|
||||||
if keyid == '':
|
|
||||||
return
|
|
||||||
app.config.set_per('accounts', self.account, 'keyname', '')
|
|
||||||
app.config.set_per('accounts', self.account, 'keyid', '')
|
|
||||||
else:
|
|
||||||
if keyid == keyid_new:
|
|
||||||
return
|
|
||||||
app.config.set_per(
|
|
||||||
'accounts', self.account, 'keyname', keyname_new)
|
|
||||||
app.config.set_per(
|
|
||||||
'accounts', self.account, 'keyid', keyid_new)
|
|
||||||
|
|
||||||
def get_option_value(self):
|
|
||||||
keyid = app.config.get_per('accounts', self.account, 'keyid')
|
|
||||||
keyname = app.config.get_per('accounts', self.account, 'keyname')
|
|
||||||
if keyid is not None:
|
|
||||||
return '\n'.join((keyid, keyname))
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def set_activatable(self, name, value):
|
|
||||||
active = self.account in app.connections
|
|
||||||
self.get_parent().set_activatable(app.is_installed('GPG') and active)
|
|
||||||
|
|
|
@ -194,7 +194,6 @@ class GajimRemote(Server):
|
||||||
<method name='send_chat_message'>
|
<method name='send_chat_message'>
|
||||||
<arg name='jid' type='s' />
|
<arg name='jid' type='s' />
|
||||||
<arg name='message' type='s' />
|
<arg name='message' type='s' />
|
||||||
<arg name='keyID' type='s' />
|
|
||||||
<arg name='account' type='s' />
|
<arg name='account' type='s' />
|
||||||
<arg direction='out' type='b' />
|
<arg direction='out' type='b' />
|
||||||
</method>
|
</method>
|
||||||
|
@ -214,7 +213,6 @@ class GajimRemote(Server):
|
||||||
<arg name='jid' type='s' />
|
<arg name='jid' type='s' />
|
||||||
<arg name='subject' type='s' />
|
<arg name='subject' type='s' />
|
||||||
<arg name='message' type='s' />
|
<arg name='message' type='s' />
|
||||||
<arg name='keyID' type='s' />
|
|
||||||
<arg name='account' type='s' />
|
<arg name='account' type='s' />
|
||||||
<arg direction='out' type='b' />
|
<arg direction='out' type='b' />
|
||||||
</method>
|
</method>
|
||||||
|
@ -339,7 +337,7 @@ class GajimRemote(Server):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
chatstate = ''
|
chatstate = ''
|
||||||
self.raise_signal('MessageSent', (obj.conn.name, [
|
self.raise_signal('MessageSent', (obj.conn.name, [
|
||||||
obj.jid, obj.message, obj.keyID, chatstate]))
|
obj.jid, obj.message, chatstate]))
|
||||||
|
|
||||||
def on_time(self, obj):
|
def on_time(self, obj):
|
||||||
self.raise_signal('EntityTime', (obj.conn.name, [obj.jid.getStripped(),
|
self.raise_signal('EntityTime', (obj.conn.name, [obj.jid.getStripped(),
|
||||||
|
@ -360,7 +358,7 @@ class GajimRemote(Server):
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
self.raise_signal(event, (obj.conn.name, [obj.jid, obj.show,
|
self.raise_signal(event, (obj.conn.name, [obj.jid, obj.show,
|
||||||
obj.status, obj.resource, obj.prio, obj.keyID, obj.timestamp]))
|
obj.status, obj.resource, obj.prio, obj.timestamp]))
|
||||||
|
|
||||||
def on_subscribe_presence_received(self, obj):
|
def on_subscribe_presence_received(self, obj):
|
||||||
self.raise_signal('Subscribe', (obj.conn.name, [obj.jid, obj.status,
|
self.raise_signal('Subscribe', (obj.conn.name, [obj.jid, obj.status,
|
||||||
|
@ -504,7 +502,6 @@ class GajimRemote(Server):
|
||||||
def _send_message(self,
|
def _send_message(self,
|
||||||
jid,
|
jid,
|
||||||
message,
|
message,
|
||||||
keyID,
|
|
||||||
account,
|
account,
|
||||||
type_='chat',
|
type_='chat',
|
||||||
subject=None):
|
subject=None):
|
||||||
|
@ -514,8 +511,6 @@ class GajimRemote(Server):
|
||||||
"""
|
"""
|
||||||
if not jid or not message:
|
if not jid or not message:
|
||||||
return False
|
return False
|
||||||
if not keyID:
|
|
||||||
keyID = ''
|
|
||||||
|
|
||||||
connected_account = self._get_account_and_contact(account, jid)[0]
|
connected_account = self._get_account_and_contact(account, jid)[0]
|
||||||
if connected_account:
|
if connected_account:
|
||||||
|
@ -534,29 +529,25 @@ class GajimRemote(Server):
|
||||||
account=connected_account,
|
account=connected_account,
|
||||||
jid=jid,
|
jid=jid,
|
||||||
message=message,
|
message=message,
|
||||||
keyID=keyID,
|
|
||||||
type_=type_,
|
type_=type_,
|
||||||
control=ctrl))
|
control=ctrl))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def send_chat_message(self, jid, message, keyID, account):
|
def send_chat_message(self, jid, message, account):
|
||||||
"""
|
"""
|
||||||
Send chat 'message' to 'jid', using account (optional) 'account'. If keyID
|
Send chat 'message' to 'jid', using account (optional) 'account'.
|
||||||
is specified, encrypt the message with the pgp key
|
|
||||||
"""
|
"""
|
||||||
jid = self._get_real_jid(jid, account)
|
jid = self._get_real_jid(jid, account)
|
||||||
return self._send_message(jid, message, keyID, account)
|
return self._send_message(jid, message, account)
|
||||||
|
|
||||||
def send_single_message(self, jid, subject, message, keyID, account):
|
def send_single_message(self, jid, subject, message, account):
|
||||||
"""
|
"""
|
||||||
Send single 'message' to 'jid', using account (optional) 'account'. If
|
Send single 'message' to 'jid', using account (optional) 'account'.
|
||||||
keyID is specified, encrypt the message with the pgp key
|
|
||||||
"""
|
"""
|
||||||
jid = self._get_real_jid(jid, account)
|
jid = self._get_real_jid(jid, account)
|
||||||
return self._send_message(jid, message, keyID, account, 'normal',
|
return self._send_message(jid, message, account, 'normal', subject)
|
||||||
subject)
|
|
||||||
|
|
||||||
def send_groupchat_message(self, room_jid, message, account):
|
def send_groupchat_message(self, room_jid, message, account):
|
||||||
"""
|
"""
|
||||||
|
@ -879,14 +870,7 @@ class GajimRemote(Server):
|
||||||
contact_dict['name'] = GLib.Variant('s', name)
|
contact_dict['name'] = GLib.Variant('s', name)
|
||||||
contact_dict['show'] = GLib.Variant('s', prim_contact.show)
|
contact_dict['show'] = GLib.Variant('s', prim_contact.show)
|
||||||
contact_dict['jid'] = GLib.Variant('s', prim_contact.jid)
|
contact_dict['jid'] = GLib.Variant('s', prim_contact.jid)
|
||||||
if prim_contact.keyID:
|
|
||||||
keyID = None
|
|
||||||
if len(prim_contact.keyID) == 8:
|
|
||||||
keyID = prim_contact.keyID
|
|
||||||
elif len(prim_contact.keyID) == 16:
|
|
||||||
keyID = prim_contact.keyID[8:]
|
|
||||||
if keyID:
|
|
||||||
contact_dict['openpgp'] = GLib.Variant('s', keyID)
|
|
||||||
resources = GLib.VariantBuilder(GLib.VariantType('a(sis)'))
|
resources = GLib.VariantBuilder(GLib.VariantType('a(sis)'))
|
||||||
for contact in contacts:
|
for contact in contacts:
|
||||||
resource_props = (contact.resource, int(contact.priority),
|
resource_props = (contact.resource, int(contact.priority),
|
||||||
|
|
|
@ -998,14 +998,9 @@ class RosterWindow:
|
||||||
# FIXME: integrate into add_contact()
|
# FIXME: integrate into add_contact()
|
||||||
def add_to_not_in_the_roster(self, account, jid, nick='', resource='',
|
def add_to_not_in_the_roster(self, account, jid, nick='', resource='',
|
||||||
groupchat=False):
|
groupchat=False):
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
contact = app.contacts.create_not_in_roster_contact(
|
contact = app.contacts.create_not_in_roster_contact(
|
||||||
jid=jid, account=account, resource=resource, name=nick,
|
jid=jid, account=account, resource=resource, name=nick,
|
||||||
keyID=keyID, groupchat=groupchat)
|
groupchat=groupchat)
|
||||||
app.contacts.add_contact(account, contact)
|
app.contacts.add_contact(account, contact)
|
||||||
self.add_contact(contact.jid, account)
|
self.add_contact(contact.jid, account)
|
||||||
return contact
|
return contact
|
||||||
|
@ -1871,19 +1866,13 @@ class RosterWindow:
|
||||||
show = 'offline' # show is offline by default
|
show = 'offline' # show is offline by default
|
||||||
status = '' # no status message by default
|
status = '' # no status message by default
|
||||||
|
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
|
|
||||||
if app.jid_is_transport(jid):
|
if app.jid_is_transport(jid):
|
||||||
array[jid]['groups'] = [_('Transports')]
|
array[jid]['groups'] = [_('Transports')]
|
||||||
#TRANSP - potential
|
#TRANSP - potential
|
||||||
contact1 = app.contacts.create_contact(jid=ji, account=account,
|
contact1 = app.contacts.create_contact(jid=ji, account=account,
|
||||||
name=name, groups=array[jid]['groups'], show=show,
|
name=name, groups=array[jid]['groups'], show=show,
|
||||||
status=status, sub=array[jid]['subscription'],
|
status=status, sub=array[jid]['subscription'],
|
||||||
ask=array[jid]['ask'], resource=resource, keyID=keyID)
|
ask=array[jid]['ask'], resource=resource)
|
||||||
app.contacts.add_contact(account, contact1)
|
app.contacts.add_contact(account, contact1)
|
||||||
|
|
||||||
# If we already have chat windows opened, update them with new
|
# If we already have chat windows opened, update them with new
|
||||||
|
@ -2031,14 +2020,9 @@ class RosterWindow:
|
||||||
jid, txt, nickname, groups_list, auto_auth)
|
jid, txt, nickname, groups_list, auto_auth)
|
||||||
contact = app.contacts.get_contact_with_highest_priority(account, jid)
|
contact = app.contacts.get_contact_with_highest_priority(account, jid)
|
||||||
if not contact:
|
if not contact:
|
||||||
keyID = ''
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
if jid in attached_keys:
|
|
||||||
keyID = attached_keys[attached_keys.index(jid) + 1]
|
|
||||||
contact = app.contacts.create_contact(jid=jid, account=account,
|
contact = app.contacts.create_contact(jid=jid, account=account,
|
||||||
name=nickname, groups=groups_list, show='requested', status='',
|
name=nickname, groups=groups_list, show='requested', status='',
|
||||||
ask='none', sub='subscribe', keyID=keyID)
|
ask='none', sub='subscribe')
|
||||||
app.contacts.add_contact(account, contact)
|
app.contacts.add_contact(account, contact)
|
||||||
else:
|
else:
|
||||||
if not _('Not in Roster') in contact.get_shown_groups():
|
if not _('Not in Roster') in contact.get_shown_groups():
|
||||||
|
@ -2079,13 +2063,6 @@ class RosterWindow:
|
||||||
if app.connections[account].connected < 2:
|
if app.connections[account].connected < 2:
|
||||||
self.set_connecting_state(account)
|
self.set_connecting_state(account)
|
||||||
|
|
||||||
keyid = app.config.get_per('accounts', account, 'keyid')
|
|
||||||
if keyid and not app.connections[account].gpg:
|
|
||||||
WarningDialog(_('OpenPGP is not usable'),
|
|
||||||
_('Gajim needs python-gnupg >= 0.3.8\n'
|
|
||||||
'Beware there is an incompatible Python package called gnupg.\n'
|
|
||||||
'You will be connected to %s without OpenPGP.') % account)
|
|
||||||
|
|
||||||
self.send_status_continue(account, status, txt, auto, to)
|
self.send_status_continue(account, status, txt, auto, to)
|
||||||
|
|
||||||
def send_pep(self, account, pep_dict):
|
def send_pep(self, account, pep_dict):
|
||||||
|
@ -2941,46 +2918,6 @@ class RosterWindow:
|
||||||
_('Also remove all contacts in this group from your roster'),
|
_('Also remove all contacts in this group from your roster'),
|
||||||
on_response_ok=on_ok)
|
on_response_ok=on_ok)
|
||||||
|
|
||||||
def on_assign_pgp_key(self, widget, contact, account):
|
|
||||||
attached_keys = app.config.get_per('accounts', account,
|
|
||||||
'attached_gpg_keys').split()
|
|
||||||
keys = {}
|
|
||||||
keyID = _('None')
|
|
||||||
for i in range(len(attached_keys) // 2):
|
|
||||||
keys[attached_keys[2*i]] = attached_keys[2*i+1]
|
|
||||||
if attached_keys[2*i] == contact.jid:
|
|
||||||
keyID = attached_keys[2*i+1]
|
|
||||||
public_keys = app.connections[account].ask_gpg_keys()
|
|
||||||
public_keys[_('None')] = _('None')
|
|
||||||
|
|
||||||
def on_key_selected(keyID):
|
|
||||||
if keyID is None:
|
|
||||||
return
|
|
||||||
if keyID[0] == _('None'):
|
|
||||||
if contact.jid in keys:
|
|
||||||
del keys[contact.jid]
|
|
||||||
keyID = ''
|
|
||||||
else:
|
|
||||||
keyID = keyID[0]
|
|
||||||
keys[contact.jid] = keyID
|
|
||||||
|
|
||||||
ctrl = app.interface.msg_win_mgr.get_control(contact.jid, account)
|
|
||||||
if ctrl:
|
|
||||||
ctrl.update_ui()
|
|
||||||
|
|
||||||
keys_str = ''
|
|
||||||
for jid in keys:
|
|
||||||
keys_str += jid + ' ' + keys[jid] + ' '
|
|
||||||
app.config.set_per('accounts', account, 'attached_gpg_keys',
|
|
||||||
keys_str)
|
|
||||||
for u in app.contacts.get_contacts(account, contact.jid):
|
|
||||||
u.keyID = helpers.prepare_and_validate_gpg_keyID(account,
|
|
||||||
contact.jid, keyID)
|
|
||||||
|
|
||||||
dialogs.ChooseGPGKeyDialog(_('Assign OpenPGP Key'),
|
|
||||||
_('Select a key to apply to the contact'), public_keys,
|
|
||||||
on_key_selected, selected=keyID, transient_for=self.window)
|
|
||||||
|
|
||||||
def on_edit_groups(self, widget, list_):
|
def on_edit_groups(self, widget, list_):
|
||||||
dialogs.EditGroupsDialog(list_)
|
dialogs.EditGroupsDialog(list_)
|
||||||
|
|
||||||
|
|
3
mypy.ini
3
mypy.ini
|
@ -27,9 +27,6 @@ ignore_missing_imports = True
|
||||||
[mypy-pybonjour.*]
|
[mypy-pybonjour.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
[mypy-gnupg.*]
|
|
||||||
ignore_missing_imports = True
|
|
||||||
|
|
||||||
[mypy-encodings.*]
|
[mypy-encodings.*]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue