Russian localizetion updated
[ussd-widget] / ussd-widget / src / usr / lib / hildon-desktop / ussd-widget.py
index 2b1ab82..79c92d3 100755 (executable)
@@ -11,7 +11,10 @@ import time
 import re
 import gettext
 import fcntl
-from subprocess import *
+import dbus
+import subprocess
+import gsmdecode
+from dbus.mainloop.glib import DBusGMainLoop
 
 try :
        t = gettext.translation('ussd-widget', '/usr/share/locale')
@@ -32,12 +35,14 @@ ussd_languages_localized = [_("German"), _("English"), _("Italian"), _("French")
 class USSD_Controller:
        def __init__( self, widget ) :
                self.widget = widget
-               # number, parser, chain, interval, regexp, width, execute_at_start, retry pattern, font, name, language, show_message_box, message_box_parser, additional arguments, regexp group 
-               self.default_config = ["", "", "", 0, "", 0, True, [], pango.FontDescription("Nokia Sans 18"), _("Click to update"), 15, False, "", "", 1]
+               # number, parser, chain, interval, regexp, width, execute_at_start, retry pattern, font, name, language, show_message_box, message_box_parser, additional arguments, regexp group, use SMS listener, SMS number, SMS regexp, SMS timeout
+               self.default_config = ["", "", "", 0, "", 0, True, [], pango.FontDescription("Nokia Sans 18"), _("Click to update"), 15, False, "", "", 1, False, "", "", 60]
                self.config = self.default_config
                self.timeout_version = 0
                self.retry_version = 0
                self.retry_state = 0
+               self.sms_counter = 0
+               self.sms_reply = ""
 
        def save_config( self ) :
                configname = os.getenv("HOME")+"/.ussdWidget.conf"
@@ -87,7 +92,11 @@ class USSD_Controller:
                fconfig.writelines(["#Show banner\n", "show_box="+str(self.config[11]), "\n"])
                fconfig.writelines(["#USSD reply language\n", "language="+str(self.config[10]), "\n"])
                fconfig.writelines(["#Additional ussdquery.py arguments\n", "args="+self.config[13], "\n"])
-               fconfig.writelines(["#Regexp matching group\n", "reggroup="+self.config[14], "\n"])
+               fconfig.writelines(["#Regexp matching group\n", "reggroup="+str(self.config[14]), "\n"])
+               fconfig.writelines(["#Use SMS listener\n", "listen_sms="+str(self.config[15]), "\n"])
+               fconfig.writelines(["#Number,from which SMS should come\n", "sms_number="+self.config[16], "\n"])
+               fconfig.writelines(["#SMS RegExp pattern\n", "sms_regexp="+self.config[17], "\n"])
+               fconfig.writelines(["#SMS timeout\n", "sms_timeout="+str(self.config[18]), "\n"])
                fconfig.close()
 
                fcntl.flock(lockf,fcntl.LOCK_UN)
@@ -212,6 +221,22 @@ class USSD_Controller:
                                                error = True
                                                print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
                                                continue
+                               elif line[0] == "listen_sms" :
+                                       if line[1].strip() == "True" :
+                                               self.config[15] = True
+                                       else :
+                                               self.config[15] = False
+                               elif line[0] == "sms_number" :
+                                       self.config[16] = line[1].strip()
+                               elif line[0] == "sms_regexp" :
+                                       self.config[17] = line[1].strip()
+                               elif line[0] == "sms_timeout" :
+                                       try:
+                                               self.config[18] = int(line[1].strip())
+                                       except:
+                                               error = True
+                                               print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+                                               continue
                                else :
                                        error = True
                                        print _("Error reading config on line %(line)d. Unexpected variable: ")%{"line":i}+line[0]
@@ -248,6 +273,10 @@ class USSD_Controller:
                        if not check_number(dialog.ussdNumber.get_text()):
                                dialog.on_error_ussd_number()
                                continue
+               
+                       if not check_number(dialog.sms_number.get_text()):
+                               dialog.on_error_sms_number()
+                               continue
 
                        # Parse retry pattern
                        retry = dialog.retryEdit.get_text().strip()
@@ -284,7 +313,11 @@ class USSD_Controller:
                        dialog.show_box.get_active(),
                        dialog.b_parser.get_text(),
                        dialog.args.get_text(),
-                       dialog.reggroup.get_value()
+                       dialog.reggroup.get_value(),
+                       dialog.sms_listener.get_active(),
+                       dialog.sms_number.get_text(),
+                       dialog.sms_regexp.get_text(),
+                       dialog.sms_timeout.get_value() 
                ]
 
                widget.set_bg_color(dialog.bg_color)
@@ -303,6 +336,21 @@ class USSD_Controller:
                        self.widget.set_text(_("Click to update"))
                return
 
