- bitbake updated versions git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@417 96b47cad-a561-4643-ad3b-153ac7d7599c
743 lines
28 KiB
Python
743 lines
28 KiB
Python
#coding: utf-8
|
|
|
|
import sys, subprocess, os
|
|
try:
|
|
import pygtk
|
|
pygtk.require("2.0")
|
|
import gtk, gobject, glib, pango
|
|
except ImportError:
|
|
sys.exit(1)
|
|
|
|
try:
|
|
import pace
|
|
except ImportError:
|
|
print >> sys.stderr, "Could not import pace module, please install pyPACE"
|
|
sys.exit(1)
|
|
|
|
from eid_gui_globals import IMAGES, GLADE_FILE, EPA_ATR
|
|
from widgets import customCheckButton
|
|
from logic import cardChecker, break_text
|
|
|
|
class MokoWindow(gtk.Window):
|
|
"""
|
|
Base class for all our dialogues. It's a simple gtk.Window, with a title at
|
|
the top and to buttons at the bottom. In the middle there is a vBox where
|
|
we can put custom stuff for each dialoge.
|
|
All the dialoges are stored as a double-linked list, so the navigation from
|
|
one window to another is easy
|
|
"""
|
|
|
|
def __init__(self, title, predecessor):
|
|
super(MokoWindow, self).__init__()
|
|
|
|
self.title_str = title
|
|
self.predecessor = predecessor
|
|
|
|
assert(isinstance(self.title_str, basestring))
|
|
|
|
self.connect("destroy", gtk.main_quit)
|
|
#Display resolution of OpenMoko minus height of the SHR toolbar
|
|
self.set_size_request(480, 586)
|
|
#self.set_resizable(False)
|
|
|
|
#Main VBox, which consists of the title, the body, and the buttons
|
|
self.__vb = gtk.VBox(False, 5)
|
|
self.add(self.__vb)
|
|
|
|
#Title label at the top of the window
|
|
lbl_title = gtk.Label(self.title_str)
|
|
lbl_title.modify_font(pango.FontDescription("sans 14"))
|
|
lbl_title.set_alignment(0.5, 0.0)
|
|
self.__vb.pack_start(lbl_title, False, False)
|
|
|
|
#Body VBox for custom widgets
|
|
self.body = gtk.VBox(False)
|
|
#Add a scrollbar in case we must display lots of information
|
|
#Only use a vertical spacebar, not a horizontal one
|
|
scrolled_window = gtk.ScrolledWindow()
|
|
scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
|
|
scrolled_window.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
|
|
self.__vb.pack_start(scrolled_window)
|
|
scrolled_window.add_with_viewport(self.body)
|
|
|
|
#Add two buttons at the bottom of the window
|
|
hbox = gtk.HBox(True, 5)
|
|
if self.predecessor is None:
|
|
btnBack = gtk.Button("Abbruch")
|
|
else:
|
|
btnBack = gtk.Button(u"Zurück")
|
|
btnBack.set_size_request(150, 75)
|
|
btnBack.connect("clicked", self.btnBack_clicked, None)
|
|
if isinstance(btnBack.child, gtk.Label):
|
|
lbl = btnBack.child
|
|
else:
|
|
raise ValueError("Button does not have a label")
|
|
lbl.modify_font(pango.FontDescription("sans 12"))
|
|
hbox.pack_start(btnBack, True, True)
|
|
|
|
btnForward = gtk.Button("Weiter")
|
|
btnForward.set_size_request(150, 75)
|
|
btnForward.connect("clicked", self.btnForward_clicked, None)
|
|
if isinstance(btnForward.child, gtk.Label):
|
|
lbl = btnForward.child
|
|
else:
|
|
raise ValueError("Button does not have a label")
|
|
hbox.pack_start(btnForward, True, True)
|
|
lbl.modify_font(pango.FontDescription("sans 12"))
|
|
self.__vb.pack_start(hbox, False, False)
|
|
|
|
#Place window in the center of the screen
|
|
self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
|
|
|
|
def btnBack_clicked(self, widget, data=None):
|
|
"""Go back to the previous window. If there is no previous window,
|
|
do nothing"""
|
|
if self.predecessor is None:
|
|
gtk.main_quit()
|
|
else:
|
|
self.predecessor.show_all()
|
|
self.hide()
|
|
|
|
def btnForward_clicked(self, widget, data=None):
|
|
"""Go to the next window. If there is no next window, do nothing"""
|
|
if self.successor is None:
|
|
pass
|
|
else:
|
|
self.successor.show_all()
|
|
self.hide()
|
|
|
|
|
|
class MsgBox(gtk.Dialog):
|
|
"""The gtk.MessageDialog looks like crap on the Moko, so we write our own"""
|
|
|
|
def __init__(self, parent, msg, img_type):
|
|
#img is a string which is used as a key for the IMAGES dictionary
|
|
if img_type is not None:
|
|
assert IMAGES.has_key(img_type)
|
|
|
|
super(MsgBox, self).__init__(title="", parent=parent,
|
|
flags= gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
|
|
|
#Dialog will be resized to screen width no matter what. Therefore we
|
|
#use the whole width from the beginning to avoid being resized
|
|
self.set_size_request(480, 160)
|
|
|
|
hbox_top = gtk.HBox()
|
|
lbl = gtk.Label(break_text(msg, 35))
|
|
#lbl.set_width_chars(20)
|
|
#lbl.set_line_wrap(True)
|
|
btn = gtk.Button("Ok")
|
|
btn.connect("clicked", self.__destroy)
|
|
img = gtk.Image()
|
|
img.set_from_file(IMAGES[img_type])
|
|
hbox_top.pack_start(img, False, False)
|
|
hbox_top.pack_start(lbl, True, True)
|
|
self.vbox.pack_start(hbox_top)
|
|
#self.vbox.pack_start(gtk.HSeparator())
|
|
hbox_bottom = gtk.HBox()
|
|
spacer = gtk.Label("")
|
|
spacer.set_size_request(380, 25)
|
|
hbox_bottom.pack_start(spacer, False, False)
|
|
hbox_bottom.pack_start(btn, True, True)
|
|
self.vbox.pack_start(hbox_bottom)
|
|
#hbox.pack_start(vbox)
|
|
#self.add(hbox)
|
|
|
|
#self.set_decorated(False)
|
|
self.set_modal(True)
|
|
|
|
self.show_all()
|
|
|
|
def __destroy(self, widget=None, data=None):
|
|
self.destroy()
|
|
|
|
|
|
class CertificateDescriptionWindow(MokoWindow):
|
|
"""This window is used to display information about the service provider,
|
|
extracted from the terminal certificate and the appendant certificate
|
|
description"""
|
|
|
|
def __init__(self, binDescription, binCert):
|
|
super(CertificateDescriptionWindow, self).__init__("Dienstanbieter", None)
|
|
|
|
#binDesciption contains the Certificate Description as a octet string
|
|
desc = pace.d2i_CVC_CERTIFICATE_DESCRIPTION(binDescription)
|
|
|
|
self.successor = CVCWindow(binCert, self)
|
|
|
|
#Display the validity period:
|
|
cvc = pace.d2i_CV_CERT(binCert)
|
|
effective_date = self.formatDate(pace.get_effective_date(cvc))
|
|
expiration_date = self.formatDate(pace.get_expiration_date(cvc))
|
|
validity_period = effective_date + " - " + expiration_date
|
|
#self.addRow(u"Gültig ab:", effective_date)
|
|
#self.addRow(u"Gültig bis:", expiration_date)
|
|
self.addRow(u"Gültigkeitszeitraum:", validity_period)
|
|
|
|
#Display issuer Name and possibly URL and subject name and possibly URL
|
|
issuer_name = pace.get_issuer_name(desc)
|
|
issuer_url = pace.get_issuer_url(desc)
|
|
self.addRow("Zertifikatsaussteller:", issuer_name)
|
|
if issuer_url is not None:
|
|
self.addRow("", issuer_url, True)
|
|
subject_name = pace.get_subject_name(desc)
|
|
subject_url = pace.get_subject_url(desc)
|
|
self.addRow("Zertifikatsinhaber:", subject_name)
|
|
if subject_url is not None:
|
|
self.addRow("", subject_url, True)
|
|
self.set_focus(None)
|
|
|
|
#Extract, format and display the terms of usage
|
|
terms = pace.get_termsOfUsage(desc)
|
|
formated_terms = self.formatTOU(terms)
|
|
self.addRow("Beschreibung:", formated_terms)
|
|
|
|
self.show_all()
|
|
|
|
def formatDate(self, date):
|
|
"""Take a date string in the form that is included in CV Certificates
|
|
(YYMMDD) and return it in a better readable format (DD.MM.YYYY)"""
|
|
|
|
assert(isinstance(date, basestring))
|
|
assert(len(date) == 6)
|
|
|
|
year = date[:2]
|
|
month = date[2:4]
|
|
day = date[4:6] #Cut off trailing \0
|
|
|
|
#Implicit assumption: 2000 <= year < 3000
|
|
return day + "." + month + "." + "20" + year
|
|
|
|
def formatTOU(self, terms):
|
|
|
|
# for s in self.terms_array:
|
|
# s = s.replace(", ", "\n").strip()
|
|
# lbl = gtk.Label(s)
|
|
# lbl.set_alignment(0.0, 0.0)
|
|
# lbl.set_width_chars(60)
|
|
# lbl.set_line_wrap(True) #XXX: Doesn't work on the Moko
|
|
# lbl.modify_font(pango.FontDescription("bold"))
|
|
# self.body.pack_start(gtk.HSeparator())
|
|
# self.body.pack_start(lbl, False, False, 2)
|
|
|
|
return terms.replace(", ", "\r\n")
|
|
|
|
def addRow(self, title, text, url=False):
|
|
if not url: #URL has no title
|
|
lbl_title = gtk.Label(title)
|
|
lbl_title.set_width_chars(20)
|
|
lbl_title.set_alignment(0.0, 0.5)
|
|
lbl_title.modify_font(pango.FontDescription("bold"))
|
|
self.body.pack_start(lbl_title)
|
|
hbox = gtk.HBox()
|
|
lbl = gtk.Label("") #Empty label for spacing
|
|
lbl.set_width_chars(3)
|
|
hbox.pack_start(lbl)
|
|
if url:
|
|
lblText = gtk.LinkButton(text, text)
|
|
if isinstance(lblText.child, gtk.Label):
|
|
lbl = lblText.child
|
|
else:
|
|
raise ValueError("Button does not have a label")
|
|
lbl.set_width_chars(40)
|
|
|
|
else:
|
|
lblText = gtk.Label(text)
|
|
lblText.set_width_chars(40)
|
|
|
|
lblText.set_alignment(0.0, 0.0)
|
|
hbox.pack_start(lblText)
|
|
self.body.pack_start(hbox)
|
|
|
|
class CVCWindow(MokoWindow):
|
|
"""This window is used to display and modify the access encoded in the CHAT
|
|
of a CV Certificate"""
|
|
|
|
def __init__(self, binCert, predecessor):
|
|
super(CVCWindow, self).__init__("Zugriffsrechte", predecessor)
|
|
|
|
self.successor = PinpadGTK("pin", None)
|
|
|
|
#Convert the binary certificate to the internal representation and
|
|
#extract the relative authorization from the chat
|
|
cvc = pace.d2i_CV_CERT(binCert)
|
|
chat = pace.cvc_get_chat(cvc)
|
|
self.chat_oid = pace.get_chat_oid(chat)
|
|
self.chat = pace.get_binary_chat(chat)
|
|
self.rel_auth = []
|
|
for char in self.chat:
|
|
self.rel_auth.append(ord(char))
|
|
self.rel_auth_len = len(self.rel_auth)
|
|
|
|
#Extract the access rights from the CHAT and display them in the window
|
|
self.access_rights = []
|
|
j = 0
|
|
for i in range((self.rel_auth_len - 1) * 8 - 2):
|
|
if (i % 8 == 0):
|
|
j += 1
|
|
if self.rel_auth[self.rel_auth_len - j] & (1 << (i % 8)):
|
|
chk = customCheckButton(i, self.body)
|
|
self.access_rights.append(chk)
|
|
|
|
def btnForward_clicked(self, widget, data=None):
|
|
""" Check wether any access right have been deselected and modify the
|
|
CHAT accordingly """
|
|
|
|
for right in self.access_rights:
|
|
if not right.is_active():
|
|
idx = right.idx
|
|
self.rel_auth[len(self.chat) - 1 - idx / 8] ^= (1 << (idx % 8))
|
|
|
|
#super(CVCWindow, self).btnForward_clicked(widget, data)
|
|
new_chat_list = []
|
|
for byte in self.chat_oid:
|
|
new_chat_list.append(ord(byte))
|
|
new_chat_list.append(len(self.rel_auth))
|
|
new_chat_list.extend(self.rel_auth)
|
|
new_chat = self.__formatHexString(new_chat_list)
|
|
self.successor.set_chat(new_chat)
|
|
self.successor.show()
|
|
self.hide()
|
|
|
|
def __formatHexString(self, int_list):
|
|
"""Take a list of integers and convert it to a hex string, where the
|
|
individual bytes are seperated by a colon (e.g. "0f:00:00")"""
|
|
hex_str = ""
|
|
for i in int_list:
|
|
byte = hex(i)[2:]
|
|
if len(byte) == 1: byte = "0" + byte
|
|
hex_str += byte + ":"
|
|
|
|
return hex_str[:-1]
|
|
|
|
class PinpadGTK(object):
|
|
"""This a simple GTK based GUI to enter a PIN/PUK/CAN"""
|
|
|
|
def __init__(self, secret="pin", chat=None, cert=None):
|
|
btn_names = ["btnOne", "btnTwo", "btnThree", "btnFour", "btnFive",
|
|
"btnSix", "btnSeven", "btnEight", "btnNine", "btnDel",
|
|
"btnZero", "btnOk"]
|
|
|
|
self.pin = ""
|
|
self.secret = secret
|
|
if self.secret == "pin" or self.secret == "can":
|
|
self.secret_len = 6
|
|
elif self.secret == "transport":
|
|
self.secret_len = 5
|
|
elif self.secret == "puk":
|
|
self.secret_len = 10
|
|
else:
|
|
raise ValueError("Unknwon secret type: %s" % self.secret)
|
|
#gtk.main_quit()
|
|
self.chat = chat
|
|
self.cert = cert
|
|
|
|
#Set the Glade file
|
|
self.gladefile = GLADE_FILE
|
|
self.builder = gtk.Builder()
|
|
try:
|
|
self.builder.add_from_file(self.gladefile)
|
|
except glib.GError, err:
|
|
#If we encounter an exception at startup, we display a popup with
|
|
#the error message
|
|
popup = MsgBox(None, err.message, "error")
|
|
popup.run()
|
|
popup.destroy()
|
|
raise err
|
|
|
|
#Get the Main Window, and connect the "destroy" event
|
|
#Create our dictionay and connect it
|
|
dic = { "on_btnOne_clicked" : self.digit_clicked,
|
|
"on_btnTwo_clicked" : self.digit_clicked,
|
|
"on_btnThree_clicked" : self.digit_clicked,
|
|
"on_btnFour_clicked" : self.digit_clicked,
|
|
"on_btnFive_clicked" : self.digit_clicked,
|
|
"on_btnSix_clicked" : self.digit_clicked,
|
|
"on_btnSeven_clicked" : self.digit_clicked,
|
|
"on_btnEight_clicked" : self.digit_clicked,
|
|
"on_btnNine_clicked" : self.digit_clicked,
|
|
"on_btnZero_clicked" : self.digit_clicked,
|
|
"on_btnOk_clicked" : self.btnOk_clicked,
|
|
"on_btnDel_clicked" : self.btnDel_clicked,
|
|
"on_MainWindow_destroy_event" : self.shutdown,
|
|
"on_MainWindow_destroy" : self.shutdown }
|
|
|
|
self.builder.connect_signals(dic)
|
|
|
|
#Change the font for the text field and set it up up properly for the
|
|
#type of secret we are using
|
|
self.output = self.builder.get_object("txtOutput")
|
|
self.output.modify_font(pango.FontDescription("Monospace 16"))
|
|
self.output.set_text(self.secret_len * "_")
|
|
|
|
#Look for card and set the label accordingly
|
|
self.lbl_cardStatus = self.builder.get_object("lbl_cardStatus")
|
|
#Fetch the status image
|
|
self.img_cardStatus = self.builder.get_object("img_cardStatus")
|
|
self.img_cardStatus.set_from_file(IMAGES["error"])
|
|
|
|
#We only start the thread for polling the card when the window
|
|
#is shown
|
|
self.cardChecker = None
|
|
|
|
#Change the font for the buttons
|
|
#For this you have to retrieve the label of each button and change
|
|
#its font
|
|
for btn_name in btn_names:
|
|
btn = self.builder.get_object(btn_name)
|
|
if btn.get_use_stock():
|
|
lbl = btn.child.get_children()[1]
|
|
elif isinstance(btn.child, gtk.Label):
|
|
lbl = btn.child
|
|
else:
|
|
raise ValueError("Button does not have a label")
|
|
|
|
if (btn_name == "btnOk" or btn_name == "btnDel"):
|
|
lbl.modify_font(pango.FontDescription("sans 14"))
|
|
else:
|
|
lbl.modify_font(pango.FontDescription("sans 18"))
|
|
|
|
#Display Main Window
|
|
self.window = self.builder.get_object("MainWindow")
|
|
self.window.connect("destroy", self.shutdown)
|
|
self.window.hide()
|
|
|
|
def show(self):
|
|
"""Start the polling thread, then show the window"""
|
|
if self.cardChecker is None:
|
|
self.cardChecker = cardChecker(self.lbl_cardStatus, self.img_cardStatus,
|
|
EPA_ATR)
|
|
gobject.idle_add(self.cardChecker.start)
|
|
self.window.show_all()
|
|
|
|
def set_chat(self, chat):
|
|
self.chat = chat
|
|
|
|
def shutdown(self, widget):
|
|
"""Stop the cardChecker thread before exiting the application"""
|
|
if self.cardChecker is not None:
|
|
self.cardChecker.stop()
|
|
self.cardChecker.join()
|
|
gtk.main_quit()
|
|
|
|
def btnDel_clicked(self, widget):
|
|
"""Remove one digit from the pin and replace the last * in the output
|
|
field with an underscore"""
|
|
#Disable the "Okay" button
|
|
btn_okay = self.builder.get_object("btnOk")
|
|
btn_okay.set_sensitive(False)
|
|
#Delete the last digit
|
|
self.pin = self.pin[:-1]
|
|
num_digits = len(self.pin)
|
|
#Update display
|
|
output = num_digits * '*' + (self.secret_len - num_digits) * "_"
|
|
self.output.set_text(output)
|
|
|
|
def digit_clicked(self, widget):
|
|
"""We indicate the ammount of digits with underscores
|
|
For every digit that is enterd we replace one underscore with a star
|
|
We only accept secret_len digits and ignore every additional digit"""
|
|
digit = widget.get_label()
|
|
assert digit.isdigit()
|
|
txt = self.output.get_text()
|
|
if len(self.pin) < self.secret_len:
|
|
# self.output.set_text(txt.replace("_", digit, 1))
|
|
idx = txt.find('_')
|
|
output = txt[:idx] + digit + txt[idx+1:]
|
|
self.output.set_text(output)
|
|
gobject.timeout_add(750, self.__hide_digit, idx)
|
|
self.pin += digit
|
|
#If we have enough digits we enable the "Okay" button
|
|
if len(self.pin) == self.secret_len:
|
|
btn_okay = self.builder.get_object("btnOk")
|
|
btn_okay.set_sensitive(True)
|
|
|
|
def __hide_digit(self, idx):
|
|
"""Change character number idx of self.output text to a '*' """
|
|
output = ""
|
|
txt = self.output.get_text()
|
|
if txt[idx].isdigit():
|
|
output = txt[:idx] + '*' + txt[idx+1:]
|
|
self.output.set_text(output)
|
|
return False
|
|
|
|
def btnOk_clicked(self, widget):
|
|
"""Pass the entered secret to npa-tool. If npa-tool is run
|
|
sucessfully exit, otherwise restart the PIN entry"""
|
|
|
|
env_args = os.environ
|
|
|
|
#cmd contains the command and all the parameters for our subproccess
|
|
cmd = ["npa-tool"]
|
|
|
|
#We have to select the type of secret to use via a command line
|
|
#parameter and provide the actual secret via an environment variable
|
|
if self.secret == "pin" or self.secret == "transport":
|
|
cmd.append("--pin")
|
|
env_args["PIN"] = self.pin
|
|
elif self.secret == "can":
|
|
cmd.append("--can")
|
|
env_args["CAN"] = self.pin
|
|
elif self.secret == "puk":
|
|
cmd.append("--puk")
|
|
env_args["PUK"] = self.pin
|
|
else:
|
|
raise ValueError("Unknown secret type: %s" % self.secret)
|
|
|
|
#If we have a CHAT, we pass it to npa-tool
|
|
#if (self.chat):
|
|
# cmd.append("--chat=" + self.chat)
|
|
|
|
cmd.append("-v")
|
|
|
|
#Try to call npa-tool. This is a blocking call. An animation is being
|
|
#shown while the subprocess is running
|
|
try:
|
|
#Stop polling the card while PACE is running
|
|
self.cardChecker.pause()
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, env=env_args,
|
|
close_fds=True)
|
|
except OSError:
|
|
popup = MsgBox(self.window, "npa-tool wurde nicht gefunden",
|
|
"error")
|
|
popup.run()
|
|
popup.destroy()
|
|
self.cardChecker.resume() #Restart cardChecker
|
|
return
|
|
|
|
#Show the animation to indicate that the program is not dead
|
|
waiting = gtk.Window(gtk.WINDOW_POPUP)
|
|
animation = gtk.gdk.PixbufAnimation(IMAGES["wait"])
|
|
img = gtk.Image()
|
|
img.set_from_animation(animation)
|
|
waiting.add(img)
|
|
waiting.set_position(gtk.WIN_POS_CENTER_ALWAYS)
|
|
waiting.set_modal(True)
|
|
waiting.set_transient_for(self.window)
|
|
waiting.set_decorated(False)
|
|
waiting.show_all()
|
|
|
|
#Try to keep the GUI responsive by taking care of the event queue
|
|
line = proc.stdout.readline()
|
|
while line:
|
|
while gtk.events_pending():
|
|
gtk.main_iteration()
|
|
line = proc.stdout.readline()
|
|
|
|
#Get the return value of the npa-tool process
|
|
ret = proc.poll()
|
|
waiting.destroy()
|
|
self.cardChecker.resume()
|
|
|
|
if (ret == 0):
|
|
popup = MsgBox(self.window, "Pin wurde korrekt eingegeben", "apply")
|
|
popup.run()
|
|
popup.destroy()
|
|
#XXX: Actually we should return to the application that started
|
|
#the PIN entry
|
|
self.shutdown(None)
|
|
else:
|
|
popup = MsgBox(self.window, "PIN wurde falsch eingegeben", "error")
|
|
popup.run()
|
|
popup.destroy()
|
|
self.output.set_text(self.secret_len * "_")
|
|
self.pin = ""
|
|
|
|
class PINChanger(PinpadGTK):
|
|
"""Window that allows the user to change the PIN of his or her card
|
|
This is basically a simple state machine similiar to this chain:
|
|
start->old_pin->new_pin1->new_pin2->end"""
|
|
|
|
def __init__(self):
|
|
super(PINChanger, self).__init__(secret="pin")
|
|
|
|
self.states = ("old_pin", "first_new_pin", "second_new_pin")
|
|
self.state = 0
|
|
self.__old_pin = ""
|
|
self.__new_pin1 = ""
|
|
self.__update_gui()
|
|
|
|
def btnOk_clicked(self, widget, data=None):
|
|
"""PIN was entered . Check it and according to this check proceed
|
|
to the next state"""
|
|
|
|
if self.__check_entry(self.pin):
|
|
self.__next_state()
|
|
else:
|
|
self.__previous_state()
|
|
self.__update_gui()
|
|
|
|
def __check_entry(self, pin):
|
|
"""Process the PIN entered
|
|
@return: boolean"""
|
|
|
|
if self.states[self.state] == "old_pin":
|
|
#Try to perform PACE with the card to see if the PIN is correct
|
|
if self.__check_old_pin():
|
|
self.__old_pin = pin
|
|
return True
|
|
else:
|
|
return False
|
|
elif self.states[self.state] == "first_new_pin":
|
|
self.__new_pin1 = pin
|
|
return True
|
|
elif self.states[self.state] == "second_new_pin":
|
|
if pin == self.__new_pin1:
|
|
return self.__change_pin()
|
|
else:
|
|
self.__new_pin1 = ""
|
|
return False
|
|
|
|
def __previous_state(self):
|
|
"""Go to the previous state"""
|
|
|
|
if self.states[self.state] == "old_pin":
|
|
#Keep on trying
|
|
pass
|
|
elif self.states[self.state] == "first_new_pin":
|
|
#Can't happen because we always return True in self.__check_entry
|
|
pass
|
|
elif self.states[self.state] == "second_new_pin":
|
|
self.state = 1 #Enter first PIN again
|
|
|
|
def __next_state(self):
|
|
"""Go to the next state"""
|
|
if self.state <= 1:
|
|
self.state += 1
|
|
else:
|
|
self.__success()
|
|
|
|
#Clear input for next state
|
|
self.pin = ""
|
|
|
|
def __update_gui(self):
|
|
"""Update the GUI elements according to the current state"""
|
|
|
|
lbl_title = self.builder.get_object("lblInstruction")
|
|
txt_out = self.builder.get_object("txtOutput")
|
|
btn_ok = self.builder.get_object("btnOk")
|
|
|
|
txt_out.set_text('_' * self.secret_len)
|
|
|
|
if self.states[self.state] == "old_pin":
|
|
lbl_title.set_text("Alte PIN eingeben")
|
|
elif self.states[self.state] == "first_new_pin":
|
|
lbl_title.set_text("Neue PIN eingeben")
|
|
elif self.states[self.state] == "second_new_pin":
|
|
lbl_title.set_text("PIN wiederholen")
|
|
lbl_title.modify_font(pango.FontDescription("bold 11"))
|
|
|
|
btn_ok.set_sensitive(False)
|
|
|
|
def __check_old_pin(self):
|
|
"""Run PACE with the old pin to see if it is correct"""
|
|
#cmd contains the command and all the parameters for our subproccess
|
|
cmd = ["npa-tool", "--pin=" + self.pin]
|
|
|
|
#Try to call npa-tool. This is a blocking call. An animation is being
|
|
#shown while the subprocess is running
|
|
try:
|
|
#Stop polling the card while PACE is running
|
|
self.cardChecker.pause()
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
|
|
except OSError:
|
|
popup = MsgBox(self.window, "npa-tool wurde nicht gefunden",
|
|
"error")
|
|
popup.run()
|
|
popup.destroy()
|
|
self.cardChecker.resume() #Restart cardChecker
|
|
return False
|
|
|
|
#Show the animation to indicate that the program is not dead
|
|
waiting = gtk.Window(gtk.WINDOW_POPUP)
|
|
animation = gtk.gdk.PixbufAnimation(IMAGES["wait"])
|
|
img = gtk.Image()
|
|
img.set_from_animation(animation)
|
|
waiting.add(img)
|
|
waiting.set_position(gtk.WIN_POS_CENTER_ALWAYS)
|
|
waiting.set_modal(True)
|
|
waiting.set_transient_for(self.window)
|
|
waiting.set_decorated(False)
|
|
waiting.show_all()
|
|
|
|
#Try to keep the GUI responsive by taking care of the event queue
|
|
line = proc.stdout.readline()
|
|
while line:
|
|
while gtk.events_pending():
|
|
gtk.main_iteration()
|
|
line = proc.stdout.readline()
|
|
|
|
#Get the return value of the npa-tool process
|
|
ret = proc.poll()
|
|
waiting.destroy()
|
|
self.cardChecker.resume()
|
|
|
|
if (ret == 0):
|
|
return True
|
|
else:
|
|
popup = MsgBox(self.window, "PIN wurde falsch eingegeben", "error")
|
|
popup.run()
|
|
popup.destroy()
|
|
return False
|
|
|
|
|
|
def __change_pin(self):
|
|
"""Change the pin using npa-tool"""
|
|
|
|
#cmd contains the command and all the parameters for our subproccess
|
|
cmd = ["npa-tool", "--pin=" + self.__old_pin,
|
|
"--new-pin=" + self.__new_pin1]
|
|
|
|
#Try to call npa-tool. This is a blocking call. An animation is being
|
|
#shown while the subprocess is running
|
|
try:
|
|
#Stop polling the card while PACE is running
|
|
self.cardChecker.pause()
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
|
|
except OSError:
|
|
popup = MsgBox(self.window, "npa-tool wurde nicht gefunden",
|
|
"error")
|
|
popup.run()
|
|
popup.destroy()
|
|
self.cardChecker.resume() #Restart cardChecker
|
|
return
|
|
|
|
#Show the animation to indicate that the program is not dead
|
|
waiting = gtk.Window(gtk.WINDOW_POPUP)
|
|
animation = gtk.gdk.PixbufAnimation(IMAGES["wait"])
|
|
img = gtk.Image()
|
|
img.set_from_animation(animation)
|
|
waiting.add(img)
|
|
waiting.set_position(gtk.WIN_POS_CENTER_ALWAYS)
|
|
waiting.set_modal(True)
|
|
waiting.set_transient_for(self.window)
|
|
waiting.set_decorated(False)
|
|
waiting.show_all()
|
|
|
|
#Try to keep the GUI responsive by taking care of the event queue
|
|
line = proc.stdout.readline()
|
|
while line:
|
|
while gtk.events_pending():
|
|
gtk.main_iteration()
|
|
line = proc.stdout.readline()
|
|
|
|
#Get the return value of the npa-tool process
|
|
ret = proc.poll()
|
|
waiting.destroy()
|
|
self.cardChecker.resume()
|
|
|
|
if (ret == 0):
|
|
return True
|
|
else:
|
|
popup = MsgBox(self.window, "Ein Fehler ist aufgetreten", "error")
|
|
popup.run()
|
|
popup.destroy()
|
|
return False
|
|
|
|
def __success(self):
|
|
suc = MsgBox(self.window, u"PIN wurde erfolgreich geändert", "apply")
|
|
suc.run()
|
|
suc.destroy()
|
|
self.cardChecker.stop()
|
|
gtk.main_quit() #Actually we should return to the caller of the window
|