Merge branch 'master' into 'gnotification'

# Conflicts:
#   gajim/gajim.py
This commit is contained in:
Yann Leboulanger 2017-09-21 20:44:18 +02:00
commit 66e9bc4e03
2869 changed files with 1813 additions and 3429 deletions

View file

@ -3,7 +3,7 @@
CONF_ARGS=""
echo "[encoding: UTF-8]" > po/POTFILES.in \
&& for p in `ls data/gui/*.ui`; do echo "[type: gettext/glade]$p" >> \
&& for p in `ls gajim/data/gui/*.ui`; do echo "[type: gettext/glade]$p" >> \
po/POTFILES.in; done \
&& ls -1 data/org.gajim.Gajim.appdata.xml.in data/org.gajim.Gajim.desktop.in.in data/gajim-remote.desktop.in.in \
gajim/*.py gajim/common/*.py gajim/command_system/*.py gajim/command_system/implementation/*.py gajim/common/zeroconf/*.py gajim/plugins/*.py | grep -v ipython_view.py >> \

View file

@ -54,12 +54,13 @@ AC_SUBST(PACKAGE)
AC_CONFIG_FILES([
Makefile
data/Makefile
data/gui/Makefile
data/emoticons/Makefile
gajim/data/Makefile
gajim/data/gui/Makefile
gajim/data/emoticons/Makefile
data/pixmaps/Makefile
data/iconsets/Makefile
data/moods/Makefile
data/activities/Makefile
gajim/data/iconsets/Makefile
gajim/data/moods/Makefile
gajim/data/activities/Makefile
icons/Makefile
data/org.gajim.Gajim.appdata.xml
data/org.gajim.Gajim.desktop.in

View file

@ -1,4 +1,4 @@
SUBDIRS = gui emoticons pixmaps iconsets moods activities
SUBDIRS = pixmaps
@INTLTOOL_DESKTOP_RULE@
appstreamdir = $(datadir)/metainfo/
@ -13,25 +13,12 @@ desktop_DATA = $(desktop_in_files:.desktop.in.in=.desktop)
installdefsdir = $(gajim_srcdir)/common
installdefs_DATA = defs.py
soundsdir = $(pkgdatadir)/data/sounds
sounds_DATA = $(srcdir)/sounds/*.wav
styledir = $(pkgdatadir)/data/style
style_DATA = $(srcdir)/style/*.css
otherdir = $(pkgdatadir)/data/other
other_DATA = other/servers.xml other/dh4096.pem
# other/cacert.pem is used only on Windows. On Unix platforms
# use CA certificates installed in /etc/ssl/certs
man_MANS = gajim.1 gajim-remote.1 gajim-history-manager.1
EXTRA_DIST = $(appstream_in_files) \
$(desktop_in_files) \
$(sounds_DATA) \
$(style_DATA) \
$(other_DATA) \
$(man_MANS) \
defs.py.in

View file

@ -27,7 +27,7 @@ Where to look for logs file
.El
.Sh FILES
.Bl -tag -width Ds
.It $XDG_DATA_DIR/gajim/logs.db
.It $XDG_DATA_HOME/gajim/logs.db
The history database log file used when
.Op Fl c
is not specified.

View file

@ -66,19 +66,19 @@ Where to look for configuration files
.El
.Sh FILES
.Bl -tag -width Ds
.It $XDG_CACHE_DIR/gajim/cache.db
.It $XDG_CACHE_HOME/gajim/cache.db
Cache database file of transports, caps, roster entry, and roster group.
.It $XDG_CACHE_DIR/gajim/avatars/
.It $XDG_CACHE_HOME/gajim/avatars/
Cache directory of avatars.
.It $XDG_CACHE_DIR/gajim/vcards/
.It $XDG_CACHE_HOME/gajim/vcards/
Cache directory of vCards (virtual cards).
.It $XDG_CONFIG_DIR/gajim/
.It $XDG_CONFIG_HOME/gajim/
The config-path used when
.Op Fl c
is not specified.
.It $XDG_DATA_DIR/gajim/certs/
.It $XDG_DATA_HOME/gajim/certs/
Directory where certificates are stored.
.It $XDG_DATA_DIR/gajim/logs.db
.It $XDG_DATA_HOME/gajim/logs.db
The history database log file.
For more information on database logs see
<https://dev.gajim.org/gajim/gajim/wikis/development/LogsDatabase>.

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
SUBDIRS = data
INCLUDES = \
$(PYTHON_INCLUDES)
export MACOSX_DEPLOYMENT_TARGET=10.4

698
gajim/accounts_window.py Normal file
View file

@ -0,0 +1,698 @@
from functools import partial
from gi.repository import Gtk, Gio, GLib, Gdk
from gajim.common import app
from gajim.gtkgui_helpers import get_image_button
from gajim import gtkgui_helpers
from gajim import gui_menu_builder
from gajim.common import passwords
from gajim import dialogs
from gajim import config
from gajim.common import helpers
from gajim.common.connection import Connection
from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
from gajim.options_dialog import OptionsDialog, OptionsBox
from gajim.common.const import Option, OptionKind, OptionType
class AccountsWindow(Gtk.ApplicationWindow):
def __init__(self):
Gtk.ApplicationWindow.__init__(self)
self.set_application(app.app)
self.set_position(Gtk.WindowPosition.CENTER)
self.set_show_menubar(False)
self.set_name('AccountsWindow')
self.set_size_request(500, -1)
self.set_resizable(False)
self.need_relogin = {}
glade_objects = [
'stack', 'box', 'actionbar', 'headerbar', 'back_button',
'menu_button', 'account_page', 'account_list']
self.builder = gtkgui_helpers.get_gtk_builder('accounts_window.ui')
for obj in glade_objects:
setattr(self, obj, self.builder.get_object(obj))
self.set_titlebar(self.headerbar)
menu = Gio.Menu()
menu.append('Merge Accounts', 'app.merge')
menu.append('Use PGP Agent', 'app.agent')
self.menu_button.set_menu_model(menu)
button = get_image_button('list-add-symbolic', 'Add')
button.set_action_name('app.add-account')
self.actionbar.pack_start(button)
accounts = app.config.get_per('accounts')
accounts.sort()
for account in accounts:
self.need_relogin[account] = self.get_relogin_options(account)
account_item = Account(account, self)
self.account_list.add(account_item)
account_item.set_activatable()
self.add(self.box)
self.builder.connect_signals(self)
self.connect('destroy', self.on_destroy)
self.connect('key-press-event', self.on_key_press)
self.show_all()
def on_key_press(self, widget, event):
if event.keyval == Gdk.KEY_Escape:
self.destroy()
def on_destroy(self, *args):
self.check_relogin()
del app.interface.instances['accounts']
def on_child_visible(self, stack, *args):
page = stack.get_visible_child_name()
if page is None:
return
if page == 'main':
self.menu_button.show()
self.back_button.hide()
self.check_relogin()
else:
self.back_button.show()
self.menu_button.hide()
def on_back_button(self, *args):
page = self.stack.get_visible_child_name()
child = self.stack.get_visible_child()
self.remove_all_pages()
if page == 'account':
child.toggle.set_active(False)
self.stack.add_named(self.account_page, 'main')
self.stack.set_visible_child_name('main')
self.update_accounts()
else:
self.stack.add_named(child.parent, 'account')
self.stack.set_visible_child_name('account')
def update_accounts(self):
for row in self.account_list.get_children():
row.get_child().update()
@staticmethod
def on_row_activated(listbox, row):
row.get_child().on_row_activated()
def remove_all_pages(self):
for page in self.stack.get_children():
self.stack.remove(page)
def set_page(self, page, name):
self.remove_all_pages()
self.stack.add_named(page, name)
page.update()
page.show_all()
self.stack.set_visible_child(page)
def update_proxy_list(self):
page = self.stack.get_child_by_name('connetion')
if page is None:
return
page.options['proxy'].update_values()
def check_relogin(self):
for account in self.need_relogin:
options = self.get_relogin_options(account)
active = app.config.get_per('accounts', account, 'active')
if options != self.need_relogin[account]:
self.need_relogin[account] = options
if active:
self.relog(account)
break
def relog(self, account):
if app.connections[account].connected == 0:
return
if account == app.ZEROCONF_ACC_NAME:
app.connections[app.ZEROCONF_ACC_NAME].update_details()
return
def login(account, show_before, status_before):
"""
Login with previous status
"""
# first make sure connection is really closed,
# 0.5 may not be enough
app.connections[account].disconnect(True)
app.interface.roster.send_status(
account, show_before, status_before)
def relog(account):
show_before = app.SHOW_LIST[app.connections[account].connected]
status_before = app.connections[account].status
app.interface.roster.send_status(
account, 'offline', _('Be right back.'))
GLib.timeout_add(500, login, account, show_before, status_before)
dialogs.YesNoDialog(
_('Relogin now?'),
_('If you want all the changes to apply instantly, '
'you must relogin.'),
transient_for=self,
on_response_yes=lambda *args: relog(account))
@staticmethod
def get_relogin_options(account):
if account == app.ZEROCONF_ACC_NAME:
options = ['zeroconf_first_name', 'zeroconf_last_name',
'zeroconf_jabber_id', 'zeroconf_email', 'keyid']
else:
options = ['client_cert', 'proxy', 'resource',
'use_custom_host', 'custom_host', 'custom_port',
'keyid']
values = []
for option in options:
values.append(app.config.get_per('accounts', account, option))
return values
def on_remove_account(self, button, account):
if app.events.get_events(account):
dialogs.ErrorDialog(
_('Unread events'),
_('Read all pending events before removing this account.'),
transient_for=self)
return
if app.config.get_per('accounts', account, 'is_zeroconf'):
# Should never happen as button is insensitive
return
win_opened = False
if app.interface.msg_win_mgr.get_controls(acct=account):
win_opened = True
elif account in app.interface.instances:
for key in app.interface.instances[account]:
if (app.interface.instances[account][key] and
key != 'remove_account'):
win_opened = True
break
# Detect if we have opened windows for this account
def remove(account):
if (account in app.interface.instances and
'remove_account' in app.interface.instances[account]):
dialog = app.interface.instances[account]['remove_account']
dialog.window.present()
else:
if account not in app.interface.instances:
app.interface.instances[account] = {}
app.interface.instances[account]['remove_account'] = \
config.RemoveAccountWindow(account)
if win_opened:
dialogs.ConfirmationDialog(
_('You have opened chat in account %s') % account,
_('All chat and groupchat windows will be closed. '
'Do you want to continue?'),
on_response_ok=(remove, account))
else:
remove(account)
def remove_account(self, account):
for row in self.account_list.get_children():
if row.get_child().account == account:
self.account_list.remove(row)
del self.need_relogin[account]
break
def add_account(self, account):
account_item = Account(account, self)
self.account_list.add(account_item)
account_item.set_activatable()
self.account_list.show_all()
self.stack.show_all()
self.need_relogin[account] = self.get_relogin_options(account)
def select_account(self, account):
for row in self.account_list.get_children():
if row.get_child().account == account:
self.account_list.emit('row-activated', row)
break
@staticmethod
def enable_account(account):
if account == app.ZEROCONF_ACC_NAME:
app.connections[account] = ConnectionZeroconf(account)
else:
app.connections[account] = Connection(account)
# update variables
app.interface.instances[account] = {
'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {},
'online_dialog': {}, 'sub_request': {}}
app.interface.minimized_controls[account] = {}
app.connections[account].connected = 0
app.groups[account] = {}
app.contacts.add_account(account)
app.gc_connected[account] = {}
app.automatic_rooms[account] = {}
app.newly_added[account] = []
app.to_be_removed[account] = []
if account == app.ZEROCONF_ACC_NAME:
app.nicks[account] = app.ZEROCONF_ACC_NAME
else:
app.nicks[account] = app.config.get_per(
'accounts', account, 'name')
app.block_signed_in_notifications[account] = True
app.sleeper_state[account] = 'off'
app.encrypted_chats[account] = []
app.last_message_time[account] = {}
app.status_before_autoaway[account] = ''
app.transport_avatar[account] = {}
app.gajim_optional_features[account] = []
app.caps_hash[account] = ''
helpers.update_optional_features(account)
# refresh roster
if len(app.connections) >= 2:
# Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
gui_menu_builder.build_accounts_menu()
@staticmethod
def disable_account(account):
app.interface.roster.close_all(account)
if account == app.ZEROCONF_ACC_NAME:
app.connections[account].disable_account()
app.connections[account].cleanup()
del app.connections[account]
del app.interface.instances[account]
del app.interface.minimized_controls[account]
del app.nicks[account]
del app.block_signed_in_notifications[account]
del app.groups[account]
app.contacts.remove_account(account)
del app.gc_connected[account]
del app.automatic_rooms[account]
del app.to_be_removed[account]
del app.newly_added[account]
del app.sleeper_state[account]
del app.encrypted_chats[account]
del app.last_message_time[account]
del app.status_before_autoaway[account]
del app.transport_avatar[account]
del app.gajim_optional_features[account]
del app.caps_hash[account]
if len(app.connections) >= 2:
# Do not merge accounts if only one exists
app.interface.roster.regroup = app.config.get('mergeaccounts')
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
gui_menu_builder.build_accounts_menu()
class Account(Gtk.Box):
def __init__(self, account, parent):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
spacing=12)
self.account = account
if account == app.ZEROCONF_ACC_NAME:
self.options = ZeroConfPage(account)
else:
self.options = AccountPage(account)
self.parent = parent
switch = Gtk.Switch()
switch.set_active(app.config.get_per('accounts', account, 'active'))
switch.set_vexpand(False)
switch.set_valign(Gtk.Align.CENTER)
switch.set_halign(Gtk.Align.START)
if account == app.ZEROCONF_ACC_NAME and not app.HAVE_ZEROCONF:
switch.set_sensitive(False)
switch.set_active(False)
switch.connect('notify::active', self.on_switch, self.account)
account_label = app.config.get_per('accounts', account, 'account_label')
self.label = Gtk.Label(label=account_label or account)
self.label.set_halign(Gtk.Align.START)
self.label.set_hexpand(True)
self.add(switch)
self.add(self.label)
if account != app.ZEROCONF_ACC_NAME:
button = get_image_button('list-remove-symbolic', 'Remove')
button.connect('clicked', parent.on_remove_account, account)
self.add(button)
def set_activatable(self):
if self.account == app.ZEROCONF_ACC_NAME:
self.get_parent().set_activatable(app.HAVE_ZEROCONF)
def on_switch(self, switch, param, account):
old_state = app.config.get_per('accounts', account, 'active')
state = switch.get_active()
if old_state == state:
return
if (account in app.connections and
app.connections[account].connected > 0):
# connecting or connected
dialogs.ErrorDialog(
_('You are currently connected to the server'),
_('To disable the account, you must be disconnected.'),
transient_for=self.parent)
switch.set_active(not state)
return
if state:
self.parent.enable_account(account)
else:
self.parent.disable_account(account)
app.config.set_per('accounts', account, 'active', state)
def on_row_activated(self):
self.options.update_states()
self.parent.set_page(self.options, 'account')
def update(self):
account_label = app.config.get_per(
'accounts', self.account, 'account_label')
self.label.set_text(account_label or self.account)
class GenericOptionPage(Gtk.Box):
def __init__(self, account, parent, options):
Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
self.account = account
self.parent = parent
self.toggle = get_image_button('document-edit-symbolic',
_('Rename account label'), toggle=True)
self.toggle.connect('toggled', self.set_entry_text)
self.entry = Gtk.Entry()
self.entry.set_sensitive(False)
self.entry.set_name('AccountNameEntry')
self.set_entry_text(self.toggle, update=True)
box = Gtk.Box()
if isinstance(self, AccountPage):
box.pack_start(self.toggle, False, True, 0)
box.pack_start(self.entry, True, True, 0)
self.listbox = OptionsBox(account)
self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
for option in options:
self.listbox.add_option(option)
self.listbox.update_states()
self.pack_start(box, False, False, 0)
self.pack_start(self.listbox, True, True, 0)
self.listbox.connect('row-activated', self.on_row_activated)
def update_states(self):
self.listbox.update_states()
def on_row_activated(self, listbox, row):
self.toggle.set_active(False)
row.get_child().on_row_activated()
def set_entry_text(self, toggle, update=False):
account_label = app.config.get_per(
'accounts', self.account, 'account_label')
if update:
self.entry.set_text(account_label or self.account)
return
if toggle.get_active():
self.entry.set_sensitive(True)
self.entry.grab_focus()
else:
self.entry.set_sensitive(False)
value = self.entry.get_text()
if not value:
value = account_label or self.account
app.config.set_per('accounts', self.account,
'account_label', value or self.account)
if app.config.get_per('accounts', self.account, 'active'):
app.interface.roster.draw_account(self.account)
def update(self):
self.set_entry_text(self.toggle, update=True)
def set_page(self, options, name):
options.update_states()
self.get_toplevel().set_page(options, name)
class AccountPage(GenericOptionPage):
def __init__(self, account, parent=None):
general = partial(
self.set_page, GeneralPage(account, self), 'general')
connection = partial(
self.set_page, ConnectionPage(account, self), 'connection')
options = [
Option(OptionKind.LOGIN, _('Login'), OptionType.DIALOG,
props={'dialog': LoginDialog}),
Option(OptionKind.ACTION, _('Profile'), OptionType.ACTION,
'-profile', props={'action_args': account}),
Option(OptionKind.CALLBACK, _('General'),
name='general', props={'callback': general}),
Option(OptionKind.CALLBACK, _('Connection'),
name='connection', props={'callback': connection}),
Option(OptionKind.ACTION, _('Import Contacts'), OptionType.ACTION,
'-import-contacts', props={'action_args': account}),
Option(OptionKind.DIALOG, _('Client Certificate'),
OptionType.DIALOG, props={'dialog': CertificateDialog}),
Option(OptionKind.GPG, _('OpenPGP Key'), OptionType.DIALOG,
props={'dialog': None}),
]
GenericOptionPage.__init__(self, account, parent, options)
class GeneralPage(GenericOptionPage):
def __init__(self, account, parent=None):
options = [
Option(OptionKind.SWITCH, _('Connect on startup'),
OptionType.ACCOUNT_CONFIG, 'autoconnect'),
Option(OptionKind.SWITCH, _('Reconnect when connection is lost'),
OptionType.ACCOUNT_CONFIG, 'autoreconnect'),
Option(OptionKind.SWITCH, _('Save conversations for all contacts'),
OptionType.ACCOUNT_CONFIG, 'no_log_for',
desc=_('Store conversations on the harddrive')),
Option(OptionKind.SWITCH, _('Server Message Archive'),
OptionType.ACCOUNT_CONFIG, 'sync_logs_with_server',
desc=_('Messages get stored on the server.\n'
'The archive is used to sync messages\n'
'between multiple devices.\n'
'XEP-0313')),
Option(OptionKind.SWITCH, _('Global Status'),
OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
desc=_('Synchronise the status of all accounts')),
Option(OptionKind.SWITCH, _('Message Carbons'),
OptionType.ACCOUNT_CONFIG, 'enable_message_carbons',
desc=_('All your other online devices get copies\n'
'of sent and received messages.\n'
'XEP-0280')),
Option(OptionKind.SWITCH, _('Use file transfer proxies'),
OptionType.ACCOUNT_CONFIG, 'use_ft_proxies'),
]
GenericOptionPage.__init__(self, account, parent, options)
class ConnectionPage(GenericOptionPage):
def __init__(self, account, parent=None):
options = [
Option(OptionKind.SWITCH, 'HTTP_PROXY',
OptionType.ACCOUNT_CONFIG, 'use_env_http_proxy',
desc=_('Use environment variable')),
Option(OptionKind.PROXY, _('Proxy'),
OptionType.ACCOUNT_CONFIG, 'proxy', name='proxy'),
Option(OptionKind.SWITCH, _('Warn on insecure connection'),
OptionType.ACCOUNT_CONFIG,
'warn_when_insecure_ssl_connection'),
Option(OptionKind.SWITCH, _('Send keep-alive packets'),
OptionType.ACCOUNT_CONFIG, 'keep_alives_enabled'),
Option(OptionKind.HOSTNAME, _('Hostname'), OptionType.DIALOG,
desc=_('Manually set the hostname for the server'),
props={'dialog': CutstomHostnameDialog}),
Option(OptionKind.ENTRY, _('Resource'),
OptionType.ACCOUNT_CONFIG, 'resource'),
Option(OptionKind.PRIORITY, _('Priority'),
OptionType.DIALOG, props={'dialog': PriorityDialog}),
]
GenericOptionPage.__init__(self, account, parent, options)
class ZeroConfPage(GenericOptionPage):
def __init__(self, account, parent=None):
options = [
Option(OptionKind.DIALOG, _('Credentials'),
OptionType.DIALOG, props={'dialog': CredentialsDialog}),
Option(OptionKind.SWITCH, _('Connect on startup'),
OptionType.ACCOUNT_CONFIG, 'autoconnect',
desc=_('Use environment variable')),
Option(OptionKind.SWITCH, _('Save conversations for all contacts'),
OptionType.ACCOUNT_CONFIG, 'no_log_for',
desc=_('Store conversations on the harddrive')),
Option(OptionKind.SWITCH, _('Global Status'),
OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
desc=_('Synchronize the status of all accounts')),
Option(OptionKind.GPG, _('OpenPGP Key'),
OptionType.DIALOG, props={'dialog': None}),
]
GenericOptionPage.__init__(self, account, parent, options)
class CredentialsDialog(OptionsDialog):
def __init__(self, account, parent):
options = [
Option(OptionKind.ENTRY, _('First Name'),
OptionType.ACCOUNT_CONFIG, 'zeroconf_first_name'),
Option(OptionKind.ENTRY, _('Last Name'),
OptionType.ACCOUNT_CONFIG, 'zeroconf_last_name'),
Option(OptionKind.ENTRY, _('Jabber ID'),
OptionType.ACCOUNT_CONFIG, 'zeroconf_jabber_id'),
Option(OptionKind.ENTRY, _('Email'),
OptionType.ACCOUNT_CONFIG, 'zeroconf_email'),
]
OptionsDialog.__init__(self, parent, _('Credential Options'),
Gtk.DialogFlags.MODAL, options, account)
class PriorityDialog(OptionsDialog):
def __init__(self, account, parent):
neg_priority = app.config.get('enable_negative_priority')
if neg_priority:
range_ = (-128, 127)
else:
range_ = (0, 127)
options = [
Option(OptionKind.SWITCH, _('Adjust to status'),
OptionType.ACCOUNT_CONFIG, 'adjust_priority_with_status',
'adjust'),
Option(OptionKind.SPIN, _('Priority'),
OptionType.ACCOUNT_CONFIG, 'priority',
enabledif=('adjust', False), props={'range_': range_}),
]
OptionsDialog.__init__(self, parent, _('Priority'),
Gtk.DialogFlags.MODAL, options, account)
self.connect('destroy', self.on_destroy)
def on_destroy(self, *args):
# Update priority
if self.account not in app.connections:
return
show = app.SHOW_LIST[app.connections[self.account].connected]
status = app.connections[self.account].status
app.connections[self.account].change_status(show, status)
class CutstomHostnameDialog(OptionsDialog):
def __init__(self, account, parent):
options = [
Option(OptionKind.SWITCH, _('Enable'),
OptionType.ACCOUNT_CONFIG, 'use_custom_host', name='custom'),
Option(OptionKind.ENTRY, _('Hostname'),
OptionType.ACCOUNT_CONFIG, 'custom_host',
enabledif=('custom', True)),
Option(OptionKind.ENTRY, _('Port'),
OptionType.ACCOUNT_CONFIG, 'custom_port',
enabledif=('custom', True)),
]
OptionsDialog.__init__(self, parent, _('Connection Options'),
Gtk.DialogFlags.MODAL, options, account)
class CertificateDialog(OptionsDialog):
def __init__(self, account, parent):
options = [
Option(OptionKind.FILECHOOSER, _('Client Certificate'),
OptionType.ACCOUNT_CONFIG, 'client_cert',
props={'filefilter': (_('PKCS12 Files'), '*.p12')}),
Option(OptionKind.SWITCH, _('Encrypted Certificate'),
OptionType.ACCOUNT_CONFIG, 'client_cert_encrypted'),
]
OptionsDialog.__init__(self, parent, _('Certificate Options'),
Gtk.DialogFlags.MODAL, options, account)
class LoginDialog(OptionsDialog):
def __init__(self, account, parent):
options = [
Option(OptionKind.ENTRY, _('Password'),
OptionType.ACCOUNT_CONFIG, 'password', name='password',
enabledif=('savepass', True)),
Option(OptionKind.SWITCH, _('Save Password'),
OptionType.ACCOUNT_CONFIG, 'savepass', name='savepass'),
Option(OptionKind.CHANGEPASSWORD, _('Change Password'),
OptionType.DIALOG, callback=self.on_password_change,
props={'dialog': None}),
]
OptionsDialog.__init__(self, parent, _('Login Options'),
Gtk.DialogFlags.MODAL, options, account)
self.connect('destroy', self.on_destroy)
def on_password_change(self, new_password, data):
self.get_option('password').entry.set_text(new_password)
def on_destroy(self, *args):
savepass = app.config.get_per('accounts', self.account, 'savepass')
if not savepass:
passwords.save_password(self.account, '')

View file

@ -25,10 +25,12 @@ from gajim.common.exceptions import GajimGeneralException
from gi.repository import Gtk
import sys
import os
from gajim import config
from gajim import dialogs
from gajim import features_window
from gajim import shortcuts_window
from gajim import accounts_window
import gajim.plugins.gui
from gajim import history_window
from gajim import disco
@ -57,10 +59,10 @@ class AppActions():
interface.instances['plugins'] = gajim.plugins.gui.PluginsWindow()
def on_accounts(self, action, param):
if 'accounts' in interface.instances:
interface.instances['accounts'].window.present()
if 'accounts' in app.interface.instances:
app.interface.instances['accounts'].present()
else:
interface.instances['accounts'] = config.AccountsWindow()
app.interface.instances['accounts'] = accounts_window.AccountsWindow()
def on_history_manager(self, action, param):
from gajim.history_manager import HistoryManager
@ -131,6 +133,35 @@ class AppActions():
def on_single_message(self, action, param):
dialogs.SingleMessageWindow(param.get_string(), action='send')
def on_merge_accounts(self, action, param):
action.set_state(param)
value = param.get_boolean()
app.config.set('mergeaccounts', value)
if len(app.connections) >= 2: # Do not merge accounts if only one active
app.interface.roster.regroup = value
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
def on_use_pgp_agent(self, action, param):
action.set_state(param)
app.config.set('use_gpg_agent', param.get_boolean())
def on_add_account(self, action, param):
if 'account_creation_wizard' in app.interface.instances:
app.interface.instances['account_creation_wizard'].window.present()
else:
app.interface.instances['account_creation_wizard'] = \
config.AccountCreationWizardWindow()
def on_import_contacts(self, action, param):
account = param.get_string()
if 'import_contacts' in app.interface.instances:
app.interface.instances['import_contacts'].dialog.present()
else:
app.interface.instances['import_contacts'] = \
dialogs.SynchroniseSelectAccountDialog(account)
# Advanced Actions
def on_archiving_preferences(self, action, param):
@ -174,6 +205,13 @@ class AppActions():
interface.instances[account]['xml_console'] = \
dialogs.XMLConsoleWindow(account)
def on_manage_proxies(self, action, param):
if 'manage_proxies' in app.interface.instances:
app.interface.instances['manage_proxies'].window.present()
else:
app.interface.instances['manage_proxies'] = \
config.ManageProxiesWindow(interface.roster.window)
# Admin Actions
def on_set_motd(self, action, param):

View file

@ -298,7 +298,8 @@ class StandardGroupChatCommands(CommandContainer):
@doc(_("Invite a user to a room for a reason"))
def invite(self, jid, reason):
self.connection.send_invite(self.room_jid, jid, reason)
return _("Invited %s to %s") % (jid, self.room_jid)
return _("Invited %(jid)s to %(room_jid)s") % {'jid': jid,
'room_jid': self.room_jid}
@command(raw=True, empty=True)
@doc(_("Join a group chat given by a jid, optionally using given nickname"))

View file

@ -53,7 +53,7 @@ ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher
nec = None # Network Events Controller
plugin_manager = None # Plugins Manager
log = logging.getLogger('gajim')
glog = logging.getLogger('gajim')
logger = None
@ -88,8 +88,6 @@ else:
os_info = None # used to cache os information
gmail_domains = ['gmail.com', 'googlemail.com']
transport_type = {} # list the type of transport
last_message_time = {} # list of time of the latest incomming message
@ -176,7 +174,7 @@ try:
'''
v_gnupg = gnupg.__version__
if V(v_gnupg) < V('0.3.8') or V(v_gnupg) > V('1.0.0'):
log.info('Gajim needs python-gnupg >= 0.3.8')
glog.info('Gajim needs python-gnupg >= 0.3.8')
HAVE_GPG = False
except ImportError:
HAVE_GPG = False
@ -247,7 +245,7 @@ try:
if sleepy.SUPPORTED:
HAVE_IDLE = True
except Exception:
log.debug(_('Unable to load idle module'))
glog.info(_('Unable to load idle module'))
HAVE_IDLE = False
@ -261,7 +259,8 @@ gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE,
nbxmpp.NS_SSN, nbxmpp.NS_MOOD, nbxmpp.NS_ACTIVITY, nbxmpp.NS_NICK,
nbxmpp.NS_ROSTERX, nbxmpp.NS_SECLABEL, nbxmpp.NS_HASHES_2,
nbxmpp.NS_HASHES_MD5, nbxmpp.NS_HASHES_SHA1, nbxmpp.NS_HASHES_SHA256,
nbxmpp.NS_HASHES_SHA512, nbxmpp.NS_CONFERENCE, nbxmpp.NS_CORRECT]
nbxmpp.NS_HASHES_SHA512, nbxmpp.NS_CONFERENCE, nbxmpp.NS_CORRECT,
nbxmpp.NS_EME]
# Optional features gajim supports per account
gajim_optional_features = {}
@ -493,3 +492,7 @@ def get_priority(account, show):
elif prio > 127:
prio = 127
return prio
def log(domain):
root = 'gajim.'
return logging.getLogger(root + domain)

View file

@ -250,7 +250,7 @@ def check_and_possibly_move_config():
continue
if not os.path.exists(src):
continue
print(_('moving %s to %s') % (src, dst))
print(_('moving %(src)s to %(dst)s') % {'src': src, 'dst': dst})
shutil.move(src, dst)
app.logger.init_vars()
app.logger.attach_cache_database()

View file

@ -123,6 +123,7 @@ class Config:
'print_time': [ opt_str, 'always', _('\'always\' - print time for every message.\n\'sometimes\' - print time every print_ichat_every_foo_minutes minute.\n\'never\' - never print time.')],
'print_time_fuzzy': [ opt_int, 0, _('Print time in chats using Fuzzy Clock. Value of fuzziness from 1 to 4, or 0 to disable fuzzyclock. 1 is the most precise clock, 4 the least precise one. This is used only if print_time is \'sometimes\'.') ],
'emoticons_theme': [opt_str, 'noto-emoticons', '', True ],
'ascii_emoticons': [opt_bool, True, _('Enable ASCII emoticons'), True],
'ascii_formatting': [ opt_bool, True,
_('Treat * / _ pairs as possible formatting characters.'), True],
'show_ascii_formatting_chars': [ opt_bool, True, _('If True, do not '
@ -176,9 +177,6 @@ class Config:
'time_stamp': [ opt_str, '[%X] ', _('This option let you customize timestamp that is printed in conversation. For exemple "[%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') ],
'after_nickname': [ opt_str, ':', _('Characters that are printed after the nickname in conversations') ],
'notify_on_new_gmail_email': [ opt_bool, True ],
'notify_on_new_gmail_email_extra': [ opt_bool, False ],
'notify_on_new_gmail_email_command': [ opt_str, '', _('Specify the command to run when new mail arrives, e.g.: /usr/bin/getmail -q') ],
'use_gpg_agent': [ opt_bool, False ],
'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?')],
@ -192,7 +190,7 @@ class Config:
'key_up_lines': [opt_int, 25, _('How many lines to store for Ctrl+KeyUP.')],
'version': [ opt_str, defs.version ], # which version created the config
'search_engine': [opt_str, 'https://www.google.com/search?&q=%s&sourceid=gajim'],
'dictionary_url': [opt_str, 'WIKTIONARY', _("Either custom url with %s in it where %s is the word/phrase or 'WIKTIONARY' which means use wiktionary.")],
'dictionary_url': [opt_str, 'WIKTIONARY', _("Either custom url with %%s in it where %%s is the word/phrase or 'WIKTIONARY' which means use wiktionary.")],
'always_english_wikipedia': [opt_bool, False],
'always_english_wiktionary': [opt_bool, True],
'remote_control': [opt_bool, False, _('If checked, Gajim can be controlled remotely using gajim-remote.'), True],
@ -323,6 +321,7 @@ class Config:
__options_per_key = {
'accounts': ({
'name': [ opt_str, '', '', True ],
'account_label': [ opt_str, '', '', False ],
'hostname': [ opt_str, '', '', True ],
'anonymous_auth': [ opt_bool, False ],
'client_cert': [ opt_str, '', '', True ],
@ -535,7 +534,6 @@ class Config:
'message_sent': [ False, 'sent.wav' ],
'muc_message_highlight': [ True, 'gc_message1.wav', _('Sound to play when a group chat message contains one of the words in muc_highlight_words, or when a group chat message contains your nickname.')],
'muc_message_received': [ False, 'gc_message2.wav', _('Sound to play when any MUC message arrives.') ],
'gmail_received': [ False, 'message1.wav' ],
}
themes_default = {

View file

@ -98,9 +98,11 @@ class ConfigPaths:
base = expand('~/.local/share')
self.data_root = os.path.join(base, 'gajim')
basedir = os.environ.get('GAJIM_BASEDIR', defs.basedir)
import pkg_resources
basedir = pkg_resources.resource_filename("gajim", ".")
self.add('DATA', None, os.path.join(basedir, 'data'))
self.add('GUI', None, os.path.join(basedir, 'data', 'gui'))
basedir = os.environ.get('GAJIM_BASEDIR', defs.basedir)
self.add('ICONS', None, os.path.join(basedir, 'icons'))
self.add('HOME', None, os.path.expanduser('~'))
self.add('PLUGINS_BASE', None, os.path.join(basedir, 'plugins'))
@ -142,8 +144,7 @@ class ConfigPaths:
d = {'LOG_DB': 'logs.db', 'MY_CACERTS': 'cacerts.pem',
'MY_EMOTS': 'emoticons', 'MY_ICONSETS': 'iconsets',
'MY_MOOD_ICONSETS': 'moods', 'MY_ACTIVITY_ICONSETS': 'activities',
'PLUGINS_USER': 'plugins',
'RNG_SEED': 'rng_seed'}
'PLUGINS_USER': 'plugins'}
for name in d:
d[name] += profile
self.add(name, Type.DATA, windowsify(d[name]))

View file

@ -1083,7 +1083,8 @@ class Connection(CommonConnection, ConnectionHandlers):
self._hosts = [ {'host': h, 'port': p, 'ssl_port': ssl_p, 'prio': 10,
'weight': 10} ]
self._hostname = hostname
if use_srv:
if use_srv and self._proxy is None:
# add request for srv query to the resolve, on result '_on_resolve'
# will be called
app.resolver.resolve('_xmpp-client._tcp.' + helpers.idn_to_ascii(
@ -1451,7 +1452,9 @@ class Connection(CommonConnection, ConnectionHandlers):
return
if hasattr(con, 'Resource'):
self.server_resource = con.Resource
self.registered_name = con._registered_name
if con._registered_name is not None:
log.info('Bound JID: %s', con._registered_name)
self.registered_name = con._registered_name
if app.config.get_per('accounts', self.name, 'anonymous_auth'):
# Get jid given by server
old_jid = app.get_jid_from_account(self.name)
@ -1821,9 +1824,10 @@ class Connection(CommonConnection, ConnectionHandlers):
self.sm.resuming = False # back to previous state
# Discover Stun server(s)
hostname = app.config.get_per('accounts', self.name, 'hostname')
app.resolver.resolve('_stun._udp.' + helpers.idn_to_ascii(hostname),
self._on_stun_resolved)
if self._proxy is None:
hostname = app.config.get_per('accounts', self.name, 'hostname')
app.resolver.resolve('_stun._udp.' + helpers.idn_to_ascii(hostname),
self._on_stun_resolved)
def _on_stun_resolved(self, host, result_array):
if len(result_array) != 0:
@ -1911,9 +1915,6 @@ class Connection(CommonConnection, ConnectionHandlers):
get_action(self.name + '-archive').set_enabled(True)
if obj.fjid == hostname:
if nbxmpp.NS_GMAILNOTIFY in obj.features:
app.gmail_domains.append(obj.fjid)
self.request_gmail_notifications()
if nbxmpp.NS_SECLABEL in obj.features:
self.seclabel_supported = True
for identity in obj.identities:
@ -2074,8 +2075,9 @@ class Connection(CommonConnection, ConnectionHandlers):
return
if type_ == 'message':
if len(contacts) == 1:
msg = _('Sent contact: "%s" (%s)') % (contacts[0].get_full_jid(),
contacts[0].get_shown_name())
msg = _('Sent contact: "%(jid)s" (%(name)s)') % {
'jid': contacts[0].get_full_jid(),
'name': contacts[0].get_shown_name()}
else:
msg = _('Sent contacts:')
for contact in contacts:

View file

@ -1080,6 +1080,27 @@ class ConnectionHandlersBase:
app.plugin_manager.extension_point(
'decrypt', self, obj, self._on_message_received)
if not obj.encrypted:
# XEP-0380
enc_tag = obj.stanza.getTag('encryption', namespace=nbxmpp.NS_EME)
if enc_tag:
ns = enc_tag.getAttr('namespace')
if ns:
if ns == 'urn:xmpp:otr:0':
obj.msgtxt = _('This message was encrypted with OTR '
'and could not be decrypted.')
elif ns == 'jabber:x:encrypted':
obj.msgtxt = _('This message was encrypted with Legacy '
'OpenPGP and could not be decrypted. You can install '
'the PGP plugin to handle those messages.')
elif ns == 'urn:xmpp:openpgp:0':
obj.msgtxt = _('This message was encrypted with '
'OpenPGP for XMPP and could not be decrypted.')
else:
enc_name = enc_tag.getAttr('name')
if not enc_name:
enc_name = ns
obj.msgtxt = _('This message was encrypted with %s '
'and could not be decrypted.') % enc_name
self._on_message_received(obj)
def _on_message_received(self, obj):
@ -1155,6 +1176,8 @@ class ConnectionHandlersBase:
return True
def _nec_gc_message_received(self, obj):
if obj.conn.name != self.name:
return
if app.config.should_log(obj.conn.name, obj.jid) and not \
obj.timestamp < obj.conn.last_history_time[obj.jid] and obj.msgtxt and \
obj.nick:
@ -1356,7 +1379,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
client_caps_factory=capscache.create_suitable_client_caps)
ConnectionJingle.__init__(self)
ConnectionHandlersBase.__init__(self)
self.gmail_url = None
# keep the latest subscribed event for each jid to prevent loop when we
# acknowledge presences
@ -1375,9 +1397,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
self.privacy_default_list = None
self.gmail_last_tid = None
self.gmail_last_time = None
app.nec.register_incoming_event(PrivateStorageBookmarksReceivedEvent)
app.nec.register_incoming_event(BookmarksReceivedEvent)
app.nec.register_incoming_event(
@ -1415,8 +1434,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
self._nec_roster_received)
app.ged.register_event_handler('iq-error-received', ged.CORE,
self._nec_iq_error_received)
app.ged.register_event_handler('gmail-new-mail-received', ged.CORE,
self._nec_gmail_new_mail_received)
app.ged.register_event_handler('ping-received', ged.CORE,
self._nec_ping_received)
app.ged.register_event_handler('subscribe-presence-received',
@ -1461,8 +1478,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
self._nec_roster_received)
app.ged.remove_event_handler('iq-error-received', ged.CORE,
self._nec_iq_error_received)
app.ged.remove_event_handler('gmail-new-mail-received', ged.CORE,
self._nec_gmail_new_mail_received)
app.ged.remove_event_handler('ping-received', ged.CORE,
self._nec_ping_received)
app.ged.remove_event_handler('subscribe-presence-received',
@ -1719,44 +1734,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
app.nec.push_incoming_event(TimeResultReceivedEvent(None, conn=self,
stanza=iq_obj))
def _gMailNewMailCB(self, con, iq_obj):
"""
Called when we get notified of new mail messages in gmail account
"""
log.debug('gMailNewMailCB')
app.nec.push_incoming_event(GmailNewMailReceivedEvent(None, conn=self,
stanza=iq_obj))
raise nbxmpp.NodeProcessed
def _nec_gmail_new_mail_received(self, obj):
if obj.conn.name != self.name:
return
if not self.connection or self.connected < 2:
return
# we'll now ask the server for the exact number of new messages
jid = app.get_jid_from_account(self.name)
log.debug('Got notification of new gmail e-mail on %s. Asking the '
'server for more info.' % jid)
iq = nbxmpp.Iq(typ='get')
query = iq.setTag('query')
query.setNamespace(nbxmpp.NS_GMAILNOTIFY)
# we want only be notified about newer mails
if self.gmail_last_tid:
query.setAttr('newer-than-tid', self.gmail_last_tid)
if self.gmail_last_time:
query.setAttr('newer-than-time', self.gmail_last_time)
self.connection.send(iq)
def _gMailQueryCB(self, con, iq_obj):
"""
Called when we receive results from Querying the server for mail messages
in gmail account
"""
log.debug('gMailQueryCB')
app.nec.push_incoming_event(GMailQueryReceivedEvent(None, conn=self,
stanza=iq_obj))
raise nbxmpp.NodeProcessed
def _rosterItemExchangeCB(self, con, msg):
"""
XEP-0144 Roster Item Echange
@ -2121,28 +2098,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
# hashes of already received messages
self.received_message_hashes = []
def request_gmail_notifications(self):
if not self.connection or self.connected < 2:
return
# It's a gmail account,
# inform the server that we want e-mail notifications
our_jid = helpers.parse_jid(app.get_jid_from_account(self.name))
log.debug(('%s is a gmail account. Setting option '
'to get e-mail notifications on the server.') % (our_jid))
iq = nbxmpp.Iq(typ='set', to=our_jid)
iq.setAttr('id', 'MailNotify')
query = iq.setTag('usersetting')
query.setNamespace(nbxmpp.NS_GTALKSETTING)
query = query.setTag('mailnotifications')
query.setAttr('value', 'true')
self.connection.send(iq)
# Ask how many messages there are now
iq = nbxmpp.Iq(typ='get')
iq.setID(self.connection.getAnID())
query = iq.setTag('query')
query.setNamespace(nbxmpp.NS_GMAILNOTIFY)
self.connection.send(iq)
def _SearchCB(self, con, iq_obj):
log.debug('SearchCB')
app.nec.push_incoming_event(SearchFormReceivedEvent(None,
@ -2258,10 +2213,6 @@ ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream):
con.RegisterHandler('iq', self._HttpAuthCB, 'get', nbxmpp.NS_HTTP_AUTH)
con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
nbxmpp.NS_COMMANDS)
con.RegisterHandler('iq', self._gMailNewMailCB, 'set',
nbxmpp.NS_GMAILNOTIFY)
con.RegisterHandler('iq', self._gMailQueryCB, 'result',
nbxmpp.NS_GMAILNOTIFY)
con.RegisterHandler('iq', self._DiscoverInfoGetCB, 'get',
nbxmpp.NS_DISCO_INFO)
con.RegisterHandler('iq', self._DiscoverItemsGetCB, 'get',

View file

@ -232,61 +232,6 @@ class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.time_info = t.astimezone(contact_tz()).strftime('%c')
return True
class GMailQueryReceivedEvent(nec.NetworkIncomingEvent):
name = 'gmail-notify'
base_network_events = []
def generate(self):
if not self.stanza.getTag('mailbox'):
return
mb = self.stanza.getTag('mailbox')
if not mb.getAttr('url'):
return
self.conn.gmail_url = mb.getAttr('url')
if mb.getNamespace() != nbxmpp.NS_GMAILNOTIFY:
return
self.newmsgs = mb.getAttr('total-matched')
if not self.newmsgs:
return
if self.newmsgs == '0':
return
# there are new messages
self.gmail_messages_list = []
if mb.getTag('mail-thread-info'):
gmail_messages = mb.getTags('mail-thread-info')
for gmessage in gmail_messages:
unread_senders = []
for sender in gmessage.getTag('senders').getTags('sender'):
if sender.getAttr('unread') != '1':
continue
if sender.getAttr('name'):
unread_senders.append(sender.getAttr('name') + \
'< ' + sender.getAttr('address') + '>')
else:
unread_senders.append(sender.getAttr('address'))
if not unread_senders:
continue
gmail_subject = gmessage.getTag('subject').getData()
gmail_snippet = gmessage.getTag('snippet').getData()
tid = int(gmessage.getAttr('tid'))
if not self.conn.gmail_last_tid or \
tid > self.conn.gmail_last_tid:
self.conn.gmail_last_tid = tid
self.gmail_messages_list.append({
'From': unread_senders,
'Subject': gmail_subject,
'Snippet': gmail_snippet,
'url': gmessage.getAttr('url'),
'participation': gmessage.getAttr('participation'),
'messages': gmessage.getAttr('messages'),
'date': gmessage.getAttr('date')})
self.conn.gmail_last_time = int(mb.getAttr('result-time'))
self.jid = app.get_jid_from_account(self.name)
log.debug('You have %s new gmail e-mails on %s.', self.newmsgs, self.jid)
return True
class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'roster-item-exchange-received'
base_network_events = []
@ -671,18 +616,6 @@ class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
self.errcode = self.stanza.getErrorCode()
return True
class GmailNewMailReceivedEvent(nec.NetworkIncomingEvent):
name = 'gmail-new-mail-received'
base_network_events = []
def generate(self):
if not self.stanza.getTag('new-mail'):
return
if self.stanza.getTag('new-mail').getNamespace() != \
nbxmpp.NS_GMAILNOTIFY:
return
return True
class PingReceivedEvent(nec.NetworkIncomingEvent):
name = 'ping-received'
base_network_events = []

View file

@ -1,3 +1,34 @@
from enum import IntEnum, unique
from collections import namedtuple
Option = namedtuple('Option', 'kind label type value name callback data desc enabledif props')
Option.__new__.__defaults__ = (None,) * len(Option._fields)
@unique
class OptionKind(IntEnum):
ENTRY = 0
SWITCH = 1
SPIN = 2
ACTION = 3
LOGIN = 4
DIALOG = 5
CALLBACK = 6
PROXY = 7
HOSTNAME = 8
PRIORITY = 9
FILECHOOSER = 10
CHANGEPASSWORD = 11
GPG = 12
@unique
class OptionType(IntEnum):
ACCOUNT_CONFIG = 0
CONFIG = 1
BOOL = 2
ACTION = 3
DIALOG = 4
THANKS = u"""\
Alexander Futász
Alexander V. Butenko

View file

@ -76,54 +76,8 @@ def base28(n):
else:
return base28_chr[n]
def add_entropy_sources_OpenSSL():
# Other possibly variable data. This are very low quality sources of
# entropy, but some of them are installation dependent and can be hard
# to guess for the attacker.
# Data available on all platforms Unix, Windows
sources = [sys.argv, sys.builtin_module_names,
sys.copyright, sys.getfilesystemencoding(), sys.hexversion,
sys.modules, sys.path, sys.version, sys.api_version,
os.environ, os.getcwd(), os.getpid()]
for s in sources:
OpenSSL.rand.add(str(s).encode('utf-8'), 1)
# On Windows add the current contents of the screen to the PRNG state.
# if os.name == 'nt':
# OpenSSL.rand.screen()
# The /proc filesystem on POSIX systems contains many random variables:
# memory statistics, interrupt counts, network packet counts
if os.name == 'posix':
dirs = ['/proc', '/proc/net', '/proc/self']
for d in dirs:
if os.access(d, os.R_OK):
for filename in os.listdir(d):
OpenSSL.rand.add(filename.encode('utf-8'), 0)
try:
with open(d + os.sep + filename, "r") as fp:
# Limit the ammount of read bytes, in case a memory
# file was opened
OpenSSL.rand.add(str(fp.read(5000)).encode('utf-8'),
1)
except:
# Ignore all read and access errors
pass
PYOPENSSL_PRNG_PRESENT = False
try:
import OpenSSL.rand
PYOPENSSL_PRNG_PRESENT = True
except ImportError:
# PyOpenSSL PRNG not available
pass
def random_bytes(bytes_):
if PYOPENSSL_PRNG_PRESENT:
OpenSSL.rand.add(os.urandom(bytes_), bytes_)
return OpenSSL.rand.bytes(bytes_)
else:
return os.urandom(bytes_)
return os.urandom(bytes_)
def generate_nonce():
return random_bytes(8)

View file

@ -121,8 +121,10 @@ class JingleRTPContent(JingleContent):
InformationEvent(
None, conn=self.session.connection, level='error',
pri_txt=_('%s configuration error') % text.capitalize(),
sec_txt=_('Couldnt setup %s. Check your configuration.\n\n'
'Pipeline was:\n%s\n\nError was:\n%s') % (text, pipeline, str(e))))
sec_txt=_('Couldnt setup %(text)s. Check your '
'configuration.\n\nPipeline was:\n%(pipeline)s\n\n'
'Error was:\n%(error)s') % {'text': text,
'pipeline': pipeline, 'error': str(e)}))
raise JingleContentSetupException
def add_remote_candidates(self, candidates):
@ -228,9 +230,9 @@ class JingleRTPContent(JingleContent):
InformationEvent(
None, conn=self.session.connection, level='error',
pri_txt=_('GStreamer error'),
sec_txt=_('Error: %s\nDebug: %s' %
(message.get_structure().get_value('gerror'),
message.get_structure().get_value('debug')))))
sec_txt=_('Error: %(error)s\nDebug: %(debug)s' % {
'error': message.get_structure().get_value('gerror'),
'debug': message.get_structure().get_value('debug')})))
sink_pad = self.p2psession.get_property('sink-pad')

View file

@ -27,6 +27,9 @@
import os
import logging
import gi
from gi.repository import GLib
from gajim.common import app
__all__ = ['get_password', 'save_password']
@ -78,10 +81,15 @@ class LibSecretPasswordStorage(PasswordStorage):
def save_password(self, account_name, password, update=True):
server = app.config.get_per('accounts', account_name, 'hostname')
user = app.config.get_per('accounts', account_name, 'name')
display_name = _('XMPP account %s@%s') % (user, server)
display_name = _('XMPP account %s') % user + '@' + server
attributes = {'user': user, 'server': server, 'protocol': 'xmpp'}
return self.Secret.password_store_sync(self.GAJIM_SCHEMA, attributes,
self.Secret.COLLECTION_DEFAULT, display_name, password or '', None)
try:
return self.Secret.password_store_sync(
self.GAJIM_SCHEMA, attributes, self.Secret.COLLECTION_DEFAULT,
display_name, password or '', None)
except GLib.Error as error:
log.error(error)
return False
class SecretWindowsPasswordStorage(PasswordStorage):

View file

@ -23,7 +23,7 @@ import functools
log = logging.getLogger('gajim.c.resolver')
if __name__ == '__main__':
sys.path.append('..')
sys.path.append('../..')
from gajim.common import i18n
from gajim.common import configpaths
configpaths.gajimpaths.init(None)

File diff suppressed because it is too large Load diff

View file

@ -918,13 +918,12 @@ class ConversationTextview(GObject.GObject):
if special_text.startswith(scheme):
text_is_valid_uri = True
possible_emot_ascii_caps = special_text.upper() # emoticons keys are CAPS
if iter_:
end_iter = iter_
else:
end_iter = buffer_.get_end_iter()
pixbuf = emoticons.get_pixbuf(possible_emot_ascii_caps)
pixbuf = emoticons.get_pixbuf(special_text)
if app.config.get('emoticons_theme') and pixbuf and graphics:
# it's an emoticon
anchor = buffer_.create_child_anchor(end_iter)

19
gajim/data/Makefile.am Normal file
View file

@ -0,0 +1,19 @@
SUBDIRS = gui emoticons iconsets moods activities
@INTLTOOL_DESKTOP_RULE@
soundsdir = $(gajim_srcdir)/data/sounds
sounds_DATA = $(srcdir)/sounds/*.wav
styledir = $(gajim_srcdir)/data/style
style_DATA = $(srcdir)/style/*.css
otherdir = $(gajim_srcdir)/data/other
other_DATA = other/servers.xml other/dh4096.pem
# other/cacert.pem is used only on Windows. On Unix platforms
# use CA certificates installed in /etc/ssl/certs
EXTRA_DIST = $(sounds_DATA) \
$(style_DATA) \
$(other_DATA)
MAINTAINERCLEANFILES = Makefile.in

View file

@ -1,4 +1,4 @@
activitiesdir = $(pkgdatadir)/data/activities
activitiesdir = $(gajim_srcdir)/data/activities
nobase_dist_activities_DATA = $(srcdir)/*/*/*
MAINTAINERCLEANFILES = Makefile.in

View file

Before

Width:  |  Height:  |  Size: 909 B

After

Width:  |  Height:  |  Size: 909 B

View file

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 854 B

View file

Before

Width:  |  Height:  |  Size: 842 B

After

Width:  |  Height:  |  Size: 842 B

View file

Before

Width:  |  Height:  |  Size: 1,005 B

After

Width:  |  Height:  |  Size: 1,005 B

View file

Before

Width:  |  Height:  |  Size: 957 B

After

Width:  |  Height:  |  Size: 957 B

View file

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 916 B

View file

Before

Width:  |  Height:  |  Size: 808 B

After

Width:  |  Height:  |  Size: 808 B

View file

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 749 B

View file

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 980 B

View file

Before

Width:  |  Height:  |  Size: 891 B

After

Width:  |  Height:  |  Size: 891 B

View file

Before

Width:  |  Height:  |  Size: 706 B

After

Width:  |  Height:  |  Size: 706 B

View file

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 906 B

View file

Before

Width:  |  Height:  |  Size: 918 B

After

Width:  |  Height:  |  Size: 918 B

View file

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 712 B

View file

Before

Width:  |  Height:  |  Size: 868 B

After

Width:  |  Height:  |  Size: 868 B

View file

Before

Width:  |  Height:  |  Size: 906 B

After

Width:  |  Height:  |  Size: 906 B

View file

Before

Width:  |  Height:  |  Size: 844 B

After

Width:  |  Height:  |  Size: 844 B

View file

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 969 B

View file

Before

Width:  |  Height:  |  Size: 1,016 B

After

Width:  |  Height:  |  Size: 1,016 B

View file

Before

Width:  |  Height:  |  Size: 912 B

After

Width:  |  Height:  |  Size: 912 B

View file

Before

Width:  |  Height:  |  Size: 901 B

After

Width:  |  Height:  |  Size: 901 B

View file

Before

Width:  |  Height:  |  Size: 957 B

After

Width:  |  Height:  |  Size: 957 B

View file

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 723 B

View file

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 625 B

View file

Before

Width:  |  Height:  |  Size: 894 B

After

Width:  |  Height:  |  Size: 894 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 854 B

View file

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 900 B

View file

Before

Width:  |  Height:  |  Size: 846 B

After

Width:  |  Height:  |  Size: 846 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View file

Before

Width:  |  Height:  |  Size: 1,013 B

After

Width:  |  Height:  |  Size: 1,013 B

View file

Before

Width:  |  Height:  |  Size: 759 B

After

Width:  |  Height:  |  Size: 759 B

View file

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 793 B

View file

Before

Width:  |  Height:  |  Size: 1,012 B

After

Width:  |  Height:  |  Size: 1,012 B

View file

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 981 B

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 907 B

After

Width:  |  Height:  |  Size: 907 B

View file

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 752 B

View file

Before

Width:  |  Height:  |  Size: 917 B

After

Width:  |  Height:  |  Size: 917 B

View file

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View file

Before

Width:  |  Height:  |  Size: 703 B

After

Width:  |  Height:  |  Size: 703 B

View file

Before

Width:  |  Height:  |  Size: 997 B

After

Width:  |  Height:  |  Size: 997 B

View file

Before

Width:  |  Height:  |  Size: 967 B

After

Width:  |  Height:  |  Size: 967 B

View file

Before

Width:  |  Height:  |  Size: 839 B

After

Width:  |  Height:  |  Size: 839 B

View file

Before

Width:  |  Height:  |  Size: 719 B

After

Width:  |  Height:  |  Size: 719 B

View file

Before

Width:  |  Height:  |  Size: 866 B

After

Width:  |  Height:  |  Size: 866 B

View file

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 793 B

View file

Before

Width:  |  Height:  |  Size: 990 B

After

Width:  |  Height:  |  Size: 990 B

View file

Before

Width:  |  Height:  |  Size: 913 B

After

Width:  |  Height:  |  Size: 913 B

View file

Before

Width:  |  Height:  |  Size: 921 B

After

Width:  |  Height:  |  Size: 921 B

View file

Before

Width:  |  Height:  |  Size: 964 B

After

Width:  |  Height:  |  Size: 964 B

View file

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 621 B

View file

Before

Width:  |  Height:  |  Size: 956 B

After

Width:  |  Height:  |  Size: 956 B

View file

Before

Width:  |  Height:  |  Size: 958 B

After

Width:  |  Height:  |  Size: 958 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 955 B

After

Width:  |  Height:  |  Size: 955 B

View file

Before

Width:  |  Height:  |  Size: 836 B

After

Width:  |  Height:  |  Size: 836 B

View file

Before

Width:  |  Height:  |  Size: 792 B

After

Width:  |  Height:  |  Size: 792 B

View file

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 651 B

View file

Before

Width:  |  Height:  |  Size: 865 B

After

Width:  |  Height:  |  Size: 865 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 868 B

After

Width:  |  Height:  |  Size: 868 B

View file

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 982 B

View file

Before

Width:  |  Height:  |  Size: 960 B

After

Width:  |  Height:  |  Size: 960 B

View file

Before

Width:  |  Height:  |  Size: 696 B

After

Width:  |  Height:  |  Size: 696 B

View file

Before

Width:  |  Height:  |  Size: 897 B

After

Width:  |  Height:  |  Size: 897 B

View file

Before

Width:  |  Height:  |  Size: 773 B

After

Width:  |  Height:  |  Size: 773 B

View file

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 825 B

View file

Before

Width:  |  Height:  |  Size: 962 B

After

Width:  |  Height:  |  Size: 962 B

View file

Before

Width:  |  Height:  |  Size: 797 B

After

Width:  |  Height:  |  Size: 797 B

View file

Before

Width:  |  Height:  |  Size: 345 B

After

Width:  |  Height:  |  Size: 345 B

Some files were not shown because too many files have changed in this diff Show more