~ubuntu-branches/ubuntu/vivid/blueman/vivid-proposed

« back to all changes in this revision

Viewing changes to blueman/main/PPPConnection.py

  • Committer: Package Import Robot
  • Author(s): Artur Rona
  • Date: 2014-12-24 18:33:36 UTC
  • mfrom: (2.3.8 sid)
  • Revision ID: package-import@ubuntu.com-20141224183336-cyb82ot0y8tz8flq
Tags: 1.99~alpha1-1ubuntu1
* Merge from Debian unstable.  Remaining changes:
  - debian/patches/01_dont_autostart_lxde.patch:
    + Don't autostart the applet in LXDE.
  - debian/patches/03_filemanager_fix.patch:
    + Add support for more filemanagers.
* debian/patches/02_dont_crash_on_non-bluetooth_card.patch:
  - Dropped, no longer applicable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Valmantas Paliksa <walmis at balticum-tv dot lt>
2
 
#
3
 
# Licensed under the GNU General Public License Version 3
4
 
#
5
 
# This program is free software: you can redistribute it and/or modify
6
 
# it under the terms of the GNU General Public License as published by
7
 
# the Free Software Foundation, either version 3 of the License, or
8
 
# (at your option) any later version.
9
 
#
10
 
# This program is distributed in the hope that it will be useful,
11
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
# GNU General Public License for more details.
14
 
#
15
 
# You should have received a copy of the GNU General Public License
16
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 
18
 
 
19
 
import glib
 
1
from blueman.Functions import dprint
20
2
import tty
21
3
import termios
22
4
import os
23
5
import subprocess
24
 
import gobject
 
6
from gi.repository import GObject
25
7
import errno
26
8
import re
27
9
 
28
 
pppd_errors = {}
29
 
pppd_errors[1] = """An immediately fatal error of some kind  occurred, such as an essential system call failing, or running out of virtual memory."""
30
 
pppd_errors[2] = """An  error  was detected in processing the options given, such as two mutually exclusive options being used."""
31
 
pppd_errors[3] = """Pppd is not setuid-root and the invoking user is not root."""
32
 
pppd_errors[4] = """The kernel does not support PPP, for example, the  PPP kernel driver is not included or cannot be loaded."""
33
 
pppd_errors[5] = """Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal."""
34
 
pppd_errors[6] = """The serial port could not be locked."""
35
 
pppd_errors[7] = """The serial port could not be opened."""
36
 
pppd_errors[8] = """The connect script failed (returned a non-zero exit status)."""
37
 
pppd_errors[9] = """The command specified as the argument to the  pty  option  could not be run."""
38
 
pppd_errors[10] = """The PPP negotiation failed, that is, it didn't reach the point where at least one network protocol (e.g. IP) was running."""
39
 
pppd_errors[11] = """The peer system failed (or refused) to authenticate itself."""
40
 
pppd_errors[12] = """The link was established successfully and terminated because  it was idle."""
41
 
pppd_errors[13] = """The link was established successfully and terminated because the connect time limit was reached."""
42
 
pppd_errors[14] = """Callback was negotiated  and  an  incoming  call  should  arrive shortly."""
43
 
pppd_errors[15] = """The link was terminated because the peer is not responding to echo requests."""
44
 
pppd_errors[16] = """The link was terminated by the modem hanging up."""
45
 
pppd_errors[17] = """The PPP negotiation failed because serial loopback was detected."""
46
 
pppd_errors[18] = """The init script failed (returned a non-zero exit status)."""
47
 
pppd_errors[19] = """We failed to authenticate ourselves to the peer."""
 
10
pppd_errors = {
 
11
    1: """An immediately fatal error of some kind  occurred, such as an essential system call failing, or running out of virtual memory.""",
 
12
    2: """An  error  was detected in processing the options given, such as two mutually exclusive options being used.""",
 
13
    3: """Pppd is not setuid-root and the invoking user is not root.""",
 
14
    4: """The kernel does not support PPP, for example, the  PPP kernel driver is not included or cannot be loaded.""",
 
15
    5: """Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal.""",
 
16
    6: """The serial port could not be locked.""", 7: """The serial port could not be opened.""",
 
17
    8: """The connect script failed (returned a non-zero exit status).""",
 
18
    9: """The command specified as the argument to the  pty  option  could not be run.""",
 
19
    10: """The PPP negotiation failed, that is, it didn't reach the point where at least one network protocol (e.g. IP) was running.""",
 
20
    11: """The peer system failed (or refused) to authenticate itself.""",
 
21
    12: """The link was established successfully and terminated because  it was idle.""",
 
22
    13: """The link was established successfully and terminated because the connect time limit was reached.""",
 
23
    14: """Callback was negotiated  and  an  incoming  call  should  arrive shortly.""",
 
24
    15: """The link was terminated because the peer is not responding to echo requests.""",
 
25
    16: """The link was terminated by the modem hanging up.""",
 
26
    17: """The PPP negotiation failed because serial loopback was detected.""",
 
27
    18: """The init script failed (returned a non-zero exit status).""",
 
28
    19: """We failed to authenticate ourselves to the peer."""
 
29
}
48
30
 
