import gsmdecode
import re
import fcntl
-
-if len(sys.argv) != 2 and len(sys.argv) != 3:
- print "Usage: ussdquery.py <ussd number> [<language>]\nAllowed languages: German, English, Italian, French, Spanish, Dutch, Swedish, Danish, Portuguese, Finnish, Norwegian, Greek, Turkish, Reserved1, Reserved2, Unspecified"
+import os
+import stat
+
+def check_number(number):
+ if number == "":
+ return False
+ for s in number :
+ if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
+ return False
+ return True
+
+if len(sys.argv) == 1:
+ print "Usage:\nussdquery.py <ussd number> [options]\nussdquery.py interactive [options]\n"+\
+"Options:\n-l language. Allowed languages: German, English, Italian, French, Spanish, Dutch, Swedish, Danish, Portuguese, Finnish, Norwegian, Greek, Turkish, Reserved1, Reserved2, Unspecified\n"+\
+"-r retry count. 0 default. Use -1 for infinite.\n-f If specified, errors, which occur on last query are threated as fatal\n"+\
+"-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"+\
+"-d delimeter. Default is '\\n> '"+\
+"For USSD menu navigation divide USSD number via spacebars for every nex menu selection. Type exit in interactive mode to exit."
sys.exit()
+retry = 0
+allow_last_error = True
+delimiter = "\n> "
language = 15
-if len(sys.argv) == 3:
- if sys.argv[2] == "German":
- language = 0
- elif sys.argv[2] == "English":
- language = 1
- elif sys.argv[2] == "Italian":
- language = 2
- elif sys.argv[2] == "French":
- language = 3
- elif sys.argv[2] == "Spanish":
- language = 4
- elif sys.argv[2] == "Dutch":
- language = 5
- elif sys.argv[2] == "Swedish":
- language = 6
- elif sys.argv[2] == "Danish":
- language = 7
- elif sys.argv[2] == "Portuguese":
- language = 8
- elif sys.argv[2] == "Finnish":
- language = 9
- elif sys.argv[2] == "Norwegian":
- language = 10
- elif sys.argv[2] == "Greek":
- language = 11
- elif sys.argv[2] == "Turkish":
- language = 12
- elif sys.argv[2] == "Reserved1":
- language = 13
- elif sys.argv[2] == "Reserved2":
- language = 14
- elif sys.argv[2] == "Unspecified":
- language = 15
- else:
- print >> sys.stderr, "Language unknown, falling back to unspecified."
+timeout = 30
+
+if sys.argv[1] == "interactive":
+ number = "interactive"
+else:
+ number = sys.argv[1].split(" ")
+ for n in number:
+ if not check_number(n):
+ print >> sys.stderr, "Sintax error in USSD number."
+ sys.exit(-7)
+
+# Parsing command line options
+arg = 1
+state = "arg"
+while arg < len(sys.argv)-1:
+ arg += 1
+ if state == "arg":
+ if sys.argv[arg] == "-l":
+ state = "lang"
+ continue
+ if sys.argv[arg] == "-r":
+ state = "retry"
+ continue
+ if sys.argv[arg] == "-t":
+ state = "timeout"
+ continue
+ if sys.argv[arg] == "-d":
+ state = "delim"
+ continue
+ if sys.argv[arg] == "-f":
+ allow_last_error = False
+ continue
+
+ if state == "lang":
+ if sys.argv[arg] == "German":
+ language = 0
+ elif sys.argv[arg] == "English":
+ language = 1
+ elif sys.argv[arg] == "Italian":
+ language = 2
+ elif sys.argv[arg] == "French":
+ language = 3
+ elif sys.argv[arg] == "Spanish":
+ language = 4
+ elif sys.argv[arg] == "Dutch":
+ language = 5
+ elif sys.argv[arg] == "Swedish":
+ language = 6
+ elif sys.argv[arg] == "Danish":
+ language = 7
+ elif sys.argv[arg] == "Portuguese":
+ language = 8
+ elif sys.argv[arg] == "Finnish":
+ language = 9
+ elif sys.argv[arg] == "Norwegian":
+ language = 10
+ elif sys.argv[arg] == "Greek":
+ language = 11
+ elif sys.argv[arg] == "Turkish":
+ language = 12
+ elif sys.argv[arg] == "Reserved1":
+ language = 13
+ elif sys.argv[arg] == "Reserved2":
+ language = 14
+ elif sys.argv[arg] == "Unspecified":
+ language = 15
+ else:
+ print >> sys.stderr, "Language unknown, falling back to unspecified."
+ state = "arg"
+ continue
+
+ if state == "delim":
+ if number == "interactive":
+ delimiter = sys.argv[arg]
+ else:
+ print >> sys.stderr, "Delimiter is only supported in interactive mode."
+ state = "arg"
+ continue
+
+ if state == "retry":
+ if number == "interactive":
+ print >> sys.stderr, "Retry is only supported in normal mode."
+ else:
+ try:
+ retry = int(sys.argv[arg])
+ if retry < -1:
+ print >> sys.stderr, "Number of allowed errors must be >= -1. -1 assumed."
+ retry = -1
+ except:
+ print >> sys.stderr, "Retry must be an integer."
+ sys.exit(-5)
+ state = "arg"
+ continue
+ if state == "timeout":
+ try:
+ timeout = int(sys.argv[arg])
+ except:
+ print >> sys.stderr, "Timeout must be an integer."
+ sys.exit(-5)
+ state = "arg"
+ continue
+
+ print >> sys.stderr, "Unrecogmized argument: "+sys.argv[arg]
+
+if retry == -1:
+ retry_forever = True
+else:
+ retry_forever = False
# We have only one modem, simultaneous acces wouldn't bring anything good
lockf = open("/tmp/ussdquery.lock", 'a')
fcntl.flock(lockf,fcntl.LOCK_EX)
-
-# Operations should timeout in 30 seconds.
-# I'm not shure, that readline uses timeouts
+try:
+ os.chmod("/tmp/ussdquery.lock", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)
+except:
+ None
child = None
response = ""
-retry = 5
-while response != "OK" and retry > 0 :
+init_retry = 5
+while response != "OK" and init_retry > 0 :
if child == None :
# OK response should be recieved shortly
child = pexpect.spawn('pnatd', [], 2)
response = ""
if response != "OK" :
time.sleep(0.5)
- retry -= 1
+ init_retry -= 1
if response != "OK" :
print >> sys.stderr, "Couldn't init modem."
lockf.close()
sys.exit (-1)
-child.timeout = 30
-
-try :
- child.send('at+cusd=1,"'+(sys.argv[1])+'",'+str(language)+'\r')
- # Read our query echoed back
- child.readline()
-
- #Read and parse reply
- replystring = child.readline().decode('string_escape')
-except pexpect.TIMEOUT:
- print >> sys.stderr, "Timeout. Modem didn't reply."
- child.kill(9)
- fcntl.flock(lockf,fcntl.LOCK_UN)
- lockf.close()
- sys.exit (-2)
-
-child.sendeof()
-fcntl.flock(lockf,fcntl.LOCK_UN)
-lockf.close()
+child.timeout = timeout
+# Now we are ready to send commands
+
+stage = 0
+if number == "interactive":
+ sys.stdout.write(delimiter)
+while number == "interactive" or stage < len(number):
+ if number == "interactive":
+ cnumber = sys.stdin.readline().strip()
+ if cnumber == "exit":
+ child.kill(9)
+ fcntl.flock(lockf,fcntl.LOCK_UN)
+ lockf.close()
+ sys.exit (-2)
+ if not check_number (cnumber):
+ sys.stdout.write ("Sintax error in USSD number"+delimiter)
+ continue
+ else:
+ cnumber = number[stage]
-#replystring = "+CUSD: 0,\"OCTATOK 165.65 p.\n 3BOHu 333! HOBbIu PA3DEL: ХuT-nAPAD MY3bIKu (3p. MuHYTA)\",1"
+ if retry == -1 and not retry_forever:
+ print >> sys.stderr, "Retry limit is over. Giving up."
+ break
-if replystring.strip() == "ERROR" :
- print >> sys.stderr, "Modem returned ERROR. Query not executed."
- sys.exit (-3)
+ try :
+ child.send('at+cusd=1,"'+cnumber+'",'+str(language)+'\r')
+ # Read our query echoed back
+ child.readline()
+
+ #Read and parse reply
+ replystring = child.readline().decode('string_escape')
+ # This will read out unneeded info from modem
+ child.readline()
+ child.readline()
+ except pexpect.TIMEOUT:
+ print >> sys.stderr, "Timeout. Modem didn't reply."
+ child.kill(9)
+ fcntl.flock(lockf,fcntl.LOCK_UN)
+ lockf.close()
+ sys.exit (-2)
-try:
- reresult = re.match("(?s)^\\+CUSD: \\d+,\"(.*)\",(\\d+)$", replystring.strip())
- reply = reresult.group(1)
- encoding = reresult.group(2)
-except:
- print >> sys.stderr, "Couldn't parse modem answer."
- sys.exit (-4)
+ if replystring.strip() == "ERROR" :
+ retry -= 1
+ print >> sys.stderr, "Modem returned ERROR. Query not executed."
+ continue
+
+ try:
+ reresult = re.match("(?s)^\\+CUSD: \\d+,\"(.*)\",(\\d+)$", replystring.strip())
+ reply = reresult.group(1)
+ encoding = reresult.group(2)
+ except:
+ retry -= 1
+ print >> sys.stderr, "Couldn't parse modem answer."
+ continue
-# Decoding ansver
-reply = gsmdecode.decode(reply, int(encoding))
+ # Decoding ansver
+ reply = gsmdecode.decode(reply, int(encoding))
-print reply
+ if number == "interactive":
+ # prints line feed
+ sys.stdout.write(reply+delimiter)
+ else:
+ if stage == len(number)-1:
+ print reply
+ stage += 1
+ if not allow_last_error and namber != "interactive" and stage == len(number) - 1:
+ retry = 0
+child.sendeof()
+fcntl.flock(lockf,fcntl.LOCK_UN)
+lockf.close()