import logging
import time
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
logger = logging.getLogger(name=__name__)
class EIPConductorApp(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).
"""
def __init__(self, *args, **kwargs):
opts = kwargs.pop('opts')
config_file = getattr(opts, 'config_file', None)
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,
status_signals=(self.statusChange.emit, ),
debug=self.debugmode)
# XXX remove skip download when sample service is ready
self.conductor.run_checks(skip_download=True)
self.error_check()
# XXX should receive "ready" signal
# it is called from LeapWindow now.
#if self.conductor.autostart:
#self.start_or_stopVPN()
if self.debugmode:
self.startStopButton.clicked.connect(
lambda: self.start_or_stopVPN())
def error_check(self):
# XXX refactor (by #504)
if self.conductor.missing_definition is True:
dialog = ErrorDialog()
dialog.criticalMessage(
'The default '
'definition.json file cannot be found',
'error')
if self.conductor.missing_provider is True:
dialog = ErrorDialog()
dialog.criticalMessage(
'Missing provider. Add a remote_ip entry '
'under section [provider] in eip.cfg',
'error')
if self.conductor.missing_vpn_keyfile is True:
dialog = ErrorDialog()
dialog.criticalMessage(
'Could not find the vpn keys file',
'error')
# ... btw, review pending.
# os.kill of subprocess fails if we have
# some of this errors.
if self.conductor.bad_provider is True:
dialog = ErrorDialog()
dialog.criticalMessage(
'Bad provider entry. Check that remote_ip entry '
'has an IP under section [provider] in eip.cfg',
'error')
if self.conductor.bad_keyfile_perms is True:
dialog = ErrorDialog()
dialog.criticalMessage(
'The vpn keys file has bad permissions',
'error')
if self.conductor.missing_auth_agent is True:
dialog = ErrorDialog()
dialog.warningMessage(
'We could not find any authentication '
'agent in your system.
'
'Make sure you have '
'polkit-gnome-authentication-agent-1 '
'running and try again.',
'error')
if self.conductor.missing_pkexec is True:
dialog = ErrorDialog()
dialog.warningMessage(
'We could not find pkexec in your '
'system.
Do you want to try '
'setuid workaround? '
'(DOES NOTHING YET)',
'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:
return
if self.conductor.with_errors:
#XXX how to wait on pkexec???
#something better that this workaround, plz!!
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)
# 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)
@QtCore.pyqtSlot()
def start_or_stopVPN(self):
"""
stub for running child process with vpn
"""
if self.eip_service_started is False:
try:
self.conductor.connect()
# XXX move this to error queue
except eip_exceptions.EIPNoCommandError:
dialog = ErrorDialog()
dialog.warningMessage(
'No suitable openvpn command found. '
'
(Might be a permissions problem)',
'error')
if self.debugmode:
self.startStopButton.setText('&Disconnect')
self.eip_service_started = True
# XXX what is optimum polling interval?
# too little is overkill, too much
# will miss transition states..
# XXX decouple! (timer is init by icons class).
# should bring it here?
# to its own class?
self.timer.start(constants.TIMER_MILLISECONDS)
return
if self.eip_service_started is True:
self.conductor.disconnect()
if self.debugmode:
self.startStopButton.setText('&Connect')
self.eip_service_started = False
self.timer.stop()
return