USSD menu navigation implementation
[ussd-widget] / ussd-common / src / usr / bin / ussdquery.py
index 1798ae7..e414752 100755 (executable)
@@ -13,58 +13,150 @@ import sys
 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)
@@ -80,7 +172,7 @@ while response != "OK" and retry > 0 :
                response = ""
        if response != "OK" :
                time.sleep(0.5)
-               retry -= 1
+               init_retry -= 1
 
 if response != "OK" :
        print >> sys.stderr, "Couldn't init modem."
@@ -90,42 +182,74 @@ if response != "OK" :
         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()