+       def handle_sms(self, pdumsg, msgcenter, message, sendernumber):
+               # Timeout was recieved first
+               if self.sms_ready:
+                       return
+
+               if self.config[16] == "" or self.config[16] == sendernumber:
+                       pdu = gsmdecode.decode_pdu (pdumsg)
+                       if pdu != None :
+                               self.sms_reply += pdu['user_data']
+                               if not pdu['part']:
+                                       if self.config[17] == "" or re.search( self.config[17], message, re.MULTILINE | re.UNICODE ):
+                                               self.sms_ready = True
+                                               self.sms_signal.remove()
+                                               self.process_reply()
+
        def callback_ussd_data( self, source, condition ):
                if condition == gobject.IO_IN or condition == gobject.IO_PRI :
                        data = source.read( )
@@ -315,6 +363,7 @@ class USSD_Controller:
 
                elif condition == gobject.IO_HUP or condition == gobject.IO_ERR :
                        self.cb_ready = 1
+                       self.ussd_ready = True
                        self.process_reply()
                        return False
 
@@ -329,7 +378,7 @@ class USSD_Controller:
        def call_external_script( self, ussd_code, language ):
                self.cb_ready = 0
                self.cb_reply = "";
-               p = Popen(['/usr/bin/ussdquery.py', ussd_code, "-l", ussd_languages[language]] + smart_split_string(self.config[13],"%"), stdout=PIPE)
+               p = subprocess.Popen(['/usr/bin/ussdquery.py', ussd_code, "-l", ussd_languages[language]] + smart_split_string(self.config[13],"%","&"), stdout=subprocess.PIPE)
                gobject.io_add_watch( p.stdout, gobject.IO_IN | gobject.IO_PRI | gobject.IO_HUP | gobject.IO_ERR , self.callback_ussd_data )
 
        def ussd_renew(self, widget, event):
@@ -337,6 +386,19 @@ class USSD_Controller:
                        if self.config :
                                widget.processing = 1
                                widget.set_text(_("Processing"), 0)
+
+                               self.ussd_ready = False
+                               self.sms_ready = False
+                               self.sms_reply = ""
+
+                               if self.config[15]:
+                                       self.sms_counter += 1
+                                       self.retry_timer = gobject.timeout_add (1000*self.config[18], self.sms_timeout, self.sms_counter)
+                                       
+                                       DBusGMainLoop(set_as_default=True)
+                                       self.bus = dbus.SystemBus()
+                                       self.sms_signal = self.bus.add_signal_receiver(self.handle_sms, path='/com/nokia/phone/SMS',   dbus_interface='Phone.SMS', signal_name='IncomingSegment')
+
                                self.call_external_script( self.config[0], self.config[10] )
                        else :
                                widget.processing = 0
@@ -344,9 +406,13 @@ class USSD_Controller:
                                widget.set_text(_("No config"), 0)
 
        def process_reply( self ):
+               if not self.ussd_ready or not self.sms_ready and self.config[15]:
+                       return
+
                reply = self.cb_reply.strip()
+               sms_reply = self.sms_reply.strip()
                
-               if reply == "" :
+               if reply == "" or self.config[15] and sms_reply == "" :
                        self.widget.error = 1
                        self.widget.set_text (_("Error"), 5000)
                        if self.retry_state == len(self.config[7]):
@@ -358,34 +424,48 @@ class USSD_Controller:
                else :
                        self.widget.error = 0
                        # Apply regexp
+                       reresult1 = reresult2 = None
                        if self.config[4] != "":
-                               try :
-                                       reply = re.search( self.config[4], reply, re.MULTILINE | re.UNICODE ).group( self.config[14] )
-                               except Exception, e:
-                                       self.widget.error = 1
-                                       reply = _("Regexp Error: ") + str( e ) + "\n" + reply
+                               reresult1 = re.search( self.config[4], reply, re.MULTILINE | re.UNICODE )
+                       if self.config[17] != "":
+                               reresult2 = re.search( self.config[17], sms_reply, re.MULTILINE | re.UNICODE )
                        w_reply = b_reply = reply
                        if self.widget.error == 0:
                                # Pass to box parser
                                if self.config[12] != "" and self.config[11]:
                                        try:
