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 *
19 # Needed for correct output of utf-8 symbols.
20 sys.stdout=file("/dev/stdout", "wb")
22 def check_number(number):
26 if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
30 if len(sys.argv) == 1:
31 print "Usage:\nussdquery.py <ussd number> [options]\nussdquery.py interactive [options]\n"+\
32 "Options:\n-l language. Allowed languages: German, English, Italian, French, Spanish, Dutch, Swedish, Danish, Portuguese, Finnish, Norwegian, Greek, Turkish, Reserved1, Reserved2, Unspecified\n"+\
33 "-r retry count. 0 default. Use -1 for infinite.\n-f If specified, errors, which occur on last query are threated as fatal\n"+\
34 "-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"+\
35 "-d delimeter. Default is '\\n> '"+\
36 "For USSD menu navigation divide USSD number via spacebars for every next menu selection. Type exit in interactive mode to exit."
43 lockf = open("/tmp/ussdquery.lock", 'a')
44 fcntl.flock(lockf,fcntl.LOCK_EX)
46 os.chmod("/tmp/ussdquery.lock", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
52 fcntl.flock(lockf,fcntl.LOCK_UN)
55 def init_modem(modem):
56 # We have only one modem, simultaneous acces wouldn't bring anything good
60 while response != "OK" and init_retry > 0 :
62 # OK response should be recieved shortly
63 modem = pexpect.spawn('pnatd', [], 2)
66 # Read our "at" command
69 response = modem.readline().strip()
70 except pexpect.TIMEOUT:
79 # Switch output encoding to GSM default encoding
80 modem.send('at+cscs="GSM"\r');
84 response = modem.readline().strip()
85 except pexpect.TIMEOUT:
95 print >> sys.stderr, "Couldn't init modem."
101 modem.timeout = timeout
104 def close_modem (modem):
110 allow_last_error = True
115 if sys.argv[1] == "interactive":
116 number = "interactive"
118 number = sys.argv[1].split(" ")
120 if not check_number(n):
121 print >> sys.stderr, "Syntax error in USSD number."
124 # Parsing command line options
127 while arg < len(sys.argv)-1:
130 if sys.argv[arg] == "-l":
133 if sys.argv[arg] == "-r":
136 if sys.argv[arg] == "-t":
139 if sys.argv[arg] == "-d":
142 if sys.argv[arg] == "-f":
143 allow_last_error = False
147 if sys.argv[arg] == "German":
149 elif sys.argv[arg] == "English":
151 elif sys.argv[arg] == "Italian":
153 elif sys.argv[arg] == "French":
155 elif sys.argv[arg] == "Spanish":
157 elif sys.argv[arg] == "Dutch":
159 elif sys.argv[arg] == "Swedish":
161 elif sys.argv[arg] == "Danish":
163 elif sys.argv[arg] == "Portuguese":
165 elif sys.argv[arg] == "Finnish":
167 elif sys.argv[arg] == "Norwegian":
169 elif sys.argv[arg] == "Greek":
171 elif sys.argv[arg] == "Turkish":
173 elif sys.argv[arg] == "Reserved1":
175 elif sys.argv[arg] == "Reserved2":
177 elif sys.argv[arg] == "Unspecified":
180 print >> sys.stderr, "Language unknown, falling back to unspecified."
185 if number == "interactive":
186 delimiter = sys.argv[arg]
188 print >> sys.stderr, "Delimiter is only supported in interactive mode."
193 if number == "interactive":
194 print >> sys.stderr, "Retry is only supported in normal mode."
197 retry = int(sys.argv[arg])
199 print >> sys.stderr, "Number of allowed errors must be >= -1. -1 assumed."
202 print >> sys.stderr, "Retry must be an integer."
206 if state == "timeout":
208 timeout = int(sys.argv[arg])
210 print >> sys.stderr, "Timeout must be an integer."
215 print >> sys.stderr, "Unrecogmized argument: "+sys.argv[arg]
220 retry_forever = False
224 # Now we are ready to send commands
227 if number == "interactive":
228 sys.stdout.write(delimiter)
230 while number == "interactive" or stage < len(number):
232 modem = init_modem(modem)
234 if number == "interactive":
235 cnumber = sys.stdin.readline().strip()
236 if cnumber == "exit":
239 if not check_number (cnumber):
240 sys.stdout.write ("Syntax error in USSD number"+delimiter)
244 cnumber = number[stage]
246 if retry == -1 and not retry_forever:
247 print >> sys.stderr, "Retry limit is over. Giving up."
251 modem.send('at+cusd=1,"'+cnumber+'",'+str(language)+'\r')
252 # Read our query echoed back
255 #Read and parse reply
256 replystring = modem.readline().decode('string_escape')
257 # This will read out unneeded info from modem
260 except pexpect.TIMEOUT:
261 print >> sys.stderr, "Timeout. Modem didn't reply."
265 if replystring.strip() == "ERROR" :
267 print >> sys.stderr, "Modem returned ERROR. Query not executed."
271 reresult = re.match("(?s)^\\+CUSD: (\\d+),\"(.*)\",(\\d+)$", replystring.strip())
273 # 0 no further user action required (network initiated USSD-Notify, or no further information needed after mobile initiated operation)
274 # 1 further user action required (network initiated USSD-Request, or further information needed after mobile initiated operation)
275 # 2 USSD terminated by network
276 # 3 other local client has responded
277 # 4 operation not supported
279 reply_status=reresult.group(1)
280 reply = reresult.group(2)
281 # See GSM 07.07 and GSM 03.38
282 encoding = reresult.group(3)
285 print >> sys.stderr, "Couldn't parse modem answer: "+replystring
288 if reply_status == 0:
289 # May be somebody else needs it
292 elif reply_status == 2:
293 print >> sys.stderr, "USSD terminated by network."
294 elif reply_status == 3:
295 print >> sys.stderr, "Error: other local client has responded."
296 elif reply_status == 4:
297 print >> sys.stderr, "Operation not supported."
298 elif reply_status == 5:
299 print >> sys.stderr, "Network time out."
302 reply = gsmdecode.decode(reply, int(encoding))
304 if number == "interactive":
306 sys.stdout.write(reply+delimiter)
309 if stage == len(number)-1:
312 if not allow_last_error and namber != "interactive" and stage == len(number) - 1: