1
# Copyright (C) 2008 Valmantas Paliksa <walmis at balticum-tv dot lt>
3
# Licensed under the GNU General Public License Version 3
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.
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.
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/>.
1
from blueman.Functions import dprint
6
from gi.repository import GObject
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."""
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."""
50
32
class PPPException(Exception):
53
class PPPConnection(gobject.GObject):
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,)),
60
def __init__(self, port, number="*99#", apn="", user="", pwd=""):
61
gobject.GObject.__init__(self)
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"])
79
self.commands.insert(-1, ('AT+CGDCONT=1,"IP","%s"' % self.apn, self.simple_callback))
85
def simple_callback(self, response):
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)
98
raise PPPException("Bad modem response %s, expected CONNECT" % response[0])
100
def __cmd_response_cb(self, response, exception, item_id):
102
self.emit("error-occurred", str(exception))
105
self.commands[item_id][1](response)
106
except PPPException, e:
107
self.emit("error-occurred", str(e))
110
self.send_commands(item_id+1)
113
def send_commands(self, id=0):
115
item = self.commands[id]
120
(command, callback, terminators) = item
122
(command, callback) = item
123
terminators = ["OK", "ERROR"]
125
self.send_command(command)
126
self.wait_for_reply(self.__cmd_response_cb, terminators, id)
131
self.file = os.open(self.port, os.O_RDWR | os.O_EXCL | os.O_NONBLOCK | os.O_NOCTTY)
133
tty.setraw(self.file)
135
attrs = termios.tcgetattr(self.file)
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
145
attrs[2] &= ~(termios.CBAUD | termios.CSIZE | termios.CSTOPB | termios.CLOCAL | termios.PARENB)
146
attrs[2] |= (termios.B9600 | termios.CS8 | termios.CREAD | termios.PARENB)
148
termios.tcsetattr(self.file, termios.TCSANOW, attrs)
150
termios.tcflush(self.file, termios.TCIOFLUSH)
154
def on_pppd_stdout(self, source, cond):
155
if cond & glib.IO_ERR or cond & glib.IO_HUP:
158
line = source.readline()
159
m = re.match("Using interface (ppp[0-9]*)", line)
161
self.interface = m.groups(1)[0]
167
def check_pppd(self):
168
status = self.pppd.poll()
171
self.emit("connected", self.interface)
174
msg = "pppd exited: " + pppd_errors[int(status)]
176
msg = "pppd exited with unknown error"
178
self.emit("error-occurred", msg)
180
print "pppd exited with status %d" % status
184
def send_command(self, command):
185
dprint("-->", command)
186
os.write(self.file, "%s\r\n" % command)
187
termios.tcdrain(self.file)
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"))
195
self.buffer += os.read(self.file, 1)
197
if e.errno == errno.EAGAIN:
201
on_done(None, PPPException("Socket error"))
206
lines = self.buffer.split("\r\n")
212
for t in terminators:
217
lines = filter(lambda x: x != "", lines)
218
lines = map(lambda x: x.strip("\r\n"), lines)
219
dprint("<-- ", lines)
226
def wait_for_reply(self, callback, terminators=["OK", "ERROR"], *user_data):
228
glib.source_remove(self.io_watch)
229
callback(None, PPPException("Modem initialization timed out"), *user_data)
234
def on_done(ret, exception):
235
glib.source_remove(self.timeout)
236
callback(ret, exception, *user_data)
240
self.term_found = False
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)
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,))
42
def __init__(self, port, number="*99#", apn="", user="", pwd=""):
43
GObject.GObject.__init__(self)
56
("ATZ E0 V1 X4 &C1 +FCLASS=0", self.simple_callback),
57
("ATE0", self.simple_callback),
58
("AT+GCAP", self.simple_callback),
60
"ATD%s" % self.number,
61
self.connect_callback,
62
["CONNECT", "NO CARRIER", "BUSY", "NO ANSWER", "NO DIALTONE", "OK", "ERROR"]
66
self.commands.insert(-1, ('AT+CGDCONT=1,"IP","%s"' % self.apn, self.simple_callback))
72
def simple_callback(self, response):
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)
87
raise PPPException("Bad modem response %s, expected CONNECT" % response[0])
89
def __cmd_response_cb(self, response, exception, item_id):
91
self.emit("error-occurred", str(exception))
94
self.commands[item_id][1](response)
95
except PPPException as e:
96
self.emit("error-occurred", str(e))
99
self.send_commands(item_id + 1)
101
def send_commands(self, id=0):
103
item = self.commands[id]
108
(command, callback, terminators) = item
110
(command, callback) = item
111
terminators = ["OK", "ERROR"]
113
self.send_command(command)
114
self.wait_for_reply(self.__cmd_response_cb, terminators, id)
118
self.file = os.open(self.port, os.O_RDWR | os.O_EXCL | os.O_NONBLOCK | os.O_NOCTTY)
120
tty.setraw(self.file)
122
attrs = termios.tcgetattr(self.file)
124
attrs[0] &= ~(termios.IGNCR | termios.ICRNL | termios.IUCLC | termios.INPCK | termios.IXON | termios.IXANY |
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
133
attrs[2] &= ~(termios.CBAUD | termios.CSIZE | termios.CSTOPB | termios.CLOCAL | termios.PARENB)
134
attrs[2] |= (termios.B9600 | termios.CS8 | termios.CREAD | termios.PARENB)
136
termios.tcsetattr(self.file, termios.TCSANOW, attrs)
138
termios.tcflush(self.file, termios.TCIOFLUSH)
142
def on_pppd_stdout(self, source, cond):
143
if cond & GObject.IO_ERR or cond & GObject.IO_HUP:
146
line = source.readline()
147
m = re.match("Using interface (ppp[0-9]*)", line)
149
self.interface = m.groups(1)[0]
155
def check_pppd(self):
156
status = self.pppd.poll()
157
if status is not None:
159
self.emit("connected", self.interface)
162
msg = "pppd exited: " + pppd_errors[int(status)]
164
msg = "pppd exited with unknown error"
166
self.emit("error-occurred", msg)
168
print("pppd exited with status %d" % status)
172
def send_command(self, command):
173
dprint("-->", command)
174
os.write(self.file, "%s\r\n" % command)
175
termios.tcdrain(self.file)
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"))
183
self.buffer += os.read(self.file, 1)
185
if e.errno == errno.EAGAIN:
189
on_done(None, PPPException("Socket error"))
194
lines = self.buffer.split("\r\n")
200
for t in terminators:
205
lines = filter(lambda x: x != "", lines)
206
lines = map(lambda x: x.strip("\r\n"), lines)
207
dprint("<-- ", lines)
214
def wait_for_reply(self, callback, terminators=["OK", "ERROR"], *user_data):
216
GObject.source_remove(self.io_watch)
217
callback(None, PPPException("Modem initialization timed out"), *user_data)
221
def on_done(ret, exception):
222
GObject.source_remove(self.timeout)
223
callback(ret, exception, *user_data)
227
self.term_found = False
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)