49
31
 
50
32
class PPPException(Exception):
51
 
        pass
52
 
 
53
 
class PPPConnection(gobject.GObject):
54
 
        __gsignals__ = {
55
 
                #arg: interface name eg. ppp0
56
 
                'connected' : (gobject.SIGNAL_NO_HOOKS, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
57
 
                'error-occurred' : (gobject.SIGNAL_NO_HOOKS, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)),
58
 
 
59
 
        }
60
 
        def __init__(self, port, number="*99#", apn="", user="", pwd=""):
61
 
                gobject.GObject.__init__(self)
62
 
                
63
 
                self.apn = apn
64
 
                self.number = number
65
 
                self.user = user
66
 
                self.pwd = pwd
67
 
                self.port = port
68
 
                self.interface = None
69
 
                
70
 
                self.pppd = None
71
 
                self.file = None
72
 
                
73
 
                self.commands = [("ATZ E0 V1 X4 &C1 +FCLASS=0", self.simple_callback), 
74
 
                                 ("ATE0", self.simple_callback), 
75
 
                                 ("AT+GCAP", self.simple_callback), 
76
 
                                 ("ATD%s" % self.number, self.connect_callback, ["CONNECT", "NO CARRIER", "BUSY", "NO ANSWER", "NO DIALTONE", "OK", "ERROR"])
77
 
                                ]
78
 
                if self.apn != "":              
79
 
                        self.commands.insert(-1, ('AT+CGDCONT=1,"IP","%s"' % self.apn, self.simple_callback))
80
 
                                
81
 
        def cleanup(self):
82
 
                os.close(self.file)
83
 
                self.file = None
84
 
        
85
 
        def simple_callback(self, response):
86
 
                pass
87
 
                
88
 
        def connect_callback(self, response):
89
 
                if "CONNECT" in response:
90
 
                        dprint("Starting pppd")
91
 
                        self.pppd = subprocess.Popen(["/usr/sbin/pppd", "%s" % self.port, "115200", "defaultroute", "updetach", "usepeerdns"], bufsize=1, stdout=subprocess.PIPE)
92
 
                        glib.io_add_watch(self.pppd.stdout, glib.IO_IN | glib.IO_ERR | glib.IO_HUP, self.on_pppd_stdout)
93
 
                        glib.timeout_add(1000, self.check_pppd)                         
94
 
                        
95
 
                        self.cleanup()
96
 
                else:
97
 
                        self.cleanup()
98
 
                        raise PPPException("Bad modem response %s, expected CONNECT" % response[0])
99
 
        
100
 
        def __cmd_response_cb(self, response, exception, item_id):
101
 
                if exception:
102
 
                        self.emit("error-occurred", str(exception))
103
 
                else:   
104
 
                        try:
105
 
                                self.commands[item_id][1](response)
106
 
                        except PPPException, e:
107
 
                                self.emit("error-occurred", str(e))
108
 
                                return
109
 
                                
110
 
                        self.send_commands(item_id+1)
111
 
 
112
 
        
113
 
        def send_commands(self, id=0):
114
 
                try:
115
 
                        item = self.commands[id]
116
 
                except IndexError:
117
 
                        return
118
 
                        
119
 
                if len(item) == 3:
120
 
                        (command, callback, terminators) = item
121
 
                else:
122
 
                        (command, callback) = item
123
 
                        terminators = ["OK", "ERROR"]
124
 
                        
125
 
                self.send_command(command)
126
 
                self.wait_for_reply(self.__cmd_response_cb, terminators, id)    
127
 
                                
128
 
 
129
 
        def Connect(self):
130
 
                        
131
 
                self.file = os.open(self.port, os.O_RDWR | os.O_EXCL | os.O_NONBLOCK | os.O_NOCTTY)
132
 
                
133
 
                tty.setraw(self.file)
134
 
                
135
 
                attrs = termios.tcgetattr(self.file)
136
 
                
137
 
                attrs[0] &= ~(termios.IGNCR | termios.ICRNL | termios.IUCLC | termios.INPCK | termios.IXON | termios.IXANY | termios.IGNPAR)
138
 
                attrs[1] &= ~(termios.OPOST | termios.OLCUC | termios.OCRNL | termios.ONLCR | termios.ONLRET)
139
 
                attrs[3] &= ~(termios.ICANON | termios.XCASE | termios.ECHO | termios.ECHOE | termios.ECHONL)
140
 
                attrs[3] &= ~(termios.ECHO | termios.ECHOE)
141
 
                attrs[6][termios.VMIN] = 1
142
 
                attrs[6][termios.VTIME] = 0
143
 
                attrs[6][termios.VEOF] = 1
144
 
 
145
 
                attrs[2] &= ~(termios.CBAUD | termios.CSIZE | termios.CSTOPB | termios.CLOCAL | termios.PARENB)
146
 
                attrs[2] |= (termios.B9600 | termios.CS8 | termios.CREAD | termios.PARENB)
147
 
                
148
 
                termios.tcsetattr(self.file, termios.TCSANOW, attrs)
149
 
                
150
 
                termios.tcflush(self.file, termios.TCIOFLUSH)
151
 
                
152
 
                self.send_commands()    
153
 
                
154
 
        def on_pppd_stdout(self, source, cond):
155
 
                if cond & glib.IO_ERR or cond & glib.IO_HUP:
156
 
                        return False
157
 
 
158
 
                line = source.readline()        
159
 
                m = re.match("Using interface (ppp[0-9]*)", line)
160
 
                if m:
161
 
                        self.interface = m.groups(1)[0]
162
 
                
163
 
                print line
164
 
        
165
 
                return True
166
 
        
167
 
        def check_pppd(self):
168
 
                status = self.pppd.poll()       
169
 
                if status != None:
170
 
                        if status == 0:
171
 
                                self.emit("connected", self.interface)
172
 
                        else:
173
 
                                try:
174
 
                                        msg = "pppd exited: " + pppd_errors[int(status)]
175
 
                                except KeyError:
176
 
                                        msg = "pppd exited with unknown error"
177
 
                                        
178
 
                                self.emit("error-occurred", msg)
179
 
                                
180
 
                        print "pppd exited with status %d" % status
181
 
                        return False
182
 
                return True
183
 
 
184
 
        def send_command(self, command):
185
 
                dprint("-->", command)
186
 
                os.write(self.file, "%s\r\n" % command)
187
 
                termios.tcdrain(self.file)
188
 
                
189
 
        def on_data_ready(self, source, condition, terminators, on_done):
190
 
                if condition & glib.IO_ERR or condition & glib.IO_HUP:
191
 
                        on_done(None, PPPException("Socket error"))
192
 
                        self.cleanup()
193
 
                        return False
194
 
                try:
195
 
                        self.buffer += os.read(self.file, 1)
196
 
                except OSError, e:
197
 
                        if e.errno == errno.EAGAIN:
198
 
                                dprint("Got EAGAIN")
199
 
                                return True
200
 
                        else:
201
 
                                on_done(None, PPPException("Socket error"))
202
 
                                dprint(e)
203
 
                                self.cleanup()
204
 
                                return False
205
 
 
206
 
                lines = self.buffer.split("\r\n")
207
 
                found = False
208
 
                for l in lines:
209
 
                        if l == "":
210
 
                                pass            
211
 
                        else:
212
 
                                for t in terminators:
213
 
                                        if t in l:
214
 
                                                found = True
215
 
 
216
 
                if found:
217
 
                        lines = filter(lambda x: x != "", lines)
218
 
                        lines = map(lambda x: x.strip("\r\n"), lines)
219
 
                        dprint("<-- ", lines)
220
 
                        
221
 
                        on_done(lines, None)
222
 
                        return False            
223
 
                        
224
 
                return True
225
 
                
226
 
        def wait_for_reply(self, callback, terminators=["OK", "ERROR"], *user_data):
227
 
                def on_timeout():
228
 
                        glib.source_remove(self.io_watch)
229
 
                        callback(None, PPPException("Modem initialization timed out"), *user_data)
230
 
                        self.cleanup()
231
 
                        return False
232
 
                        
233
 
                        
234
 
                def on_done(ret, exception):
235
 
                        glib.source_remove(self.timeout)
236
 
                        callback(ret, exception, *user_data)
237
 
                        
238
 
                
239
 
                self.buffer = ""
240
 
                self.term_found = False
241
 
                                
242
 
                self.io_watch = glib.io_add_watch(self.file, glib.IO_IN | glib.IO_ERR | glib.IO_HUP, self.on_data_ready, terminators, on_done)
243
 
                self.timeout = glib.timeout_add(15000, on_timeout)
244
 
                
 
33
    pass
 
34
 
 
35
 
 
36
class PPPConnection(GObject.GObject):
 
37
    __gsignals__ = {  # arg: interface name eg. ppp0
 
38
        'connected': (GObject.SignalFlags.NO_HOOKS, None, (GObject.TYPE_PYOBJECT,)),
 
39
        'error-occurred': (GObject.SignalFlags.NO_HOOKS, None, (GObject.TYPE_PYOBJECT,))
 
40
    }
 
41
 
 
42
    def __init__(self, port, number="*99#", apn="", user="", pwd=""):
 
43
        GObject.GObject.__init__(self)
 
44
 
 
45
        self.apn = apn
 
46
        self.number = number
 
47
        self.user = user
 
48
        self.pwd = pwd
 
49
        self.port = port
 
50
        self.interface = None
 
51
 
 
52
        self.pppd = None
 
53
        self.file = None
 
54
 
 
55
        self.commands = [
 
56
            ("ATZ E0 V1 X4 &C1 +FCLASS=0", self.simple_callback),
 
57
            ("ATE0", self.simple_callback),
 
58
            ("AT+GCAP", self.simple_callback),
 
59
            (
 
60
                "ATD%s" % self.number,
 
61
                self.connect_callback,
 
62
                ["CONNECT", "NO CARRIER", "BUSY", "NO ANSWER", "NO DIALTONE", "OK", "ERROR"]
 
63
            )
 
64
        ]
 
65
        if self.apn != "":
 
66
            self.commands.insert(-1, ('AT+CGDCONT=1,"IP","%s"' % self.apn, self.simple_callback))
 
67
 
 
68
    def cleanup(self):
 
69
        os.close(self.file)
 
70
        self.file = None
 
71
 
 
72
    def simple_callback(self, response):
 
73
        pass
 
74
 
 
75
    def connect_callback(self, response):
 
76
        if "CONNECT" in response:
 
77
            dprint("Starting pppd")
 
78
            self.pppd = subprocess.Popen(
 
79
                ["/usr/sbin/pppd", "%s" % self.port, "115200", "defaultroute", "updetach", "usepeerdns"], bufsize=1,
 
80
                stdout=subprocess.PIPE)
 
81
            GObject.io_add_watch(self.pppd.stdout, GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP, self.on_pppd_stdout)
 
82
            GObject.timeout_add(1000, self.check_pppd)
 
83
 
 
84
            self.cleanup()
 
85
        else:
 
86
            self.cleanup()
 
87
            raise PPPException("Bad modem response %s, expected CONNECT" % response[0])
 
88
 
 
89
    def __cmd_response_cb(self, response, exception, item_id):
 
90
        if exception:
 
91
            self.emit("error-occurred", str(exception))
 
92
        else:
 
93
            try:
 
94
                self.commands[item_id][1](response)
 
95
            except PPPException as e:
 
96
                self.emit("error-occurred", str(e))
 
97
                return
 
98
 
 
99
            self.send_commands(item_id + 1)
 
100
 
 
101
    def send_commands(self, id=0):
 
102
        try:
 
103
            item = self.commands[id]
 
104
        except IndexError:
 
105
            return
 
106
 
 
107
        if len(item) == 3:
 
108
            (command, callback, terminators) = item
 
109
        else:
 
110
            (command, callback) = item
 
111
            terminators = ["OK", "ERROR"]
 
112
 
 
113
        self.send_command(command)
 
114
        self.wait_for_reply(self.__cmd_response_cb, terminators, id)
 
115
 
 
116
    def Connect(self):
 
117
 
 
118
        self.file = os.open(self.port, os.O_RDWR | os.O_EXCL | os.O_NONBLOCK | os.O_NOCTTY)
 
119
 
 
120
        tty.setraw(self.file)
 
121
 
 
122
        attrs = termios.tcgetattr(self.file)
 
123
 
 
124
        attrs[0] &= ~(termios.IGNCR | termios.ICRNL | termios.IUCLC | termios.INPCK | termios.IXON | termios.IXANY |
 
125
                      termios.IGNPAR)
 
