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 "-m gain modem lock imidiately '"+\
37 "For USSD menu navigation divide USSD number via spacebars for every next menu selection. Type exit in interactive mode to exit."
44 lockf = open("/tmp/ussdquery.lock", 'a')
45 fcntl.flock(lockf,fcntl.LOCK_EX)
47 os.chmod("/tmp/ussdquery.lock", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
53 fcntl.flock(lockf,fcntl.LOCK_UN)
56 def init_modem(modem):
57 # We have only one modem, simultaneous acces wouldn't bring anything good
61 while response != "OK" and init_retry > 0 :
63 # OK response should be recieved shortly
64 modem = pexpect.spawn('pnatd', [], 2)
67 # Read our "at" command
70 response = modem.readline().strip()
71 except pexpect.TIMEOUT:
80 # Switch output encoding to GSM default encoding
81 modem.send('at+cscs="GSM"\r');
85 response = modem.readline().strip()
86 except pexpect.TIMEOUT:
96 print >> sys.stderr, "Couldn't init modem."
102 modem.timeout = timeout
105 def close_modem (modem):
111 allow_last_error = True
116 if sys.argv[1] == "interactive":
117 number = "interactive"
119 number = sys.argv[1].split(" ")
121 if not check_number(n):
122 print >> sys.stderr, "Syntax error in USSD number."
127 # Parsing command line options
130 while arg < len(sys.argv)-1:
133 if sys.argv[arg] == "-l":
136 if sys.argv[arg] == "-r":
139 if sys.argv[arg] == "-t":
142 if sys.argv[arg] == "-d":
145 if sys.argv[arg] == "-f":
146 allow_last_error = False
148 if sys.argv[arg] == "-m":
149 modem = init_modem(modem)
153 if sys.argv[arg] == "German":
155 elif sys.argv[arg] == "English":
157 elif sys.argv[arg] == "Italian":
159 elif sys.argv[arg] == "French":
161 elif sys.argv[arg] == "Spanish":
163 elif sys.argv[arg] == "Dutch":
165 elif sys.argv[arg] == "Swedish":
167 elif sys.argv[arg] == "Danish":
168 lang std::cerr << reply.data();
170 elif sys.argv[arg] == "Portuguese":
172 elif sys.argv[arg] == "Finnish":
174 elif sys.argv[arg] == "Norwegian":
176 elif sys.argv[arg] == "Greek":
178 elif sys.argv[arg] == "Turkish":
180 elif sys.argv[arg] == "Reserved1":
182 elif sys.argv[arg] == "Reserved2":
184 elif sys.argv[arg] == "Unspecified":
187 print >> sys.stderr, "Language unknown, falling back to unspecified."
192 if number == "interactive":
193 delimiter = sys.argv[arg]
195 print >> sys.stderr, "Delimiter is only supported in interactive mode."
200 if number == "interactive":
201 print >> sys.stderr, "Retry is only supported in normal mode."
204 retry = int(sys.argv[arg])
206 print >> sys.stderr, "Number of allowed errors must be >= -1. -1 assumed."
209 print >> sys.stderr, "Retry must be an integer."
213 if state == "timeout":
215 timeout = int(sys.argv[arg])
217 print >> sys.stderr, "Timeout must be an integer."
222 print >> sys.stderr, "Unrecogmized argument: "+sys.argv[arg]
227 retry_forever = False
229 # Now we are ready to send commands
232 if number == "interactive":
233 sys.stdout.write(delimiter)
235 while number == "interactive" or stage < len(number):
236 if number == "interactive":
237 cnumber = sys.stdin.readline().strip()
238 if cnumber == "exit":
242 if not check_number (cnumber):
243 sys.stdout.write ("Syntax error in USSD number"+delimiter)
247 cnumber = number[stage]
250 modem = init_modem(modem)
252 if retry == -1 and not retry_forever:
253 print >> sys.stderr, "Retry limit is over. Giving up."
257 modem.send('at+cusd=1,"'+cnumber+'",'+str(language)+'\r')
258 # Read our query echoed back
261 #Read and parse reply
262 replystring = modem.readline().decode('string_escape')
263 # This will read out unneeded info from modem
266 except pexpect.TIMEOUT:
267 print >> sys.stderr, "Timeout. Modem didn't reply."
271 if replystring.strip() == "ERROR" :
273 print >> sys.stderr, "Modem returned ERROR. Query not executed."
277 reresult = re.match("(?s)^\\+CUSD: (\\d+),\"(.*)\",(\\d+)$", replystring.strip())
279 # 0 no further user action required (network initiated USSD-Notify, or no further information needed after mobile initiated operation)
280 # 1 further user action required (network initiated USSD-Request, or further information needed after mobile initiated operation)
281 # 2 USSD terminated by network
282 # 3 other local client has responded
283 # 4 operation not supported
285 reply_status=reresult.group(1)
286 reply = reresult.group(2)
287 # See GSM 07.07 and GSM 03.38
288 encoding = reresult.group(3)
291 print >> sys.stderr, "Couldn't parse modem answer: "+replystring
294 if reply_status == 0:
295 # May be somebody else needs it
298 elif reply_status == 2:
299 print >> sys.stderr, "USSD terminated by network."
300 elif reply_status == 3:
301 print >> sys.stderr, "Error: other local client has responded."
302 elif reply_status == 4:
303 print >> sys.stderr, "Operation not supported."
304 elif reply_status == 5:
305 print >> sys.stderr, "Network time out."
308 reply = gsmdecode.decode(reply, int(encoding))
310 if number == "interactive":
312 sys.stdout.write(reply+delimiter)
315 if stage == len(number)-1:
318 if not allow_last_error and namber != "interactive" and stage == len(number) - 1: