|
|
|
import sys
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
|
|
|
|
from i18n import _
|
|
|
|
|
|
|
|
from importlib.machinery import SourceFileLoader
|
|
|
|
|
|
|
|
|
|
|
|
class RubberStamp:
|
|
|
|
"""Howdy rubber stamp"""
|
|
|
|
|
|
|
|
UI_TEXT = "ui_text"
|
|
|
|
UI_SUBTEXT = "ui_subtext"
|
|
|
|
|
|
|
|
def set_ui_text(self, text, type=None):
|
|
|
|
"""Convert an ui string to input howdy-gtk understands"""
|
|
|
|
typedec = "M"
|
|
|
|
|
|
|
|
if type == self.UI_SUBTEXT:
|
|
|
|
typedec = "S"
|
|
|
|
|
|
|
|
return self.send_ui_raw(typedec + "=" + text)
|
|
|
|
|
|
|
|
def send_ui_raw(self, command):
|
|
|
|
"""Write raw command to howdy-gtk stdin"""
|
|
|
|
if self.config.getboolean("debug", "verbose_stamps", fallback=False):
|
|
|
|
print("Sending command to howdy-gtk: " + command)
|
|
|
|
|
|
|
|
# Add a newline because the ui reads per line
|
|
|
|
command += " \n"
|
|
|
|
if self.gtk_proc:
|
|
|
|
# Send the command as bytes
|
|
|
|
self.gtk_proc.stdin.write(bytearray(command.encode("utf-8")))
|
|
|
|
self.gtk_proc.stdin.flush()
|
|
|
|
|
|
|
|
def execute(config, gtk_proc, opencv):
|
|
|
|
verbose = config.getboolean("debug", "verbose_stamps", fallback=False)
|
|
|
|
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
installed_stamps = []
|
|
|
|
|
|
|
|
# Go through each file in the rubberstamp folder
|
|
|
|
for filename in os.listdir(dir_path):
|
|
|
|
# Remove non-readable file or directories
|
|
|
|
if not os.path.isfile(dir_path + "/" + filename):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Remove meta files
|
|
|
|
if filename in ["__init__.py", ".gitignore"]:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Add the found file to the list of enabled rubberstamps
|
|
|
|
installed_stamps.append(filename.split(".")[0])
|
|
|
|
|
|
|
|
if verbose: print("Installed rubberstamps: " + ", ".join(installed_stamps))
|
|
|
|
|
|
|
|
# Get the rules defined in the config
|
|
|
|
raw_rules = config.get("rubberstamps", "stamp_rules")
|
|
|
|
rules = raw_rules.split("\n")
|
|
|
|
|
|
|
|
# Go through the rules one by one
|
|
|
|
for rule in rules:
|
|
|
|
rule = rule.strip()
|
|
|
|
|
|
|
|
if len(rule) <= 1:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Parse the rule with regex
|
|
|
|
regex_result = re.search("^(\w+)\s+([\w\.]+)\s+([a-z]+)(.*)?$", rule, re.IGNORECASE)
|
|
|
|
|
|
|
|
# Error out if the regex did not match (invalid line)
|
|
|
|
if not regex_result:
|
|
|
|
print(_("Error parsing rubberstamp rule: {}").format(rule))
|
|
|
|
continue
|
|
|
|
|
|
|
|
type = regex_result.group(1)
|
|
|
|
|
|
|
|
# Error out if the stamp name in the rule is not a file
|
|
|
|
if type not in installed_stamps:
|
|
|
|
print(_("Stamp not installed: {}").format(type))
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Load the module from file
|
|
|
|
module = SourceFileLoader(type, dir_path + "/" + type + ".py").load_module()
|
|
|
|
|
|
|
|
# Try to get the class with the same name
|
|
|
|
try:
|
|
|
|
constructor = getattr(module, type)
|
|
|
|
except AttributeError:
|
|
|
|
print(_("Stamp error: Class {} not found").format(type))
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Init the class and set common values
|
|
|
|
instance = constructor()
|
|
|
|
instance.verbose = verbose
|
|
|
|
instance.config = config
|
|
|
|
instance.gtk_proc = gtk_proc
|
|
|
|
instance.opencv = opencv
|
|
|
|
|
|
|
|
# Set some opensv shorthands
|
|
|
|
instance.video_capture = opencv["video_capture"]
|
|
|
|
instance.face_detector = opencv["face_detector"]
|
|
|
|
instance.pose_predictor = opencv["pose_predictor"]
|
|
|
|
instance.clahe = opencv["clahe"]
|
|
|
|
|
|
|
|
# Parse and set the 2 required options for all rubberstamps
|
|
|
|
instance.options = {
|
|
|
|
"timeout": float(re.sub("[a-zA-Z]", "", regex_result.group(2))),
|
|
|
|
"failsafe": regex_result.group(3) != "faildeadly"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Try to get the class do declare its other config variables
|
|
|
|
try:
|
|
|
|
instance.declare_config()
|
|
|
|
except Exception:
|
|
|
|
print(_("Internal error in rubberstamp configuration declaration:"))
|
|
|
|
|
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Split the optional arguments at the end of the rule by spaces
|
|
|
|
raw_options = regex_result.group(4).split()
|
|
|
|
|
|
|
|
# For each of those aoptional arguments
|
|
|
|
for option in raw_options:
|
|
|
|
# Get the key to the left, and the value to the right of the equal sign
|
|
|
|
key, value = option.split("=")
|
|
|
|
|
|
|
|
# Error out if a key has been set that was not declared by the module before
|
|
|
|
if key not in instance.options:
|
|
|
|
print("Unknow config option for rubberstamp " + type + ": " + key)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Convert the argument string to an int or float if the declared option has that type
|
|
|
|
if isinstance(instance.options[key], int):
|
|
|
|
value = int(value)
|
|
|
|
elif isinstance(instance.options[key], float):
|
|
|
|
value = float(value)
|
|
|
|
|
|
|
|
instance.options[key] = value
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
print("Stamp \"" + type + "\" options parsed:")
|
|
|
|
print(instance.options)
|
|
|
|
print("Executing stamp")
|
|
|
|
|
|
|
|
# Make the stamp fail by default
|
|
|
|
result = False
|
|
|
|
|
|
|
|
# Run the stamp code
|
|
|
|
try:
|
|
|
|
result = instance.run()
|
|
|
|
except Exception:
|
|
|
|
print(_("Internal error in rubberstamp:"))
|
|
|
|
|
|
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
|
|
continue
|
|
|
|
|
|
|
|
if verbose: print("Stamp \"" + type + "\" returned: " + str(result))
|
|
|
|
|
|
|
|
# Abort authentication if the stamp returned false
|
|
|
|
if result is False:
|
|
|
|
if verbose: print("Authentication aborted by rubber stamp")
|
|
|
|
sys.exit(14)
|
|
|
|
|
|
|
|
# This is outside the for loop, so we've run all the rules
|
|
|
|
if verbose: print("All rubberstamps processed, authentication successful")
|
|
|
|
|
|
|
|
# Exit with no errors
|
|
|
|
sys.exit(0)
|