#!/usr/bin/env sh # rofipass - rofi front end for pass # # Derived from rofipass by Augusto Coronel (https://github.com/aocoronel/rofipass), # released under the MIT License, whose notice is kept below as it requires. # Modifications Copyright (C) 2026 Danilo M. , released under # the GNU General Public License version 2 (see LICENSE). # # MIT License # # Copyright (c) 2025 Augusto Coronel # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in all # copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. ### Colors help_color="#7c5cff" div_color="#334433" label="#f067fc" ### ### Bindings kb_copy_email="Ctrl+2" # EMAIL kb_copy_otp="Ctrl-1" # COPY TOTP kb_add_pass="Ctrl+r" # PASSWORD kb_add_otp="Ctrl+t" # ADD TOTP kb_delete="Ctrl-x" # DELETE kb_edit="Ctrl+y" # EDIT kb_close_tomb="Ctrl-z" # CLOSE TOMB kb_open_tomb="Ctrl-o" # OPEN TOMB ### clipmethod="$XDG_SESSION_TYPE" passdir=${PASSWORD_STORE_DIR:-$HOME/.password-store} COLOR_YELLOW="$(tput setaf 226)" COLOR_RED="$(tput setaf 196)" COLOR_GREEN="$(tput setaf 82)" COLOR_BLUE="$(tput setaf 87)" COLOR_RESET=$(tput sgr0) printfc() { mode=$1 case $mode in "ERROR" | "FATAL") color="$COLOR_RED" ;; "WARN") mode="WARNING" color="$COLOR_YELLOW" ;; "DEBUG") color="$COLOR_BLUE" ;; "INFO") color="$COLOR_GREEN" ;; esac text=$2 printf "%s[%s]:%s %s\n" "${color}" "${mode}" "${COLOR_RESET}" "${text}" } check_command() { for cmd in "$@"; do if ! command -v "$cmd" >/dev/null 2>&1; then printfc FATAL "$cmd is not installed." return 1 fi done } _notify() { message="$1" header="rofipass" urgency="$2" time=${3:-$time} echo "$message" if [ "$time" -eq 0 ]; then notify-send -u "$urgency" "$header" "$message" else notify-send -u "$urgency" "$header" "$message" --expire-time="$time"000 --wait fi } die() { mode=$1 message=$2 urgency=$3 printfc "$mode" "$message" _notify "$message" "$urgency" exit 1 } version() { echo "v1.1.0" } usage() { cat < Options: -e Set editor -f Lift swap restrictions (for tomb) -h Display this help message and exit -l Default password length on generation -t Default terminal emulator (for tomb) -v Display the current version number -T Clearing time Example: rofipass -f -l 72 -t kitty -e nvim -T 10 EOF } _rofi() { rofi -dmenu -theme ~/.config/rofi/udt/list.rasi -i -no-levenshtein-sort "$@" } copy_email() { email=$(pass "$menu" | sed -n 's/^email=//p') case "$clipmethod" in "x11") xclip "$email" || die FATAL "failed to copy the email." "critical" _notify "Copied to clipboard. Clearing in $time seconds" "normal" ;; "wayland") wl-copy "$email" || die FATAL "failed to copy the email." "critical" _notify "Copied to clipboard. Clearing in $time seconds" "normal" ;; esac # clearboard } append_otp() { check_command zbarimg || die FATAL "zbarimg is not installed." "critical" case "$clipmethod" in "x11") xclip -o | zbarimg -q --raw - | pass otp append "$menu" || die FATAL "failed to append OTP to $menu" "critical" _notify "Inserted OTP successfully to $menu" "normal" ;; "wayland") wl-paste --type image/png | zbarimg -q --raw - | pass otp append "$menu" || die FATAL "failed to append OTP to $menu" "critical" _notify "Inserted OTP successfully to $menu" "normal" ;; esac } clearboard() { [ -x "$(command -v notifyctl)" ] && notifyctl close-all case "$clipmethod" in "x11") echo "" | xclip -sel clip ;; "wayland") [ -x "$(command -v cliphist)" ] && cliphist wipe echo "" | wl-copy ;; esac } tomb_open() { if [ -n "$FORCE" ]; then $term -e sh -c "pass open -f" else $term -e sh -c "pass open" fi } tomb_close() { $term -e sh -c "pass close" pass_val=$? if [ "$pass_val" -ne 0 ]; then die FATAL "failed to close tomb." -u "critical" else _notify "Your password tomb has been closed!" "normal" 0 fi } deleteMenu() { delask=$(printf "1. Yes\n2. No" | _rofi -p '󰆴 Delete:' -mesg "Really delete $menu?") val=$? [ $val -eq 1 ] && { _notify "Cancelled!" "low" 0 main } [ "$delask" = "1. Yes" ] && pass rm -f "$menu" && _notify "Deleted $menu" "normal" 0 main } add_password() { length=${length:-72} addmenu=$( echo | _rofi -p '󰐕 New password:' -lines 0 \ -theme-str 'window { width: 25em; } listview { lines: 0; } entry { placeholder: "Enter password name"; }' ) val=$? if [ $val -eq 1 ]; then _notify "Cancelled!" "low" 0 main elif [ $val -eq 0 ]; then if command -v "passgen.py" >/dev/null 2>&1; then passgen.py 3 | pass insert -e "$addmenu" else pass generate "$addmenu" "$length" fi if [ $val -eq 1 ]; then _notify ERROR "failed to add password" 0 main else _notify "Added $addmenu" "normal" 0 fi fi main } edit_password() { $term -e sh -c "export EDITOR=\"$editor\"; pass edit \"$menu\"" } main() { enable_tomb=0 enable_otp=0 enable_zbar=0 if ! command -v "tomb" >/dev/null 2>&1; then enable_tomb=1 fi if ! command -v "zbarimg" >/dev/null 2>&1; then enable_zbar=1 fi pass otp -h >/dev/null 2>&1 || enable_otp=1 HELP="" HELP="$HELPActions: ${kb_add_pass}: Add | ${kb_delete}: Delete | ${kb_edit}: Edit" if [ "$enable_zbar" -eq 0 ]; then ZBAR_HELP=" | ${kb_add_otp}: Add OTP" HELP="$HELP$ZBAR_HELP" fi HELP="$HELP Copy: Enter: Copy Password | ${kb_copy_email}: Copy Email" if [ "$enable_otp" -eq 0 ]; then OTP_HELP=" | ${kb_copy_otp}: Copy OTP" HELP="$HELP$OTP_HELP" fi if [ "$enable_tomb" -eq 0 ]; then TOMB_HELP="Tomb: ${kb_close_tomb}: Close Tomb | ${kb_open_tomb}: Open Tomb" HELP="$HELP $TOMB_HELP" fi pass=$(find -L "$passdir" -type f -name '*.gpg' -printf '%P\n' | sed 's/\.gpg//') menu=$(echo "${pass}" | _rofi -p '󰌋 Pass:' -mesg "${HELP}" -kb-custom-1 "${kb_add_pass}" -kb-custom-2 "${kb_copy_otp}" -kb-custom-3 "${kb_delete}" -kb-custom-4 "${kb_edit}" -kb-custom-5 "${kb_close_tomb}" -kb-custom-6 "${kb_open_tomb}" -kb-custom-7 "${kb_copy_email}" -kb-custom-8 "${kb_add_otp}") val=$? case "$val" in 1) exit ;; 12) deleteMenu ;; 11) # OTP Copy [ "$enable_otp" -eq 0 ] && { timeout 3 pass otp -c "$menu" || die ERROR "failed to copy OTP!" "normal" _notify "Copied to clipboard. Clearing in $time seconds" "normal" # clearboard } ;; 10) add_password ;; 13) edit_password ;; 14) [ "$enable_tomb" -eq 0 ] && tomb_close ;; 15) [ "$enable_tomb" -eq 0 ] && tomb_open ;; 16) copy_email ;; 17) [ "$enable_zbar" -eq 0 ] && append_otp ;; 0) pass -c "$menu" || die ERROR "failed to copy the password!" "normal" _notify "Copied to clipboard. Clearing in $time seconds" "normal" # clearboard ;; esac } while getopts ":hvfl:t:T:e:" opt; do case "$opt" in h) usage exit 0 ;; e) editor="$OPTARG" ;; v) version exit 0 ;; f) FORCE=true ;; l) length="$OPTARG" ;; t) term="$OPTARG" ;; T) time="$OPTARG" ;; ?) printfc FATAL "invalid option '-$OPTARG'" >&2 exit 1 ;; esac done shift $((OPTIND - 1)) check_command rofi pass || exit 1 [ ! -d "$passdir" ] && die FATAL "no password directory found!" "low" [ -z "$editor" ] && editor=${EDITOR:-nano} term=${term:-kitty} time=${time:-5} main