126
        attrs[1] &= ~(termios.OPOST | termios.OLCUC | termios.OCRNL | termios.ONLCR | termios.ONLRET)
 
127
        attrs[3] &= ~(termios.ICANON | termios.XCASE | termios.ECHO | termios.ECHOE | termios.ECHONL)
 
128
        attrs[3] &= ~(termios.ECHO | termios.ECHOE)
 
129
        attrs[6][termios.VMIN] = 1
 
130
        attrs[6][termios.VTIME] = 0
 
131
        attrs[6][termios.VEOF] = 1
 
132
 
 
133
        attrs[2] &= ~(termios.CBAUD | termios.CSIZE | termios.CSTOPB | termios.CLOCAL | termios.PARENB)
 
134
        attrs[2] |= (termios.B9600 | termios.CS8 | termios.CREAD | termios.PARENB)
 
135
 
 
136
        termios.tcsetattr(self.file, termios.TCSANOW, attrs)
 
137
 
 
138
        termios.tcflush(self.file, termios.TCIOFLUSH)
 
139
 
 
140
        self.send_commands()
 
141
 
 
142
    def on_pppd_stdout(self, source, cond):
 
143
        if cond & GObject.IO_ERR or cond & GObject.IO_HUP:
 
144
            return False
 
145
 
 
146
        line = source.readline()
 
147
        m = re.match("Using interface (ppp[0-9]*)", line)
 
148
        if m:
 
149
            self.interface = m.groups(1)[0]
 
150
 
 
151
        print(line)
 
152
 
 
153
        return True
 
154
 
 
155
    def check_pppd(self):
 
156
        status = self.pppd.poll()
 
157
        if status is not None:
 
158
            if status == 0:
 
159
                self.emit("connected", self.interface)
 
160
            else:
 
161
                try:
 
162
                    msg = "pppd exited: " + pppd_errors[int(status)]
 
163
                except KeyError:
 
164
                    msg = "pppd exited with unknown error"
 
165
 
 
166
                self.emit("error-occurred", msg)
 
167
 
 
168
            print("pppd exited with status %d" % status)
 
169
            return False
 
170
        return True
 
171
 
 
172
    def send_command(self, command):
 
173
        dprint("-->", command)
 
174
        os.write(self.file, "%s\r\n" % command)
 
175
        termios.tcdrain(self.file)
 
176
 
 
177
    def on_data_ready(self, source, condition, terminators, on_done):
 
178
        if condition & GObject.IO_ERR or condition & GObject.IO_HUP:
 
179
            on_done(None, PPPException("Socket error"))
 
180
            self.cleanup()
 
181
            return False
 
182
        try:
 
183
            self.buffer += os.read(self.file, 1)
 
184
        except OSError as e:
 
185
            if e.errno == errno.EAGAIN:
 
186
                dprint("Got EAGAIN")
 
187
                return True
 
188
            else:
 
189
                on_done(None, PPPException("Socket error"))
 
190
                dprint(e)
 
191
                self.cleanup()
 
192
                return False
 
193
 
 
194
        lines = self.buffer.split("\r\n")
 
195
        found = False
 
196
        for l in lines:
 
197
            if l == "":
 
198
                pass
 
199
            else:
 
200
                for t in terminators:
 
201
                    if t in l:
 
202
                        found = True
 
203
 
 
204
        if found:
 
205
            lines = filter(lambda x: x != "", lines)
 
206
            lines = map(lambda x: x.strip("\r\n"), lines)
 
207
            dprint("<-- ", lines)
 
208
 
 
209
            on_done(lines, None)
 
210
            return False
 
211
 
 
212
        return True
 
213
 
 
214
    def wait_for_reply(self, callback, terminators=["OK", "ERROR"], *user_data):
 
215
        def on_timeout():
 
216
            GObject.source_remove(self.io_watch)
 
217
            callback(None, PPPException("Modem initialization timed out"), *user_data)
 
218
            self.cleanup()
 
219
            return False
 
220
 
 
221
        def on_done(ret, exception):
 
222
            GObject.source_remove(self.timeout)
 
223
            callback(ret, exception, *user_data)
 
224
 
 
225
 
 
226
        self.buffer = ""
 
227
        self.term_found = False
 
228
 
 
229
        self.io_watch = GObject.io_add_watch(self.file, GObject.IO_IN | GObject.IO_ERR | GObject.IO_HUP, self.on_data_ready,
 
230
                                          terminators, on_done)
 
231
        self.timeout = GObject.timeout_add(15000, on_timeout)