-                                               p = Popen(smart_split_string(self.config[12], reply), stdout=PIPE)
+                                               p = subprocess.Popen(smart_split_string(self.config[12], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
                                                b_reply = p.communicate()[0].strip()
                                        except Exception, e:
                                                print _("Couldn't exec banner parser:")+str(e)
                                                self.widget.error = 1
+                               else:
+                                       if self.config[4] != "":
+                                               try :
+                                                       b_reply = reresult1.group( self.config[14] )
+                                               except Exception, e:
+                                                       self.widget.error = 1
+                                                       b_reply = _("Group not found: \n") + reply
+                                        
                                # Pass to widget parser
                                if self.config[1] != "":
                                        try:
-                                               p = Popen(smart_split_string(self.config[1], reply), stdout=PIPE)
+                                               p = subprocess.Popen(smart_split_string(self.config[1], reply, sms_reply, reresult1, reresult2), stdout=subprocess.PIPE)
                                                w_reply = p.communicate()[0].strip()
                                        except Exception, e:
                                                print _("Couldn't exec widget parser:")+str(e)
                                                self.widget.error = 1
+                               else:
+                                       if self.config[4] != "":
+                                               try :
+                                                       w_reply = reresult1.group( self.config[14] )
+                                               except Exception, e:
+                                                       self.widget.error = 1
+                                                       w_reply = _("Group not found: \n") + reply
                                # Pass to chain
                                if self.config[2] != "":
                                        try:
-                                               p = Popen(smart_split_string(self.config[2], reply))
+                                               p = subprocess.Popen(smart_split_string(self.config[2], reply, sms_reply, reresult1, reresult2))
                                        except Exception, e:
                                                print _("Couldn't exec chain:")+str(e)
                                                self.widget.error = 1
@@ -396,6 +476,14 @@ class USSD_Controller:
                        self.widget.set_text(w_reply)
                self.widget.processing = 0
 
+       def sms_timeout(self, version):
+               if version == self.sms_counter :
+                       self.sms_reply = ""
+                       self.sms_ready = True
+                       self.sms_signal.remove()
+                       self.process_reply()
+               return False
+
        def timed_renew(self, version):
                if version < self.timeout_version :
                        return False
@@ -455,7 +543,7 @@ class UssdConfigDialog(gtk.Dialog):
                self.args.set_text(config[13])
                self.reggroup = hildon.NumberEditor(0, 255)
                self.reggroup.set_value(config[14])
-
+               
                selector = hildon.TouchSelector(text=True)
                for i in ussd_languages_localized:
                        selector.append_text(i)
@@ -468,6 +556,7 @@ class UssdConfigDialog(gtk.Dialog):
                self.wname = hildon.Entry(gtk.HILDON_SIZE_AUTO)
                self.wname.set_text(config[9])
                self.show_box = gtk.CheckButton(_("Enable banner. Parser:"))
+               self.show_box.connect("toggled", self.show_box_changed)
                self.show_box.set_active(config[11])
 
                text = ""
@@ -550,7 +639,7 @@ class UssdConfigDialog(gtk.Dialog):
                b_parserBox.add(bphelp)
                vbox.add(b_parserBox)
                vbox.add(self.b_parser)
-
+               
                chainBox = gtk.HBox()
                chainLabel = gtk.Label(_("Chain"))
                chainLabel.set_alignment(0,0.6)
@@ -623,26 +712,69 @@ class UssdConfigDialog(gtk.Dialog):
                viewBox.add(self.textColorButton)
                viewBox.add(self.colorButton)
                vbox.add(viewBox)
+       
+               self.sms_box = gtk.VBox()       
+               self.sms_listener = gtk.CheckButton(_("Enable SMS listener."))
+               self.sms_listener.connect("toggled", self.sms_box_changed)
+               self.sms_listener.set_active(config[15])
+               vbox.add (self.sms_listener)
+
+               self.sms_number = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.sms_number.set_text(config[16])
+               smsNumberBox = gtk.HBox()
+               smsNumberLabel = gtk.Label(_("SMS number"))
+               smsNumberLabel.set_alignment(0,0.6)
+               smsNumberLabel.set_size_request(100, -1)
+               self.sms_number.set_size_request(200, -1)
+               smsNumberBox.add(smsNumberLabel)
+               smsNumberBox.add(self.sms_number)
+               self.sms_box.add(smsNumberBox)
                
+               smsRegexpLabel = gtk.Label(_("Regular expression"))
+               smsRegexpLabel.set_alignment(0,0.6)
+               self.sms_box.add(smsRegexpLabel)
+               
+               self.sms_regexp = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+               self.sms_regexp.set_text(config[17])
+               self.sms_box.add(self.sms_regexp)
+
+               self.sms_timeout = hildon.NumberEditor(0, 9999)
+               self.sms_timeout.set_value(config[18])
+               sms_timeout_box = gtk.HBox()
+               timeoutLabel = gtk.Label(_("Timeout"))
+               timeoutLabel.set_alignment(0,0.6)
+               secondsLabel = gtk.Label(_("seconds"))
+               timeoutLabel.set_size_request(140, -1)
+               self.sms_timeout.set_size_request(50, -1)
+               secondsLabel.set_size_request(40, -1)
+               sms_timeout_box.add(timeoutLabel)
+               sms_timeout_box.add(self.sms_timeout)
+               sms_timeout_box.add(secondsLabel)
+               self.sms_box.add(sms_timeout_box)
+               
+               vbox.add(self.sms_box)
+
                vbox.add(gtk.Label(_("DO NOT CHANGE. Unspecified is what you want.")))
                vbox.add(self.language)
 
                self.show_all()
+               self.show_box_changed(None)
+               self.sms_box_changed(None)
                self.parent
 
        #============ Dialog helper functions =============
        def on_show_phelp(self, widget):
-               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on widget.\n       Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on widget.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
                dialog.run()
                dialog.destroy()
 
        def on_show_bphelp(self, widget):
-               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on banner.\n       Format:\n% would be replaced by reply\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility, output of utility would be shown to you on banner.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n      Hint: use echo \"Your string %\" to prepend your string to reply."))
                dialog.run()
                dialog.destroy()
        
        def on_show_chelp(self, widget):
-               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility after parser utility. May be used for logging, statistics etc.\n       Format:\n% would be replaced by reply\n\\ invalidates special meaning of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility"))
+               dialog = pHelpDialog(_("Format help"), _("Reply would be passed to specified utility after parser utility. May be used for logging, statistics etc.\n       Format:\n% would be replaced by reply\n%N with N'th regexp matching group\n& would be replaced with sms content\n&N with N'th sms regexp group\n\\ invalidates special meaming of following symbol\n\" and ' work as usual\nspace delimits command line parameters of utility\n"))
                dialog.run()
                dialog.destroy()
 
@@ -701,36 +833,85 @@ class UssdConfigDialog(gtk.Dialog):
                self.font = pango.FontDescription (fontDialog.get_font_name())
                fontDialog.destroy()
 
-def smart_split_string (str, query) :
+       def show_box_changed (self, event):
+               if self.show_box.get_active():
+                       self.b_parser.show()
+               else:
+                       self.b_parser.hide()
+
+       def sms_box_changed (self, event):
+               if self.sms_listener.get_active():
+                       self.sms_box.show()
+               else:
+                       self.sms_box.hide()
+
+def smart_split_string (str, reply1, reply2, reres1 = None, reres2 = None) :
        word = ""
        result = []
        # Is simbol backslashed?
        bs = 0
        # Quotes: 1 - ", 2 - ', 0 - no quotes
        qs = 0
+       # Read out number
+       num = -1
+       # Current substitution simbol
+       subst = ''
+
        for i in range(len(str)) :
-         if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
-                 qs = 0
-         elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
-                 if str[i] == '"':
-                         qs = 1
-                 else :
-                         qs = 2
-         elif bs == 0 and str[i] == '\\' :
-                 bs = 1
-         elif bs == 0 and str[i] == '%' :
-                 word += query
-                 ws = 0
-         else :
-                 if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
-                         word += "\\"
-                 if qs == 0 and (str[i] == " " or str[i] == "\t") :
-                         if word != "" :
-                                 result.append(word)
-                                 word = ""
-                 else :
-                         word += str[i]
-                 bs = 0 
+               if num>= 0:
+                       if str[i] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
+                               num *= 10
+                               num += int(str[i])
+                               continue
+                       else:
+                               if subst == '&':
+                                       if reres2 != None and num != 0:
+                                               word += reres2.group(num)
+                                       else:
+                                               word += reply2
+                               else:
+                                       if reres1 != None and num != 0:
+                                               word += reres1.group(num)
+                                       else:
+                                               word += reply1
+                               ws = 0
+                               num = -1
+                               # Delete backslash if it delimites usual numbers from % or &
+                               if str[i] == '\\' and i < len(str)-1 and str[i+1] in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] :
+                                       continue
+               if bs == 0 and (str[i] == '"' and qs == 1 or str[i] == "'" and qs == 2) :
+                       qs = 0
+               elif bs == 0 and qs == 0 and (str[i] == '"' or str[i] == "'") :
+                       if str[i] == '"':
+                               qs = 1
+                       else :
+                               qs = 2
+               elif bs == 0 and str[i] == '\\' :
+                       bs = 1
+               elif bs == 0 and (str[i] == '%' or str[i] == '&') :
+                       subst = str[i]
+                       num = 0
+               else :
+                       if bs == 1 and str[i] != '\\' and str[i] != '"' and str[i] != "'" :
+                               word += "\\"
+                       if qs == 0 and (str[i] == " " or str[i] == "\t") :
+                               if word != "" :
+                                       result.append(word)
+                                       word = ""
+                       else :
+                               word += str[i]
+                       bs = 0 
+       
+       if subst == '&':
+               if reres2 != None and num != 0 and num != -1:
+                       word += reres2.group(num)
+               else:
+                       word += reply2
+       elif subst == '%':
+               if reres1 != None and num != 0 and num != -1:
+                       word += reres1.group(num)
+               else:
+                       word += reply1
        if word != "" :
                result.append(word)
        return result