diff options
19 files changed, 189 insertions, 147 deletions
diff --git a/changes/feature_init-check-resolvconf b/changes/feature_init-check-resolvconf new file mode 100644 index 00000000..81733910 --- /dev/null +++ b/changes/feature_init-check-resolvconf @@ -0,0 +1 @@ +- Warn user if resolvconf cannot be found. diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 05f81f0b..e965604a 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -39,7 +39,6 @@ # M:::::::::::~NMMM7???7MMMM:::::::::::::::::::::::NMMMI??I7MMMM:::::::::::::M # M::::::::::::::7MMMMMMM+:::::::::::::::::::::::::::?MMMMMMMZ:::::::::::::::M # (thanks to: http://www.glassgiant.com/ascii/) -import logging import signal import sys import os @@ -50,10 +49,7 @@ from PySide import QtCore, QtGui from leap.bitmask import __version__ as VERSION from leap.bitmask.util import leap_argparse -from leap.bitmask.util import log_silencer, LOG_FORMAT -from leap.bitmask.util.leap_log_handler import LeapLogHandler -from leap.bitmask.util.streamtologger import StreamToLogger -from leap.bitmask.platform_init import IS_WIN +from leap.bitmask.logs.utils import get_logger from leap.bitmask.services.mail import plumber from leap.common.events import server as event_server from leap.mail import __version__ as MAIL_VERSION @@ -89,90 +85,6 @@ def sigterm_handler(*args, **kwargs): mainwindow.quit() -def add_logger_handlers(debug=False, logfile=None, replace_stdout=True): - """ - Create the logger and attach the handlers. - - :param debug: the level of the messages that we should log - :type debug: bool - :param logfile: the file name of where we should to save the logs - :type logfile: str - :return: the new logger with the attached handlers. - :rtype: logging.Logger - """ - # TODO: get severity from command line args - if debug: - level = logging.DEBUG - else: - level = logging.WARNING - - # Create logger and formatter - logger = logging.getLogger(name='leap') - logger.setLevel(level) - formatter = logging.Formatter(LOG_FORMAT) - - # Console handler - try: - import coloredlogs - console = coloredlogs.ColoredStreamHandler(level=level) - except ImportError: - console = logging.StreamHandler() - console.setLevel(level) - console.setFormatter(formatter) - using_coloredlog = False - else: - using_coloredlog = True - - if using_coloredlog: - replace_stdout = False - - silencer = log_silencer.SelectiveSilencerFilter() - console.addFilter(silencer) - logger.addHandler(console) - logger.debug('Console handler plugged!') - - # LEAP custom handler - leap_handler = LeapLogHandler() - leap_handler.setLevel(level) - leap_handler.addFilter(silencer) - logger.addHandler(leap_handler) - logger.debug('Leap handler plugged!') - - # File handler - if logfile is not None: - logger.debug('Setting logfile to %s ', logfile) - fileh = logging.FileHandler(logfile) - fileh.setLevel(logging.DEBUG) - fileh.setFormatter(formatter) - fileh.addFilter(silencer) - logger.addHandler(fileh) - logger.debug('File handler plugged!') - - if replace_stdout: - replace_stdout_stderr_with_logging(logger) - - return logger - - -def replace_stdout_stderr_with_logging(logger): - """ - Replace: - - the standard output - - the standard error - - the twisted log output - with a custom one that writes to the logger. - """ - # Disabling this on windows since it breaks ALL THE THINGS - # The issue for this is #4149 - if not IS_WIN: - sys.stdout = StreamToLogger(logger, logging.DEBUG) - sys.stderr = StreamToLogger(logger, logging.ERROR) - - # Replace twisted's logger to use our custom output. - from twisted.python import log - log.startLogging(sys.stdout) - - def do_display_version(opts): """ Display version and exit. @@ -214,6 +126,14 @@ def main(): mail_logfile = opts.mail_log_file start_hidden = opts.start_hidden + replace_stdout = True + if opts.repair or opts.import_maildir: + # We don't want too much clutter on the comand mode + # this could be more generic with a Command class. + replace_stdout = False + + logger = get_logger(debug, logfile, replace_stdout) + ############################################################# # Given how paths and bundling works, we need to delay the imports # of certain parts that depend on this path settings. @@ -232,13 +152,6 @@ def main(): BaseConfig.standalone = standalone - replace_stdout = True - if opts.repair or opts.import_maildir: - # We don't want too much clutter on the comand mode - # this could be more generic with a Command class. - replace_stdout = False - logger = add_logger_handlers(debug, logfile, replace_stdout) - # ok, we got logging in place, we can satisfy mail plumbing requests # and show logs there. it normally will exit there if we got that path. do_mail_plumbing(opts) diff --git a/src/leap/bitmask/backend_app.py b/src/leap/bitmask/backend_app.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/leap/bitmask/backend_app.py diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/leap/bitmask/frontend_app.py diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 69ef2616..8b9f2d44 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -250,7 +250,7 @@ class EIPStatusWidget(QtGui.QWidget): def set_action_eip_startstop(self, action_eip_startstop): """ - Sets the action_eip_startstop to use. + Set the action_eip_startstop to use. :param action_eip_startstop: action_eip_status to be used :type action_eip_startstop: QtGui.QAction @@ -393,6 +393,9 @@ class EIPStatusWidget(QtGui.QWidget): """ Enable firewall-down button. """ + retry_msg = self.tr("Retry") + self.ui.btnEipStartStop.setText(retry_msg) + self._action_eip_startstop.setText(retry_msg) self.ui.btnFwDown.show() def _on_fw_down_button_clicked(self): @@ -405,6 +408,7 @@ class EIPStatusWidget(QtGui.QWidget): # XXX do actual check msg = "Traffic is being routed in the clear." + self.ui.btnEipStartStop.setText(self.tr("Turn ON")) self.set_eip_message(msg) self.set_eip_status("") @@ -421,15 +425,13 @@ class EIPStatusWidget(QtGui.QWidget): self._reset_traffic_rates() self.ui.eip_bandwidth.hide() - # XXX FIXME ! ----------------------- this needs to - # accomodate the messages about firewall status. Right now - # we're assuming it works correctly, but we should test fw + # This is assuming the firewall works correctly, but we should test fw # status positively. # Or better call it from the conductor... clear_traffic = self.tr("Traffic is being routed in the clear.") unreachable_net = self.tr("Network is unreachable.") - failed_msg = self.tr("Cannot start Encrypted Internet") + failed_msg = self.tr("Error connecting") if restart: msg = unreachable_net @@ -441,14 +443,15 @@ class EIPStatusWidget(QtGui.QWidget): self.ui.lblEIPStatus.show() self.show() - def eip_failed_to_restart(self): + def eip_failed_to_connect(self): """ - Update EIP messages. + Update EIP messages with error during (re)connection. """ - msg = self.tr("Could not restart Encrypted Internet") + msg = self.tr("Error connecting.") self.ui.lblEIPMessage.setText(msg) self.ui.lblEIPStatus.show() - self.set_eip_status(self.tr("You can launch the service manually.")) + self.set_eip_status(self.tr("Bitmask is blocking " + "unencrypted traffic.")) self.show_fw_down_button() @QtCore.Slot(dict) diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py index f19b172f..3a8354b1 100644 --- a/src/leap/bitmask/gui/loggerwindow.py +++ b/src/leap/bitmask/gui/loggerwindow.py @@ -27,7 +27,7 @@ from twisted.internet import threads from ui_loggerwindow import Ui_LoggerWindow from leap.bitmask.util.constants import PASTEBIN_API_DEV_KEY -from leap.bitmask.util.leap_log_handler import LeapLogHandler +from leap.bitmask.logs.leap_log_handler import LeapLogHandler from leap.bitmask.util import pastebin from leap.common.check import leap_assert, leap_assert_type diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index c22c8892..3ef994b1 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -52,7 +52,7 @@ from leap.bitmask.services import EIP_SERVICE, MX_SERVICE from leap.bitmask.util import make_address from leap.bitmask.util.keyring_helpers import has_keyring -from leap.bitmask.util.leap_log_handler import LeapLogHandler +from leap.bitmask.logs.leap_log_handler import LeapLogHandler if IS_WIN: from leap.bitmask.platform_init.locks import WindowsLock diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui index ee24ca71..7216bb0a 100644 --- a/src/leap/bitmask/gui/ui/eip_status.ui +++ b/src/leap/bitmask/gui/ui/eip_status.ui @@ -242,7 +242,7 @@ <item row="0" column="3"> <widget class="QPushButton" name="btnFwDown"> <property name="text"> - <string>Allow unencrypted traffic</string> + <string>Turn Off</string> </property> </widget> </item> diff --git a/src/leap/bitmask/logs/__init__.py b/src/leap/bitmask/logs/__init__.py new file mode 100644 index 00000000..0516b304 --- /dev/null +++ b/src/leap/bitmask/logs/__init__.py @@ -0,0 +1,3 @@ +# levelname length == 8, since 'CRITICAL' is the longest +LOG_FORMAT = ('%(asctime)s - %(levelname)-8s - ' + 'L#%(lineno)-4s : %(name)s:%(funcName)s() - %(message)s') diff --git a/src/leap/bitmask/util/leap_log_handler.py b/src/leap/bitmask/logs/leap_log_handler.py index 807e53d4..24141638 100644 --- a/src/leap/bitmask/util/leap_log_handler.py +++ b/src/leap/bitmask/logs/leap_log_handler.py @@ -21,7 +21,7 @@ import logging from PySide import QtCore -from leap.bitmask.util import LOG_FORMAT +from leap.bitmask.logs import LOG_FORMAT class LogHandler(logging.Handler): diff --git a/src/leap/bitmask/util/log_silencer.py b/src/leap/bitmask/logs/log_silencer.py index 56b290e4..56b290e4 100644 --- a/src/leap/bitmask/util/log_silencer.py +++ b/src/leap/bitmask/logs/log_silencer.py diff --git a/src/leap/bitmask/util/streamtologger.py b/src/leap/bitmask/logs/streamtologger.py index 25a06718..25a06718 100644 --- a/src/leap/bitmask/util/streamtologger.py +++ b/src/leap/bitmask/logs/streamtologger.py diff --git a/src/leap/bitmask/util/tests/test_leap_log_handler.py b/src/leap/bitmask/logs/tests/test_leap_log_handler.py index 518fd35b..20b09aef 100644 --- a/src/leap/bitmask/util/tests/test_leap_log_handler.py +++ b/src/leap/bitmask/logs/tests/test_leap_log_handler.py @@ -24,7 +24,7 @@ except ImportError: import logging -from leap.bitmask.util.leap_log_handler import LeapLogHandler +from leap.bitmask.logs.leap_log_handler import LeapLogHandler from leap.bitmask.util.pyside_tests_helper import BasicPySlotCase from leap.common.testing.basetest import BaseLeapTest diff --git a/src/leap/bitmask/util/tests/test_streamtologger.py b/src/leap/bitmask/logs/tests/test_streamtologger.py index fc97b794..9bbadde8 100644 --- a/src/leap/bitmask/util/tests/test_streamtologger.py +++ b/src/leap/bitmask/logs/tests/test_streamtologger.py @@ -26,7 +26,7 @@ except ImportError: import logging import sys -from leap.bitmask.util.streamtologger import StreamToLogger +from leap.bitmask.logs.streamtologger import StreamToLogger from leap.common.testing.basetest import BaseLeapTest diff --git a/src/leap/bitmask/logs/utils.py b/src/leap/bitmask/logs/utils.py new file mode 100644 index 00000000..06959c45 --- /dev/null +++ b/src/leap/bitmask/logs/utils.py @@ -0,0 +1,92 @@ +import logging +import sys + +from leap.bitmask.logs import LOG_FORMAT +from leap.bitmask.logs.log_silencer import SelectiveSilencerFilter +from leap.bitmask.logs.leap_log_handler import LeapLogHandler +from leap.bitmask.logs.streamtologger import StreamToLogger +from leap.bitmask.platform_init import IS_WIN + + +def get_logger(debug=False, logfile=None, replace_stdout=True): + """ + Create the logger and attach the handlers. + + :param debug: the level of the messages that we should log + :type debug: bool + :param logfile: the file name of where we should to save the logs + :type logfile: str + :return: the new logger with the attached handlers. + :rtype: logging.Logger + """ + # TODO: get severity from command line args + if debug: + level = logging.DEBUG + else: + level = logging.WARNING + + # Create logger and formatter + logger = logging.getLogger(name='leap') + logger.setLevel(level) + formatter = logging.Formatter(LOG_FORMAT) + + # Console handler + try: + import coloredlogs + console = coloredlogs.ColoredStreamHandler(level=level) + except ImportError: + console = logging.StreamHandler() + console.setLevel(level) + console.setFormatter(formatter) + using_coloredlog = False + else: + using_coloredlog = True + + if using_coloredlog: + replace_stdout = False + + silencer = SelectiveSilencerFilter() + console.addFilter(silencer) + logger.addHandler(console) + logger.debug('Console handler plugged!') + + # LEAP custom handler + leap_handler = LeapLogHandler() + leap_handler.setLevel(level) + leap_handler.addFilter(silencer) + logger.addHandler(leap_handler) + logger.debug('Leap handler plugged!') + + # File handler + if logfile is not None: + logger.debug('Setting logfile to %s ', logfile) + fileh = logging.FileHandler(logfile) + fileh.setLevel(logging.DEBUG) + fileh.setFormatter(formatter) + fileh.addFilter(silencer) + logger.addHandler(fileh) + logger.debug('File handler plugged!') + + if replace_stdout: + replace_stdout_stderr_with_logging(logger) + + return logger + + +def replace_stdout_stderr_with_logging(logger): + """ + Replace: + - the standard output + - the standard error + - the twisted log output + with a custom one that writes to the logger. + """ + # Disabling this on windows since it breaks ALL THE THINGS + # The issue for this is #4149 + if not IS_WIN: + sys.stdout = StreamToLogger(logger, logging.DEBUG) + sys.stderr = StreamToLogger(logger, logging.ERROR) + + # Replace twisted's logger to use our custom output. + from twisted.python import log + log.startLogging(sys.stdout) diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py index 14d96c9b..b282a229 100644 --- a/src/leap/bitmask/platform_init/initializers.py +++ b/src/leap/bitmask/platform_init/initializers.py @@ -14,15 +14,14 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. - """ -Platform dependant initializing code +Platform-dependant initialization code. """ - import logging import os import platform import stat +import sys import subprocess import tempfile @@ -47,7 +46,7 @@ _system = platform.system() def init_platform(): """ - Returns the right initializer for the platform we are running in, or + Return the right initializer for the platform we are running in, or None if no proper initializer is found """ initializer = None @@ -79,7 +78,7 @@ UPDOWN_BADEXEC_MSG = BADEXEC_MSG % ( def get_missing_updown_dialog(): """ - Creates a dialog for notifying of missing updown scripts. + Create a dialog for notifying of missing updown scripts. Returns that dialog. :rtype: QtGui.QMessageBox instance @@ -101,7 +100,7 @@ def get_missing_updown_dialog(): def check_missing(): """ - Checks for the need of installing missing scripts, and + Check for the need of installing missing scripts, and raises a dialog to ask user for permission to do it. """ config = LeapSettings() @@ -149,7 +148,7 @@ def check_missing(): def _windows_has_tap_device(): """ - Loops over the windows registry trying to find if the tap0901 tap driver + Loop over the windows registry trying to find if the tap0901 tap driver has been installed on this machine. """ import _winreg as reg @@ -175,7 +174,7 @@ def _windows_has_tap_device(): def WindowsInitializer(): """ - Raises a dialog in case that the windows tap driver has not been found + Raise a dialog in case that the windows tap driver has not been found in the registry, asking the user for permission to install the driver """ if not _windows_has_tap_device(): @@ -219,7 +218,7 @@ def WindowsInitializer(): def _darwin_has_tun_kext(): """ - Returns True only if we found a directory under the system kext folder + Return True only if we found a directory under the system kext folder containing a kext named tun.kext, AND we found a startup item named 'tun' """ # XXX we should be smarter here and use kextstats output. @@ -235,7 +234,7 @@ def _darwin_has_tun_kext(): def _darwin_install_missing_scripts(badexec, notfound): """ - Tries to install the missing up/down scripts. + Try to install the missing up/down scripts. :param badexec: error for notifying execution error during command. :type badexec: str @@ -290,7 +289,7 @@ def _darwin_install_missing_scripts(badexec, notfound): def DarwinInitializer(): """ - Raises a dialog in case that the osx tuntap driver has not been found + Raise a dialog in case that the osx tuntap driver has not been found in the registry, asking the user for permission to install the driver """ # XXX split this function into several @@ -344,9 +343,49 @@ def DarwinInitializer(): # # Linux initializers # + +def _get_missing_resolvconf_dialog(): + """ + Create a dialog for notifying about missing openresolv. + + :rtype: QtGui.QMessageBox instance + """ + NO_RESOLVCONF = ( + "Could not find <b>resolvconf</b> installed in your system.\n" + "Do you want to quit Bitmask now?") + + EXPLAIN = ( + "Encrypted Internet needs resolvconf installed to work properly.\n" + "Please use your package manager to install it.\n") + + msg = QtGui.QMessageBox() + msg.setWindowTitle(msg.tr("Missing resolvconf framework")) + msg.setText(msg.tr(NO_RESOLVCONF)) + # but maybe the user really deserve to know more + msg.setInformativeText(msg.tr(EXPLAIN)) + msg.setStandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + msg.setDefaultButton(QtGui.QMessageBox.Yes) + return msg + + +def _linux_check_resolvconf(): + """ + Raise a dialog warning about the lack of the resolvconf framework. + """ + RESOLVCONF_PATH = "/sbin/resolvconf" + missing = not os.path.isfile(RESOLVCONF_PATH) + + if missing: + msg = _get_missing_resolvconf_dialog() + ret = msg.exec_() + + if ret == QtGui.QMessageBox.Yes: + sys.exit() + + def _linux_install_missing_scripts(badexec, notfound): """ - Tries to install the missing up/down scripts. + Try to install the missing up/down scripts. :param badexec: error for notifying execution error during command. :type badexec: str @@ -397,7 +436,11 @@ def _linux_install_missing_scripts(badexec, notfound): def LinuxInitializer(): """ - Raises a dialog in case that either updown scripts or policykit file - are missing or they have incorrect permissions. + Raise a dialog if needed files are missing. + + Missing files can be either system-wide resolvconf, bitmask-root, or + policykit file. The dialog will also be raised if some of those files are + found to have incorrect permissions. """ + _linux_check_resolvconf() check_missing() diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py index cde53631..a8821160 100644 --- a/src/leap/bitmask/services/eip/conductor.py +++ b/src/leap/bitmask/services/eip/conductor.py @@ -258,7 +258,7 @@ class EIPConductor(object): """ logger.debug("TLS Error: eip_stop (failed)") self.qtsigs.connection_died_signal.emit() - QtDelayedCall(1000, self._eip_status.eip_failed_to_restart) + QtDelayedCall(1000, self._eip_status.eip_failed_to_connect) @QtCore.Slot(int) def _eip_finished(self, exitCode): @@ -311,8 +311,7 @@ class EIPConductor(object): error=True) signal = self.qtsigs.connection_died_signal self._eip_status.show_fw_down_button() - msg = self._eip_status.tr("Outgoing traffic is blocked") - self._eip_status.set_eip_message(msg) + self._eip_status.eip_failed_to_connect() if exitCode == 0 and IS_MAC: # XXX remove this warning after I fix cocoasudo. diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py index 54845a8a..955768d1 100644 --- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py +++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py @@ -63,28 +63,21 @@ def _is_auth_agent_running(): :return: True if it's running, False if it's not. :rtype: boolean """ + # Note that gnome-shell does not uses a separate process for the + # polkit-agent, it uses a polkit-agent within its own process so we can't + # ps-grep a polkit process, we can ps-grep gnome-shell itself. + # the [x] thing is to avoid grep match itself polkit_options = [ 'ps aux | grep "polkit-[g]nome-authentication-agent-1"', 'ps aux | grep "polkit-[k]de-authentication-agent-1"', 'ps aux | grep "polkit-[m]ate-authentication-agent-1"', - 'ps aux | grep "[l]xpolkit"' + 'ps aux | grep "[l]xpolkit"', + 'ps aux | grep "[g]nome-shell"', ] is_running = [commands.getoutput(cmd) for cmd in polkit_options] - # gnome-shell does not uses a separate process for the polkit-agent, it - # uses a polkit-agent within its own process so we can't ps-grep it. - is_running = any(is_running) - if not is_running: - is_gnome_shell = commands.getoutput('ps aux | grep [g]nome-shell') - - # $DESKTOP_SESSION == 'gnome' -> gnome-shell - # $DESKTOP_SESSION == 'gnome-fallback' -> gnome-shell fallback mode, - # uses polkit-gnome... - if is_gnome_shell and os.getenv("DESKTOP_SESSION") == 'gnome': - is_running = True - - return is_running + return any(is_running) def _try_to_launch_agent(): diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py index 2b2cd874..c35be99e 100644 --- a/src/leap/bitmask/util/__init__.py +++ b/src/leap/bitmask/util/__init__.py @@ -28,11 +28,6 @@ from leap.common.config import get_path_prefix as common_get_path_prefix # We'll give your money back if it does not alleviate the eye strain, at least. -# levelname length == 8, since 'CRITICAL' is the longest -LOG_FORMAT = ('%(asctime)s - %(levelname)-8s - ' - 'L#%(lineno)-4s : %(name)s:%(funcName)s() - %(message)s') - - def first(things): """ Return the head of a collection. |