gajim.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2026
October
September
August
July
June
May
April
March
February
January
2025
December
November
October
September
August
July
June
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
2021
December
November
October
September
August
July
June
May
April
March
February
January
2020
December
November
October
September
August
July
June
May
April
March
February
January
2019
December
November
October
September
August
July
June
May
April
March
February
January
2018
December
November
October
September
August
July
June
May
April
March
February
January
2017
December
November
October
September
August
July
June
May
April
March
February
January
2016
December
November
October
September
August
July
June
May
April
March
February
January
2015
December
November
October
September
August
July
June
May
April
March
February
January
2014
December
November
October
September
August
July
June
May
April
March
February
January
2013
December
November
October
September
August
July
June
May
April
March
February
January
2012
December
November
October
September
August
July
June
May
April
March
February
January
2011
December
November
October
September
August
July
June
May
April
March
February
January
2010
December
November
October
September
August
July
June
May
April
March
February
January
2009
December
November
October
September
August
July
June
May
April
March
February
January
2008
December
November
October
September
August
July
June
May
April
March
February
January
2007
December
November
October
September
August
July
June
May
April
March
February
January
2006
December
November
October
September
August
July
June
May
April
List overview
Download
Commits
April 2018
----- 2026 -----
October 2026
September 2026
August 2026
July 2026
June 2026
May 2026
April 2026
March 2026
February 2026
January 2026
----- 2025 -----
December 2025
November 2025
October 2025
September 2025
August 2025
July 2025
June 2025
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
----- 2021 -----
December 2021
November 2021
October 2021
September 2021
August 2021
July 2021
June 2021
May 2021
April 2021
March 2021
February 2021
January 2021
----- 2020 -----
December 2020
November 2020
October 2020
September 2020
August 2020
July 2020
June 2020
May 2020
April 2020
March 2020
February 2020
January 2020
----- 2019 -----
December 2019
November 2019
October 2019
September 2019
August 2019
July 2019
June 2019
May 2019
April 2019
March 2019
February 2019
January 2019
----- 2018 -----
December 2018
November 2018
October 2018
September 2018
August 2018
July 2018
June 2018
May 2018
April 2018
March 2018
February 2018
January 2018
----- 2017 -----
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
February 2017
January 2017
----- 2016 -----
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
----- 2015 -----
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
----- 2014 -----
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
----- 2013 -----
December 2013
November 2013
October 2013
September 2013
August 2013
July 2013
June 2013
May 2013
April 2013
March 2013
February 2013
January 2013
----- 2012 -----
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
May 2012
April 2012
March 2012
February 2012
January 2012
----- 2011 -----
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
----- 2010 -----
December 2010
November 2010
October 2010
September 2010
August 2010
July 2010
June 2010
May 2010
April 2010
March 2010
February 2010
January 2010
----- 2009 -----
December 2009
November 2009
October 2009
September 2009
August 2009
July 2009
June 2009
May 2009
April 2009
March 2009
February 2009
January 2009
----- 2008 -----
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
January 2008
----- 2007 -----
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
February 2007
January 2007
----- 2006 -----
December 2006
November 2006
October 2006
September 2006
August 2006
July 2006
June 2006
May 2006
April 2006
commits@gajim.org
1 participants
95 discussions
Start a n
N
ew thread
[Git][gajim/gajim][master] 3 commits: Import app only once
by Philipp Hörist
25 Apr '18
25 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: b4f6116e by Philipp Hörist at 2018-04-25T20:59:23+02:00 Import app only once - - - - - 8c23d5a4 by Philipp Hörist at 2018-04-25T21:24:05+02:00 Move some imports - - - - - 707599dd by Philipp Hörist at 2018-04-25T21:26:02+02:00 Dont determine paths at module import time - - - - - 4 changed files: - gajim/application.py - gajim/common/caps_cache.py - gajim/common/logger.py - gajim/history_manager.py Changes: ===================================== gajim/application.py ===================================== --- a/gajim/application.py +++ b/gajim/application.py @@ -43,9 +43,13 @@ from urllib.parse import unquote from gi.repository import GLib, Gio, Gtk +from gajim.common import app from gajim.common import i18n from gajim.common import configpaths from gajim.common import logging_helpers +from gajim.common import exceptions +from gajim.common import caps_cache +from gajim.common import logger class GajimApplication(Gtk.Application): @@ -121,12 +125,8 @@ class GajimApplication(Gtk.Application): def _startup(self, application): # Create and initialize Application Paths & Databases - from gajim.common import app app.detect_dependencies() configpaths.create_paths() - from gajim.common import exceptions - from gajim.common import logger - from gajim.common import caps_cache try: app.logger = logger.Logger() caps_cache.initialize(app.logger) @@ -171,7 +171,6 @@ class GajimApplication(Gtk.Application): gui_menu_builder.build_accounts_menu() def _open_uris(self, uris): - from gajim.common import app for uri in uris: app.log('uri_handler').info('open %s', uri) if not uri.startswith('xmpp:'): @@ -213,7 +212,6 @@ class GajimApplication(Gtk.Application): self.interface.roster.prepare_quit() # Commit any outstanding SQL transactions - from gajim.common import app app.logger.commit() def _handle_remote_options(self, application, command_line): @@ -287,7 +285,6 @@ class GajimApplication(Gtk.Application): def add_actions(self): ''' Build Application Actions ''' from gajim.app_actions import AppActions - from gajim.common import app action = AppActions(self) self.account_actions = [ ===================================== gajim/common/caps_cache.py ===================================== --- a/gajim/common/caps_cache.py +++ b/gajim/common/caps_cache.py @@ -436,8 +436,7 @@ class MucCapsCache: identities, features, data = [], [], [] query_childs = stanza.getQueryChildren() if not query_childs: - from gajim.common import app - app.log('gajim.muc').warning('%s returned empty disco info', jid) + log.warning('%s returned empty disco info', jid) return for child in query_childs: ===================================== gajim/common/logger.py ===================================== --- a/gajim/common/logger.py +++ b/gajim/common/logger.py @@ -49,9 +49,6 @@ from gajim.common.const import ( JIDConstant, KindConstant, ShowConstant, TypeConstant, SubscriptionConstant) -LOG_DB_PATH = configpaths.get('LOG_DB') -LOG_DB_FOLDER, LOG_DB_FILE = os.path.split(LOG_DB_PATH) -CACHE_DB_PATH = configpaths.get('CACHE_DB') LOGS_SQL_STATEMENT = ''' CREATE TABLE jids( @@ -132,6 +129,8 @@ class Logger: self._jid_ids = {} self._con = None self._commit_timout_id = None + self._log_db_path = configpaths.get('LOG_DB') + self._cache_db_path = configpaths.get('CACHE_DB') self._create_databases() self._migrate_databases() @@ -139,23 +138,23 @@ class Logger: self._get_jid_ids_from_db() def _create_databases(self): - if os.path.isdir(LOG_DB_PATH): + if os.path.isdir(self._log_db_path): log.error(_('%s is a directory but should be a file'), - LOG_DB_PATH) + self._log_db_path) sys.exit() - if os.path.isdir(CACHE_DB_PATH): + if os.path.isdir(self._cache_db_path): log.error(_('%s is a directory but should be a file'), - CACHE_DB_PATH) + self._cache_db_path) sys.exit() - if not os.path.exists(LOG_DB_PATH): - if os.path.exists(CACHE_DB_PATH): - os.remove(CACHE_DB_PATH) - self._create(LOGS_SQL_STATEMENT, LOG_DB_PATH) + if not os.path.exists(self._log_db_path): + if os.path.exists(self._cache_db_path): + os.remove(self._cache_db_path) + self._create(LOGS_SQL_STATEMENT, self._log_db_path) - if not os.path.exists(CACHE_DB_PATH): - self._create(CACHE_SQL_STATEMENT, CACHE_DB_PATH) + if not os.path.exists(self._cache_db_path): + self._create(CACHE_SQL_STATEMENT, self._cache_db_path) @staticmethod def _create(statement, path): @@ -181,11 +180,11 @@ class Logger: def _migrate_databases(self): try: - con = sqlite.connect(LOG_DB_PATH) + con = sqlite.connect(self._log_db_path) self._migrate_logs(con) con.close() - con = sqlite.connect(CACHE_DB_PATH) + con = sqlite.connect(self._cache_db_path) self._migrate_cache(con) con.close() except Exception: @@ -267,7 +266,7 @@ class Logger: def _connect_databases(self): self._con = sqlite.connect( - LOG_DB_PATH, timeout=20.0, isolation_level='IMMEDIATE') + self._log_db_path, timeout=20.0, isolation_level='IMMEDIATE') self._con.row_factory = self.namedtuple_factory @@ -278,7 +277,7 @@ class Logger: self._set_synchronous(False) try: self._con.execute("ATTACH DATABASE '%s' AS cache" % - CACHE_DB_PATH.replace("'", "''")) + self._cache_db_path.replace("'", "''")) except Exception as error: log.exception('Error') self._con.close() @@ -680,7 +679,7 @@ class Logger: sql, tuple(jids) + (restore, pending)).fetchall() except sqlite.DatabaseError: self.dispatch('DB_ERROR', - exceptions.DatabaseMalformed(LOG_DB_PATH)) + exceptions.DatabaseMalformed(self._log_db_path)) return [] messages.reverse() ===================================== gajim/history_manager.py ===================================== --- a/gajim/history_manager.py +++ b/gajim/history_manager.py @@ -84,7 +84,6 @@ if is_standalone(): from gajim.common import app from gajim import gtkgui_helpers -from gajim.common.logger import LOG_DB_PATH from gajim.common.const import JIDConstant, KindConstant from gajim.common import helpers from gajim import dialogs @@ -109,9 +108,10 @@ class HistoryManager: # set the icon to all windows Gtk.Window.set_default_icon_list(pixs) - if not os.path.exists(LOG_DB_PATH): + log_db_path = configpaths.get('LOG_DB') + if not os.path.exists(log_db_path): dialogs.ErrorDialog(_('Cannot find history logs database'), - '%s does not exist.' % LOG_DB_PATH) + '%s does not exist.' % log_db_path) sys.exit() xml = gtkgui_helpers.get_gtk_builder('history_manager.ui') @@ -128,8 +128,8 @@ class HistoryManager: self.jids_already_in = [] # holds jids that we already have in DB self.AT_LEAST_ONE_DELETION_DONE = False - self.con = sqlite3.connect(LOG_DB_PATH, timeout=20.0, - isolation_level='IMMEDIATE') + self.con = sqlite3.connect( + log_db_path, timeout=20.0, isolation_level='IMMEDIATE') self.cur = self.con.cursor() self._init_jids_listview() View it on GitLab:
https://dev.gajim.org/gajim/gajim/compare/105427b8fdf6af213e73aacca48cda7ad…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/compare/105427b8fdf6af213e73aacca48cda7ad…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Use consistent way to get paths
by Philipp Hörist
25 Apr '18
25 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 105427b8 by Philipp Hörist at 2018-04-25T20:49:37+02:00 Use consistent way to get paths This lets us now import the app module without setting paths - - - - - 19 changed files: - gajim/common/app.py - gajim/common/configpaths.py - gajim/common/connection.py - gajim/common/connection_handlers.py - gajim/common/connection_handlers_events.py - gajim/common/helpers.py - gajim/common/jingle_content.py - gajim/common/jingle_xtls.py - gajim/config.py - gajim/gtkgui_helpers.py - gajim/gui_interface.py - gajim/history_manager.py - gajim/plugins/gajimplugin.py - gajim/plugins/gui.py - gajim/plugins/pluginmanager.py - gajim/plugins/plugins_i18n.py - gajim/vcard.py - test/lib/__init__.py - test/test_pluginmanager.py Changes: ===================================== gajim/common/app.py ===================================== --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -31,7 +31,6 @@ import os import sys import logging -import locale import uuid from distutils.version import LooseVersion as V from collections import namedtuple @@ -63,20 +62,6 @@ logger = None # some plugins use that gajimpaths = configpaths.gajimpaths -AVATAR_PATH = configpaths.get('AVATAR') -MY_EMOTS_PATH = configpaths.get('MY_EMOTS') -MY_ICONSETS_PATH = configpaths.get('MY_ICONSETS') -MY_MOOD_ICONSETS_PATH = configpaths.get('MY_MOOD_ICONSETS') -MY_ACTIVITY_ICONSETS_PATH = configpaths.get('MY_ACTIVITY_ICONSETS') -MY_CACERTS = configpaths.get('MY_CACERTS') -MY_PEER_CERTS_PATH = configpaths.get('MY_PEER_CERTS') -DATA_DIR = configpaths.get('DATA') -ICONS_DIR = configpaths.get('ICONS') -HOME_DIR = configpaths.get('HOME') -PLUGINS_DIRS = [configpaths.get('PLUGINS_BASE'), - configpaths.get('PLUGINS_USER')] -PLUGINS_CONFIG_DIR = configpaths.get('PLUGINS_CONFIG_DIR') -MY_CERT_DIR = configpaths.get('MY_CERT') RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname']) ===================================== gajim/common/configpaths.py ===================================== --- a/gajim/common/configpaths.py +++ b/gajim/common/configpaths.py @@ -32,6 +32,9 @@ from gajim.common.const import PathType, PathLocation def get(key): + if key == 'PLUGINS_DIRS': + return [_paths['PLUGINS_BASE'], + _paths['PLUGINS_USER']] return _paths[key] @@ -43,6 +46,10 @@ def get_paths(type_): yield _paths[key] +def override_path(*args, **kwargs): + _paths._add(*args, **kwargs) + + def set_separation(active: bool): _paths.profile_separation = active ===================================== gajim/common/connection.py ===================================== --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -1217,7 +1217,7 @@ class Connection(CommonConnection, ConnectionHandlers): cacerts = '' if os.name == 'nt': cacerts = certifi.where() - mycerts = common.app.MY_CACERTS + mycerts = common.configpaths.get('MY_CACERTS') tls_version = app.config.get_per('accounts', self.name, 'tls_version') cipher_list = app.config.get_per('accounts', self.name, 'cipher_list') ===================================== gajim/common/connection_handlers.py ===================================== --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -47,6 +47,7 @@ from gajim.common import helpers from gajim.common import app from gajim.common import dataforms from gajim.common import jingle_xtls +from gajim.common import configpaths from gajim.common.caps_cache import muc_caps_cache from gajim.common.commands import ConnectionCommands from gajim.common.pubsub import ConnectionPubSub @@ -391,7 +392,7 @@ class ConnectionVcard: else: app.log('avatar').info( 'Update (vCard): %s %s', obj.nick, obj.avatar_sha) - path = os.path.join(app.AVATAR_PATH, obj.avatar_sha) + path = os.path.join(configpaths.get('AVATAR'), obj.avatar_sha) if not os.path.isfile(path): app.log('avatar').info( 'Request (vCard): %s', obj.nick) @@ -572,7 +573,7 @@ class ConnectionVcard: current_sha = app.config.get_per('accounts', self.name, 'avatar_sha') if current_sha == avatar_sha: - path = os.path.join(app.AVATAR_PATH, current_sha) + path = os.path.join(configpaths.get('AVATAR'), current_sha) if not os.path.isfile(path): app.log('avatar').info( 'Caching (vCard): %s', current_sha) ===================================== gajim/common/connection_handlers_events.py ===================================== --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -40,6 +40,7 @@ from gajim.common import helpers from gajim.common import app from gajim.common import i18n from gajim.common import dataforms +from gajim.common import configpaths from gajim.common.zeroconf.zeroconf import Constant from gajim.common.const import KindConstant from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS @@ -2738,7 +2739,8 @@ class NotificationEvent(nec.NetworkIncomingEvent): if jid: # we want an avatar puny_jid = helpers.sanitize_filename(jid) - path_to_file = os.path.join(app.AVATAR_PATH, puny_jid) + suffix + path_to_file = os.path.join( + configpaths.get('AVATAR'), puny_jid) + suffix path_to_local_file = path_to_file + '_local' for extension in ('.png', '.jpeg'): path_to_local_file_full = path_to_local_file + extension ===================================== gajim/common/helpers.py ===================================== --- a/gajim/common/helpers.py +++ b/gajim/common/helpers.py @@ -53,6 +53,7 @@ import nbxmpp from gajim.common.i18n import Q_ from gajim.common.i18n import ngettext +from gajim.common import configpaths try: import precis_i18n.codec @@ -863,8 +864,8 @@ def check_soundfile_path(file_, dirs=None): :return the path to file or None if it doesn't exists. """ if dirs is None: - dirs = [app.configpaths.get('MY_DATA'), - app.DATA_DIR] + dirs = [configpaths.get('MY_DATA'), + configpaths.get('DATA')] if not file_: return None @@ -891,8 +892,8 @@ def strip_soundfile_path(file_, dirs=None, abs=True): return None if dirs is None: - dirs = [app.configpaths.get('MY_DATA'), - app.DATA_DIR] + dirs = [configpaths.get('MY_DATA'), + configpaths.get('DATA')] name = os.path.basename(file_) for d in dirs: @@ -1257,31 +1258,32 @@ def get_current_show(account): return app.SHOW_LIST[status] def get_iconset_path(iconset): - if os.path.isdir(os.path.join(app.DATA_DIR, 'iconsets', iconset)): - return os.path.join(app.DATA_DIR, 'iconsets', iconset) - elif os.path.isdir(os.path.join(app.MY_ICONSETS_PATH, iconset)): - return os.path.join(app.MY_ICONSETS_PATH, iconset) + if os.path.isdir(os.path.join(configpaths.get('DATA'), 'iconsets', iconset)): + return os.path.join(configpaths.get('DATA'), 'iconsets', iconset) + elif os.path.isdir(os.path.join(configpaths.get('MY_ICONSETS'), iconset)): + return os.path.join(configpaths.get('MY_ICONSETS'), iconset) def get_mood_iconset_path(iconset): - if os.path.isdir(os.path.join(app.DATA_DIR, 'moods', iconset)): - return os.path.join(app.DATA_DIR, 'moods', iconset) - elif os.path.isdir(os.path.join(app.MY_MOOD_ICONSETS_PATH, iconset)): - return os.path.join(app.MY_MOOD_ICONSETS_PATH, iconset) + if os.path.isdir(os.path.join(configpaths.get('DATA'), 'moods', iconset)): + return os.path.join(configpaths.get('DATA'), 'moods', iconset) + elif os.path.isdir( + os.path.join(configpaths.get('MY_MOOD_ICONSETS'), iconset)): + return os.path.join(configpaths.get('MY_MOOD_ICONSETS'), iconset) def get_activity_iconset_path(iconset): - if os.path.isdir(os.path.join(app.DATA_DIR, 'activities', iconset)): - return os.path.join(app.DATA_DIR, 'activities', iconset) - elif os.path.isdir(os.path.join(app.MY_ACTIVITY_ICONSETS_PATH, + if os.path.isdir(os.path.join(configpaths.get('DATA'), 'activities', iconset)): + return os.path.join(configpaths.get('DATA'), 'activities', iconset) + elif os.path.isdir(os.path.join(configpaths.get('MY_ACTIVITY_ICONSETS'), iconset)): - return os.path.join(app.MY_ACTIVITY_ICONSETS_PATH, iconset) + return os.path.join(configpaths.get('MY_ACTIVITY_ICONSETS'), iconset) def get_transport_path(transport): - if os.path.isdir(os.path.join(app.DATA_DIR, 'iconsets', 'transports', + if os.path.isdir(os.path.join(configpaths.get('DATA'), 'iconsets', 'transports', transport)): - return os.path.join(app.DATA_DIR, 'iconsets', 'transports', transport) - elif os.path.isdir(os.path.join(app.MY_ICONSETS_PATH, 'transports', + return os.path.join(configpaths.get('DATA'), 'iconsets', 'transports', transport) + elif os.path.isdir(os.path.join(configpaths.get('MY_ICONSETS'), 'transports', transport)): - return os.path.join(app.MY_ICONSETS_PATH, 'transports', transport) + return os.path.join(configpaths.get('MY_ICONSETS'), 'transports', transport) # No transport folder found, use default jabber one return get_iconset_path(app.config.get('iconset')) @@ -1606,13 +1608,13 @@ def version_condition(current_version, required_version): def get_available_emoticon_themes(): emoticons_themes = [] - emoticons_data_path = os.path.join(app.DATA_DIR, 'emoticons') + emoticons_data_path = os.path.join(configpaths.get('DATA'), 'emoticons') font_theme_path = os.path.join( - app.DATA_DIR, 'emoticons', 'font-emoticons', 'emoticons_theme.py') + configpaths.get('DATA'), 'emoticons', 'font-emoticons', 'emoticons_theme.py') folders = os.listdir(emoticons_data_path) - if os.path.isdir(app.MY_EMOTS_PATH): - folders += os.listdir(app.MY_EMOTS_PATH) + if os.path.isdir(configpaths.get('MY_EMOTS')): + folders += os.listdir(configpaths.get('MY_EMOTS')) file = 'emoticons_theme.py' if os.name == 'nt' and not os.path.exists(font_theme_path): @@ -1628,11 +1630,11 @@ def get_available_emoticon_themes(): return emoticons_themes def get_emoticon_theme_path(theme): - emoticons_data_path = os.path.join(app.DATA_DIR, 'emoticons', theme) + emoticons_data_path = os.path.join(configpaths.get('DATA'), 'emoticons', theme) if os.path.exists(emoticons_data_path): return emoticons_data_path - emoticons_user_path = os.path.join(app.MY_EMOTS_PATH, theme) + emoticons_user_path = os.path.join(configpaths.get('MY_EMOTS'), theme) if os.path.exists(emoticons_user_path): return emoticons_user_path ===================================== gajim/common/jingle_content.py ===================================== --- a/gajim/common/jingle_content.py +++ b/gajim/common/jingle_content.py @@ -19,6 +19,7 @@ Handles Jingle contents (XEP 0166) import os from gajim.common import app +from gajim.common import configpaths import nbxmpp from gajim.common.jingle_xtls import SELF_SIGNED_CERTIFICATE from gajim.common.jingle_xtls import load_cert_file @@ -221,8 +222,8 @@ class JingleContent: if self.use_security: security = nbxmpp.simplexml.Node( tag=nbxmpp.NS_JINGLE_XTLS + ' security') - certpath = os.path.join(app.MY_CERT_DIR, SELF_SIGNED_CERTIFICATE)\ - + '.cert' + certpath = os.path.join( + configpaths.get('MY_CERT'), SELF_SIGNED_CERTIFICATE) + '.cert' cert = load_cert_file(certpath) if cert: try: ===================================== gajim/common/jingle_xtls.py ===================================== --- a/gajim/common/jingle_xtls.py +++ b/gajim/common/jingle_xtls.py @@ -21,6 +21,7 @@ import os import nbxmpp from gajim.common import app +from gajim.common import configpaths log = logging.getLogger('gajim.c.jingle_xtls') @@ -111,18 +112,18 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None): elif fingerprint == 'client': ctx.set_verify(SSL.VERIFY_PEER, verify_cb or default_callback) - cert_name = os.path.join(app.MY_CERT_DIR, SELF_SIGNED_CERTIFICATE) + cert_name = os.path.join(configpaths.get('MY_CERT'), SELF_SIGNED_CERTIFICATE) ctx.use_privatekey_file((cert_name + '.pkey').encode('utf-8')) ctx.use_certificate_file((cert_name + '.cert').encode('utf-8')) # Try to load Diffie-Hellman parameters. # First try user DH parameters, if this fails load the default DH parameters - dh_params_name = os.path.join(app.MY_CERT_DIR, DH_PARAMS) + dh_params_name = os.path.join(configpaths.get('MY_CERT'), DH_PARAMS) try: with open(dh_params_name, "r") as dh_params_file: ctx.load_tmp_dh(dh_params_name.encode('utf-8')) except FileNotFoundError as err: - default_dh_params_name = os.path.join(app.DATA_DIR, + default_dh_params_name = os.path.join(configpaths.get('DATA'), 'other', DEFAULT_DH_PARAMS) try: with open(default_dh_params_name, "r") as default_dh_params_file: @@ -134,7 +135,7 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None): if remote_jid: store = ctx.get_cert_store() - path = os.path.join(os.path.expanduser(app.MY_PEER_CERTS_PATH), + path = os.path.join(os.path.expanduser(configpaths.get('MY_PEER_CERTS')), remote_jid) + '.cert' if os.path.exists(path): load_cert_file(path, cert_store=store) @@ -151,7 +152,7 @@ def read_cert(certpath): return certificate def send_cert(con, jid_from, sid): - certpath = os.path.join(app.MY_CERT_DIR, SELF_SIGNED_CERTIFICATE) + \ + certpath = os.path.join(configpaths.get('MY_CERT'), SELF_SIGNED_CERTIFICATE) + \ '.cert' certificate = read_cert(certpath) iq = nbxmpp.Iq('result', to=jid_from) @@ -170,7 +171,7 @@ def send_cert(con, jid_from, sid): def handle_new_cert(con, obj, jid_from): jid = app.get_jid_without_resource(jid_from) - certpath = os.path.join(os.path.expanduser(app.MY_PEER_CERTS_PATH), jid) + certpath = os.path.join(os.path.expanduser(configpaths.get('MY_PEER_CERTS')), jid) certpath += '.cert' id_ = obj.getAttr('id') @@ -188,7 +189,7 @@ def handle_new_cert(con, obj, jid_from): approve_pending_content(id_) def check_cert(jid, fingerprint): - certpath = os.path.join(os.path.expanduser(app.MY_PEER_CERTS_PATH), jid) + certpath = os.path.join(os.path.expanduser(configpaths.get('MY_PEER_CERTS')), jid) certpath += '.cert' if os.path.exists(certpath): cert = load_cert_file(certpath) ===================================== gajim/config.py ===================================== --- a/gajim/config.py +++ b/gajim/config.py @@ -58,6 +58,7 @@ from gajim.common import app from gajim.common import connection from gajim.common import dataforms from gajim.common import ged +from gajim.common import configpaths from gajim.accounts_window import AccountsWindow try: @@ -202,9 +203,10 @@ class PreferencesWindow: self.update_theme_list() # iconset - iconsets_list = os.listdir(os.path.join(app.DATA_DIR, 'iconsets')) - if os.path.isdir(app.MY_ICONSETS_PATH): - iconsets_list += os.listdir(app.MY_ICONSETS_PATH) + iconsets_list = os.listdir( + os.path.join(configpaths.get('DATA'), 'iconsets')) + if os.path.isdir(configpaths.get('MY_ICONSETS')): + iconsets_list += os.listdir(configpaths.get('MY_ICONSETS')) # new model, image in 0, string in 1 model = Gtk.ListStore(Gtk.Image, str) renderer_image = cell_renderer_image.CellRendererImage(0, 0) @@ -217,8 +219,8 @@ class PreferencesWindow: self.iconset_combobox.set_model(model) l = [] for dir in iconsets_list: - if not os.path.isdir(os.path.join(app.DATA_DIR, 'iconsets', dir)) \ - and not os.path.isdir(os.path.join(app.MY_ICONSETS_PATH, dir)): + if not os.path.isdir(os.path.join(configpaths.get('DATA'), 'iconsets', dir)) \ + and not os.path.isdir(os.path.join(configpaths.get('MY_ICONSETS'), dir)): continue if dir != '.svn' and dir != 'transports': l.append(dir) @@ -2292,7 +2294,8 @@ class AccountCreationWizardWindow: self.update_proxy_list() # parse servers.xml - servers_xml = os.path.join(app.DATA_DIR, 'other', 'servers.xml') + servers_xml = os.path.join( + configpaths.get('DATA'), 'other', 'servers.xml') servers = gtkgui_helpers.parse_server_xml(servers_xml) servers_model = self.xml.get_object('server_liststore') for server in servers: @@ -2532,16 +2535,17 @@ class AccountCreationWizardWindow: 'hostname'] # Check if cert is already in file certs = '' - if os.path.isfile(app.MY_CACERTS): - f = open(app.MY_CACERTS) + my_ca_certs = configpaths.get('MY_CACERTS') + if os.path.isfile(my_ca_certs): + f = open(my_ca_certs) certs = f.read() f.close() if self.ssl_cert in certs: dialogs.ErrorDialog(_('Certificate Already in File'), _('This certificate is already in file %s, so it\'s ' - 'not added again.') % app.MY_CACERTS) + 'not added again.') % my_ca_certs) else: - f = open(app.MY_CACERTS, 'a') + f = open(my_ca_certs, 'a') f.write(hostname + '\n') f.write(self.ssl_cert + '\n\n') f.close() ===================================== gajim/gtkgui_helpers.py ===================================== --- a/gajim/gtkgui_helpers.py +++ b/gajim/gtkgui_helpers.py @@ -52,7 +52,7 @@ from gajim.common import pep from gajim.common import configpaths gtk_icon_theme = Gtk.IconTheme.get_default() -gtk_icon_theme.append_search_path(app.ICONS_DIR) +gtk_icon_theme.append_search_path(configpaths.get('ICONS')) class Color: BLACK = Gdk.RGBA(red=0, green=0, blue=0, alpha=1) @@ -610,7 +610,7 @@ def get_pep_as_pixbuf(pep_class): elif isinstance(pep_class, pep.UserTunePEP): icon = get_icon_pixmap('audio-x-generic', quiet=True) if not icon: - path = os.path.join(app.DATA_DIR, 'emoticons', 'static', + path = os.path.join(configpaths.get('DATA'), 'emoticons', 'static', 'music.png') return GdkPixbuf.Pixbuf.new_from_file(path) return icon ===================================== gajim/gui_interface.py ===================================== --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -1352,16 +1352,17 @@ class Interface: if is_checked[0]: # Check if cert is already in file certs = '' - if os.path.isfile(app.MY_CACERTS): - f = open(app.MY_CACERTS) + my_ca_certs = configpaths.get('MY_CACERTS') + if os.path.isfile(my_ca_certs): + f = open(my_ca_certs) certs = f.read() f.close() if obj.cert in certs: dialogs.ErrorDialog(_('Certificate Already in File'), _('This certificate is already in file %s, so it\'s ' - 'not added again.') % app.MY_CACERTS) + 'not added again.') % my_ca_certs) else: - f = open(app.MY_CACERTS, 'a') + f = open(my_ca_certs, 'a') f.write(server + '\n') f.write(obj.cert + '\n\n') f.close() @@ -2446,14 +2447,15 @@ class Interface: pixbuf = pixbuf.scale_simple(AvatarSize.PROFILE, AvatarSize.PROFILE, GdkPixbuf.InterpType.BILINEAR) - publish_path = os.path.join(app.AVATAR_PATH, 'temp_publish') + publish_path = os.path.join( + configpaths.get('AVATAR'), 'temp_publish') pixbuf.savev(publish_path, 'png', [], []) with open(publish_path, 'rb') as file: data = file.read() return self.save_avatar(data) sha = hashlib.sha1(data).hexdigest() - path = os.path.join(app.AVATAR_PATH, sha) + path = os.path.join(configpaths.get('AVATAR'), sha) try: with open(path, "wb") as output_file: output_file.write(data) @@ -2475,7 +2477,7 @@ class Interface: size = size * scale if publish: - path = os.path.join(app.AVATAR_PATH, filename) + path = os.path.join(configpaths.get('AVATAR'), filename) with open(path, 'rb') as file: data = file.read() return data @@ -2488,7 +2490,7 @@ class Interface: except KeyError: pass - path = os.path.join(app.AVATAR_PATH, filename) + path = os.path.join(configpaths.get('AVATAR'), filename) if not os.path.isfile(path): return @@ -2532,7 +2534,7 @@ class Interface: @staticmethod def avatar_exists(filename): - path = os.path.join(app.AVATAR_PATH, filename) + path = os.path.join(configpaths.get('AVATAR'), filename) if not os.path.isfile(path): return False return True @@ -2601,7 +2603,8 @@ class Interface: mood = received_mood if received_mood in pep.MOODS else 'unknown' return gtkgui_helpers.load_mood_icon(mood).get_pixbuf() elif isinstance(pep_obj, pep.UserTunePEP): - path = os.path.join(app.DATA_DIR, 'emoticons', 'static', 'music.png') + path = os.path.join( + configpaths.get('DATA'), 'emoticons', 'static', 'music.png') return GdkPixbuf.Pixbuf.new_from_file(path) elif isinstance(pep_obj, pep.UserActivityPEP): pep_ = pep_obj._pep_specific_data ===================================== gajim/history_manager.py ===================================== --- a/gajim/history_manager.py +++ b/gajim/history_manager.py @@ -47,6 +47,7 @@ from gi.repository import GLib from gi.repository import Gio from gajim.common import i18n +from gajim.common import configpaths def is_standalone(): @@ -59,10 +60,6 @@ def is_standalone(): if is_standalone(): - # Standalone Mode - # Must be done before importing app - from gajim.common import configpaths - try: shortargs = 'hvsc:l:p:' longargs = 'help verbose separate config-path= loglevel= profile=' @@ -474,7 +471,7 @@ class HistoryManager: dlg = xml.get_object('filechooserdialog') dlg.set_title(_('Exporting History Logs…')) - dlg.set_current_folder(app.HOME_DIR) + dlg.set_current_folder(configpaths.get('HOME')) dlg.props.do_overwrite_confirmation = True response = dlg.run() ===================================== gajim/plugins/gajimplugin.py ===================================== --- a/gajim/plugins/gajimplugin.py +++ b/gajim/plugins/gajimplugin.py @@ -27,7 +27,7 @@ Base class for implementing plugin. import os import locale -from gajim.common import app +from gajim.common import configpaths from gajim.plugins.helpers import log_calls, log from gajim.plugins.gui import GajimPluginConfigDialog @@ -213,7 +213,7 @@ class GajimPluginConfig(): def __init__(self, plugin): self.plugin = plugin self.FILE_PATH = os.path.join( - app.PLUGINS_CONFIG_DIR, self.plugin.short_name) + configpaths.get('PLUGINS_CONFIG_DIR'), self.plugin.short_name) self.data = {} @log_calls('GajimPluginConfig') ===================================== gajim/plugins/gui.py ===================================== --- a/gajim/plugins/gui.py +++ b/gajim/plugins/gui.py @@ -38,6 +38,7 @@ from gajim import gtkgui_helpers from gajim.dialogs import WarningDialog, YesNoDialog, ArchiveChooserDialog from gajim.htmltextview import HtmlTextView from gajim.common import app +from gajim.common import configpaths from gajim.plugins.helpers import log_calls from gajim.plugins.helpers import GajimPluginActivateException from gajim.plugins.plugins_i18n import _ @@ -161,8 +162,8 @@ class PluginsWindow(object): self.plugin_description_textview, None) self.plugin_description_textview.set_property('sensitive', True) - self.uninstall_plugin_button.set_property('sensitive', - app.PLUGINS_DIRS[1] in plugin.__path__) + self.uninstall_plugin_button.set_property( + 'sensitive', configpaths.get('PLUGINS_USER') in plugin.__path__) self.configure_plugin_button.set_property( 'sensitive', plugin.config_dialog is not None) ===================================== gajim/plugins/pluginmanager.py ===================================== --- a/gajim/plugins/pluginmanager.py +++ b/gajim/plugins/pluginmanager.py @@ -36,6 +36,7 @@ from pkg_resources import parse_version from gajim.common import app from gajim.common import nec +from gajim.common import configpaths from gajim.common.exceptions import PluginsystemError from gajim.plugins.helpers import log, log_calls, Singleton @@ -107,7 +108,7 @@ class PluginManager(metaclass=Singleton): Registered names with instances of encryption Plugins. ''' - for path in [app.PLUGINS_DIRS[1], app.PLUGINS_DIRS[0]]: + for path in reversed(configpaths.get('PLUGINS_DIRS')): pc = PluginManager.scan_dir_for_plugins(path) self.add_plugins(pc) @@ -534,7 +535,7 @@ class PluginManager(metaclass=Singleton): try: if module_name in sys.modules: - if path == app.PLUGINS_DIRS[0]: + if path == configpaths.get('PLUGINS_BASE'): # Only reload plugins from Gajim base dir when they # dont exist. This means plugins in the user path are # always preferred. @@ -632,7 +633,7 @@ class PluginManager(metaclass=Singleton): if len(dirs) > 1: raise PluginsystemError(_('Archive is malformed')) - base_dir, user_dir = app.PLUGINS_DIRS + base_dir, user_dir = configpaths.get('PLUGINS_DIRS') plugin_dir = os.path.join(user_dir, dirs[0]) if os.path.isdir(plugin_dir): ===================================== gajim/plugins/plugins_i18n.py ===================================== --- a/gajim/plugins/plugins_i18n.py +++ b/gajim/plugins/plugins_i18n.py @@ -23,9 +23,10 @@ import gettext from os import path as os_path import os from gajim.common import app +from gajim.common import configpaths APP = 'gajim_plugins' -plugins_locale_dir = os_path.join(app.PLUGINS_DIRS[1], 'locale') +plugins_locale_dir = os_path.join(configpaths.get('PLUGINS_USER'), 'locale') if os.name != 'nt': locale.setlocale(locale.LC_ALL, '') ===================================== gajim/vcard.py ===================================== --- a/gajim/vcard.py +++ b/gajim/vcard.py @@ -44,6 +44,7 @@ from gajim import gtkgui_helpers from gajim.common import helpers from gajim.common import app from gajim.common import ged +from gajim.common import configpaths from gajim.common.i18n import Q_ from gajim.common.const import AvatarSize @@ -84,7 +85,7 @@ class VcardWindow: self.real_resource = contact.resource puny_jid = helpers.sanitize_filename(contact.jid) - local_avatar_basepath = os.path.join(app.AVATAR_PATH, puny_jid) + \ + local_avatar_basepath = os.path.join(configpaths.get('AVATAR'), puny_jid) + \ '_local' for extension in ('.png', '.jpeg'): local_avatar_path = local_avatar_basepath + extension ===================================== test/lib/__init__.py ===================================== --- a/test/lib/__init__.py +++ b/test/lib/__init__.py @@ -47,7 +47,7 @@ def setup_env(): import logging logging.basicConfig() - app.DATA_DIR = gajim_root + '/gajim/data' + configpaths.override_path('DATA', gajim_root + '/gajim/data') app.use_x = use_x app.contacts = LegacyContactsAPI() app.connections = {} ===================================== test/test_pluginmanager.py ===================================== --- a/test/test_pluginmanager.py +++ b/test/test_pluginmanager.py @@ -54,9 +54,8 @@ configpaths.set_config_root(configdir) configpaths.init() # for some reason common.app needs to be imported before xmpppy? -from gajim.common import app -app.DATA_DIR = gajim_root + '/gajim/data' +configpaths.override_path('DATA', gajim_root + '/gajim/data') from common.stanza_session import StanzaSession View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/105427b8fdf6af213e73aacca48cda7ada…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/105427b8fdf6af213e73aacca48cda7ada…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Refactor Gajim startup
by Philipp Hörist
25 Apr '18
25 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 52cbae69 by Philipp Hörist at 2018-04-25T19:16:38+02:00 Refactor Gajim startup Remove as much as possible startup code from GtkApplication to stay more true to our goal to separate GUI from the rest of Gajim - - - - - 3 changed files: - + gajim/application.py - gajim/common/i18n.py - gajim/gajim.py Changes: ===================================== gajim/application.py ===================================== --- /dev/null +++ b/gajim/application.py @@ -0,0 +1,397 @@ +# -*- coding:utf-8 -*- +## src/gajim.py +## +## Copyright (C) 2003-2017 Yann Leboulanger <asterix AT
lagaule.org
> +## Copyright (C) 2004-2005 Vincent Hanquez <tab AT
snarc.org
> +## Copyright (C) 2005 Alex Podaras <bigpod AT
gmail.com
> +## Norman Rasmussen <norman AT rasmussen.co.za> +## Stéphan Kochen <stephan AT kochen.nl> +## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT
gmail.com
> +## Alex Mauer <hawke AT
hawkesnest.net
> +## Copyright (C) 2005-2007 Travis Shirk <travis AT
pobox.com
> +## Nikos Kouremenos <kourem AT
gmail.com
> +## Copyright (C) 2006 Junglecow J <junglecow AT
gmail.com
> +## Stefan Bethge <stefan AT lanpartei.de> +## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT
lapin.org
> +## Copyright (C) 2007 Lukas Petrovicky <lukas AT
petrovicky.net
> +## James Newton <redshodan AT
gmail.com
> +## Copyright (C) 2007-2008 Brendan Taylor <whateley AT
gmail.com
> +## Julien Pivotto <roidelapluie AT
gmail.com
> +## Stephan Erb <steve-e AT h3c.de> +## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT
webkeks.org
> +## Copyright (C) 2016-2017 Emmanuel Gil Peyrot <linkmauve AT linkmauve.fr> +## Philipp Hörist <philipp AT
hoerist.com
> +## +## 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 sys +import os +from urllib.parse import unquote + +from gi.repository import GLib, Gio, Gtk + +from gajim.common import i18n +from gajim.common import configpaths +from gajim.common import logging_helpers + + +class GajimApplication(Gtk.Application): + '''Main class handling activation and command line.''' + + def __init__(self): + Gtk.Application.__init__(self, application_id='org.gajim.Gajim', + flags=( + Gio.ApplicationFlags.HANDLES_COMMAND_LINE | + Gio.ApplicationFlags.HANDLES_OPEN)) + + self.add_main_option('version', ord('V'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Show the application\'s version')) + self.add_main_option('quiet', ord('q'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Show only critical errors')) + self.add_main_option('separate', ord('s'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Separate profile files completely (even ' + 'history database and plugins)')) + self.add_main_option('verbose', ord('v'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Print XML stanzas and other debug ' + 'information')) + self.add_main_option('profile', ord('p'), GLib.OptionFlags.NONE, + GLib.OptionArg.STRING, + _('Use defined profile in configuration ' + 'directory'), 'NAME') + self.add_main_option('config-path', ord('c'), GLib.OptionFlags.NONE, + GLib.OptionArg.STRING, + _('Set configuration directory'), 'PATH') + self.add_main_option('loglevel', ord('l'), GLib.OptionFlags.NONE, + GLib.OptionArg.STRING, + _('Configure logging system'), 'LEVEL') + self.add_main_option('warnings', ord('w'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Show all warnings')) + self.add_main_option('ipython', ord('i'), GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Open IPython shell')) + self.add_main_option('show-next-pending-event', 0, GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Pops up a window with the next pending event')) + self.add_main_option('start-chat', 0, GLib.OptionFlags.NONE, + GLib.OptionArg.NONE, + _('Start a new chat')) + + self.add_main_option_entries(self._get_remaining_entry()) + + self.connect('handle-local-options', self._handle_local_options) + self.connect('command-line', self._handle_remote_options) + self.connect('startup', self._startup) + self.connect('activate', self._activate) + + self.interface = None + + GLib.set_prgname('gajim') + if GLib.get_application_name() != 'Gajim': + GLib.set_application_name('Gajim') + + @staticmethod + def _get_remaining_entry(): + option = GLib.OptionEntry() + option.arg = GLib.OptionArg.STRING_ARRAY + option.arg_data = None + option.arg_description = ('[URI …]') + option.flags = GLib.OptionFlags.NONE + option.long_name = GLib.OPTION_REMAINING + option.short_name = 0 + return [option] + + def _startup(self, application): + + # Create and initialize Application Paths & Databases + from gajim.common import app + app.detect_dependencies() + configpaths.create_paths() + from gajim.common import exceptions + from gajim.common import logger + from gajim.common import caps_cache + try: + app.logger = logger.Logger() + caps_cache.initialize(app.logger) + except exceptions.DatabaseMalformed as error: + dlg = Gtk.MessageDialog( + None, + Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, + Gtk.MessageType.ERROR, + Gtk.ButtonsType.OK, + _('Database Error')) + dlg.format_secondary_text(str(error)) + dlg.run() + dlg.destroy() + sys.exit() + + # Set Application Menu + app.app = self + path = os.path.join(configpaths.get('GUI'), 'application_menu.ui') + builder = Gtk.Builder() + builder.set_translation_domain(i18n.DOMAIN) + builder.add_from_file(path) + menubar = builder.get_object("menubar") + appmenu = builder.get_object("appmenu") + if app.prefers_app_menu(): + self.set_app_menu(appmenu) + else: + # Add it to the menubar instead + menubar.prepend_submenu('Gajim', appmenu) + self.set_menubar(menubar) + + def _activate(self, application): + if self.interface is not None: + self.interface.roster.window.present() + return + from gajim.gui_interface import Interface + from gajim import gtkgui_helpers + self.interface = Interface() + gtkgui_helpers.load_css() + self.interface.run(self) + self.add_actions() + from gajim import gui_menu_builder + gui_menu_builder.build_accounts_menu() + + def _open_uris(self, uris): + from gajim.common import app + for uri in uris: + app.log('uri_handler').info('open %s', uri) + if not uri.startswith('xmpp:'): + continue + # remove xmpp: + uri = uri[5:] + try: + jid, cmd = uri.split('?') + except ValueError: + # No query argument + jid, cmd = uri, 'message' + if cmd == 'join': + self.interface.join_gc_minimal(None, jid) + elif cmd == 'roster': + self.activate_action('add-contact', GLib.Variant('s', jid)) + elif cmd.startswith('message'): + attributes = cmd.split(';') + message = None + for key in attributes: + if key.startswith('body'): + try: + message = unquote(key.split('=')[1]) + except Exception: + app.log('uri_handler').error('Invalid URI: %s', cmd) + accounts = list(app.connections.keys()) + if not accounts: + continue + if len(accounts) == 1: + app.interface.new_chat_from_jid(accounts[0], jid, message) + else: + self.activate_action('start-chat') + start_chat_window = app.interface.instances['start_chat'] + start_chat_window.search_entry.set_text(jid) + + def do_shutdown(self, *args): + Gtk.Application.do_shutdown(self) + # Shutdown GUI and save config + if hasattr(self.interface, 'roster') and self.interface.roster: + self.interface.roster.prepare_quit() + + # Commit any outstanding SQL transactions + from gajim.common import app + app.logger.commit() + + def _handle_remote_options(self, application, command_line): + # Parse all options that should be executed on a remote instance + options = command_line.get_options_dict() + + remote_commands = ['ipython', + 'show-next-pending-event', + 'start-chat', + ] + + remaining = options.lookup_value(GLib.OPTION_REMAINING, + GLib.VariantType.new('as')) + + for cmd in remote_commands: + if options.contains(cmd): + self.activate_action(cmd) + return 0 + + if remaining is not None: + self._open_uris(remaining.unpack()) + return 0 + + self.activate() + return 0 + + def _handle_local_options(self, application, + options: GLib.VariantDict) -> int: + # Parse all options that have to be executed before ::startup + if options.contains('profile'): + # Incorporate profile name into application id + # to have a single app instance for each profile. + profile = options.lookup_value('profile').get_string() + app_id = '%s.%s' % (self.get_application_id(), profile) + self.set_application_id(app_id) + configpaths.set_profile(profile) + if options.contains('separate'): + configpaths.set_separation(True) + if options.contains('config-path'): + path = options.lookup_value('config-path').get_string() + configpaths.set_config_root(path) + + configpaths.init() + logging_helpers.init() + + if options.contains('quiet'): + logging_helpers.set_quiet() + if options.contains('verbose'): + logging_helpers.set_verbose() + if options.contains('loglevel'): + loglevel = options.lookup_value('loglevel').get_string() + logging_helpers.set_loglevels(loglevel) + if options.contains('warnings'): + self.show_warnings() + + return -1 + + def show_warnings(self): + import traceback + import warnings + + def warn_with_traceback(message, category, filename, lineno, + file=None, line=None): + traceback.print_stack(file=sys.stderr) + sys.stderr.write(warnings.formatwarning(message, category, + filename, lineno, line)) + + warnings.showwarning = warn_with_traceback + warnings.filterwarnings(action="always") + + def add_actions(self): + ''' Build Application Actions ''' + from gajim.app_actions import AppActions + from gajim.common import app + action = AppActions(self) + + self.account_actions = [ + ('-start-single-chat', action.on_single_message, 'online', 's'), + ('-join-groupchat', action.on_join_gc, 'online', 's'), + ('-add-contact', action.on_add_contact, 'online', 's'), + ('-services', action.on_service_disco, 'online', 's'), + ('-profile', action.on_profile, 'feature', 's'), + ('-xml-console', action.on_xml_console, 'always', 's'), + ('-server-info', action.on_server_info, 'online', 's'), + ('-archive', action.on_archiving_preferences, 'feature', 's'), + ('-sync-history', action.on_history_sync, 'online', 's'), + ('-privacylists', action.on_privacy_lists, 'feature', 's'), + ('-send-server-message', + action.on_send_server_message, 'online', 's'), + ('-set-motd', action.on_set_motd, 'online', 's'), + ('-update-motd', action.on_update_motd, 'online', 's'), + ('-delete-motd', action.on_delete_motd, 'online', 's'), + ('-activate-bookmark', + action.on_activate_bookmark, 'online', 'a{sv}'), + ('-open-event', action.on_open_event, 'always', 'a{sv}'), + ('-import-contacts', action.on_import_contacts, 'online', 's'), + ] + + # General Stateful Actions + + act = Gio.SimpleAction.new_stateful( + 'merge', None, + GLib.Variant.new_boolean(app.config.get('mergeaccounts'))) + act.connect('change-state', action.on_merge_accounts) + self.add_action(act) + + act = Gio.SimpleAction.new_stateful( + 'agent', None, + GLib.Variant.new_boolean(app.config.get('use_gpg_agent'))) + self.add_action(act) + + # General Actions + + self.general_actions = [ + ('quit', action.on_quit), + ('accounts', action.on_accounts), + ('add-account', action.on_add_account), + ('manage-proxies', action.on_manage_proxies), + ('start-chat', action.on_new_chat), + ('bookmarks', action.on_manage_bookmarks), + ('history-manager', action.on_history_manager), + ('preferences', action.on_preferences), + ('plugins', action.on_plugins), + ('file-transfer', action.on_file_transfers), + ('history', action.on_history), + ('shortcuts', action.on_keyboard_shortcuts), + ('features', action.on_features), + ('content', action.on_contents), + ('about', action.on_about), + ('faq', action.on_faq), + ('ipython', action.toggle_ipython), + ('show-next-pending-event', action.show_next_pending_event), + ] + + act = Gio.SimpleAction.new('add-contact', GLib.VariantType.new('s')) + act.connect("activate", action.on_add_contact_jid) + self.add_action(act) + + for action in self.general_actions: + action_name, func = action + act = Gio.SimpleAction.new(action_name, None) + act.connect("activate", func) + self.add_action(act) + + accounts_list = sorted(app.config.get_per('accounts')) + if not accounts_list: + return + if len(accounts_list) > 1: + for acc in accounts_list: + self.add_account_actions(acc) + else: + self.add_account_actions(accounts_list[0]) + + def add_account_actions(self, account): + for action in self.account_actions: + action_name, func, state, type_ = action + action_name = account + action_name + if self.lookup_action(action_name): + # We already added this action + continue + act = Gio.SimpleAction.new( + action_name, GLib.VariantType.new(type_)) + act.connect("activate", func) + if state != 'always': + act.set_enabled(False) + self.add_action(act) + + def remove_account_actions(self, account): + for action in self.account_actions: + action_name = account + action[0] + self.remove_action(action_name) + + def set_account_actions_state(self, account, new_state=False): + for action in self.account_actions: + action_name, _, state, _ = action + if not new_state and state in ('online', 'feature'): + # We go offline + self.lookup_action(account + action_name).set_enabled(False) + elif new_state and state == 'online': + # We go online + self.lookup_action(account + action_name).set_enabled(True) ===================================== gajim/common/i18n.py ===================================== --- a/gajim/common/i18n.py +++ b/gajim/common/i18n.py @@ -133,7 +133,6 @@ def initialize_win_translation(): def initialize_direction_mark(): - import gi from gi.repository import Gtk global direction_mark ===================================== gajim/gajim.py ===================================== --- a/gajim/gajim.py +++ b/gajim/gajim.py @@ -1,461 +1,112 @@ -# -*- coding:utf-8 -*- -## src/gajim.py -## -## Copyright (C) 2003-2017 Yann Leboulanger <asterix AT
lagaule.org
> -## Copyright (C) 2004-2005 Vincent Hanquez <tab AT
snarc.org
> -## Copyright (C) 2005 Alex Podaras <bigpod AT
gmail.com
> -## Norman Rasmussen <norman AT rasmussen.co.za> -## Stéphan Kochen <stephan AT kochen.nl> -## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT
gmail.com
> -## Alex Mauer <hawke AT
hawkesnest.net
> -## Copyright (C) 2005-2007 Travis Shirk <travis AT
pobox.com
> -## Nikos Kouremenos <kourem AT
gmail.com
> -## Copyright (C) 2006 Junglecow J <junglecow AT
gmail.com
> -## Stefan Bethge <stefan AT lanpartei.de> -## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT
lapin.org
> -## Copyright (C) 2007 Lukas Petrovicky <lukas AT
petrovicky.net
> -## James Newton <redshodan AT
gmail.com
> -## Copyright (C) 2007-2008 Brendan Taylor <whateley AT
gmail.com
> -## Julien Pivotto <roidelapluie AT
gmail.com
> -## Stephan Erb <steve-e AT h3c.de> -## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT
webkeks.org
> -## Copyright (C) 2016-2017 Emmanuel Gil Peyrot <linkmauve AT linkmauve.fr> -## Philipp Hörist <philipp AT
hoerist.com
> -## -## 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/
>. -## +# -*- coding: utf-8 -*- +# +# Copyright (C) 2018 Philipp Hörist <philipp AT
hoerist.com
> +# +# 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, either version 3 of the License, or +# (at your option) any later version. +# +# 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 sys import os +import sys import signal -from urllib.parse import unquote - -import gi -gi.require_version('GLib', '2.0') -gi.require_version('Gio', '2.0') -gi.require_version('Gtk', '3.0') -gi.require_version('Gdk', '3.0') -gi.require_version('GObject', '2.0') -gi.require_version('Pango', '1.0') -from gi.repository import GLib, Gio, Gtk +import platform +from ctypes import CDLL, byref, create_string_buffer +from ctypes.util import find_library +from distutils.version import LooseVersion as V +# Install _() in namespace from gajim.common import i18n -from gajim.common import configpaths -from gajim.common import logging_helpers - -MIN_NBXMPP_VER = "0.6.4" -MIN_GTK_VER = "3.22.0" - - -class GajimApplication(Gtk.Application): - '''Main class handling activation and command line.''' - - def __init__(self): - Gtk.Application.__init__(self, application_id='org.gajim.Gajim', - flags=( - Gio.ApplicationFlags.HANDLES_COMMAND_LINE | - Gio.ApplicationFlags.HANDLES_OPEN)) - - self.add_main_option('version', ord('V'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Show the application\'s version')) - self.add_main_option('quiet', ord('q'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Show only critical errors')) - self.add_main_option('separate', ord('s'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Separate profile files completely (even ' - 'history database and plugins)')) - self.add_main_option('verbose', ord('v'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Print XML stanzas and other debug ' - 'information')) - self.add_main_option('profile', ord('p'), GLib.OptionFlags.NONE, - GLib.OptionArg.STRING, - _('Use defined profile in configuration ' - 'directory'), 'NAME') - self.add_main_option('config-path', ord('c'), GLib.OptionFlags.NONE, - GLib.OptionArg.STRING, - _('Set configuration directory'), 'PATH') - self.add_main_option('loglevel', ord('l'), GLib.OptionFlags.NONE, - GLib.OptionArg.STRING, - _('Configure logging system'), 'LEVEL') - self.add_main_option('warnings', ord('w'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Show all warnings')) - self.add_main_option('ipython', ord('i'), GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Open IPython shell')) - self.add_main_option('show-next-pending-event', 0, GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Pops up a window with the next pending event')) - self.add_main_option('start-chat', 0, GLib.OptionFlags.NONE, - GLib.OptionArg.NONE, - _('Start a new chat')) - - self.add_main_option_entries(self._get_remaining_entry()) - - self.connect('handle-local-options', self._handle_local_options) - self.connect('command-line', self._handle_remote_options) - self.connect('startup', self._startup) - self.connect('activate', self._activate) - - self.interface = None - - GLib.set_prgname('gajim') - if GLib.get_application_name() != 'Gajim': - GLib.set_application_name('Gajim') - - @staticmethod - def _get_remaining_entry(): - option = GLib.OptionEntry() - option.arg = GLib.OptionArg.STRING_ARRAY - option.arg_data = None - option.arg_description = ('[URI …]') - option.flags = GLib.OptionFlags.NONE - option.long_name = GLib.OPTION_REMAINING - option.short_name = 0 - return [option] - - def _startup(self, application): - from gajim import gtkexcepthook - gtkexcepthook.init() - - try: - import nbxmpp - except ImportError: - print('Gajim needs python-nbxmpp to run. Quitting…') - sys.exit(1) - - from distutils.version import LooseVersion as V - if V(nbxmpp.__version__) < V(MIN_NBXMPP_VER): - print('Gajim needs python-nbxmpp >= %s to run. ' - 'Quitting...' % MIN_NBXMPP_VER) - sys.exit(1) - - gtk_ver = '%s.%s.%s' % (Gtk.get_major_version(), - Gtk.get_minor_version(), - Gtk.get_micro_version()) - if V(gtk_ver) < V(MIN_GTK_VER): - print('Gajim needs GTK+ >= %s to run. ' - 'Quitting...' % MIN_GTK_VER) - sys.exit(1) - - # Create and initialize Application Paths & Databases - from gajim.common import app - i18n.initialize_direction_mark() - app.detect_dependencies() - configpaths.create_paths() - from gajim.common import exceptions - from gajim.common import logger - from gajim.common import caps_cache - try: - app.logger = logger.Logger() - caps_cache.initialize(app.logger) - except exceptions.DatabaseMalformed as error: - dlg = Gtk.MessageDialog( - None, - Gtk.DialogFlags.DESTROY_WITH_PARENT | Gtk.DialogFlags.MODAL, - Gtk.MessageType.ERROR, - Gtk.ButtonsType.OK, - _('Database Error')) - dlg.format_secondary_text(str(error)) - dlg.run() - dlg.destroy() - sys.exit() - from ctypes import CDLL, byref, create_string_buffer - from ctypes.util import find_library - import platform +_MIN_NBXMPP_VER = "0.6.4" +_MIN_GTK_VER = "3.22.0" - sysname = platform.system() - if sysname in ('Linux', 'FreeBSD', 'OpenBSD', 'NetBSD'): - libc = CDLL(find_library('c')) - # The constant defined in <linux/prctl.h> which is used to set the name - # of the process. - PR_SET_NAME = 15 +def _check_required_deps(): + try: + import nbxmpp + except ImportError: + print('Gajim needs python-nbxmpp to run. Quitting…') + sys.exit(1) - if sysname == 'Linux': - proc_name = b'gajim' - buff = create_string_buffer(len(proc_name)+1) - buff.value = proc_name - libc.prctl(PR_SET_NAME, byref(buff), 0, 0, 0) - elif sysname in ('FreeBSD', 'OpenBSD', 'NetBSD'): - libc.setproctitle('gajim') + from distutils.version import LooseVersion as V + if V(nbxmpp.__version__) < V(_MIN_NBXMPP_VER): + print('Gajim needs python-nbxmpp >= %s to run. ' + 'Quitting...' % _MIN_NBXMPP_VER) + sys.exit(1) - def sigint_cb(num, stack): - print('SIGINT/SIGTERM received') - self.quit() - # ^C exits the application normally - signal.signal(signal.SIGINT, sigint_cb) - signal.signal(signal.SIGTERM, sigint_cb) - # Set Application Menu - app.app = self - path = os.path.join(configpaths.get('GUI'), 'application_menu.ui') - builder = Gtk.Builder() - builder.set_translation_domain(i18n.DOMAIN) - builder.add_from_file(path) - menubar = builder.get_object("menubar") - appmenu = builder.get_object("appmenu") - if app.prefers_app_menu(): - self.set_app_menu(appmenu) - else: - # Add it to the menubar instead - menubar.prepend_submenu('Gajim', appmenu) - self.set_menubar(menubar) +def _init_gui(gui): + if gui == 'GTK': + _init_gtk() - def _activate(self, application): - if self.interface is not None: - self.interface.roster.window.present() - return - from gajim.gui_interface import Interface - from gajim import gtkgui_helpers - self.interface = Interface() - gtkgui_helpers.load_css() - self.interface.run(self) - self.add_actions() - from gajim import gui_menu_builder - gui_menu_builder.build_accounts_menu() - def _open_uris(self, uris): - from gajim.common import app - for uri in uris: - app.log('uri_handler').info('open %s', uri) - if not uri.startswith('xmpp:'): - continue - # remove xmpp: - uri = uri[5:] - try: - jid, cmd = uri.split('?') - except ValueError: - # No query argument - jid, cmd = uri, 'message' - if cmd == 'join': - self.interface.join_gc_minimal(None, jid) - elif cmd == 'roster': - self.activate_action('add-contact', GLib.Variant('s', jid)) - elif cmd.startswith('message'): - attributes = cmd.split(';') - message = None - for key in attributes: - if key.startswith('body'): - try: - message = unquote(key.split('=')[1]) - except Exception: - app.log('uri_handler').error('Invalid URI: %s', cmd) - accounts = list(app.connections.keys()) - if not accounts: - continue - if len(accounts) == 1: - app.interface.new_chat_from_jid(accounts[0], jid, message) - else: - self.activate_action('start-chat') - start_chat_window = app.interface.instances['start_chat'] - start_chat_window.search_entry.set_text(jid) +def _init_gtk(): + import gi + gi.require_version('GLib', '2.0') + gi.require_version('Gio', '2.0') + gi.require_version('Gtk', '3.0') + gi.require_version('Gdk', '3.0') + gi.require_version('GObject', '2.0') + gi.require_version('Pango', '1.0') - def do_shutdown(self, *args): - Gtk.Application.do_shutdown(self) - # Shutdown GUI and save config - if hasattr(self.interface, 'roster') and self.interface.roster: - self.interface.roster.prepare_quit() + from gi.repository import Gtk + gtk_ver = '%s.%s.%s' % (Gtk.get_major_version(), + Gtk.get_minor_version(), + Gtk.get_micro_version()) + if V(gtk_ver) < V(_MIN_GTK_VER): + print('Gajim needs GTK+ >= %s to run. ' + 'Quitting...' % _MIN_GTK_VER) + sys.exit(1) - # Commit any outstanding SQL transactions - from gajim.common import app - app.logger.commit() + from gajim import gtkexcepthook + gtkexcepthook.init() - def _handle_remote_options(self, application, command_line): - # Parse all options that should be executed on a remote instance - options = command_line.get_options_dict() + i18n.initialize_direction_mark() - remote_commands = ['ipython', - 'show-next-pending-event', - 'start-chat', - ] + from gajim.application import GajimApplication - remaining = options.lookup_value(GLib.OPTION_REMAINING, - GLib.VariantType.new('as')) + application = GajimApplication() + _install_terminate(application) + application.run(sys.argv) - for cmd in remote_commands: - if options.contains(cmd): - self.activate_action(cmd) - return 0 - if remaining is not None: - self._open_uris(remaining.unpack()) - return 0 +def _set_proc_title(): + sysname = platform.system() + if sysname in ('Linux', 'FreeBSD', 'OpenBSD', 'NetBSD'): + libc = CDLL(find_library('c')) - self.activate() - return 0 + # The constant defined in <linux/prctl.h> which is used to set the name + # of the process. + PR_SET_NAME = 15 - def _handle_local_options(self, application, - options: GLib.VariantDict) -> int: - # Parse all options that have to be executed before ::startup - if options.contains('profile'): - # Incorporate profile name into application id - # to have a single app instance for each profile. - profile = options.lookup_value('profile').get_string() - app_id = '%s.%s' % (self.get_application_id(), profile) - self.set_application_id(app_id) - configpaths.set_profile(profile) - if options.contains('separate'): - configpaths.set_separation(True) - if options.contains('config-path'): - path = options.lookup_value('config-path').get_string() - configpaths.set_config_root(path) + if sysname == 'Linux': + proc_name = b'gajim' + buff = create_string_buffer(len(proc_name)+1) + buff.value = proc_name + libc.prctl(PR_SET_NAME, byref(buff), 0, 0, 0) + elif sysname in ('FreeBSD', 'OpenBSD', 'NetBSD'): + libc.setproctitle('gajim') - configpaths.init() - logging_helpers.init() - if options.contains('quiet'): - logging_helpers.set_quiet() - if options.contains('verbose'): - logging_helpers.set_verbose() - if options.contains('loglevel'): - loglevel = options.lookup_value('loglevel').get_string() - logging_helpers.set_loglevels(loglevel) - if options.contains('warnings'): - self.show_warnings() - - return -1 - - def show_warnings(self): - import traceback - import warnings - - def warn_with_traceback(message, category, filename, lineno, - file=None, line=None): - traceback.print_stack(file=sys.stderr) - sys.stderr.write(warnings.formatwarning(message, category, - filename, lineno, line)) - - warnings.showwarning = warn_with_traceback - warnings.filterwarnings(action="always") - - def add_actions(self): - ''' Build Application Actions ''' - from gajim.app_actions import AppActions - from gajim.common import app - action = AppActions(self) - - self.account_actions = [ - ('-start-single-chat', action.on_single_message, 'online', 's'), - ('-join-groupchat', action.on_join_gc, 'online', 's'), - ('-add-contact', action.on_add_contact, 'online', 's'), - ('-services', action.on_service_disco, 'online', 's'), - ('-profile', action.on_profile, 'feature', 's'), - ('-xml-console', action.on_xml_console, 'always', 's'), - ('-server-info', action.on_server_info, 'online', 's'), - ('-archive', action.on_archiving_preferences, 'feature', 's'), - ('-sync-history', action.on_history_sync, 'online', 's'), - ('-privacylists', action.on_privacy_lists, 'feature', 's'), - ('-send-server-message', - action.on_send_server_message, 'online', 's'), - ('-set-motd', action.on_set_motd, 'online', 's'), - ('-update-motd', action.on_update_motd, 'online', 's'), - ('-delete-motd', action.on_delete_motd, 'online', 's'), - ('-activate-bookmark', - action.on_activate_bookmark, 'online', 'a{sv}'), - ('-open-event', action.on_open_event, 'always', 'a{sv}'), - ('-import-contacts', action.on_import_contacts, 'online', 's'), - ] - - # General Stateful Actions - - act = Gio.SimpleAction.new_stateful( - 'merge', None, - GLib.Variant.new_boolean(app.config.get('mergeaccounts'))) - act.connect('change-state', action.on_merge_accounts) - self.add_action(act) - - act = Gio.SimpleAction.new_stateful( - 'agent', None, - GLib.Variant.new_boolean(app.config.get('use_gpg_agent'))) - self.add_action(act) - - # General Actions - - self.general_actions = [ - ('quit', action.on_quit), - ('accounts', action.on_accounts), - ('add-account', action.on_add_account), - ('manage-proxies', action.on_manage_proxies), - ('start-chat', action.on_new_chat), - ('bookmarks', action.on_manage_bookmarks), - ('history-manager', action.on_history_manager), - ('preferences', action.on_preferences), - ('plugins', action.on_plugins), - ('file-transfer', action.on_file_transfers), - ('history', action.on_history), - ('shortcuts', action.on_keyboard_shortcuts), - ('features', action.on_features), - ('content', action.on_contents), - ('about', action.on_about), - ('faq', action.on_faq), - ('ipython', action.toggle_ipython), - ('show-next-pending-event', action.show_next_pending_event), - ] - - act = Gio.SimpleAction.new('add-contact', GLib.VariantType.new('s')) - act.connect("activate", action.on_add_contact_jid) - self.add_action(act) - - for action in self.general_actions: - action_name, func = action - act = Gio.SimpleAction.new(action_name, None) - act.connect("activate", func) - self.add_action(act) - - accounts_list = sorted(app.config.get_per('accounts')) - if not accounts_list: - return - if len(accounts_list) > 1: - for acc in accounts_list: - self.add_account_actions(acc) - else: - self.add_account_actions(accounts_list[0]) - - def add_account_actions(self, account): - for action in self.account_actions: - action_name, func, state, type_ = action - action_name = account + action_name - if self.lookup_action(action_name): - # We already added this action - continue - act = Gio.SimpleAction.new( - action_name, GLib.VariantType.new(type_)) - act.connect("activate", func) - if state != 'always': - act.set_enabled(False) - self.add_action(act) - - def remove_account_actions(self, account): - for action in self.account_actions: - action_name = account + action[0] - self.remove_action(action_name) - - def set_account_actions_state(self, account, new_state=False): - for action in self.account_actions: - action_name, _, state, _ = action - if not new_state and state in ('online', 'feature'): - # We go offline - self.lookup_action(account + action_name).set_enabled(False) - elif new_state and state == 'online': - # We go online - self.lookup_action(account + action_name).set_enabled(True) +def _install_terminate(application): + def sigint_cb(num, stack): + print('SIGINT/SIGTERM received') + application.quit() + # ^C exits the application normally + signal.signal(signal.SIGINT, sigint_cb) + signal.signal(signal.SIGTERM, sigint_cb) def main(): @@ -463,9 +114,6 @@ def main(): if os.geteuid() == 0: sys.exit("You must not launch gajim as root, it is insecure.") - appli = GajimApplication() - appli.run(sys.argv) - - -if __name__ == '__main__': - main() + _check_required_deps() + _set_proc_title() + _init_gui('GTK') View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/52cbae69eed7f205edb8a22072f7b18e66…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/52cbae69eed7f205edb8a22072f7b18e66…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Fix Windows build
by Philipp Hörist
24 Apr '18
24 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 1c48ee40 by Philipp Hörist at 2018-04-24T23:53:18+02:00 Fix Windows build - - - - - 1 changed file: - win/_base.sh Changes: ===================================== win/_base.sh ===================================== --- a/win/_base.sh +++ b/win/_base.sh @@ -79,7 +79,8 @@ function install_deps { mingw-w64-"${ARCH}"-sqlite3 \ mingw-w64-"${ARCH}"-goocanvas \ mingw-w64-"${ARCH}"-gspell \ - mingw-w64-"${ARCH}"-hunspell + mingw-w64-"${ARCH}"-hunspell \ + mingw-w64-"${ARCH}"-"${PYTHON_ID}"-setuptools build_pip install setuptools_scm @@ -87,7 +88,6 @@ function install_deps { pyasn1 certifi
git+https://dev.gajim.org/gajim/python-nbxmpp.git
-protobuf
git+https://github.com/dlitz/pycrypto.git
cryptography pyopenssl @@ -96,6 +96,7 @@ docutils qrcode keyring pillow==4.3.0 +six " build_pip install --no-binary ":all:" \ View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/1c48ee405d1d9496731ac2c203fef31342…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/1c48ee405d1d9496731ac2c203fef31342…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Refactor i18n module
by Philipp Hörist
24 Apr '18
24 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: e2383fd7 by Philipp Hörist at 2018-04-24T23:03:19+02:00 Refactor i18n module - Move everything translation related from gajim.py to i18n.py - - - - - 4 changed files: - gajim/common/app.py - gajim/common/i18n.py - gajim/gajim.py - gajim/gtkgui_helpers.py Changes: ===================================== gajim/common/app.py ===================================== --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -80,16 +80,6 @@ MY_CERT_DIR = configpaths.get('MY_CERT') RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname']) -try: - LANG = locale.getdefaultlocale()[0] # en_US, fr_FR, el_GR etc.. -except (ValueError, locale.Error): - # unknown locale, use en is better than fail - LANG = None -if LANG is None: - LANG = 'en' -else: - LANG = LANG[:2] # en, fr, el etc.. - os_info = None # used to cache os information transport_type = {} # list the type of transport ===================================== gajim/common/i18n.py ===================================== --- a/gajim/common/i18n.py +++ b/gajim/common/i18n.py @@ -5,6 +5,7 @@ ## Copyright (C) 2004 Vincent Hanquez <tab AT
snarc.org
> ## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT
gmail.com
> ## Copyright (C) 2009 Benjamin Richter <br AT
waldteufel-online.net
> +## Copyright (C) 2018 Philipp Hörist <philipp AT
hoerist.com
> ## ## This file is part of Gajim. ## @@ -26,66 +27,137 @@ import gettext import os import unicodedata -# May be changed after GTK is imported +DOMAIN = 'gajim' +LANG = 'en' direction_mark = '\u200E' +_translations = None -def paragraph_direction_mark(text): - """ - Determine paragraph writing direction according to -
http://www.unicode.org/reports/tr9/#The_Paragraph_Level
- Returns either Unicode LTR mark or RTL mark. - """ - for char in text: - bidi = unicodedata.bidirectional(char) - if bidi == 'L': - return '\u200E' - elif bidi == 'AL' or bidi == 'R': - return '\u200F' +def initialize(): + global _translations - return '\u200E' + locale.setlocale(locale.LC_ALL, '') + # initialize_win_translation() broken + initialize_lang() + set_i18n_env() + + localedir = get_locale_dir() + if hasattr(locale, 'bindtextdomain'): + locale.bindtextdomain(DOMAIN, localedir) + + gettext.install(DOMAIN, localedir) + if gettext._translations: + _translations = list(gettext._translations.values())[0] + else: + _translations = gettext.NullTranslations() + + +def set_i18n_env(): + if os.name == 'nt': + lang = os.getenv('LANG') + if lang is None: + default_lang = locale.getdefaultlocale()[0] + if default_lang: + lang = default_lang + + if lang: + os.environ['LANG'] = lang -APP = 'gajim' -# set '' so each part of the locale that should be modified is set -# according to the environment variables -locale.setlocale(locale.LC_ALL, '') +def initialize_lang(): + global LANG + try: + # en_US, fr_FR, el_GR etc.. + LANG = locale.getdefaultlocale()[0] + LANG = LANG[:2] + except (ValueError, locale.Error): + pass -## For windows: set, if needed, a value in LANG environmental variable ## -if os.name == 'nt': - lang = os.getenv('LANG') - if lang is None: - default_lang = locale.getdefaultlocale()[0] # en_US, fr_FR, el_GR etc.. - if default_lang: - lang = default_lang - if lang: - os.environ['LANG'] = lang +def get_locale_dir(): + if os.name == 'nt': + return "../po" - localedir = "../po" -else: # try to find domain in localedir - path = gettext.find(APP) + path = gettext.find(DOMAIN) if path: # extract localedir from localedir/language/LC_MESSAGES/domain.mo path, tail = os.path.split(path) path, tail = os.path.split(path) localedir, tail = os.path.split(path) - else: # fallback to user locale + else: + # fallback to user locale base = os.getenv('XDG_DATA_HOME') if base is None or base[0] != '/': base = os.path.expanduser('~/.local/share') localedir = os.path.join(base, "locale") + return localedir + + +def initialize_win_translation(): + # broken for now + return + + if os.name != 'nt': + return + + # needed for docutils + # sys.path.append('.') + APP = 'gajim' + DIR = '../po' + lang = locale.getdefaultlocale()[0] + os.environ['LANG'] = lang + gettext.bindtextdomain(APP, DIR) + gettext.textdomain(APP) + gettext.install(APP, DIR) + + # This is for Windows translation which is currently not + # working on GTK 3.18.9 + # locale.setlocale(locale.LC_ALL, '') + # import ctypes + # import ctypes.util + # libintl_path = ctypes.util.find_library('intl') + # if libintl_path == None: + # local_intl = os.path.join('gtk', 'bin', 'intl.dll') + # if os.path.exists(local_intl): + # libintl_path = local_intl + # if libintl_path == None: + # raise ImportError('intl.dll library not found') + # libintl = ctypes.cdll.LoadLibrary(libintl_path) + # libintl.bindtextdomain(APP, DIR) + # libintl.bind_textdomain_codeset(APP, 'UTF-8') + # plugins_locale_dir = os.path.join(common.configpaths[ + # 'PLUGINS_USER'], 'locale').encode(locale.getpreferredencoding()) + # libintl.bindtextdomain('gajim_plugins', plugins_locale_dir) + # libintl.bind_textdomain_codeset('gajim_plugins', 'UTF-8') + + +def initialize_direction_mark(): + import gi + from gi.repository import Gtk + + global direction_mark + + if Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL: + direction_mark = '\u200F' - if hasattr(locale, 'bindtextdomain'): - locale.bindtextdomain(APP, localedir) -gettext.install(APP, localedir) +def paragraph_direction_mark(text): + """ + Determine paragraph writing direction according to +
http://www.unicode.org/reports/tr9/#The_Paragraph_Level
+ + Returns either Unicode LTR mark or RTL mark. + """ + for char in text: + bidi = unicodedata.bidirectional(char) + if bidi == 'L': + return '\u200E' + elif bidi == 'AL' or bidi == 'R': + return '\u200F' + + return '\u200E' -if gettext._translations: - _translation = list(gettext._translations.values())[0] -else: - _translation = gettext.NullTranslations() def Q_(text): """ @@ -107,16 +179,20 @@ def Q_(text): qualifier, text = text.split(':', 1) return text -def ngettext(s_sing, s_plural, n, replace_sing = None, replace_plural = None): + +def ngettext(s_sing, s_plural, n, replace_sing=None, replace_plural=None): """ Use as: - i18n.ngettext('leave room %s', 'leave rooms %s', len(rooms), 'a', 'a, b, c') + i18n.ngettext('leave room %s', 'leave rooms %s', len(rooms), 'a', 'a, b, c') In other words this is a hack to ngettext() to support %s %d etc.. """ - text = _translation.ngettext(s_sing, s_plural, n) + text = _translations.ngettext(s_sing, s_plural, n) if n == 1 and replace_sing is not None: text = text % replace_sing elif n > 1 and replace_plural is not None: text = text % replace_plural return text + + +initialize() ===================================== gajim/gajim.py ===================================== --- a/gajim/gajim.py +++ b/gajim/gajim.py @@ -40,7 +40,6 @@ import sys import os import signal -import locale from urllib.parse import unquote import gi @@ -156,6 +155,7 @@ class GajimApplication(Gtk.Application): # Create and initialize Application Paths & Databases from gajim.common import app + i18n.initialize_direction_mark() app.detect_dependencies() configpaths.create_paths() from gajim.common import exceptions @@ -176,41 +176,6 @@ class GajimApplication(Gtk.Application): dlg.destroy() sys.exit() - if os.name == 'nt': - import gettext - # needed for docutils - sys.path.append('.') - APP = 'gajim' - DIR = '../po' - lang = locale.getdefaultlocale()[0] - os.environ['LANG'] = lang - gettext.bindtextdomain(APP, DIR) - gettext.textdomain(APP) - gettext.install(APP, DIR) - - # This is for Windows translation which is currently not - # working on GTK 3.18.9 - # locale.setlocale(locale.LC_ALL, '') - # import ctypes - # import ctypes.util - # libintl_path = ctypes.util.find_library('intl') - # if libintl_path == None: - # local_intl = os.path.join('gtk', 'bin', 'intl.dll') - # if os.path.exists(local_intl): - # libintl_path = local_intl - # if libintl_path == None: - # raise ImportError('intl.dll library not found') - # libintl = ctypes.cdll.LoadLibrary(libintl_path) - # libintl.bindtextdomain(APP, DIR) - # libintl.bind_textdomain_codeset(APP, 'UTF-8') - # plugins_locale_dir = os.path.join(common.configpaths[ - # 'PLUGINS_USER'], 'locale').encode(locale.getpreferredencoding()) - # libintl.bindtextdomain('gajim_plugins', plugins_locale_dir) - # libintl.bind_textdomain_codeset('gajim_plugins', 'UTF-8') - - if Gtk.Widget.get_default_direction() == Gtk.TextDirection.RTL: - i18n.direction_mark = '\u200F' - from ctypes import CDLL, byref, create_string_buffer from ctypes.util import find_library import platform @@ -242,7 +207,7 @@ class GajimApplication(Gtk.Application): app.app = self path = os.path.join(configpaths.get('GUI'), 'application_menu.ui') builder = Gtk.Builder() - builder.set_translation_domain(i18n.APP) + builder.set_translation_domain(i18n.DOMAIN) builder.add_from_file(path) menubar = builder.get_object("menubar") appmenu = builder.get_object("appmenu") @@ -319,7 +284,7 @@ class GajimApplication(Gtk.Application): remote_commands = ['ipython', 'show-next-pending-event', 'start-chat', - ] + ] remaining = options.lookup_value(GLib.OPTION_REMAINING, GLib.VariantType.new('as')) ===================================== gajim/gtkgui_helpers.py ===================================== --- a/gajim/gtkgui_helpers.py +++ b/gajim/gtkgui_helpers.py @@ -124,7 +124,7 @@ def get_image_button(icon_name, tooltip, toggle=False): def get_gtk_builder(file_name, widget=None): file_path = os.path.join(configpaths.get('GUI'), file_name) builder = Gtk.Builder() - builder.set_translation_domain(i18n.APP) + builder.set_translation_domain(i18n.DOMAIN) if widget: builder.add_objects_from_file(file_path, [widget]) else: View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/e2383fd7a4cfd8d1dc38ca51fb74f97e7a…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/e2383fd7a4cfd8d1dc38ca51fb74f97e7a…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Remove unused import / codestyle
by Philipp Hörist
24 Apr '18
24 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 21d4d0cb by Philipp Hörist at 2018-04-24T21:34:53+02:00 Remove unused import / codestyle Pass args to logging method instead of string formatting - - - - - 1 changed file: - gajim/common/resolver.py Changes: ===================================== gajim/common/resolver.py ===================================== --- a/gajim/common/resolver.py +++ b/gajim/common/resolver.py @@ -17,7 +17,6 @@ ## along with Gajim. If not, see <
http://www.gnu.org/licenses/
>. ## -import sys import logging import functools @@ -25,6 +24,7 @@ from gi.repository import Gio, GLib log = logging.getLogger('gajim.c.resolver') + def get_resolver(): return GioResolver() @@ -38,7 +38,7 @@ class CommonResolver(): def resolve(self, host, on_ready, type_='srv'): host = host.lower() - log.debug('resolve %s type=%s' % (host, type_)) + log.debug('resolve %s type=%s', host, type_) assert(type_ in ['srv', 'txt']) if not host: # empty host, return empty list of srv records @@ -46,25 +46,25 @@ class CommonResolver(): return if host + type_ in self.resolved_hosts: # host is already resolved, return cached values - log.debug('%s already resolved: %s' % (host, - self.resolved_hosts[host + type_])) + log.debug('%s already resolved: %s', + host, self.resolved_hosts[host + type_]) on_ready(host, self.resolved_hosts[host + type_]) return if host + type_ in self.handlers: # host is about to be resolved by another connection, # attach our callback - log.debug('already resolving %s' % host) + log.debug('already resolving %s', host) self.handlers[host + type_].append(on_ready) else: # host has never been resolved, start now - log.debug('Starting to resolve %s using %s' % (host, self)) + log.debug('Starting to resolve %s using %s', host, self) self.handlers[host + type_] = [on_ready] self.start_resolve(host, type_) def _on_ready(self, host, type_, result_list): # practically it is impossible to be the opposite, but who knows :) host = host.lower() - log.debug('Resolving result for %s: %s' % (host, result_list)) + log.debug('Resolving result for %s: %s', host, result_list) if host + type_ not in self.resolved_hosts: self.resolved_hosts[host + type_] = result_list if host + type_ in self.handlers: @@ -94,7 +94,7 @@ class GioResolver(CommonResolver): callback = functools.partial(self._on_ready_srv, host) type_ = Gio.ResolverRecordType.SRV - resq = self.gio_resolver.lookup_records_async(host, type_, None, callback) + self.gio_resolver.lookup_records_async(host, type_, None, callback) def _on_ready_srv(self, host, source_object, result): try: View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/21d4d0cb1eff21b9787df0f42ac5010991…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/21d4d0cb1eff21b9787df0f42ac5010991…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Detect dependencys only on demand
by Philipp Hörist
24 Apr '18
24 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: e265514d by Philipp Hörist at 2018-04-24T19:36:33+02:00 Detect dependencys only on demand This allows us to import the app module without triggering dependency detection Also add is_installed() for checking if a dependency is installed and disable_dependency() in case we dont want to use a dependency - - - - - 19 changed files: - gajim/accounts_window.py - gajim/chat_control.py - gajim/chat_control_base.py - gajim/common/app.py - gajim/common/connection.py - gajim/common/connection_handlers.py - gajim/common/gpg.py - gajim/common/helpers.py - gajim/common/jingle.py - gajim/common/protocol/bytestream.py - gajim/common/stanza_session.py - gajim/config.py - gajim/dialogs.py - gajim/features_window.py - gajim/gajim.py - gajim/gui_interface.py - gajim/message_textview.py - gajim/options_dialog.py - gajim/roster_window.py Changes: ===================================== gajim/accounts_window.py ===================================== --- a/gajim/accounts_window.py +++ b/gajim/accounts_window.py @@ -384,9 +384,10 @@ class Account(Gtk.Box): def set_activatable(self): if self.account == app.ZEROCONF_ACC_NAME: - self.get_parent().set_activatable(app.HAVE_ZEROCONF) - self.get_parent().set_sensitive(app.HAVE_ZEROCONF) - if not app.HAVE_ZEROCONF: + zeroconf = app.is_installed('ZEROCONF') + self.get_parent().set_activatable(zeroconf) + self.get_parent().set_sensitive(zeroconf) + if not zeroconf: self.get_parent().set_tooltip_text( _('Please check if Avahi or Bonjour is installed.')) @@ -482,7 +483,7 @@ class GenericOptionPage(Gtk.Box): switch.set_vexpand(False) switch.set_valign(Gtk.Align.CENTER) switch.set_halign(Gtk.Align.END) - if self.account == app.ZEROCONF_ACC_NAME and not app.HAVE_ZEROCONF: + if self.account == app.ZEROCONF_ACC_NAME and not app.is_installed('ZEROCONF'): switch.set_sensitive(False) switch.set_active(False) ===================================== gajim/chat_control.py ===================================== --- a/gajim/chat_control.py +++ b/gajim/chat_control.py @@ -394,7 +394,7 @@ class ChatControl(ChatControlBase): # Jingle detection if self.contact.supports(NS_JINGLE_ICE_UDP) and \ - app.HAVE_FARSTREAM and self.contact.resource: + app.is_installed('FARSTREAM') and self.contact.resource: self.audio_available = self.contact.supports(NS_JINGLE_RTP_AUDIO) self.video_available = self.contact.supports(NS_JINGLE_RTP_VIDEO) else: ===================================== gajim/chat_control_base.py ===================================== --- a/gajim/chat_control_base.py +++ b/gajim/chat_control_base.py @@ -63,7 +63,7 @@ from gajim.command_system.implementation.middleware import CommandTools from gajim.command_system.implementation import standard from gajim.command_system.implementation import execute -if app.HAVE_SPELL: +if app.is_installed('GSPELL'): from gi.repository import Gspell @@ -487,7 +487,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): Gtk.IconSize.MENU) def set_speller(self): - if not app.HAVE_SPELL or not app.config.get('use_speller'): + if not app.is_installed('GSPELL') or not app.config.get('use_speller'): return gspell_lang = self.get_speller_language() ===================================== gajim/common/app.py ===================================== --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -11,6 +11,7 @@ ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT
gmail.com
> ## Stephan Erb <steve-e AT h3c.de> ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT
webkeks.org
> +## Copyright (C) 2018 Philipp Hörist <philipp @
hoerist.com
> ## ## This file is part of Gajim. ## @@ -34,10 +35,8 @@ import locale import uuid from distutils.version import LooseVersion as V from collections import namedtuple -import gi -import nbxmpp -from gi.repository import GLib +import nbxmpp from gajim.common import config as c_config from gajim.common import configpaths @@ -58,8 +57,6 @@ ged = ged_module.GlobalEventsDispatcher() # Global Events Dispatcher nec = None # Network Events Controller plugin_manager = None # Plugins Manager -glog = logging.getLogger('gajim') - logger = None # For backwards compatibility needed @@ -152,122 +149,6 @@ ZEROCONF_ACC_NAME = 'Local' idlequeue = None socks5queue = None -HAVE_ZEROCONF = True -try: - __import__('avahi') -except ImportError: - try: - __import__('pybonjour') - except Exception: # Linux raises ImportError, Windows raises WindowsError - HAVE_ZEROCONF = False - -HAVE_PYCRYPTO = True -try: - __import__('Crypto') -except ImportError: - HAVE_PYCRYPTO = False - -HAVE_GPG = True -GPG_BINARY = 'gpg' -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'): - glog.info('Gajim needs python-gnupg >= 0.3.8') - HAVE_GPG = False -except ImportError: - HAVE_GPG = False -else: - 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'): - GPG_BINARY = 'gpg2' - if not test_gpg(binary='gpg'): - HAVE_GPG = False - -HAVE_FARSTREAM = True -try: - if os.name == 'nt': - os.environ['FS_PLUGIN_PATH'] = 'gtk\\lib\\farstream-0.1' - os.environ['GST_PLUGIN_PATH'] = 'gtk\\lib\\gstreamer-0.10' - gi.require_version('Farstream', '0.2') - from gi.repository import Farstream - gi.require_version('Gst', '1.0') - from gi.repository import Gst - from gi.repository import GLib - try: - Gst.init(None) - conference = Gst.ElementFactory.make('fsrtpconference', None) - session = conference.new_session(Farstream.MediaType.AUDIO) - del session - del conference - except Exception as e: - glog.info(e) - HAVE_FARSTREAM = False - -except (ImportError, ValueError): - HAVE_FARSTREAM = False - -HAVE_GEOCLUE = True -try: - gi.require_version('Geoclue', '2.0') - from gi.repository import Geoclue -except (ImportError, ValueError): - HAVE_GEOCLUE = False - -HAVE_UPNP_IGD = True -try: - gi.require_version('GUPnPIgd', '1.0') - from gi.repository import GUPnPIgd - gupnp_igd = GUPnPIgd.SimpleIgd() -except ValueError: - HAVE_UPNP_IGD = False - -HAVE_PYCURL = True -try: - __import__('pycurl') -except ImportError: - HAVE_PYCURL = False - -try: - from gajim.common import sleepy - if sleepy.SUPPORTED: - HAVE_IDLE = True -except Exception: - glog.info(_('Unable to load idle module')) - HAVE_IDLE = False - -HAVE_SPELL = False -try: - spell_log = logging.getLogger('gajim.speller') - gi.require_version('Gspell', '1') - from gi.repository import Gspell - langs = Gspell.language_get_available() - for lang in langs: - spell_log.info('%s (%s) dict available', - lang.get_name(), lang.get_code()) - if langs: - HAVE_SPELL = True - else: - spell_log.info('No dicts available') -except (ImportError, ValueError): - pass - gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'} gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE, nbxmpp.NS_MUC, nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC_ADMIN, nbxmpp.NS_MUC_OWNER, @@ -287,6 +168,161 @@ gajim_optional_features = {} # Capabilities hash per account caps_hash = {} +_dependencies = { + 'AVAHI': False, + 'PYBONJOUR': False, + 'PYCRYPTO': False, + 'PYGPG': False, + 'GPG_BINARY': False, + 'FARSTREAM': False, + 'GEOCLUE': False, + 'UPNP': False, + 'PYCURL': False, + 'GSPELL': False, + 'IDLE': False, + } + + +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': + # Alias for checking zeroconf libs + return _dependencies['AVAHI'] or _dependencies['PYBONJOUR'] + return _dependencies[dependency] + +def disable_dependency(dependency): + _dependencies[dependency] = False + +def detect_dependencies(): + import gi + + # ZEROCONF + try: + if os.name == 'nt': + import pybonjour + _dependencies['PYBONJOUR'] = True + else: + import avahi + _dependencies['AVAHI'] = True + except Exception: + pass + + # PYCRYPTO + try: + import Crypto + _dependencies['PYCRYPTO'] = True + except ImportError: + 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 + try: + if os.name == 'nt': + os.environ['FS_PLUGIN_PATH'] = 'gtk\\lib\\farstream-0.1' + os.environ['GST_PLUGIN_PATH'] = 'gtk\\lib\\gstreamer-0.10' + gi.require_version('Farstream', '0.2') + from gi.repository import Farstream + gi.require_version('Gst', '1.0') + from gi.repository import Gst + try: + Gst.init(None) + conference = Gst.ElementFactory.make('fsrtpconference', None) + session = conference.new_session(Farstream.MediaType.AUDIO) + except Exception as error: + log('gajim').info(error) + _dependencies['FARSTREAM'] = True + except (ImportError, ValueError): + pass + + # GEOCLUE + try: + gi.require_version('Geoclue', '2.0') + from gi.repository import Geoclue + _dependencies['GEOCLUE'] = True + except (ImportError, ValueError): + pass + + # UPNP + try: + gi.require_version('GUPnPIgd', '1.0') + from gi.repository import GUPnPIgd + gupnp_igd = GUPnPIgd.SimpleIgd() + _dependencies['UPNP'] = True + except ValueError: + pass + + # PYCURL + try: + import pycurl + _dependencies['PYCURL'] = True + except ImportError: + pass + + # IDLE + try: + from gajim.common import sleepy + if sleepy.SUPPORTED: + _dependencies['IDLE'] = True + except Exception: + pass + + # GSPELL + try: + gi.require_version('Gspell', '1') + from gi.repository import Gspell + langs = Gspell.language_get_available() + for lang in langs: + log('gajim').info('%s (%s) dict available', + lang.get_name(), lang.get_code()) + if langs: + _dependencies['GSPELL'] = True + except (ImportError, ValueError): + pass + + # Print results + for dep, val in _dependencies.items(): + log('gajim').info('%-13s %s', dep, val) + +def get_gpg_binary(): + return _dependencies['GPG_BINARY'] + def get_an_id(): return str(uuid.uuid4()) @@ -556,8 +592,9 @@ def get_priority(account, show): return prio def log(domain): - root = 'gajim.' - return logging.getLogger(root + domain) + if domain != 'gajim': + domain = 'gajim.%s' % domain + return logging.getLogger(domain) def prefers_app_menu(): if sys.platform == 'darwin': ===================================== gajim/common/connection.py ===================================== --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -136,7 +136,7 @@ class CommonConnection: self.server_resource = self._compute_resource() self.gpg = None self.USE_GPG = False - if app.HAVE_GPG: + if app.is_installed('GPG'): self.USE_GPG = True self.gpg = gpg.GnuPG() self.status = '' @@ -598,7 +598,7 @@ class CommonConnection: self.old_show = show self.on_purpose = False self.server_resource = self._compute_resource() - if app.HAVE_GPG: + if app.is_installed('GPG'): self.USE_GPG = True self.gpg = gpg.GnuPG() app.nec.push_incoming_event(BeforeChangeShowEvent(None, @@ -638,7 +638,7 @@ class CommonConnection: self.connected = app.SHOW_LIST.index(show) idle_time = None if auto: - if app.HAVE_IDLE and app.config.get('autoaway'): + if app.is_installed('IDLE') and app.config.get('autoaway'): idle_sec = int(app.interface.sleeper.getIdleSec()) idle_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(time.time() - idle_sec)) @@ -898,7 +898,7 @@ class Connection(CommonConnection, ConnectionHandlers): app.nec.push_incoming_event(AccountNotCreatedEvent( None, conn=self, reason=reason)) return - if app.HAVE_GPG: + if app.is_installed('GPG'): self.USE_GPG = True self.gpg = gpg.GnuPG() app.nec.push_incoming_event( @@ -2755,7 +2755,7 @@ class Connection(CommonConnection, ConnectionHandlers): p = self.add_sha(p, ptype != 'unavailable') self.add_lang(p) if auto: - if app.HAVE_IDLE and app.config.get('autoaway'): + if app.is_installed('IDLE') and app.config.get('autoaway'): idle_sec = int(app.interface.sleeper.getIdleSec()) idle_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(time.time() - idle_sec)) ===================================== gajim/common/connection_handlers.py ===================================== --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -1712,7 +1712,7 @@ ConnectionHTTPUpload): def _nec_last_request_received(self, obj): if obj.conn.name != self.name: return - if app.HAVE_IDLE and app.config.get_per('accounts', self.name, + if app.is_installed('IDLE') and app.config.get_per('accounts', self.name, 'send_idle_time'): iq_obj = obj.stanza.buildReply('result') qp = iq_obj.setQuery() ===================================== gajim/common/gpg.py ===================================== --- a/gajim/common/gpg.py +++ b/gajim/common/gpg.py @@ -26,14 +26,14 @@ import os import logging from gajim.common import app -if app.HAVE_GPG: +if app.is_installed('GPG'): import gnupg gnupg.logger = logging.getLogger('gajim.c.gnupg') class GnuPG(gnupg.GPG): def __init__(self): use_agent = app.config.get('use_gpg_agent') - gnupg.GPG.__init__(self, gpgbinary=app.GPG_BINARY, use_agent=use_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 ===================================== gajim/common/helpers.py ===================================== --- a/gajim/common/helpers.py +++ b/gajim/common/helpers.py @@ -732,7 +732,7 @@ def parse_datetime(timestring, check_utc=False, convert='utc', epoch=False): return None from gajim.common import app -if app.HAVE_PYCURL: +if app.is_installed('PYCURL'): import pycurl from io import StringIO @@ -1360,13 +1360,13 @@ def update_optional_features(account = None): app.gajim_optional_features[a].append(nbxmpp.NS_CHATSTATES) if not app.config.get('ignore_incoming_xhtml'): app.gajim_optional_features[a].append(nbxmpp.NS_XHTML_IM) - if app.HAVE_PYCRYPTO \ + if app.is_installed('PYCRYPTO') \ and app.config.get_per('accounts', a, 'enable_esessions'): app.gajim_optional_features[a].append(nbxmpp.NS_ESESSION) if app.config.get_per('accounts', a, 'answer_receipts'): app.gajim_optional_features[a].append(nbxmpp.NS_RECEIPTS) app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE) - if app.HAVE_FARSTREAM: + if app.is_installed('FARSTREAM'): app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP) app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_AUDIO) app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_VIDEO) @@ -1545,7 +1545,7 @@ def _get_img_proxy(attrs, proxy): Download an image through a proxy. This function should be launched in a separated thread. """ - if not app.HAVE_PYCURL: + if not app.is_installed('PYCURL'): return '', _('PyCURL is not installed') mem, alt, max_size = '', '', 2*1024*1024 if 'max_size' in attrs: ===================================== gajim/common/jingle.py ===================================== --- a/gajim/common/jingle.py +++ b/gajim/common/jingle.py @@ -37,7 +37,7 @@ from gajim.common import app from gajim.common.jingle_session import JingleSession, JingleStates from gajim.common.jingle_ft import JingleFileTransfer from gajim.common.jingle_transport import JingleTransportSocks5, JingleTransportIBB -if app.HAVE_FARSTREAM: +if app.is_installed('FARSTREAM'): from gajim.common.jingle_rtp import JingleAudio, JingleVideo logger = logging.getLogger('gajim.c.jingle') ===================================== gajim/common/protocol/bytestream.py ===================================== --- a/gajim/common/protocol/bytestream.py +++ b/gajim/common/protocol/bytestream.py @@ -416,7 +416,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): self._add_streamhosts_to_query(query, sender, port, additional_hosts) def _add_upnp_igd_as_streamhost_to_query(self, query, file_props, iq): - if not app.HAVE_UPNP_IGD: + if not app.is_installed('UPNP'): self.connection.send(iq) return @@ -487,7 +487,7 @@ class ConnectionSocks5Bytestream(ConnectionBytestream): def no_upnp_reply(): log.debug('Got not GUPnP-IGD answer') # stop trying to use it - app.HAVE_UPNP_IGD = False + app.disable_dependency('UPNP') self.no_gupnp_reply_id = 0 self.connection.send(iq) cleanup_gupnp() ===================================== gajim/common/stanza_session.py ===================================== --- a/gajim/common/stanza_session.py +++ b/gajim/common/stanza_session.py @@ -40,7 +40,7 @@ from gajim.common import crypto import logging log = logging.getLogger('gajim.c.stanza_session') -if app.HAVE_PYCRYPTO: +if app.is_installed('PYCRYPTO'): from Crypto.Cipher import AES from Crypto.PublicKey import RSA ===================================== gajim/config.py ===================================== --- a/gajim/config.py +++ b/gajim/config.py @@ -67,7 +67,7 @@ try: except (ImportError, ValueError): HAS_GST = False -if app.HAVE_SPELL: +if app.is_installed('GSPELL'): from gi.repository import Gspell #---------- PreferencesWindow class -------------# @@ -179,7 +179,7 @@ class PreferencesWindow: self.xml.get_object('xhtml_checkbutton').set_active(st) # use speller - if app.HAVE_SPELL: + if app.is_installed('GSPELL'): st = app.config.get('use_speller') self.xml.get_object('speller_checkbutton').set_active(st) else: @@ -400,7 +400,7 @@ class PreferencesWindow: if config == value: combobox.set_active(index) - if HAS_GST and app.HAVE_FARSTREAM: + if HAS_GST and app.is_installed('FARSTREAM'): create_av_combobox('audio_input', AudioInputManager().get_devices()) create_av_combobox('audio_output', AudioOutputManager().get_devices( )) ===================================== gajim/dialogs.py ===================================== --- a/gajim/dialogs.py +++ b/gajim/dialogs.py @@ -62,7 +62,7 @@ from gajim.common.caps_cache import muc_caps_cache from gajim.common.exceptions import GajimGeneralException from gajim.common.connection_handlers_events import MessageOutgoingEvent -if app.HAVE_SPELL: +if app.is_installed('GSPELL'): from gi.repository import Gspell import logging @@ -3322,7 +3322,7 @@ class SingleMessageWindow: else: self.to_entry.set_text(to) - if app.config.get('use_speller') and app.HAVE_SPELL and action == 'send': + if app.config.get('use_speller') and app.is_installed('GSPELL') and action == 'send': lang = app.config.get('speller_language') gspell_lang = Gspell.language_lookup(lang) if gspell_lang is None: ===================================== gajim/features_window.py ===================================== --- a/gajim/features_window.py +++ b/gajim/features_window.py @@ -140,14 +140,14 @@ class FeaturesWindow: self.desc_label.set_text(text) def zeroconf_available(self): - return app.HAVE_ZEROCONF + return app.is_installed('ZEROCONF') def dbus_available(self): from gajim.common import dbus_support return dbus_support.supported def gpg_available(self): - return app.HAVE_GPG + return app.is_installed('GPG') def some_keyring_available(self): if os.name == 'nt': @@ -160,14 +160,14 @@ class FeaturesWindow: return True def speller_available(self): - return app.HAVE_SPELL + return app.is_installed('GSPELL') def idle_available(self): from gajim.common import sleepy return sleepy.SUPPORTED def pycrypto_available(self): - return app.HAVE_PYCRYPTO + return app.is_installed('PYCRYPTO') def docutils_available(self): try: @@ -177,8 +177,8 @@ class FeaturesWindow: return True def farstream_available(self): - return app.HAVE_FARSTREAM + return app.is_installed('FARSTREAM') def gupnp_igd_available(self): - return app.HAVE_UPNP_IGD + return app.is_installed('UPNP') ===================================== gajim/gajim.py ===================================== --- a/gajim/gajim.py +++ b/gajim/gajim.py @@ -156,6 +156,7 @@ class GajimApplication(Gtk.Application): # Create and initialize Application Paths & Databases from gajim.common import app + app.detect_dependencies() configpaths.create_paths() from gajim.common import exceptions from gajim.common import logger ===================================== gajim/gui_interface.py ===================================== --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -56,7 +56,7 @@ from gajim.common import events from gajim.music_track_listener import MusicTrackListener -if app.HAVE_GEOCLUE: +if app.is_installed('GEOCLUE'): from gajim.common import location_listener from gajim import gtkgui_helpers @@ -1138,7 +1138,7 @@ class Interface: app.config.get_per('accounts', account, 'publish_tune')): self.enable_music_listener() # enable location listener - if (obj.conn.pep_supported and app.HAVE_GEOCLUE and + if (obj.conn.pep_supported and app.is_installed('GEOCLUE') and app.config.get_per('accounts', account, 'publish_location')): location_listener.enable() @@ -2881,7 +2881,7 @@ class Interface: self.create_zeroconf_default_config() if app.config.get_per('accounts', app.ZEROCONF_ACC_NAME, 'active') \ - and app.HAVE_ZEROCONF: + and app.is_installed('ZEROCONF'): app.connections[app.ZEROCONF_ACC_NAME] = \ connection_zeroconf.ConnectionZeroconf(app.ZEROCONF_ACC_NAME) for account in app.config.get_per('accounts'): ===================================== gajim/message_textview.py ===================================== --- a/gajim/message_textview.py +++ b/gajim/message_textview.py @@ -30,7 +30,7 @@ from gi.repository import Pango from gajim.common import app from gajim import gtkgui_helpers -if app.HAVE_SPELL: +if app.is_installed('GSPELL'): from gi.repository import Gspell @@ -133,7 +133,7 @@ class MessageTextView(Gtk.TextView): self.toggle_speller(False) def toggle_speller(self, activate): - if app.HAVE_SPELL and app.config.get('use_speller'): + if app.is_installed('GSPELL') and app.config.get('use_speller'): spell_view = Gspell.TextView.get_from_gtk_text_view(self) spell_view.set_inline_spell_checking(activate) ===================================== gajim/options_dialog.py ===================================== --- a/gajim/options_dialog.py +++ b/gajim/options_dialog.py @@ -598,4 +598,4 @@ class GPGOption(DialogOption): def set_activatable(self, name, value): active = self.account in app.connections - self.get_parent().set_activatable(app.HAVE_GPG and active) + self.get_parent().set_activatable(app.is_installed('GPG') and active) ===================================== gajim/roster_window.py ===================================== --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -64,7 +64,7 @@ from gajim.common import app from gajim.common import helpers from gajim.common.exceptions import GajimGeneralException from gajim.common import i18n -if app.HAVE_GEOCLUE: +if app.is_installed('GEOCLUE'): from gajim.common import location_listener from gajim.common import ged from gajim.message_window import MessageWindowMgr @@ -4940,7 +4940,7 @@ class RosterWindow: item = Gtk.CheckMenuItem(_('Publish Location')) pep_submenu.append(item) - if not app.HAVE_GEOCLUE: + if not app.is_installed('GEOCLUE'): item.set_sensitive(False) else: activ = app.config.get_per('accounts', account, View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/e265514d8849dc9bd8cea08a1aa4bbcf45…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/e265514d8849dc9bd8cea08a1aa4bbcf45…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Dont check for OpenSSL we require it on install
by Philipp Hörist
23 Apr '18
23 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 986898f6 by Philipp Hörist at 2018-04-23T23:47:17+02:00 Dont check for OpenSSL we require it on install - - - - - 3 changed files: - gajim/common/app.py - gajim/common/connection.py - gajim/common/connection_handlers_events.py Changes: ===================================== gajim/common/app.py ===================================== --- a/gajim/common/app.py +++ b/gajim/common/app.py @@ -200,17 +200,6 @@ else: if not test_gpg(binary='gpg'): HAVE_GPG = False -HAVE_PYOPENSSL = True -try: - import OpenSSL.SSL - import OpenSSL.crypto - ver = OpenSSL.__version__ - ver_l = [int(i) for i in ver.split('.')] - if ver_l < [0, 12]: - raise ImportError -except Exception: - HAVE_PYOPENSSL = False - HAVE_FARSTREAM = True try: if os.name == 'nt': ===================================== gajim/common/connection.py ===================================== --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -42,7 +42,9 @@ import locale import hmac import hashlib import json +import logging from functools import partial +from string import Template try: randomsource = random.SystemRandom() @@ -54,29 +56,23 @@ import signal if os.name != 'nt': signal.signal(signal.SIGPIPE, signal.SIG_DFL) +if os.name == 'nt': + import certifi +import OpenSSL.crypto import nbxmpp +from nbxmpp import Smacks + from gajim import common from gajim.common import helpers from gajim.common import app from gajim.common import gpg from gajim.common import passwords -from gajim.common import exceptions from gajim.common import check_X509 from gajim.common.connection_handlers import * -from gajim.common.helpers import version_condition from gajim.common.contacts import GC_Contact - from gajim.gtkgui_helpers import get_action -if app.HAVE_PYOPENSSL: - import OpenSSL.crypto -if os.name == 'nt': - import certifi - -from nbxmpp import Smacks -from string import Template -import logging log = logging.getLogger('gajim.c.connection') ssl_error = { ===================================== gajim/common/connection_handlers_events.py ===================================== --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -30,8 +30,10 @@ import sys import os from time import time as time_time +import OpenSSL.crypto import nbxmpp from nbxmpp.protocol import NS_CHATSTATES + from gajim.common import atom from gajim.common import nec from gajim.common import helpers @@ -45,8 +47,6 @@ from gajim.common.jingle_transport import JingleTransportSocks5 from gajim.common.file_props import FilesProp from gajim.common.nec import NetworkEvent -if app.HAVE_PYOPENSSL: - import OpenSSL.crypto log = logging.getLogger('gajim.c.connection_handlers_events') View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/986898f69d2ad87d09cbb9ae889ca480fb…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/986898f69d2ad87d09cbb9ae889ca480fb…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Remove accidentally commited file
by Philipp Hörist
23 Apr '18
23 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 872c68b3 by Philipp Hörist at 2018-04-23T21:35:24+02:00 Remove accidentally commited file - - - - - 1 changed file: - − Logs.db Changes: ===================================== Logs.db deleted ===================================== --- a/Logs.db +++ /dev/null View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/872c68b368b21dcabf387551204a864d6d…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/872c68b368b21dcabf387551204a864d6d…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
[Git][gajim/gajim][master] Refactor database migration
by Philipp Hörist
23 Apr '18
23 Apr '18
Philipp Hörist pushed to branch master at gajim / gajim Commits: 5ee45b86 by Philipp Hörist at 2018-04-23T21:27:01+02:00 Refactor database migration - Move database migration to the logger module - Dont depend on Gajim version for migration use PRAGMA user_version - Refactoring of some methods in the logger module - Dont use cursor object, if there is no reason - Make some attributes and methods private - - - - - 3 changed files: - + Logs.db - gajim/common/logger.py - gajim/common/optparser.py Changes: ===================================== Logs.db ===================================== --- /dev/null +++ b/Logs.db ===================================== gajim/common/logger.py ===================================== --- a/gajim/common/logger.py +++ b/gajim/common/logger.py @@ -1,5 +1,5 @@ # -*- coding:utf-8 -*- -## src/common/logger.py +## gajim/common/logger.py ## ## Copyright (C) 2003-2014 Yann Leboulanger <asterix AT
lagaule.org
> ## Copyright (C) 2004-2005 Vincent Hanquez <tab AT
snarc.org
> @@ -8,6 +8,7 @@ ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT
lapin.org
> ## Copyright (C) 2007 Tomasz Melcer <liori AT
exroot.org
> ## Julien Pivotto <roidelapluie AT
gmail.com
> +## Copyright (C) 2018 Philipp Hörist <philipp AT
hoerist.com
> ## ## This file is part of Gajim. ## @@ -34,11 +35,12 @@ import time import datetime import calendar import json +import logging +import sqlite3 as sqlite from collections import namedtuple from gzip import GzipFile from io import BytesIO from gi.repository import GLib -from enum import IntEnum, unique from gajim.common import exceptions from gajim.common import app @@ -47,152 +49,206 @@ from gajim.common.const import ( JIDConstant, KindConstant, ShowConstant, TypeConstant, SubscriptionConstant) -import sqlite3 as sqlite - LOG_DB_PATH = configpaths.get('LOG_DB') LOG_DB_FOLDER, LOG_DB_FILE = os.path.split(LOG_DB_PATH) CACHE_DB_PATH = configpaths.get('CACHE_DB') -import logging +LOGS_SQL_STATEMENT = ''' + CREATE TABLE jids( + jid_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, + jid TEXT UNIQUE, + type INTEGER + ); + CREATE TABLE unread_messages( + message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, + jid_id INTEGER, + shown BOOLEAN default 0 + ); + CREATE INDEX idx_unread_messages_jid_id ON unread_messages (jid_id); + CREATE TABLE logs( + log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, + account_id INTEGER, + jid_id INTEGER, + contact_name TEXT, + time INTEGER, + kind INTEGER, + show INTEGER, + message TEXT, + subject TEXT, + additional_data TEXT, + stanza_id TEXT, + encryption TEXT, + encryption_state TEXT, + marker INTEGER + ); + CREATE TABLE last_archive_message( + jid_id INTEGER PRIMARY KEY UNIQUE, + last_mam_id TEXT, + oldest_mam_timestamp TEXT, + last_muc_timestamp TEXT + ); + CREATE INDEX idx_logs_jid_id_time ON logs (jid_id, time DESC); + CREATE INDEX idx_logs_stanza_id ON logs (stanza_id); + PRAGMA user_version=1; + ''' + +CACHE_SQL_STATEMENT = ''' + CREATE TABLE transports_cache ( + transport TEXT UNIQUE, + type INTEGER + ); + CREATE TABLE caps_cache ( + hash_method TEXT, + hash TEXT, + data BLOB, + last_seen INTEGER); + CREATE TABLE rooms_last_message_time( + jid_id INTEGER PRIMARY KEY UNIQUE, + time INTEGER + ); + CREATE TABLE roster_entry( + account_jid_id INTEGER, + jid_id INTEGER, + name TEXT, + subscription INTEGER, + ask BOOLEAN, + avatar_sha TEXT, + PRIMARY KEY (account_jid_id, jid_id) + ); + CREATE TABLE roster_group( + account_jid_id INTEGER, + jid_id INTEGER, + group_name TEXT, + PRIMARY KEY (account_jid_id, jid_id, group_name) + ); + PRAGMA user_version=1; + ''' + log = logging.getLogger('gajim.c.logger') class Logger: def __init__(self): self._jid_ids = {} - self.con = None - self.commit_timout_id = None + self._con = None + self._commit_timout_id = None + + self._create_databases() + self._migrate_databases() + self._connect_databases() + self._get_jid_ids_from_db() + def _create_databases(self): if os.path.isdir(LOG_DB_PATH): - print(_('%s is a directory but should be a file') % LOG_DB_PATH) - print(_('Gajim will now exit')) + log.error(_('%s is a directory but should be a file'), + LOG_DB_PATH) sys.exit() if os.path.isdir(CACHE_DB_PATH): - print(_('%s or %s is a directory but should be a file') % CACHE_DB_PATH) - print(_('Gajim will now exit')) + log.error(_('%s is a directory but should be a file'), + CACHE_DB_PATH) sys.exit() if not os.path.exists(LOG_DB_PATH): if os.path.exists(CACHE_DB_PATH): os.remove(CACHE_DB_PATH) - self.create_log_db() + self._create(LOGS_SQL_STATEMENT, LOG_DB_PATH) - self.init_vars() if not os.path.exists(CACHE_DB_PATH): - self.create_cache_db() - self.attach_cache_database() - - def create_log_db(self): - print(_('creating logs database')) - con = sqlite.connect(LOG_DB_PATH) - os.chmod(LOG_DB_PATH, 0o600) # rw only for us - cur = con.cursor() - # create the tables - # kind can be - # status, gcstatus, gc_msg, (we only recv for those 3), - # single_msg_recv, chat_msg_recv, chat_msg_sent, single_msg_sent - # to meet all our needs - # logs.jid_id --> jids.jid_id but Sqlite doesn't do FK etc so it's done in python code - # jids.jid text column will be JID if TC-related, room_jid if GC-related, - # ROOM_JID/nick if pm-related. - # also check optparser.py, which updates databases on gajim updates - cur.executescript( - ''' - CREATE TABLE jids( - jid_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, - jid TEXT UNIQUE, - type INTEGER - ); - - CREATE TABLE unread_messages( - message_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, - jid_id INTEGER, - shown BOOLEAN default 0 - ); - - CREATE INDEX idx_unread_messages_jid_id ON unread_messages (jid_id); - - CREATE TABLE logs( - log_line_id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE, - account_id INTEGER, - jid_id INTEGER, - contact_name TEXT, - time INTEGER, - kind INTEGER, - show INTEGER, - message TEXT, - subject TEXT, - additional_data TEXT, - stanza_id TEXT, - encryption TEXT, - encryption_state TEXT, - marker INTEGER - ); - - CREATE TABLE last_archive_message( - jid_id INTEGER PRIMARY KEY UNIQUE, - last_mam_id TEXT, - oldest_mam_timestamp TEXT, - last_muc_timestamp TEXT - ); - - CREATE INDEX idx_logs_jid_id_time ON logs (jid_id, time DESC); - - CREATE INDEX idx_logs_stanza_id ON logs (stanza_id); - - ''' - ) + self._create(CACHE_SQL_STATEMENT, CACHE_DB_PATH) - con.commit() - con.close() + @staticmethod + def _create(statement, path): + log.info(_('Creating %s'), path) + con = sqlite.connect(path) + os.chmod(path, 0o600) - def create_cache_db(self): - print(_('creating cache database')) - con = sqlite.connect(CACHE_DB_PATH) - os.chmod(CACHE_DB_PATH, 0o600) # rw only for us - cur = con.cursor() - cur.executescript( - ''' - CREATE TABLE transports_cache ( - transport TEXT UNIQUE, - type INTEGER - ); - - CREATE TABLE caps_cache ( - hash_method TEXT, - hash TEXT, - data BLOB, - last_seen INTEGER); - - CREATE TABLE rooms_last_message_time( - jid_id INTEGER PRIMARY KEY UNIQUE, - time INTEGER - ); - - CREATE TABLE IF NOT EXISTS roster_entry( - account_jid_id INTEGER, - jid_id INTEGER, - name TEXT, - subscription INTEGER, - ask BOOLEAN, - avatar_sha TEXT, - PRIMARY KEY (account_jid_id, jid_id) - ); - - CREATE TABLE IF NOT EXISTS roster_group( - account_jid_id INTEGER, - jid_id INTEGER, - group_name TEXT, - PRIMARY KEY (account_jid_id, jid_id, group_name) - ); - ''' - ) + try: + con.executescript(statement) + except Exception as error: + log.exception('Error') + con.close() + os.remove(path) + sys.exit() con.commit() con.close() @staticmethod + def _get_user_version(con) -> int: + """ Return the value of PRAGMA user_version. """ + return con.execute('PRAGMA user_version').fetchone()[0] + + def _migrate_databases(self): + try: + con = sqlite.connect(LOG_DB_PATH) + self._migrate_logs(con) + con.close() + + con = sqlite.connect(CACHE_DB_PATH) + self._migrate_cache(con) + con.close() + except Exception: + log.exception('Error') + sys.exit() + + def _migrate_logs(self, con): + if self._get_user_version(con) == 0: + # All migrations from 0.16.9 until 1.0.0 + statements = [ + 'ALTER TABLE logs ADD COLUMN "account_id" INTEGER', + 'ALTER TABLE logs ADD COLUMN "stanza_id" TEXT', + 'ALTER TABLE logs ADD COLUMN "encryption" TEXT', + 'ALTER TABLE logs ADD COLUMN "encryption_state" TEXT', + 'ALTER TABLE logs ADD COLUMN "marker" INTEGER', + 'ALTER TABLE logs ADD COLUMN "additional_data" TEXT', + '''CREATE TABLE IF NOT EXISTS last_archive_message( + jid_id INTEGER PRIMARY KEY UNIQUE, + last_mam_id TEXT, + oldest_mam_timestamp TEXT, + last_muc_timestamp TEXT + )''', + + '''CREATE INDEX IF NOT EXISTS idx_logs_stanza_id + ON logs(stanza_id)''', + 'PRAGMA user_version=1' + ] + + self._execute_multiple(con, statements) + + if self._get_user_version(con) < 2: + pass + + def _migrate_cache(self, con): + if self._get_user_version(con) == 0: + # All migrations from 0.16.9 until 1.0.0 + statements = [ + 'ALTER TABLE roster_entry ADD COLUMN "avatar_sha" TEXT', + 'PRAGMA user_version=1' + ] + self._execute_multiple(con, statements) + + if self._get_user_version(con) < 2: + pass + + @staticmethod + def _execute_multiple(con, statements): + """ + Execute mutliple statements with the option to fail on duplicates + but still continue + """ + for sql in statements: + try: + con.execute(sql) + con.commit() + except sqlite.OperationalError as error: + if str(error).startswith('duplicate column name:'): + log.info(error) + else: + log.exception('Error') + sys.exit() + + @staticmethod def namedtuple_factory(cursor, row): """ Usage: @@ -209,55 +265,33 @@ class Logger: def dispatch(self, event, error): app.ged.raise_event(event, None, str(error)) - def close_db(self): - if self.con: - self.con.close() - self.con = None - self.cur = None - - def open_db(self): - self.close_db() + def _connect_databases(self): + self._con = sqlite.connect( + LOG_DB_PATH, timeout=20.0, isolation_level='IMMEDIATE') - # FIXME: sqlite3_open wants UTF8 strings. So a path with - # non-ascii chars doesn't work. See #2812 and - #
http://lists.initd.org/pipermail/pysqlite/2005-August/000134.html
- back = os.getcwd() - os.chdir(LOG_DB_FOLDER) - - # if locked, wait up to 20 sec to unlock - # before raise (hopefully should be enough) - - self.con = sqlite.connect(LOG_DB_FILE, timeout=20.0, - isolation_level='IMMEDIATE') - os.chdir(back) - self.con.row_factory = self.namedtuple_factory + self._con.row_factory = self.namedtuple_factory # DB functions - self.con.create_function("like", 1, self._like) - self.con.create_function("get_timeout", 0, self._get_timeout) + self._con.create_function("like", 1, self._like) + self._con.create_function("get_timeout", 0, self._get_timeout) - self.cur = self.con.cursor() - self.set_synchronous(False) - - def attach_cache_database(self): + self._set_synchronous(False) try: - self.cur.execute("ATTACH DATABASE '%s' AS cache" % \ - CACHE_DB_PATH.replace("'", "''")) - except sqlite.Error as e: - log.debug("Failed to attach cache database: %s" % str(e)) + self._con.execute("ATTACH DATABASE '%s' AS cache" % + CACHE_DB_PATH.replace("'", "''")) + except Exception as error: + log.exception('Error') + self._con.close() + sys.exit() - def set_synchronous(self, sync): + def _set_synchronous(self, sync): try: if sync: - self.cur.execute("PRAGMA synchronous = NORMAL") + self._con.execute("PRAGMA synchronous = NORMAL") else: - self.cur.execute("PRAGMA synchronous = OFF") + self._con.execute("PRAGMA synchronous = OFF") except sqlite.Error as e: - log.debug("Failed to set_synchronous(%s): %s" % (sync, str(e))) - - def init_vars(self): - self.open_db() - self.get_jid_ids_from_db() + log.exception('Error') @staticmethod def _get_timeout(): @@ -277,29 +311,29 @@ class Logger: def commit(self): try: - self.con.commit() + self._con.commit() except sqlite.OperationalError as e: print(str(e), file=sys.stderr) - self.commit_timout_id = None + self._commit_timout_id = None return False def _timeout_commit(self): - if self.commit_timout_id: + if self._commit_timout_id: return - self.commit_timout_id = GLib.timeout_add(500, self.commit) + self._commit_timout_id = GLib.timeout_add(500, self.commit) def simple_commit(self, sql_to_commit): """ Helper to commit """ - self.cur.execute(sql_to_commit) + self._con.execute(sql_to_commit) self._timeout_commit() - def get_jid_ids_from_db(self): + def _get_jid_ids_from_db(self): """ Load all jid/jid_id tuples into a dict for faster access """ - rows = self.con.execute( + rows = self._con.execute( 'SELECT jid_id, jid, type FROM jids').fetchall() for row in rows: self._jid_ids[row.jid] = row @@ -327,8 +361,8 @@ class Logger: """ Return True if it's a room jid, False if it's not, None if we don't know """ - self.cur.execute('SELECT type FROM jids WHERE jid=?', (jid,)) - row = self.cur.fetchone() + row = self._con.execute( + 'SELECT type FROM jids WHERE jid=?', (jid,)).fetchone() if row is None: return None else: @@ -380,7 +414,7 @@ class Logger: return result.jid_id sql = 'SELECT jid_id, jid, type FROM jids WHERE jid = ?' - row = self.con.execute(sql, [jid]).fetchone() + row = self._con.execute(sql, [jid]).fetchone() if row is not None: self._jid_ids[jid] = row return row.jid_id @@ -390,7 +424,7 @@ class Logger: 'Unable to insert new JID because type is missing') sql = 'INSERT INTO jids (jid, type) VALUES (?, ?)' - lastrowid = self.con.execute(sql, (jid, type_)).lastrowid + lastrowid = self._con.execute(sql, (jid, type_)).lastrowid Row = namedtuple('Row', 'jid_id jid type') self._jid_ids[jid] = Row(lastrowid, jid, type_) self._timeout_commit() @@ -576,9 +610,8 @@ class Logger: """ all_messages = [] try: - self.cur.execute( - 'SELECT message_id, shown from unread_messages') - unread_results = self.cur.fetchall() + unread_results = self._con.execute( + 'SELECT message_id, shown from unread_messages').fetchall() except Exception: unread_results = [] for message in unread_results: @@ -587,20 +620,19 @@ class Logger: # here we get infos for that message, and related jid from jids table # do NOT change order of SELECTed things, unless you change function(s) # that called this function - self.cur.execute(''' + result = self._con.execute(''' SELECT logs.log_line_id, logs.message, logs.time, logs.subject, jids.jid, logs.additional_data FROM logs, jids WHERE logs.log_line_id = %d AND logs.jid_id = jids.jid_id ''' % msg_log_id - ) - results = self.cur.fetchone() - if results is None: + ).fetchone() + if result is None: # Log line is no more in logs table. remove it from unread_messages self.set_read_messages([msg_log_id]) continue - all_messages.append((results, shown)) + all_messages.append((result, shown)) return all_messages def get_last_conversation_lines(self, account, jid, pending): @@ -644,7 +676,7 @@ class Logger: kinds=', '.join(kinds)) try: - messages = self.con.execute( + messages = self._con.execute( sql, tuple(jids) + (restore, pending)).fetchall() except sqlite.DatabaseError: self.dispatch('DB_ERROR', @@ -691,7 +723,7 @@ class Logger: ORDER BY time, log_line_id '''.format(jids=', '.join('?' * len(jids))) - return self.con.execute(sql, tuple(jids) + + return self._con.execute(sql, tuple(jids) + (date.timestamp(), (date + delta).timestamp())).fetchall() @@ -734,7 +766,7 @@ class Logger: '''.format(jids=', '.join('?' * len(jids)), date_search=between if date else '') - return self.con.execute(sql, tuple(jids) + (query,)).fetchall() + return self._con.execute(sql, tuple(jids) + (query,)).fetchall() def get_days_with_logs(self, account, jid, year, month): """ @@ -772,7 +804,7 @@ class Logger: """.format(jids=', '.join('?' * len(jids)), kinds=', '.join(kinds)) - return self.con.execute(sql, tuple(jids) + + return self._con.execute(sql, tuple(jids) + (date.timestamp(), (date + delta).timestamp())).fetchall() @@ -800,7 +832,7 @@ class Logger: # fetchone() returns always at least one Row with all # attributes set to None because of the MAX() function - return self.con.execute(sql, tuple(jids)).fetchone().time + return self._con.execute(sql, tuple(jids)).fetchone().time def get_first_date_that_has_logs(self, account, jid): """ @@ -826,7 +858,7 @@ class Logger: # fetchone() returns always at least one Row with all # attributes set to None because of the MIN() function - return self.con.execute(sql, tuple(jids)).fetchone().time + return self._con.execute(sql, tuple(jids)).fetchone().time def get_date_has_logs(self, account, jid, date): """ @@ -856,7 +888,7 @@ class Logger: AND time BETWEEN ? AND ? '''.format(jids=', '.join('?' * len(jids))) - return self.con.execute(sql, tuple(jids) + + return self._con.execute(sql, tuple(jids) + (date.timestamp(), (date + delta).timestamp())).fetchone() @@ -875,7 +907,7 @@ class Logger: NATURAL JOIN jids WHERE jid = ? ''' - row = self.con.execute(sql, (jid,)).fetchone() + row = self._con.execute(sql, (jid,)).fetchone() if not row: return self.get_last_date_that_has_logs(account, jid) return row.time @@ -896,7 +928,7 @@ class Logger: (SELECT time FROM rooms_last_message_time WHERE jid_id = :jid_id AND time >= :time), :time))''' - self.con.execute(sql, {"jid_id": jid_id, "time": timestamp}) + self._con.execute(sql, {"jid_id": jid_id, "time": timestamp}) self._timeout_commit() def save_transport_type(self, jid, type_): @@ -907,11 +939,10 @@ class Logger: if not type_id: # unknown type return - self.cur.execute( - 'SELECT type from transports_cache WHERE transport = "%s"' % jid) - results = self.cur.fetchone() - if results: - if results.type == type_id: + result = self._con.execute( + 'SELECT type from transports_cache WHERE transport = "%s"' % jid).fetchone() + if result: + if result.type == type_id: return sql = 'UPDATE transports_cache SET type = %d WHERE transport = "%s"' %\ (type_id, jid) @@ -924,9 +955,7 @@ class Logger: """ Return all the type of the transports in DB """ - self.cur.execute( - 'SELECT * from transports_cache') - results = self.cur.fetchall() + results = self._con.execute('SELECT * from transports_cache').fetchall() if not results: return {} answer = {} @@ -954,7 +983,7 @@ class Logger: # the data field contains binary object (gzipped data), this is a hack # to get that data without trying to convert it to unicode try: - self.cur.execute('SELECT hash_method, hash, data FROM caps_cache;') + rows = self._con.execute('SELECT hash_method, hash, data FROM caps_cache;') except sqlite.OperationalError: # might happen when there's no caps_cache table yet # -- there's no data to read anyway then @@ -962,7 +991,7 @@ class Logger: # list of corrupted entries that will be removed to_be_removed = [] - for row in self.cur: + for row in rows: # for each row: unpack the data field # (format: (category, type, name, category, type, name, ... # ..., 'FEAT', feature1, feature2, ...).join(' ')) @@ -1013,7 +1042,7 @@ class Logger: gzip.write(data.encode('utf-8')) gzip.close() data = string.getvalue() - self.cur.execute(''' + self._con.execute(''' INSERT INTO caps_cache ( hash_method, hash, data, last_seen ) VALUES (?, ?, ?, ?); ''', (hash_method, hash_, memoryview(data), int(time.time()))) @@ -1076,10 +1105,10 @@ class Logger: jid_id = self.get_jid_id(jid) except exceptions.PysqliteOperationalError as e: raise exceptions.PysqliteOperationalError(str(e)) - self.cur.execute( + self._con.execute( 'DELETE FROM roster_group WHERE account_jid_id=? AND jid_id=?', (account_jid_id, jid_id)) - self.cur.execute( + self._con.execute( 'DELETE FROM roster_entry WHERE account_jid_id=? AND jid_id=?', (account_jid_id, jid_id)) self._timeout_commit() @@ -1101,18 +1130,18 @@ class Logger: # Update groups information # First we delete all previous groups information - self.cur.execute( + self._con.execute( 'DELETE FROM roster_group WHERE account_jid_id=? AND jid_id=?', (account_jid_id, jid_id)) # Then we add all new groups information for group in groups: - self.cur.execute('INSERT INTO roster_group VALUES(?, ?, ?)', + self._con.execute('INSERT INTO roster_group VALUES(?, ?, ?)', (account_jid_id, jid_id, group)) if name is None: name = '' - self.cur.execute(''' + self._con.execute(''' REPLACE INTO roster_entry (account_jid_id, jid_id, name, subscription, ask) VALUES(?, ?, ?, ?, ?)''', ( @@ -1130,11 +1159,11 @@ class Logger: account_jid_id = self.get_jid_id(account_jid, type_=JIDConstant.NORMAL_TYPE) # First we fill data with roster_entry informations - self.cur.execute(''' + rows = self._con.execute(''' SELECT j.jid, re.jid_id, re.name, re.subscription, re.ask, re.avatar_sha FROM roster_entry re, jids j WHERE re.account_jid_id=? AND j.jid_id=re.jid_id''', (account_jid_id,)) - for row in self.cur: + for row in rows: #jid, jid_id, name, subscription, ask jid = row.jid name = row.name @@ -1157,11 +1186,11 @@ class Logger: # Then we add group for roster entries for jid in data: - self.cur.execute(''' + rows = self._con.execute(''' SELECT group_name FROM roster_group WHERE account_jid_id=? AND jid_id=?''', (account_jid_id, data[jid]['id'])) - for row in self.cur: + for row in rows: group_name = row.group_name data[jid]['groups'].append(group_name) del data[jid]['id'] @@ -1186,7 +1215,7 @@ class Logger: DELETE FROM roster_group WHERE account_jid_id = {jid_id}; '''.format(jid_id=jid_id) - self.con.executescript(sql) + self._con.executescript(sql) self._timeout_commit() def search_for_duplicate(self, account, jid, timestamp, msg): @@ -1216,7 +1245,7 @@ class Logger: AND time BETWEEN ? AND ? ''' - result = self.con.execute( + result = self._con.execute( sql, (jid, msg, account_id, start_time, end_time)).fetchone() if result is not None: @@ -1263,14 +1292,14 @@ class Logger: WHERE stanza_id IN ({values}) AND jid_id = ? AND account_id = ? LIMIT 1 '''.format(values=', '.join('?' * len(ids))) - result = self.con.execute( + result = self._con.execute( sql, tuple(ids) + (archive_id, account_id)).fetchone() else: sql = ''' SELECT stanza_id FROM logs WHERE stanza_id IN ({values}) AND account_id = ? AND kind != ? LIMIT 1 '''.format(values=', '.join('?' * len(ids))) - result = self.con.execute( + result = self._con.execute( sql, tuple(ids) + (account_id, KindConstant.GC_MSG)).fetchone() if result is not None: @@ -1324,7 +1353,7 @@ class Logger: '''.format(columns=', '.join(kwargs.keys()), values=', '.join('?' * len(kwargs))) - lastrowid = self.con.execute( + lastrowid = self._con.execute( sql, (account_id, jid_id, time_, kind) + tuple(kwargs.values())).lastrowid log.info('Insert into DB: jid: %s, time: %s, kind: %s, stanza_id: %s', @@ -1333,7 +1362,7 @@ class Logger: if unread and kind == KindConstant.CHAT_MSG_RECV: sql = '''INSERT INTO unread_messages (message_id, jid_id) VALUES (?, (SELECT jid_id FROM jids WHERE jid = ?))''' - self.con.execute(sql, (lastrowid, jid)) + self._con.execute(sql, (lastrowid, jid)) self._timeout_commit() @@ -1358,7 +1387,7 @@ class Logger: UPDATE roster_entry SET avatar_sha = ? WHERE account_jid_id = ? AND jid_id = ? ''' - self.con.execute(sql, (sha, account_jid_id, jid_id)) + self._con.execute(sql, (sha, account_jid_id, jid_id)) self._timeout_commit() def get_archive_timestamp(self, jid, type_=None): @@ -1370,7 +1399,7 @@ class Logger: """ jid_id = self.get_jid_id(jid, type_=type_) sql = '''SELECT * FROM last_archive_message WHERE jid_id = ?''' - return self.con.execute(sql, (jid_id,)).fetchone() + return self._con.execute(sql, (jid_id,)).fetchone() def set_archive_timestamp(self, jid, **kwargs): """ @@ -1391,7 +1420,7 @@ class Logger: exists = self.get_archive_timestamp(jid) if not exists: sql = '''INSERT INTO last_archive_message VALUES (?, ?, ?, ?)''' - self.con.execute(sql, (jid_id, + self._con.execute(sql, (jid_id, kwargs.get('last_mam_id', None), kwargs.get('oldest_mam_timestamp', None), kwargs.get('last_muc_timestamp', None))) @@ -1399,6 +1428,6 @@ class Logger: args = ' = ?, '.join(kwargs.keys()) + ' = ?' sql = '''UPDATE last_archive_message SET {} WHERE jid_id = ?'''.format(args) - self.con.execute(sql, tuple(kwargs.values()) + (jid_id,)) + self._con.execute(sql, tuple(kwargs.values()) + (jid_id,)) log.info('Save archive timestamps: %s', kwargs) self._timeout_commit() ===================================== gajim/common/optparser.py ===================================== --- a/gajim/common/optparser.py +++ b/gajim/common/optparser.py @@ -1,5 +1,5 @@ # -*- coding:utf-8 -*- -## src/common/optparser.py +## gajim/common/optparser.py ## ## Copyright (C) 2003-2005 Vincent Hanquez <tab AT
snarc.org
> ## Copyright (C) 2003-2014 Yann Leboulanger <asterix AT
lagaule.org
> @@ -10,6 +10,7 @@ ## Brendan Taylor <whateley AT
gmail.com
> ## Tomasz Melcer <liori AT
exroot.org
> ## Stephan Erb <steve-e AT h3c.de> +## Copyright (C) 2018 Philipp Hörist <philipp AT
hoerist.com
> ## ## This file is part of Gajim. ## @@ -29,17 +30,15 @@ import os import sys import re -from time import time +import logging + from gajim.common import app -from gajim.common import helpers from gajim.common import caps_cache -import sqlite3 as sqlite -from gajim.common import logger -import logging log = logging.getLogger('gajim.c.optparser') + class OptionsParser: def __init__(self, filename): self.__filename = os.path.realpath(filename) @@ -150,25 +149,13 @@ class OptionsParser: self.update_config_to_016101() if old < [0, 16, 10, 2] and new >= [0, 16, 10, 2]: self.update_config_to_016102() - if old < [0, 16, 10, 3] and new >= [0, 16, 10, 3]: - self.update_config_to_016103() if old < [0, 16, 10, 4] and new >= [0, 16, 10, 4]: self.update_config_to_016104() if old < [0, 16, 10, 5] and new >= [0, 16, 10, 5]: self.update_config_to_016105() - if old < [0, 16, 11, 1] and new >= [0, 16, 11, 1]: - self.update_config_to_016111() - if old < [0, 16, 11, 2] and new >= [0, 16, 11, 2]: - self.update_config_to_016112() - if old < [0, 98, 2] and new >= [0, 98, 2]: - self.update_config_to_0982() if old < [0, 98, 3] and new >= [0, 98, 3]: self.update_config_to_0983() - if old < [0, 99, 2] and new >= [0, 99, 2]: - self.update_config_to_0992() - app.logger.init_vars() - app.logger.attach_cache_database() app.config.set('version', new_version) caps_cache.capscache.initialize_from_db() @@ -193,20 +180,6 @@ class OptionsParser: app.config.set_per('accounts', account, 'file_transfer_proxies', proxies_str) - @staticmethod - def call_sql(db_path, sql): - con = sqlite.connect(db_path) - cur = con.cursor() - try: - cur.executescript(sql) - con.commit() - except sqlite.OperationalError as e: - if str(e).startswith('duplicate column name:'): - log.info(str(e)) - else: - log.exception('Error') - con.close() - def update_config_to_01401(self): if 'autodetect_browser_mailer' not in self.old_values or 'openwith' \ not in self.old_values or \ @@ -253,22 +226,8 @@ class OptionsParser: for account in self.old_values['accounts'].keys(): app.config.del_per('accounts', account, 'minimized_gc') - self.call_sql(logger.LOG_DB_PATH, - '''ALTER TABLE logs - ADD COLUMN 'additional_data' TEXT;''' - ) app.config.set('version', '0.16.10.2') - def update_config_to_016103(self): - self.call_sql(logger.LOG_DB_PATH, - '''ALTER TABLE logs ADD COLUMN 'stanza_id' TEXT; - ALTER TABLE logs ADD COLUMN 'encryption' TEXT; - ALTER TABLE logs ADD COLUMN 'encryption_state' TEXT; - ALTER TABLE logs ADD COLUMN 'marker' INTEGER; - ''' - ) - app.config.set('version', '0.16.10.3') - def update_config_to_016104(self): app.config.set('emoticons_theme', 'noto-emoticons') app.config.set('version', '0.16.10.4') @@ -278,37 +237,6 @@ class OptionsParser: app.config.set('restore_timeout', -1) app.config.set('version', '0.16.10.5') - def update_config_to_016111(self): - self.call_sql(logger.CACHE_DB_PATH, - '''ALTER TABLE roster_entry ADD COLUMN 'avatar_sha' TEXT; - ''' - ) - app.config.set('version', '0.16.11.1') - - def update_config_to_016112(self): - self.call_sql(logger.LOG_DB_PATH, - ''' - CREATE TABLE IF NOT EXISTS last_archive_message( - jid_id INTEGER PRIMARY KEY UNIQUE, - last_mam_id TEXT, - oldest_mam_timestamp TEXT, - last_muc_timestamp TEXT - ); - ALTER TABLE logs ADD COLUMN 'account_id' INTEGER; - ''' - ) - app.config.set('version', '0.16.11.2') - - def update_config_to_0982(self): - # This fixes a typo in update_config_to_016112() - self.call_sql(logger.LOG_DB_PATH, - ''' - ALTER TABLE logs ADD COLUMN 'account_id' INTEGER; - ALTER TABLE logs ADD COLUMN 'additional_data' TEXT; - ''' - ) - app.config.set('version', '0.98.2') - def update_config_to_0983(self): for account in self.old_values['accounts'].keys(): password = self.old_values['accounts'][account]['password'] @@ -317,12 +245,3 @@ class OptionsParser: elif password == "libsecret:": app.config.set_per('accounts', account, 'password', '') app.config.set('version', '0.98.3') - - def update_config_to_0992(self): - self.call_sql(logger.LOG_DB_PATH, - ''' - CREATE INDEX IF NOT EXISTS - idx_logs_stanza_id ON logs (stanza_id); - ''' - ) - app.config.set('version', '0.99.2') View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/5ee45b86e90f8e0818d2e3f47010106a08…
--- View it on GitLab:
https://dev.gajim.org/gajim/gajim/commit/5ee45b86e90f8e0818d2e3f47010106a08…
You're receiving this email because of your account on
dev.gajim.org
.
1
0
0
0
← Newer
1
2
3
4
5
6
7
8
9
10
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
Results per page:
10
25
50
100
200