summaryrefslogtreecommitdiff
path: root/src/leap/baseapp/eip.py
blob: e0da63a224e5321c1b22378b3db0323426ac204b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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.<br/>'
                'Make sure you have '
                '<b>polkit-gnome-authentication-agent-1</b> '
                'running and try again.',
                'error')

        if self.conductor.missing_pkexec is True:
            dialog = ErrorDialog()
            dialog.warningMessage(
                'We could not find <b>pkexec</b> in your '
                'system.<br/> Do you want to try '
                '<b>setuid workaround</b>? '
                '(<i>DOES NOTHING YET</i>)',
                '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)
            #print('errors. disconnect.')
            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. '
                    '<br/>(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