summaryrefslogtreecommitdiff
path: root/src/leap/baseapp/eip.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/baseapp/eip.py')
-rw-r--r--src/leap/baseapp/eip.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py
new file mode 100644
index 00000000..b34cc82e
--- /dev/null
+++ b/src/leap/baseapp/eip.py
@@ -0,0 +1,243 @@
+from __future__ import print_function
+import logging
+import time
+#import sys
+
+from PyQt4 import QtCore
+
+from leap.baseapp.dialogs import ErrorDialog
+from leap.baseapp import constants
+from leap.eip import exceptions as eip_exceptions
+from leap.eip.eipconnection import EIPConnection
+from leap.base.checks import EVENT_CONNECT_REFUSED
+from leap.util import geo
+
+logger = logging.getLogger(name=__name__)
+
+
+class EIPConductorAppMixin(object):
+ """
+ initializes an instance of EIPConnection,
+ gathers errors, and passes status-change signals
+ from Qt land along to the conductor.
+ Connects the eip connect/disconnect logic
+ to the switches in the app (buttons/menu items).
+ """
+ ERR_DIALOG = False
+
+ def __init__(self, *args, **kwargs):
+ opts = kwargs.pop('opts')
+ config_file = getattr(opts, 'config_file', None)
+ provider = kwargs.pop('provider')
+
+ self.eip_service_started = False
+
+ # conductor (eip connection) is in charge of all
+ # vpn-related configuration / monitoring.
+ # we pass a tuple of signals that will be
+ # triggered when status changes.
+
+ self.conductor = EIPConnection(
+ watcher_cb=self.newLogLine.emit,
+ config_file=config_file,
+ checker_signals=(self.eipStatusChange.emit, ),
+ status_signals=(self.openvpnStatusChange.emit, ),
+ debug=self.debugmode,
+ ovpn_verbosity=opts.openvpn_verb,
+ provider=provider)
+
+ # Do we want to enable the skip checks w/o being
+ # in debug mode??
+ #self.skip_download = opts.no_provider_checks
+ #self.skip_verify = opts.no_ca_verify
+ self.skip_download = False
+ self.skip_verify = False
+
+ def run_eip_checks(self):
+ """
+ runs eip checks and
+ the error checking loop
+ """
+ logger.debug('running EIP CHECKS')
+ self.conductor.run_checks(
+ skip_download=self.skip_download,
+ skip_verify=self.skip_verify)
+ self.error_check()
+
+ self.start_eipconnection.emit()
+
+ def error_check(self):
+ """
+ consumes the conductor error queue.
+ pops errors, and acts accordingly (launching user dialogs).
+ """
+ logger.debug('error check')
+
+ errq = self.conductor.error_queue
+ while errq.qsize() != 0:
+ logger.debug('%s errors left in conductor queue', errq.qsize())
+ # we get exception and original traceback from queue
+ error, tb = errq.get()
+
+ # redundant log, debugging the loop.
+ logger.error('%s: %s', error.__class__.__name__, error.message)
+
+ if issubclass(error.__class__, eip_exceptions.EIPClientError):
+ self.triggerEIPError.emit(error)
+
+ else:
+ # deprecated form of raising exception.
+ raise error, None, tb
+
+ if error.failfirst is True:
+ break
+
+ @QtCore.pyqtSlot(object)
+ def onEIPError(self, error):
+ """
+ check severity and launches
+ dialogs informing user about the errors.
+ in the future we plan to derive errors to
+ our log viewer.
+ """
+ if self.ERR_DIALOG:
+ logger.warning('another error dialog suppressed')
+ return
+
+ # XXX this is actually a one-shot.
+ # On the dialog there should be
+ # a reset signal binded to the ok button
+ # or something like that.
+ self.ERR_DIALOG = True
+
+ if getattr(error, 'usermessage', None):
+ message = error.usermessage
+ else:
+ message = error.message
+
+ # XXX
+ # check headless = False before
+ # launching dialog.
+ # (so Qt tests can assert stuff)
+
+ if error.critical:
+ logger.critical(error.message)
+ #critical error (non recoverable),
+ #we give user some info and quit.
+ #(critical error dialog will exit app)
+ ErrorDialog(errtype="critical",
+ msg=message,
+ label="critical error")
+
+ elif error.warning:
+ logger.warning(error.message)
+
+ else:
+ dialog = ErrorDialog()
+ dialog.warningMessage(message, 'error')
+
+ @QtCore.pyqtSlot()
+ def statusUpdate(self):
+ """
+ polls status and updates ui with real time
+ info about transferred bytes / connection state.
+ right now is triggered by a timer tick
+ (timer controlled by StatusAwareTrayIcon class)
+ """
+ # TODO I guess it's too expensive to poll
+ # continously. move to signal events instead.
+ # (i.e., subscribe to connection status changes
+ # from openvpn manager)
+
+ if not self.eip_service_started:
+ # there is a race condition
+ # going on here. Depending on how long we take
+ # to init the qt app, the management socket
+ # is not ready yet.
+ return
+
+ #if self.conductor.with_errors:
+ #XXX how to wait on pkexec???
+ #something better that this workaround, plz!!
+ #I removed the pkexec pass authentication at all.
+ #time.sleep(5)
+ #logger.debug('timeout')
+ #logger.error('errors. disconnect')
+ #self.start_or_stopVPN() # is stop
+
+ state = self.conductor.poll_connection_state()
+ if not state:
+ return
+
+ ts, con_status, ok, ip, remote = state
+ self.set_statusbarMessage(con_status)
+ self.setIconToolTip()
+
+ ts = time.strftime("%a %b %d %X", ts)
+ if self.debugmode:
+ self.updateTS.setText(ts)
+ self.status_label.setText(con_status)
+ self.ip_label.setText(ip)
+ self.remote_label.setText(remote)
+ self.remote_country.setText(
+ geo.get_country_name(remote))
+
+ # status i/o
+
+ status = self.conductor.get_status_io()
+ if status and self.debugmode:
+ #XXX move this to systray menu indicators
+ ts, (tun_read, tun_write, tcp_read, tcp_write, auth_read) = status
+ ts = time.strftime("%a %b %d %X", ts)
+ self.updateTS.setText(ts)
+ self.tun_read_bytes.setText(tun_read)
+ self.tun_write_bytes.setText(tun_write)
+
+ # connection information via management interface
+ log = self.conductor.get_log()
+ error_matrix = [(EVENT_CONNECT_REFUSED, (self.start_or_stopVPN, ))]
+ if hasattr(self.network_checker, 'checker'):
+ self.network_checker.checker.parse_log_and_react(log, error_matrix)
+
+ @QtCore.pyqtSlot()
+ def start_or_stopVPN(self, **kwargs):
+ """
+ stub for running child process with vpn
+ """
+ if self.conductor.has_errors():
+ logger.debug('not starting vpn; conductor has errors')
+ return
+
+ if self.eip_service_started is False:
+ try:
+ self.conductor.connect()
+
+ except eip_exceptions.EIPNoCommandError as exc:
+ logger.error('tried to run openvpn but no command is set')
+ self.triggerEIPError.emit(exc)
+
+ except Exception as err:
+ # raise generic exception (Bad Thing Happened?)
+ logger.exception(err)
+ else:
+ # no errors, so go on.
+ if self.debugmode:
+ self.startStopButton.setText(self.tr('&Disconnect'))
+ self.eip_service_started = True
+ self.toggleEIPAct()
+
+ # XXX decouple! (timer is init by icons class).
+ # we could bring Timer Init to this Mixin
+ # or to its own Mixin.
+ self.timer.start(constants.TIMER_MILLISECONDS)
+ return
+
+ if self.eip_service_started is True:
+ self.network_checker.stop()
+ self.conductor.disconnect()
+ if self.debugmode:
+ self.startStopButton.setText(self.tr('&Connect'))
+ self.eip_service_started = False
+ self.toggleEIPAct()
+ self.timer.stop()
+ return