diff --git a/.travis.yml b/.travis.yml index bbd8344a..24f64115 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,3 +23,7 @@ notifications: email: on_success: never on_failure: always + +addons: + apt: + update: true diff --git a/README.md b/README.md index c219979c..9f3a4061 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Howdy for Ubuntu [![](https://img.shields.io/travis/Boltgolt/howdy/master.svg)](https://travis-ci.org/Boltgolt/howdy) [![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1)](https://github.com/Boltgolt/howdy/releases) ![](https://boltgolt.nl/howdy_badge/installs.php) +# Howdy for Ubuntu [![](https://img.shields.io/travis/Boltgolt/howdy/master.svg)](https://travis-ci.org/Boltgolt/howdy) [![](https://img.shields.io/github/release/Boltgolt/howdy.svg?colorB=4c1)](https://github.com/Boltgolt/howdy/releases) ![](https://boltgolt.nl/howdy_badge/installs.php?nc) ![](https://boltgolt.nl/howdy_badge/views.php) Windows Helloâ„¢ style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/autocomplete/howdy b/autocomplete/howdy index 40ce2a23..e3bde07d 100755 --- a/autocomplete/howdy +++ b/autocomplete/howdy @@ -5,12 +5,42 @@ _howdy() { local cur prev opts COMPREPLY=() + # The argument typed so far cur="${COMP_WORDS[COMP_CWORD]}" + # The previous argument prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="help list add remove clear" - COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + # Go though all cases we support + case "${prev}" in + # After the main command, show the commands + "howdy") + opts="add clear config disable list remove clear test" + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; + # For disable, grab the current "disabled" config option and give the reverse + "disable") + local status=$(cut -d'=' -f2 <<< $(cat /lib/security/howdy/config.ini | grep 'disabled =') | xargs echo -n) + + [ "$status" == "false" ] && COMPREPLY="true" || COMPREPLY="false" + return 0 + ;; + # List the users availible + "-U") + COMPREPLY=( $(compgen -u -- ${cur}) ) + return 0 + ;; + "--user") + COMPREPLY=( $(compgen -u -- ${cur}) ) + return 0 + ;; + *) + ;; + esac + + # Nothing matched, so return nothing return 0 } +# Register the autocomplete function complete -F _howdy howdy diff --git a/debian/changelog b/debian/changelog index ca45c855..f69f3af0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +howdy (2.2.1) xenial; urgency=medium + + * Added mechanism to keep config files between updates + * Added force_mjpeg option to fix YUYV image issues (thanks @arifeinberg!) + * Revamped the bash autocompletion script + * Fixed timeout never being reached in certain scenarios (thanks @Tkopic001!) + * Fixed issue where BGR to RGB frame conversion caused a crash (thanks @Jerezano!) + + -- boltgolt Thu, 10 May 2018 15:14:03 +0200 + howdy (2.1.0) xenial; urgency=medium * First complete PPA release diff --git a/debian/control b/debian/control index 66683112..87f91239 100644 --- a/debian/control +++ b/debian/control @@ -10,6 +10,6 @@ Package: howdy Homepage: https://github.com/Boltgolt/howdy Architecture: all Depends: ${misc:Depends}, git, python3, python3-pip, python3-dev, python3-setuptools, build-essential, libpam-python, fswebcam, libopencv-dev, python-opencv, cmake -Description: Windows Hello style authentication for Ubuntu. +Description: Howdy: Windows Hello style authentication for Ubuntu. Use your built-in IR emitters and camera in combination with face recognition to prove who you are. diff --git a/debian/postinst b/debian/postinst index efe76a18..5c40df6b 100755 --- a/debian/postinst +++ b/debian/postinst @@ -36,8 +36,34 @@ def handleStatus(status): sys.exit(1) -# We're not in fresh configuration mode (probably an upgrade), so exit +# We're not in fresh configuration mode so don't continue the setup if not os.path.exists("/tmp/howdy_picked_device"): + # Check if we have an older config we can restore + if len(sys.argv) > 2: + if os.path.exists("/tmp/howdy_config_backup_v" + sys.argv[2] + ".ini"): + # Get the config parser + import configparser + + # Load th old and new config files + oldConf = configparser.ConfigParser() + oldConf.read("/tmp/howdy_config_backup_v" + sys.argv[2] + ".ini") + newConf = configparser.ConfigParser() + newConf.read("/lib/security/howdy/config.ini") + + # Go through every setting in the old config and apply it to the new file + for section in oldConf.sections(): + for (key, value) in oldConf.items(section): + try: + newConf.set(section, key, value) + # Add a new section where needed + except configparser.NoSectionError as e: + newConf.add_section(section) + newConf.set(section, key, value) + + # Write it all to file + with open("/lib/security/howdy/config.ini", "w") as configfile: + newConf.write(configfile) + sys.exit(0) # Open the temporary file containing the device ID @@ -162,7 +188,7 @@ if "HOWDY_NO_PROMPT" not in os.environ: # Abort the whole thing if it's not if (ans.lower() != "y"): - print("Inerpeting as a \"NO\", aborting") + print("Interpreting as a \"NO\", aborting") sys.exit(1) print("Adding lines to PAM\n") diff --git a/debian/preinst b/debian/preinst index 02960a8d..33572beb 100755 --- a/debian/preinst +++ b/debian/preinst @@ -16,6 +16,20 @@ import os import re import signal +# Backup the config file if we're upgrading +if "upgrade" in sys.argv: + # Try to copy the config file as a backup + try: + subprocess.call(["cp /lib/security/howdy/config.ini /tmp/howdy_config_backup_v" + sys.argv[2] + ".ini"], shell=True) + + # Let the user know so he knows where to look on a failed install + print("Backup of Howdy config file created in /tmp/howdy_config_backup_v" + sys.argv[2] + ".ini") + except e: + print("Could not make an backup of old Howdy config file") + + # Don't continue setup when we're just upgrading + sys.exit(0) + # Don't run if we're not trying to install fresh if "install" not in sys.argv: sys.exit(0) diff --git a/src/cli/add.py b/src/cli/add.py index c9f05941..374ee743 100644 --- a/src/cli/add.py +++ b/src/cli/add.py @@ -80,6 +80,12 @@ # Open the camera video_capture = cv2.VideoCapture(int(config.get("video", "device_id"))) + +# Force MJPEG decoding if true +if config.get("debug", "force_mjpeg") == "true": + video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237) + +# Request a frame to wake the camera up video_capture.read() print("\nPlease look straight into the camera") diff --git a/src/cli/disable.py b/src/cli/disable.py index 2c2b03b6..8bc03416 100644 --- a/src/cli/disable.py +++ b/src/cli/disable.py @@ -21,13 +21,18 @@ sys.exit(1) # Translate the argument to the right string -if builtins.howdy_args.argument == "1": +if builtins.howdy_args.argument == "1" or builtins.howdy_args.argument.lower() == "true": out_value = "true" -elif builtins.howdy_args.argument == "0": +elif builtins.howdy_args.argument == "0" or builtins.howdy_args.argument.lower() == "false": out_value = "false" else: # Of it's not a 0 or a 1, it's invalid - print("Please only use a 0 (enable) or a 1 (disable) as an argument") + print("Please only use false (enable) or true (disable) as an argument") + sys.exit(1) + +# Don't do anything when the state is already the requested one +if out_value == config.get("core", "disabled"): + print("The disable option has already been set to " + out_value) sys.exit(1) # Loop though the config file and only replace the line containing the disable config diff --git a/src/cli/test.py b/src/cli/test.py index 191ce666..ba4b979f 100644 --- a/src/cli/test.py +++ b/src/cli/test.py @@ -20,6 +20,10 @@ # Start capturing from the configured webcam video_capture = cv2.VideoCapture(int(config.get("video", "device_id"))) +# Force MJPEG decoding if true +if config.get("debug", "force_mjpeg") == "true": + video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237) + # Let the user know what's up print(""" Opening a window with a test feed diff --git a/src/compare.py b/src/compare.py index db1dde9f..a1a08697 100644 --- a/src/compare.py +++ b/src/compare.py @@ -37,8 +37,6 @@ def stop(status): models = [] # Encoded face models encodings = [] -# Amount of frames already matched -tries = 0 # Amount of ingnored dark frames dark_tries = 0 @@ -62,6 +60,10 @@ def stop(status): # Start video capture on the IR camera video_capture = cv2.VideoCapture(int(config.get("video", "device_id"))) +# Force MJPEG decoding if true +if config.get("video", "force_mjpeg") == "true": + video_capture.set(cv2.CAP_PROP_FOURCC, 1196444237) + # Capture a single frame so the camera becomes active # This will let the camera adjust its light levels while we're importing for faster scanning video_capture.read() @@ -82,6 +84,10 @@ def stop(status): # Increment the frame count every loop frames += 1 + # Stop if we've exceded the time limit + if time.time() - timings[3] > int(config.get("video", "timout")): + stop(11) + # Grab a single frame of video # Don't remove ret, it doesn't work without it ret, frame = video_capture.read() @@ -109,9 +115,6 @@ def stop(status): # Save the new size for diagnostics scale_height, scale_width = frame.shape[:2] - # Convert from BGR to RGB - frame = frame[:, :, ::-1] - # Get all faces from that frame as encodings face_encodings = face_recognition.face_encodings(frame) @@ -158,9 +161,3 @@ def print_timing(label, offset): # End peacegully stop(0) - - # Stop if we've exceded the maximum retry count - if time.time() - timings[3] > int(config.get("video", "timout")): - stop(11) - - tries += 1 diff --git a/src/config.ini b/src/config.ini index 213890f1..ac5e06dd 100644 --- a/src/config.ini +++ b/src/config.ini @@ -33,6 +33,10 @@ device_id = 1 # Speeds up face recognition but can make it less precise max_height = 320 +# Force the use of Motion JPEG when decoding frames, fixes issues with +# YUYV raw frame deconding +force_mjpeg = false + # Because of flashing IR emitters, some frames can be completely unlit # Skip the frame if the lowest 1/8 of the histogram is above this percentage # of the total @@ -42,3 +46,5 @@ dark_threshold = 50 [debug] # Show a short but detailed diagnostic report in console end_report = false + +dummy = true