2 # -*- coding: utf-8 -*-
3 ## This program is free software; you can redistribute it and/or modify
4 ## it under the terms of the GNU General Public License as published
5 ## by the Free Software Foundation; version 2 and higer.
7 ## Guseynov Alexey (kibergus bark-bark gmail.com) 2010
11 from subprocess import *
21 # Needed for correct output of utf-8 symbols.
22 sys.stdout=file("/dev/stdout", "wb")
24 def check_number(number):
28 if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
32 if len(sys.argv) == 1:
33 print "Usage:\nussdquery.py <ussd number> [options]\nussdquery.py interactive [options]\n"+\
34 "Options:\n-l language. Allowed languages: German, English, Italian, French, Spanish, Dutch, Swedish, Danish, Portuguese, Finnish, Norwegian, Greek, Turkish, Reserved1, Reserved2, Unspecified\n"+\
35 "-r retry count. 0 default. Use -1 for infinite.\n-f If specified, errors, which occur on last query are threated as fatal\n"+\
36 "-t timeout in seconds. Default 30. Timeout is considered to be critical error because you can't be sure answer for what request was returned.\n"+\
37 "-d delimeter. Default is '\\n> \n'"+\
38 "-m gain modem lock imidiately '"+\
39 "-s show GUI message box on last reply"+\
40 "For USSD menu navigation divide USSD number via spacebars for every next menu selection. Type exit in interactive mode to exit."
47 lockf = open("/tmp/ussdquery.lock", 'a')
48 fcntl.flock(lockf,fcntl.LOCK_EX)
50 os.chmod("/tmp/ussdquery.lock", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
56 fcntl.flock(lockf,fcntl.LOCK_UN)
59 def init_modem(modem):
60 # We have only one modem, simultaneous acces wouldn't bring anything good
64 while response != "OK" and init_retry > 0 :
66 # OK response should be recieved shortly
67 modem = pexpect.spawn('pnatd', [], 2)
70 # Read our "at" command
73 response = modem.readline().strip()
74 except pexpect.TIMEOUT:
83 # Switch output encoding to GSM default encoding
84 modem.send('at+cscs="GSM"\r');
88 response = modem.readline().strip()
89 except pexpect.TIMEOUT:
99 print >> sys.stderr, "Couldn't init modem."
105 modem.timeout = timeout
108 def close_modem (modem):
114 allow_last_error = True
120 if sys.argv[1] == "interactive":
121 number = "interactive"
123 number = sys.argv[1].split(" ")
125 if not check_number(n):
126 print >> sys.stderr, "Syntax error in USSD number."
131 # Parsing command line options
134 while arg < len(sys.argv)-1:
137 if sys.argv[arg] == "-l":
140 if sys.argv[arg] == "-r":
143 if sys.argv[arg] == "-t":
146 if sys.argv[arg] == "-d":
149 if sys.argv[arg] == "-f":
150 allow_last_error = False
152 if sys.argv[arg] == "-s":
155 if sys.argv[arg] == "-m":
156 modem = init_modem(modem)
160 if sys.argv[arg] == "German":
162 elif sys.argv[arg] == "English":
164 elif sys.argv[arg] == "Italian":
166 elif sys.argv[arg] == "French":
168 elif sys.argv[arg] == "Spanish":
170 elif sys.argv[arg] == "Dutch":
172 elif sys.argv[arg] == "Swedish":
174 elif sys.argv[arg] == "Danish":
176 elif sys.argv[arg] == "Portuguese":
178 elif sys.argv[arg] == "Finnish":
180 elif sys.argv[arg] == "Norwegian":
182 elif sys.argv[arg] == "Greek":
184 elif sys.argv[arg] == "Turkish":
186 elif sys.argv[arg] == "Reserved1":
188 elif sys.argv[arg] == "Reserved2":
190 elif sys.argv[arg] == "Unspecified":
193 print >> sys.stderr, "Language unknown, falling back to unspecified."
198 if number == "interactive":
199 delimiter = sys.argv[arg]
201 print >> sys.stderr, "Delimiter is only supported in interactive mode."
206 if number == "interactive":
207 print >> sys.stderr, "Retry is only supported in normal mode."
210 retry = int(sys.argv[arg])
212 print >> sys.stderr, "Number of allowed errors must be >= -1. -1 assumed."
215 print >> sys.stderr, "Retry must be an integer."
219 if state == "timeout":
221 timeout = int(sys.argv[arg])
223 print >> sys.stderr, "Timeout must be an integer."
228 print >> sys.stderr, "Unrecogmized argument: "+sys.argv[arg]
233 retry_forever = False
235 bus = dbus.SystemBus()
236 ussdd = bus.get_object("su.kibergus.ussdd", "/su/kibergus/ussdd")
237 ussdd_int = dbus.Interface(ussdd, "su.kibergus.ussdd")
239 # Now we are ready to send commands
242 if number == "interactive":
243 sys.stdout.write(delimiter)
245 while number == "interactive" or stage < len(number):
246 if number == "interactive":
247 cnumber = sys.stdin.readline().strip()
248 if cnumber == "exit":
252 if not check_number (cnumber):
253 sys.stdout.write ("Syntax error in USSD number"+delimiter)
257 cnumber = number[stage]
260 modem = init_modem(modem)
262 if retry == -1 and not retry_forever:
263 print >> sys.stderr, "Retry limit is over. Giving up."
267 if number != "interactive" and not show_qussd or stage != len(number)-1:
268 ussdd_int.skip_next()
270 modem.send('at+cusd=1,"'+cnumber+'",'+str(language)+'\r')
271 # Read our query echoed back
274 #Read and parse reply
275 replystring = modem.readline().decode('string_escape')
276 # This will read out unneeded info from modem
279 except pexpect.TIMEOUT:
280 print >> sys.stderr, "Timeout. Modem didn't reply."
282 ussdd_int.show_next()
285 if replystring.strip() == "ERROR" :
287 print >> sys.stderr, "Modem returned ERROR. Query not executed."
288 ussdd_int.show_next()
292 reresult = re.match("(?s)^\\+CUSD: (\\d+),\"(.*)\",(\\d+)$", replystring.strip())
294 # 0 no further user action required (network initiated USSD-Notify, or no further information needed after mobile initiated operation)
295 # 1 further user action required (network initiated USSD-Request, or further information needed after mobile initiated operation)
296 # 2 USSD terminated by network
297 # 3 other local client has responded
298 # 4 operation not supported
300 reply_status=reresult.group(1)
301 reply = reresult.group(2)
302 # See GSM 07.07 and GSM 03.38
303 encoding = reresult.group(3)
306 print >> sys.stderr, "Couldn't parse modem answer: "+replystring
309 if reply_status == 0:
310 # May be somebody else needs it
313 elif reply_status == 2:
314 print >> sys.stderr, "USSD terminated by network."
315 elif reply_status == 3:
316 print >> sys.stderr, "Error: other local client has responded."
317 elif reply_status == 4:
318 print >> sys.stderr, "Operation not supported."
319 elif reply_status == 5:
320 print >> sys.stderr, "Network time out."
323 reply = gsmdecode.decode(reply, int(encoding))
325 if number == "interactive":
327 sys.stdout.write(reply+delimiter)
330 if stage == len(number)-1:
333 if not allow_last_error and namber != "interactive" and stage == len(number) - 1: