From 92854ed2799d790f9aec209f192cb78897c3cab0 Mon Sep 17 00:00:00 2001 From: kibergus Date: Fri, 12 Mar 2010 23:24:33 +0000 Subject: [PATCH] SMS listener and improves handler sintax git-svn-id: file:///svnroot/ussd-widget/trunk@25 d197f4d6-dc93-42ad-8354-0da1f58e353f --- ussd-widget/build_ussd-widget.py | 6 +- .../src/usr/lib/hildon-desktop/ussd-widget.py | 261 +++++++++++++++++--- 2 files changed, 224 insertions(+), 43 deletions(-) diff --git a/ussd-widget/build_ussd-widget.py b/ussd-widget/build_ussd-widget.py index 5a95e81..5b6ce3d 100644 --- a/ussd-widget/build_ussd-widget.py +++ b/ussd-widget/build_ussd-widget.py @@ -23,7 +23,7 @@ if __name__ == "__main__": p.description="Widget, that executes USSD query and displays response text\nThe main purpose is viewing your balance. In Russia all operators provide balace information via USSD queries and most part of contracts are prepaid. Ability to see your balance on desktop can be useful in such case.\nAnyway, you can configure widget to any other USSD query." p.author="Alexey Guseynov" p.mail="kibergus@gmail.com" - p.depends = "python2.5, ussd-common (>=0.0.7), python-hildondesktop (>=0.1.0-1maemo2), hildon-desktop-python-loader (>=0.1.0-1maemo2), python-gtk2, python-gobject, python-hildon, python-cairo" + p.depends = "python2.5, ussd-common (>=0.0.8), python-hildondesktop (>=0.1.0-1maemo2), hildon-desktop-python-loader (>=0.1.0-1maemo2), python-gtk2, python-gobject, python-hildon, python-cairo" p.section="user/desktop" p.icon = "./ussd-widget.png" p.arch="all" #should be all for python, any for all arch @@ -34,9 +34,9 @@ if __name__ == "__main__": # p.postinstall="""#!/bin/sh #""" #Set here your post install script - version = "0.1.3" + version = "0.1.6" build = "0" - changeloginformation = "Multiline regexps, language selected moved to the bottom and a warning added." + changeloginformation = "SMS listener. Changed parser sintax." dir_name = "src" diff --git a/ussd-widget/src/usr/lib/hildon-desktop/ussd-widget.py b/ussd-widget/src/usr/lib/hildon-desktop/ussd-widget.py index 2b1ab82..19a489e 100755 --- a/ussd-widget/src/usr/lib/hildon-desktop/ussd-widget.py +++ b/ussd-widget/src/usr/lib/hildon-desktop/ussd-widget.py @@ -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,11 +712,54 @@ 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 ============= @@ -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 -- 1.7.9.5