+import pango
+import time
+import re
+import gettext
+import fcntl
+import dbus
+import subprocess
+import gsmdecode
+import sys
+from dbus.mainloop.glib import DBusGMainLoop
+
+# Would be truncated on every reboot, and shouldn't write
+# anythong if things go right way so it is OK not to have logrotate
+log = open("/var/log/ussd-widget.log")
+print >> sys.stderr, "Writing log to /var/log/ussd-widget.log"
+
+try :
+ t = gettext.translation('ussd-widget', '/usr/share/locale')
+ _ = t.ugettext
+except IOError:
+ print >> log, "Translation file for your language not found"
+ def retme(arg):
+ return arg
+ _ = 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, 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"
+ # Aquire lock
+ lockf = open(configname+".lock", 'a')
+ fcntl.flock(lockf,fcntl.LOCK_EX)
+
+ oldconfig=""
+ try:
+ fconfig = open(configname,"r")
+ #Read configuration of other instances
+ my_section = False
+ for line in fconfig :
+ if line[0] == '%':
+ my_section = line[1:].strip() == self.id
+ if not my_section:
+ oldconfig += line
+ fconfig.close()
+ except:
+ print >> log, _("Couldn't read previous config")
+
+ fconfig = open(configname,"w")
+ fconfig.seek(0)
+ fconfig.write(oldconfig)
+ fconfig.write("%"+self.id+"\n");
+ fconfig.writelines(["#USSD query to be run by widget\n", "number="+self.config[0], "\n"])
+ fconfig.writelines(["#Parser command for widget\n", "parser="+self.config[1], "\n"])
+ fconfig.writelines(["#Parser command for banner\n", "parser_box="+self.config[12], "\n"])
+ fconfig.writelines(["#Chain command\n", "chain="+self.config[2], "\n"])
+ fconfig.writelines(["#Update interval in minutes\n", "interval="+str(self.config[3]), "\n"])
+ fconfig.writelines(["#RegExp pattern\n", "regexp="+self.config[4], "\n"])
+ fconfig.writelines(["#Widget width\n", "width="+str(self.config[5]), "\n"])
+ fconfig.writelines(["#Execute query at start\n", "query_at_start="+str(self.config[6]), "\n"])
+ fconfig.writelines(["#Retry pattern\n"])
+ fconfig.write("retry=")
+ first = True
+ for i in self.config[7]:
+ if not first:
+ fconfig.write ("-")
+ fconfig.write(str(i))
+ first = False
+ fconfig.write("\n")
+ fconfig.writelines(["#Font description\n", "font="+self.config[8].to_string(), "\n"])
+ fconfig.writelines(["#Font color\n", "text_color="+self.widget.get_text_color().to_string(), "\n"])
+ fconfig.writelines(["#Background color\n", "bg_color="+self.widget.get_bg_color().to_string(), "\n"])
+ 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)
+ lockf.close()
+
+ def get_config(self):
+ return self.config
+
+ def read_config( self, id ):
+ try :
+ self.id = id
+ config = open(os.getenv("HOME")+"/.ussdWidget.conf","r")
+
+ error = False
+ i = 0
+ my_section = False
+ for line in config :
+ i += 1
+ if line[0] == '#':
+ continue
+ if line[0] == '%':
+ my_section = line[1:].strip() == id
+ continue
+
+ if not my_section:
+ # This is config for another instace
+ continue
+
+ line=line.split('=', 1)
+
+ if len(line) != 2 :
+ error = True
+ print >> log, _("Error reading config on line %(line)d. = or # expected.")%{"line":i}
+ continue
+ if line[0] == "number" :
+ self.config[0] = line[1].strip()
+ elif line[0] == "parser" :
+ self.config[1] = line[1].strip()
+ elif line[0] == "parser_box" :
+ self.config[12] = line[1].strip()
+ elif line[0] == "chain" :
+ self.config[2] = line[1].strip()
+ elif line[0] == "interval" :
+ try:
+ self.config[3] = int(line[1].strip())
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+ continue
+ elif line[0] == "regexp" :
+ self.config[4] = line[1].strip()
+ elif line[0] == "width" :
+ try:
+ self.config[5] = int(line[1].strip())
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+ continue
+ elif line[0] == "query_at_start" :
+ if line[1].strip() == "True" :
+ self.config[6] = True
+ else :
+ self.config[6] = False
+ elif line[0] == "retry" :
+ line[1] = line[1].strip()
+ if line[1] != "":
+ line[1] = line[1].split("-")
+ i = 0
+ while i < len(line[1]) :
+ try:
+ line[1][i] = int(line[1][i])
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+ i += 1
+ self.config[7] = line[1]
+ else:
+ self.config[7] = []
+ continue
+ elif line[0] == "font" :
+ try:
+ self.config[8] = pango.FontDescription(line[1].strip())
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Pango font description expected.")%{"line":i}
+ continue
+ elif line[0] == "bg_color" :
+ try:
+ self.widget.set_bg_color(gtk.gdk.color_parse(line[1].strip()))
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
+ elif line[0] == "text_color" :
+ try:
+ self.widget.set_text_color(gtk.gdk.color_parse(line[1].strip()))
+ except:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Expected color definition.")%{"line":i}
+ elif line[0] == "name" :
+ self.config[9] = line[1].strip()
+ elif line[0] == "show_box" :
+ if line[1].strip() == "True" :
+ self.config[11] = True
+ else :
+ self.config[11] = False
+ elif line[0] == "language" :
+ try:
+ if int(line[1].strip()) >=0 and int(line[1].strip()) < len(ussd_languages):
+ self.config[10] = int(line[1].strip())
+ else:
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Unknown language code.")%{"line":i}
+ except:
+ error = True
+ print >> log, _("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 >> log, _("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 >> log, _("Error reading config on line %(line)d. Integer expected.")%{"line":i}
+ continue
+ else :
+ error = True
+ print >> log, _("Error reading config on line %(line)d. Unexpected variable: ")%{"line":i}+line[0]
+ continue
+
+ config.close()
+
+ if error :
+ self.widget.error = 1
+ self.widget.set_text (_("Config error"), 5000)
+
+ return self.config
+ except IOError:
+ self.widget.error = 1
+ self.widget.set_text (_("Config error"), 0)
+ print >> log, _("IO error while reading config")
+
+ return self.default_config
+
+ def on_show_settings( self, widget ) :
+ dialog = UssdConfigDialog(self.config, self.widget.get_bg_color(), self.widget.get_text_color(), self.id)
+
+ while True:
+ if dialog.run() != gtk.RESPONSE_OK :
+ dialog.destroy()
+ return
+
+ test = check_regexp(dialog.regexp.get_text())
+ if test :
+ dialog.on_error_regexp(test)
+ continue
+
+ # Check, that we have ussd number
+ 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()
+ if retry != "" :
+ retry = retry.split("-")
+ i = 0
+ while i < len(retry) :
+ try:
+ retry[i] = int(retry[i])
+ except:
+ dialog.on_error_retry_pattern()
+ break
+ i += 1
+
+ if i < len(retry):
+ continue
+ else :
+ retry = []
+
+ break
+
+ self.config = [
+ dialog.ussdNumber.get_text(),
+ dialog.parser.get_text(),
+ dialog.chain.get_text(),
+ dialog.update_interval.get_value(),
+ dialog.regexp.get_text(),
+ dialog.widthEdit.get_value(),
+ dialog.query_at_start.get_active(),
+ retry,
+ dialog.font,
+ dialog.wname.get_text(),
+ dialog.language.get_active(),
+ dialog.show_box.get_active(),
+ 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)
+ widget.set_text_color(dialog.text_color)
+
+ self.save_config()
+
+ widget.set_width(self.config[5])
+ self.reset_timed_renew()
+ self.widget.label.modify_font(self.config[8])
+
+ dialog.destroy()
+
+ # Before running this function widget wasn't configured
+ if self.config == self.default_config:
+ 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, process):
+ if condition == gobject.IO_IN or condition == gobject.IO_PRI:
+ data = source.read()
+ self.cb_reply += data
+ return True
+
+ if condition == gobject.IO_ERR:
+ print >> log, "Communication error occured"
+ # This will force widget to show error message
+ self.cb_reply = ""