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 def check_number(number):
23 if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
27 if len(sys.argv) == 1:
28 print "Usage:\nussdquery.py <ussd number> [options]\nussdquery.py interactive [options]\n"+\
29 "Options:\n-l language. Allowed languages: German, English, Italian, French, Spanish, Dutch, Swedish, Danish, Portuguese, Finnish, Norwegian, Greek, Turkish, Reserved1, Reserved2, Unspecified\n"+\
30 "-r retry count. 0 default. Use -1 for infinite.\n-f If specified, errors, which occur on last query are threated as fatal\n"+\
31 "-t timeout in seconds. Default 30. Timeout is considered to be critical error because you can't be shure answer for what request was returned.\n"+\
32 "-d delimeter. Default is '\\n> '"+\
33 "For USSD menu navigation divide USSD number via spacebars for every nex menu selection. Type exit in interactive mode to exit."
37 allow_last_error = True
42 if sys.argv[1] == "interactive":
43 number = "interactive"
45 number = sys.argv[1].split(" ")
47 if not check_number(n):
48 print >> sys.stderr, "Sintax error in USSD number."
51 # Parsing command line options
54 while arg < len(sys.argv)-1:
57 if sys.argv[arg] == "-l":
60 if sys.argv[arg] == "-r":
63 if sys.argv[arg] == "-t":
66 if sys.argv[arg] == "-d":
69 if sys.argv[arg] == "-f":
70 allow_last_error = False
74 if sys.argv[arg] == "German":
76 elif sys.argv[arg] == "English":
78 elif sys.argv[arg] == "Italian":
80 elif sys.argv[arg] == "French":
82 elif sys.argv[arg] == "Spanish":
84 elif sys.argv[arg] == "Dutch":
86 elif sys.argv[arg] == "Swedish":
88 elif sys.argv[arg] == "Danish":
90 elif sys.argv[arg] == "Portuguese":
92 elif sys.argv[arg] == "Finnish":
94 elif sys.argv[arg] == "Norwegian":
96 elif sys.argv[arg] == "Greek":
98 elif sys.argv[arg] == "Turkish":
100 elif sys.argv[arg] == "Reserved1":
102 elif sys.argv[arg] == "Reserved2":
104 elif sys.argv[arg] == "Unspecified":
107 print >> sys.stderr, "Language unknown, falling back to unspecified."
112 if number == "interactive":
113 delimiter = sys.argv[arg]
115 print >> sys.stderr, "Delimiter is only supported in interactive mode."
120 if number == "interactive":
121 print >> sys.stderr, "Retry is only supported in normal mode."
124 retry = int(sys.argv[arg])
126 print >> sys.stderr, "Number of allowed errors must be >= -1. -1 assumed."
129 print >> sys.stderr, "Retry must be an integer."
133 if state == "timeout":
135 timeout = int(sys.argv[arg])
137 print >> sys.stderr, "Timeout must be an integer."
142 print >> sys.stderr, "Unrecogmized argument: "+sys.argv[arg]
147 retry_forever = False
149 # We have only one modem, simultaneous acces wouldn't bring anything good
150 lockf = open("/tmp/ussdquery.lock", 'a')
151 fcntl.flock(lockf,fcntl.LOCK_EX)
153 os.chmod("/tmp/ussdquery.lock", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
159 while response != "OK" and init_retry > 0 :
161 # OK response should be recieved shortly
162 child = pexpect.spawn('pnatd', [], 2)
165 # Read our "at" command
168 response = child.readline().strip()
169 except pexpect.TIMEOUT:
173 if response != "OK" :
177 if response != "OK" :
178 print >> sys.stderr, "Couldn't init modem."
181 fcntl.flock(lockf,fcntl.LOCK_UN)
185 child.timeout = timeout
186 # Now we are ready to send commands
189 if number == "interactive":
190 sys.stdout.write(delimiter)
191 while number == "interactive" or stage < len(number):
192 if number == "interactive":
193 cnumber = sys.stdin.readline().strip()
194 if cnumber == "exit":
196 fcntl.flock(lockf,fcntl.LOCK_UN)
199 if not check_number (cnumber):
200 sys.stdout.write ("Sintax error in USSD number"+delimiter)
203 cnumber = number[stage]
205 if retry == -1 and not retry_forever:
206 print >> sys.stderr, "Retry limit is over. Giving up."
210 child.send('at+cusd=1,"'+cnumber+'",'+str(language)+'\r')
211 # Read our query echoed back
214 #Read and parse reply
215 replystring = child.readline().decode('string_escape')
216 # This will read out unneeded info from modem
219 except pexpect.TIMEOUT:
220 print >> sys.stderr, "Timeout. Modem didn't reply."
222 fcntl.flock(lockf,fcntl.LOCK_UN)
226 if replystring.strip() == "ERROR" :
228 print >> sys.stderr, "Modem returned ERROR. Query not executed."
232 reresult = re.match("(?s)^\\+CUSD: \\d+,\"(.*)\",(\\d+)$", replystring.strip())
233 reply = reresult.group(1)
234 encoding = reresult.group(2)
237 print >> sys.stderr, "Couldn't parse modem answer."
241 reply = gsmdecode.decode(reply, int(encoding))
243 if number == "interactive":
245 sys.stdout.write(reply+delimiter)
247 if stage == len(number)-1:
250 if not allow_last_error and namber != "interactive" and stage == len(number) - 1:
254 fcntl.flock(lockf,fcntl.LOCK_UN)