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')
_ = retme
ussd_languages = ["German", "English", "Italian", "French", "Spanish", "Dutch", "Swedish", "Danish", "Portuguese", "Finnish", "Norwegian", "Greek", "Turkish", "Reserved1", "Reserved2", "Unspecified"]
+ussd_languages_localized = [_("German"), _("English"), _("Italian"), _("French"), _("Spanish"), _("Dutch"), _("Swedish"), _("Danish"), _("Portuguese"), _("Finnish"), _("Norwegian"), _("Greek"), _("Turkish"), _("Reserved1"), _("Reserved2"), _("Unspecified")]
# TODO Cutt off too long messages and show them in separate dialog
# how TODO widget vertical minimum size policy
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
- self.default_config = ["", "", "", 0, "", 0, True, [], pango.FontDescription("Nokia Sans 18"), _("Click to update"), 15, False, ""]
+ # 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"
fconfig.writelines(["#Widget name\n", "name="+self.config[9], "\n"])
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="+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)
except:
error = True
print _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+ elif line[0] == "args" :
+ self.config[13] = line[1].strip()
+ elif line[0] == "reggroup" :
+ try:
+ self.config[14] = int(line[1].strip())
+ except:
+ 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]
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()
dialog.wname.get_text(),
dialog.language.get_active(),
dialog.show_box.get_active(),
- dialog.b_parser.get_text()
+ dialog.b_parser.get_text(),
+ dialog.args.get_text(),
+ 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)
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( )
elif condition == gobject.IO_HUP or condition == gobject.IO_ERR :
self.cb_ready = 1
+ self.ussd_ready = True
self.process_reply()
return False
def call_external_script( self, ussd_code, language ):
self.cb_ready = 0
self.cb_reply = "";
- p = Popen(['/usr/bin/ussdquery.py', ussd_code, ussd_languages[language]], 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):
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
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]):
else :
self.widget.error = 0
# Apply regexp
+ reresult1 = reresult2 = None
if self.config[4] != "":
- try :
- reply = re.match( self.config[4], reply ).group( 1 )
- 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
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
self.widthEdit = hildon.NumberEditor(0, 1000)
self.widthEdit.set_value(config[5])
self.retryEdit = hildon.Entry(gtk.HILDON_SIZE_AUTO)
-
-
+ self.args = hildon.Entry(gtk.HILDON_SIZE_AUTO)
+ 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:
+ for i in ussd_languages_localized:
selector.append_text(i)
self.language = hildon.PickerButton(gtk.HILDON_SIZE_AUTO, hildon.BUTTON_ARRANGEMENT_HORIZONTAL)
self.language.set_selector(selector)
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 = ""
retryhelp = gtk.Button("?")
retryhelp.connect("clicked", self.on_show_retryhelp)
+
+ numberhelp = gtk.Button("?")
+ numberhelp.connect("clicked", self.on_show_number_help)
area = hildon.PannableArea()
self.vbox.add(area)
numberBox = gtk.HBox()
numberLabel = gtk.Label(_("USSD number"))
- numberLabel.set_alignment(0,0)
+ numberLabel.set_alignment(0,0.6)
numberLabel.set_size_request(100, -1)
+ numberhelp.set_size_request(1, -1)
self.ussdNumber.set_size_request(200, -1)
numberBox.add(numberLabel)
+ numberBox.add(numberhelp)
numberBox.add(self.ussdNumber)
vbox.add(numberBox)
- vbox.add(self.language)
-
vbox.add(self.query_at_start)
nameBox = gtk.HBox()
nameLabel = gtk.Label(_("Name"))
- nameLabel.set_alignment(0,0)
+ nameLabel.set_alignment(0,0.6)
nameLabel.set_size_request(100, -1)
self.wname.set_size_request(200, -1)
nameBox.add(nameLabel)
parserBox = gtk.HBox()
parserLabel = gtk.Label(_("Parser for widget"))
- parserLabel.set_alignment(0,0)
+ parserLabel.set_alignment(0,0.6)
parserLabel.set_size_request(200, -1)
phelp.set_size_request(10, -1)
parserBox.add(parserLabel)
vbox.add(self.parser)
b_parserBox = gtk.HBox()
-# parserLabel = gtk.Label(_("Parser"))
-# parserLabel.set_alignment(0,0)
self.show_box.set_size_request(200, -1)
bphelp.set_size_request(10, -1)
b_parserBox.add(self.show_box)
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)
+ chainLabel.set_alignment(0,0.6)
chainLabel.set_size_request(200, -1)
chelp.set_size_request(10, -1)
chainBox.add(chainLabel)
vbox.add(self.chain)
regexpBox = gtk.HBox()
- regexpLabel = gtk.Label(_("RegExp"))
- regexpLabel.set_alignment(0,0)
+ regexpLabel = gtk.Label(_("Regular expression"))
+ regexpLabel.set_alignment(0,0.6)
regexpLabel.set_size_request(200, -1)
+ regexpGroupLabel = gtk.Label(_("Group"))
+ regexpGroupLabel.set_size_request(1, -1)
reghelp.set_size_request(10, -1)
regexpBox.add(regexpLabel)
regexpBox.add(reghelp)
+ regexpBox.add(regexpGroupLabel)
vbox.add(regexpBox)
- vbox.add(self.regexp)
+ self.reggroup.set_size_request(1,-1);
+ self.regexp.set_size_request(250,-1);
+ regexpInputBox = gtk.HBox()
+ regexpInputBox.add(self.regexp)
+ regexpInputBox.add(self.reggroup)
+ vbox.add(regexpInputBox)
widthBox = gtk.HBox()
widthLabel = gtk.Label(_("Max. width"))
- widthLabel.set_alignment(0,0)
+ widthLabel.set_alignment(0,0.6)
symbolsLabel = gtk.Label(_("symbols"))
widthLabel.set_size_request(140, -1)
self.widthEdit.set_size_request(50, -1)
updateBox = gtk.HBox()
updateLabel = gtk.Label(_("Update every"))
- updateLabel.set_alignment(0,0)
+ updateLabel.set_alignment(0,0.6)
minutesLabel = gtk.Label(_("minutes"))
updateLabel.set_size_request(140, -1)
self.update_interval.set_size_request(50, -1)
retryBox = gtk.HBox()
retryLabel = gtk.Label(_("Retry pattern"))
- retryLabel.set_alignment(0,0)
+ retryLabel.set_alignment(0,0.6)
retryLabel.set_size_request(200, -1)
retryhelp.set_size_request(10, -1)
retryBox.add(retryLabel)
vbox.add(retryBox)
vbox.add(self.retryEdit)
+ argsLabel = gtk.Label(_("Additional ussdquery.py options"))
+ argsLabel.set_alignment(0,0.6)
+ vbox.add(argsLabel)
+ vbox.add(self.args)
+
viewBox = gtk.HBox()
viewBox.add(self.fontButton)
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()
dialog.run()
dialog.destroy()
+ def on_show_number_help(self, widget):
+ dialog = pHelpDialog(_("Format help"), _("USSD number. To perform USSD menu navigation divide queries vith spacebars. For xample '*100# 1' means 1st entry in *100# menu."))
+ dialog.run()
+ dialog.destroy()
+
def on_error_regexp(self, error):
dialog = pHelpDialog(_("Regexp syntax error"), error )
dialog.run()
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
def check_number(number):
for s in number :
- if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#"]) :
+ if not (s in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "+", "*", "#", " "]) :
return False
return True
if config[6]:
self.controller.ussd_renew(self, None)
+ self.label.modify_font(config[8])
self.controller.reset_timed_renew()
hildondesktop.HomePluginItem.do_show(self)