|
@@ -0,0 +1,880 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+
|
|
|
+# rofi-pass
|
|
|
+# (c) 2015 Rasmus Steinke <rasi@xssn.at>
|
|
|
+basecommand="$0"
|
|
|
+
|
|
|
+# set default settings
|
|
|
+_rofi () {
|
|
|
+ rofi -no-auto-select -i "$@"
|
|
|
+}
|
|
|
+
|
|
|
+_pwgen () {
|
|
|
+ pwgen -y "$@"
|
|
|
+}
|
|
|
+
|
|
|
+_image_viewer () {
|
|
|
+ feh -
|
|
|
+}
|
|
|
+
|
|
|
+config_dir=${XDG_CONFIG_HOME:-$HOME/.config}
|
|
|
+cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}
|
|
|
+
|
|
|
+# We expect to find these fields in pass(1)'s output
|
|
|
+URL_field='url'
|
|
|
+USERNAME_field='user'
|
|
|
+AUTOTYPE_field='autotype'
|
|
|
+OTPmethod_field='otp_method'
|
|
|
+
|
|
|
+default_autotype="user :tab pass"
|
|
|
+delay=2
|
|
|
+wait=0.2
|
|
|
+xdotool_delay=12
|
|
|
+default_do='menu' # menu, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl
|
|
|
+auto_enter='false'
|
|
|
+notify='false'
|
|
|
+help_color=""
|
|
|
+clip=primary
|
|
|
+clip_clear=45
|
|
|
+default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}"
|
|
|
+default_user2=john_doe
|
|
|
+password_length=12
|
|
|
+fix_layout=false
|
|
|
+
|
|
|
+# default shortcuts
|
|
|
+autotype="Alt+1"
|
|
|
+type_user="Alt+2"
|
|
|
+type_pass="Alt+3"
|
|
|
+open_url="Alt+4"
|
|
|
+copy_name="Alt+u"
|
|
|
+copy_url="Alt+l"
|
|
|
+copy_pass="Alt+p"
|
|
|
+show="Alt+o"
|
|
|
+copy_menu="Alt+c"
|
|
|
+action_menu="Alt+a"
|
|
|
+type_menu="Alt+t"
|
|
|
+help="Alt+h"
|
|
|
+switch="Alt+x"
|
|
|
+insert_pass="Alt+n"
|
|
|
+qrcode="Alt+q"
|
|
|
+previous_root="Shift+Left"
|
|
|
+next_root="Shift+Right"
|
|
|
+
|
|
|
+# Safe permissions
|
|
|
+umask 077
|
|
|
+
|
|
|
+has_qrencode() {
|
|
|
+ command -v qrencode >/dev/null 2>&1
|
|
|
+}
|
|
|
+
|
|
|
+listgpg () {
|
|
|
+ mapfile -d '' pw_list < <(find -L . -name '*.gpg' -print0)
|
|
|
+ pw_list=("${pw_list[@]#./}")
|
|
|
+ printf '%s\n' "${pw_list[@]}" | sort -n
|
|
|
+}
|
|
|
+
|
|
|
+# get all password files and output as newline-delimited text
|
|
|
+list_passwords() {
|
|
|
+ cd "${root}" || exit
|
|
|
+ mapfile -t pw_list < <(listgpg)
|
|
|
+ printf '%s\n' "${pw_list[@]%.gpg}" | sort -n
|
|
|
+}
|
|
|
+
|
|
|
+doClip () {
|
|
|
+ case "$clip" in
|
|
|
+ "primary") xclip ;;
|
|
|
+ "clipboard") xclip -selection clipboard;;
|
|
|
+ "both") xclip; xclip -o | xclip -selection clipboard;;
|
|
|
+ esac
|
|
|
+}
|
|
|
+
|
|
|
+checkIfPass () {
|
|
|
+ printf '%s\n' "${root}: $selected_password" >| "$cache_dir/rofi-pass/last_used"
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+autopass () {
|
|
|
+ x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
|
|
+ xset r off
|
|
|
+
|
|
|
+ rm -f "$cache_dir/rofi-pass/last_used"
|
|
|
+ printf '%s\n' "${root}: $selected_password" > "$cache_dir/rofi-pass/last_used"
|
|
|
+ for word in ${stuff["$AUTOTYPE_field"]}; do
|
|
|
+ case "$word" in
|
|
|
+ ":tab") xdotool key Tab;;
|
|
|
+ ":space") xdotool key space;;
|
|
|
+ ":delay") sleep "${delay}";;
|
|
|
+ ":enter") xdotool key Return;;
|
|
|
+ ":otp") printf '%s' "$(generateOTP)" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
|
|
+ "pass") printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
|
|
+ "path") printf '%s' "${selected_password}" | rev | cut -d'/' -f1 | rev | xdotool type --clearmodifiers --file -;;
|
|
|
+ *) printf '%s' "${stuff[${word}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
|
|
|
+ esac
|
|
|
+ done
|
|
|
+
|
|
|
+ if [[ ${auto_enter} == "true" ]]; then
|
|
|
+ xdotool key Return
|
|
|
+ fi
|
|
|
+
|
|
|
+ xset r "$x_repeat_enabled"
|
|
|
+ unset x_repeat_enabled
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+generateQrCode() {
|
|
|
+ has_qrencode
|
|
|
+
|
|
|
+ if [[ $? -eq "1" ]]; then
|
|
|
+ printf '%s\n' "qrencode not found" | _rofi -dmenu
|
|
|
+ exit_code=$?
|
|
|
+ if [[ $exit_code -eq "1" ]]; then
|
|
|
+ exit
|
|
|
+ else
|
|
|
+ "${basecommand}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+ checkIfPass
|
|
|
+ pass "$selected_password" | head -n 1 | qrencode -d 300 -v 8 -l H -o - | _image_viewer
|
|
|
+ if [[ $? -eq "1" ]]; then
|
|
|
+ printf '%s\n' "" | _rofi -dmenu -mesg "Image viewer not defined or cannot read from pipe"
|
|
|
+ exit_value=$?
|
|
|
+ if [[ $exit_value -eq "1" ]]; then
|
|
|
+ exit
|
|
|
+ else
|
|
|
+ "${basecommand}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+openURL () {
|
|
|
+ checkIfPass
|
|
|
+ $BROWSER "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${URL_field}: " | gawk '{sub(/:/,"")}{print $2}1' | head -1)"; exit;
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+typeUser () {
|
|
|
+ checkIfPass
|
|
|
+
|
|
|
+ x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
|
|
+ xset r off
|
|
|
+
|
|
|
+ printf '%s' "${stuff[${USERNAME_field}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
|
|
+
|
|
|
+ xset r "$x_repeat_enabled"
|
|
|
+ unset x_repeat_enabled
|
|
|
+
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+typePass () {
|
|
|
+ checkIfPass
|
|
|
+
|
|
|
+ x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
|
|
+ xset r off
|
|
|
+
|
|
|
+ printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
|
|
+
|
|
|
+ if [[ $notify == "true" ]]; then
|
|
|
+ if [[ "${stuff[notify]}" == "false" ]]; then
|
|
|
+ :
|
|
|
+ else
|
|
|
+ notify-send "rofi-pass" "finished typing password";
|
|
|
+ fi
|
|
|
+ elif [[ $notify == "false" ]]; then
|
|
|
+ if [[ "${stuff[notify]}" == "true" ]]; then
|
|
|
+ notify-send "rofi-pass" "finished typing password";
|
|
|
+ else
|
|
|
+ :
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+ xset r "$x_repeat_enabled"
|
|
|
+ unset x_repeat_enabled
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+typeField () {
|
|
|
+ checkIfPass
|
|
|
+ local to_type
|
|
|
+
|
|
|
+ x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
|
|
|
+ xset r off
|
|
|
+
|
|
|
+ case $typefield in
|
|
|
+ "OTP") to_type="$(generateOTP)" ;;
|
|
|
+ *) to_type="${stuff[${typefield}]}" ;;
|
|
|
+ esac
|
|
|
+
|
|
|
+ printf '%s' "$to_type" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
|
|
|
+
|
|
|
+ xset r "$x_repeat_enabled"
|
|
|
+ unset x_repeat_enabled
|
|
|
+ unset to_type
|
|
|
+
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+generateOTP () {
|
|
|
+ checkIfPass
|
|
|
+
|
|
|
+ # First, we check if there is a non-conventional OTP command in the pass file
|
|
|
+ if PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep -q "${OTPmethod_field}: "; then
|
|
|
+ # We execute the commands after otp_method: AS-IS
|
|
|
+ bash -c "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${OTPmethod_field}: " | cut -d' ' -f2-)"
|
|
|
+ else
|
|
|
+ # If there is no method defined, fallback to pass-otp
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass otp "$selected_password"
|
|
|
+ fi
|
|
|
+
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+copyUser () {
|
|
|
+ checkIfPass
|
|
|
+ printf '%s' "${stuff[${USERNAME_field}]}" | doClip
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+copyField () {
|
|
|
+ checkIfPass
|
|
|
+ printf '%s' "${stuff[${copyfield}]}" | doClip
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+copyURL () {
|
|
|
+ checkIfPass
|
|
|
+ printf '%s' "${stuff[${URL_field}]}" | doClip
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+copyPass () {
|
|
|
+ checkIfPass
|
|
|
+ printf '%s' "$password" | doClip
|
|
|
+ if [[ $notify == "true" ]]; then
|
|
|
+ notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds"
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ $notify == "true" ]]; then
|
|
|
+ (sleep $clip_clear; printf '%s' "" | xclip; printf '%s' "" | xclip -selection clipboard | notify-send "rofi-pass" "Clipboard cleared") &
|
|
|
+ elif [[ $notify == "false" ]]; then
|
|
|
+ (sleep $clip_clear; printf '%s' "" | xclip; printf '%s' "" | xclip -selection clipboard) &
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+viewEntry () {
|
|
|
+ checkIfPass
|
|
|
+ showEntry "${selected_password}"
|
|
|
+}
|
|
|
+
|
|
|
+generatePass () {
|
|
|
+ askmenu_content=(
|
|
|
+ "Yes"
|
|
|
+ "No"
|
|
|
+ )
|
|
|
+
|
|
|
+ askGenMenu=$(printf '%s\n' "${askmenu_content[@]}" | _rofi -dmenu -p "Generate new Password for ${selected_password}? > ")
|
|
|
+ askgen_exit=$?
|
|
|
+
|
|
|
+ if [[ $askgen_exit -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ if [[ $askGenMenu == "Yes" ]]; then
|
|
|
+ true
|
|
|
+ elif [[ $askGenMenu == "No" ]]; then
|
|
|
+ actionMenu
|
|
|
+ fi
|
|
|
+
|
|
|
+ checkIfPass
|
|
|
+
|
|
|
+ symbols_content=(
|
|
|
+ "0 Cancel"
|
|
|
+ "1 Yes"
|
|
|
+ "2 No"
|
|
|
+ )
|
|
|
+
|
|
|
+ symbols=$(printf '%s\n' "${symbols_content[@]}" | _rofi -dmenu -p "Use Symbols? > ")
|
|
|
+ symbols_val=$?
|
|
|
+
|
|
|
+ if [[ $symbols_val -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ if [[ $symbols == "0 Cancel" ]]; then
|
|
|
+ mainMenu;
|
|
|
+ elif [[ $symbols == "1 Yes" ]]; then
|
|
|
+ symbols="";
|
|
|
+ elif [[ $symbols == "2 No" ]]; then
|
|
|
+ symbols="-n";
|
|
|
+ fi
|
|
|
+
|
|
|
+ HELP="<span color='$help_color'>Enter Number or hit Enter to use default length</span>"
|
|
|
+ length=$(printf '%s' "" | _rofi -dmenu -mesg "${HELP}" -p "Password length? (Default: ${password_length}) > ")
|
|
|
+ length_exit=$?
|
|
|
+
|
|
|
+ if [[ $length_exit -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ if [[ $length == "" ]]; then
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${password_length}" > /dev/null;
|
|
|
+ else
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${length}" > /dev/null;
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+# main Menu
|
|
|
+mainMenu () {
|
|
|
+ if [[ $1 == "--bmarks" ]]; then
|
|
|
+ selected_password="$(list_passwords 2>/dev/null \
|
|
|
+ | _rofi -mesg "Bookmarks Mode. ${switch} to switch" \
|
|
|
+ -dmenu \
|
|
|
+ -kb-custom-10 "${switch}" \
|
|
|
+ -select "$entry" \
|
|
|
+ -p "rofi-pass > ")"
|
|
|
+
|
|
|
+ rofi_exit=$?
|
|
|
+
|
|
|
+ if [[ $rofi_exit -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ elif [[ $rofi_exit -eq 19 ]]; then
|
|
|
+ ${basecommand}
|
|
|
+ elif [[ $rofi_exit -eq 0 ]]; then
|
|
|
+ openURL
|
|
|
+ fi
|
|
|
+ else
|
|
|
+ unset selected_password
|
|
|
+
|
|
|
+ args=( -dmenu
|
|
|
+ -kb-custom-1 "${autotype}"
|
|
|
+ -kb-custom-2 "${type_user}"
|
|
|
+ -kb-custom-3 "${type_pass}"
|
|
|
+ -kb-custom-4 "${open_url}"
|
|
|
+ -kb-custom-5 "${copy_name}"
|
|
|
+ -kb-custom-6 "${copy_pass}"
|
|
|
+ -kb-custom-7 "${show}"
|
|
|
+ -kb-custom-8 "${copy_url}"
|
|
|
+ -kb-custom-9 "${type_menu}"
|
|
|
+ -kb-custom-10 "${previous_root}"
|
|
|
+ -kb-custom-11 "${next_root}"
|
|
|
+ -kb-custom-14 "${action_menu}"
|
|
|
+ -kb-custom-15 "${copy_menu}"
|
|
|
+ -kb-custom-16 "${help}"
|
|
|
+ -kb-custom-17 "${switch}"
|
|
|
+ -kb-custom-18 "${insert_pass}"
|
|
|
+ -kb-custom-19 "${qrcode}"
|
|
|
+ )
|
|
|
+ args+=( -kb-mode-previous "" # These keyboard shortcut options are needed, because
|
|
|
+ -kb-mode-next "" # Shift+<Left|Right> are otherwise taken by rofi.
|
|
|
+ -select "$entry"
|
|
|
+ -p "> " )
|
|
|
+
|
|
|
+ if [[ ${#roots[@]} -gt "1" || $custom_root == "true" ]]; then
|
|
|
+ args+=(-mesg "PW Store: ${root}")
|
|
|
+ fi
|
|
|
+
|
|
|
+ selected_password="$(list_passwords 2>/dev/null | _rofi "${args[@]}")"
|
|
|
+
|
|
|
+ rofi_exit=$?
|
|
|
+ if [[ $rofi_exit -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+
|
|
|
+ # Actions based on exit code, which do not need the entry.
|
|
|
+ # The exit code for -kb-custom-X is X+9.
|
|
|
+ case "${rofi_exit}" in
|
|
|
+ 19) roots_index=$(( (roots_index-1+roots_length) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
|
|
|
+ 20) roots_index=$(( (roots_index+1) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
|
|
|
+ 25) helpMenu; return;;
|
|
|
+ 26) ${basecommand} --bmarks; return;;
|
|
|
+ esac
|
|
|
+
|
|
|
+ mapfile -t password_temp < <(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
|
|
|
+ password=${password_temp[0]}
|
|
|
+
|
|
|
+ if [[ ${password} == "#FILE="* ]]; then
|
|
|
+ pass_file="${password#*=}"
|
|
|
+ mapfile -t password_temp2 < <(PASSWORD_STORE_DIR="${root}" pass show "${pass_file}")
|
|
|
+ password=${password_temp2[0]}
|
|
|
+ fi
|
|
|
+
|
|
|
+ fields=$(printf '%s\n' "${password_temp[@]:1}" | awk '$1 ~ /:$/ || /otpauth:\/\// {$1=$1;print}')
|
|
|
+ declare -A stuff
|
|
|
+ stuff["pass"]=${password}
|
|
|
+
|
|
|
+ if [[ -n $fields ]]; then
|
|
|
+ while read -r LINE; do
|
|
|
+ unset _id _val
|
|
|
+ case "$LINE" in
|
|
|
+ "otpauth://"*|"${OTPmethod_field}"*)
|
|
|
+ _id="OTP"
|
|
|
+ _val=""
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ _id="${LINE%%: *}"
|
|
|
+ _val="${LINE#* }"
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+
|
|
|
+ if [[ -n "$_id" ]]; then
|
|
|
+ stuff["${_id}"]=${_val}
|
|
|
+ fi
|
|
|
+ done < <(printf '%s\n' "${fields}")
|
|
|
+
|
|
|
+ if test "${stuff['autotype']+autotype}"; then
|
|
|
+ :
|
|
|
+ else
|
|
|
+ stuff["autotype"]="${USERNAME_field} :tab pass"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [[ -z "${stuff["${AUTOTYPE_field}"]}" ]]; then
|
|
|
+ if [[ -n $default_autotype ]]; then
|
|
|
+ stuff["${AUTOTYPE_field}"]="${default_autotype}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ if [[ -z "${stuff["${USERNAME_field}"]}" ]]; then
|
|
|
+ if [[ -n $default_user ]]; then
|
|
|
+ if [[ "$default_user" == ":filename" ]]; then
|
|
|
+ stuff["${USERNAME_field}"]="$(basename "$selected_password")"
|
|
|
+ else
|
|
|
+ stuff["${USERNAME_field}"]="${default_user}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
|
|
|
+
|
|
|
+ # actions based on keypresses
|
|
|
+ # The exit code for -kb-custom-X is X+9.
|
|
|
+ case "${rofi_exit}" in
|
|
|
+ 0) typeMenu;;
|
|
|
+ 10) sleep $wait; autopass;;
|
|
|
+ 11) sleep $wait; typeUser;;
|
|
|
+ 12) sleep $wait; typePass;;
|
|
|
+ 13) openURL;;
|
|
|
+ 14) copyUser;;
|
|
|
+ 15) copyPass;;
|
|
|
+ 16) viewEntry;;
|
|
|
+ 17) copyURL;;
|
|
|
+ 18) default_do="menu" typeMenu;;
|
|
|
+ 23) actionMenu;;
|
|
|
+ 24) copyMenu;;
|
|
|
+ 27) insertPass;;
|
|
|
+ 28) generateQrCode;;
|
|
|
+ esac
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+clearUp () {
|
|
|
+ password=''
|
|
|
+ selected_password=''
|
|
|
+ unset stuff
|
|
|
+ unset password
|
|
|
+ unset selected_password
|
|
|
+ unset password_temp
|
|
|
+ unset stuff
|
|
|
+}
|
|
|
+
|
|
|
+helpMenu () {
|
|
|
+ _rofi -dmenu -mesg "Hint: All hotkeys are configurable in config file" -p "Help > " <<- EOM
|
|
|
+ ${autotype}: Autotype
|
|
|
+ ${type_user}: Type Username
|
|
|
+ ${type_pass}: Type Password
|
|
|
+ ${qrcode}: Generate and display qrcode
|
|
|
+ ---
|
|
|
+ ${copy_name}: Copy Username
|
|
|
+ ${copy_pass}: Copy Password
|
|
|
+ ${copy_url}: Copy URL
|
|
|
+ ${open_url}: Open URL
|
|
|
+ ${copy_menu}: Copy Custom Field
|
|
|
+ ---
|
|
|
+ ${action_menu}: Edit, Move, Delete, Re-generate Submenu
|
|
|
+ ${show}: Show Password File
|
|
|
+ ${insert_pass}: Insert new Pass Entry
|
|
|
+ ${switch}: Switch Pass/Bookmark Mode
|
|
|
+ ---
|
|
|
+ ${previous_root}: Switch to previous password store (--root)
|
|
|
+ ${next_root}: Switch to next password store (--root)
|
|
|
+EOM
|
|
|
+help_val=$?
|
|
|
+
|
|
|
+if [[ $help_val -eq 1 ]]; then
|
|
|
+ exit;
|
|
|
+else
|
|
|
+ unset helptext; mainMenu;
|
|
|
+fi
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+typeMenu () {
|
|
|
+ if [[ -n $default_do ]]; then
|
|
|
+ if [[ $default_do == "menu" ]]; then
|
|
|
+ checkIfPass
|
|
|
+ local -a keys=("${!stuff[@]}")
|
|
|
+ keys=("${keys[@]/$AUTOTYPE_field}")
|
|
|
+ typefield=$({ printf '%s' "${AUTOTYPE_field}" ; printf '%s\n' "${keys[@]}" | sort; } | _rofi -dmenu -p "Choose Field to type > ")
|
|
|
+ typefield_exit=$?
|
|
|
+ if [[ $typefield_exit -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ case "$typefield" in
|
|
|
+ '') exit;;
|
|
|
+ 'pass') sleep $wait; typePass;;
|
|
|
+ "${AUTOTYPE_field}") sleep $wait; autopass;;
|
|
|
+ *) sleep $wait; typeField
|
|
|
+ esac
|
|
|
+ clearUp
|
|
|
+ elif [[ $default_do == "${AUTOTYPE_field}" ]]; then
|
|
|
+ sleep $wait; autopass
|
|
|
+ else
|
|
|
+ ${default_do}
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+copyMenu () {
|
|
|
+ checkIfPass
|
|
|
+ copyfield=$(printf '%s\n' "${!stuff[@]}" | sort | _rofi -dmenu -p "Choose Field to copy > ")
|
|
|
+ val=$?
|
|
|
+ if [[ $val -eq 1 ]]; then
|
|
|
+ exit;
|
|
|
+ fi
|
|
|
+ if [[ $copyfield == "pass" ]]; then
|
|
|
+ copyPass;
|
|
|
+ else
|
|
|
+ copyField
|
|
|
+ fi
|
|
|
+ clearUp
|
|
|
+}
|
|
|
+
|
|
|
+actionMenu () {
|
|
|
+ checkIfPass
|
|
|
+ action_content=("< Return"
|
|
|
+ "---"
|
|
|
+ "1 Move Password File"
|
|
|
+ "2 Copy Password File"
|
|
|
+ "3 Delete Password File"
|
|
|
+ "4 Edit Password File"
|
|
|
+ "5 Generate New Password"
|
|
|
+ )
|
|
|
+
|
|
|
+ action=$(printf '%s\n' "${action_content[@]}" | _rofi -dmenu -p "Choose Action > ")
|
|
|
+ if [[ ${action} == "1 Move Password File" ]]; then
|
|
|
+ manageEntry move;
|
|
|
+ elif [[ ${action} == "3 Delete Password File" ]]; then
|
|
|
+ manageEntry delete;
|
|
|
+ elif [[ ${action} == "2 Copy Password File" ]]; then
|
|
|
+ manageEntry copy;
|
|
|
+ elif [[ ${action} == "4 Edit Password File" ]]; then
|
|
|
+ manageEntry edit;
|
|
|
+ elif [[ ${action} == "5 Generate New Password" ]]; then
|
|
|
+ generatePass;
|
|
|
+ elif [[ ${action} == "< Return" ]]; then
|
|
|
+ mainMenu;
|
|
|
+ elif [[ ${action} == "" ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+showEntry () {
|
|
|
+ if [[ -z $pass_content ]]; then
|
|
|
+ pass_temp=$(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
|
|
|
+ password="${pass_temp%%$'\n'*}"
|
|
|
+ pass_key_value=$(printf '%s\n' "${pass_temp}" | tail -n+2 | grep ': ')
|
|
|
+ declare -A stuff
|
|
|
+
|
|
|
+ while read -r LINE; do
|
|
|
+ _id="${LINE%%: *}"
|
|
|
+ _val="${LINE#* }"
|
|
|
+ stuff["${_id}"]=${_val}
|
|
|
+ done < <(printf '%s\n' "${pass_key_value}")
|
|
|
+
|
|
|
+ stuff["pass"]=${password}
|
|
|
+
|
|
|
+ if test "${stuff['autotype']+autotype}"; then
|
|
|
+ :
|
|
|
+ else
|
|
|
+ stuff["autotype"]="${USERNAME_field} :tab pass"
|
|
|
+ fi
|
|
|
+
|
|
|
+ pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
|
|
|
+ fi
|
|
|
+
|
|
|
+ bla_content=("< Return"
|
|
|
+ "${pass_content}"
|
|
|
+ )
|
|
|
+
|
|
|
+ bla=$(printf '%s\n' "${bla_content[@]}" | _rofi -dmenu -mesg "Enter: Copy entry to clipboard" -p "> ")
|
|
|
+ rofi_exit=$?
|
|
|
+
|
|
|
+ word=$(printf '%s' "$bla" | gawk -F': ' '{print $1}')
|
|
|
+
|
|
|
+ if [[ ${rofi_exit} -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ elif [[ ${rofi_exit} -eq 0 ]]; then
|
|
|
+ if [[ ${bla} == "< Return" ]]; then
|
|
|
+ mainMenu
|
|
|
+ else
|
|
|
+ if [[ -z $(printf '%s' "${stuff[${word}]}") ]]; then
|
|
|
+ printf '%s' "$word" | doClip
|
|
|
+ else
|
|
|
+ printf '%s' "${stuff[${word}]}" | doClip
|
|
|
+ fi
|
|
|
+ if [[ $notify == "true" ]]; then
|
|
|
+ notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds"
|
|
|
+ fi
|
|
|
+ if [[ $notify == "true" ]]; then
|
|
|
+ (sleep $clip_clear; printf '%s' "" | xclip; printf '%s' "" | xclip -selection clipboard | notify-send "rofi-pass" "Clipboard cleared") &
|
|
|
+ elif [[ $notify == "false" ]]; then
|
|
|
+ (sleep $clip_clear; printf '%s' "" | xclip; printf '%s' "" | xclip -selection clipboard) &
|
|
|
+ fi
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ exit
|
|
|
+ unset stuff
|
|
|
+ unset password
|
|
|
+ unset selected_password
|
|
|
+ unset password_temp
|
|
|
+ unset stuff
|
|
|
+ exit
|
|
|
+}
|
|
|
+
|
|
|
+manageEntry () {
|
|
|
+ if [[ "$1" == "edit" ]]; then
|
|
|
+ EDITOR=$EDITOR PASSWORD_STORE_DIR="${root}" pass edit "${selected_password}"
|
|
|
+ mainMenu
|
|
|
+ elif [[ $1 == "move" ]]; then
|
|
|
+ cd "${root}" || exit
|
|
|
+ group_array=(*/)
|
|
|
+ group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
|
|
|
+ if [[ $group == "" ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass mv "$selected_password" "${group}"
|
|
|
+ mainMenu
|
|
|
+ elif [[ $1 == "copy" ]]; then
|
|
|
+ cd "${root}" || exit
|
|
|
+ group_array=(*/)
|
|
|
+ group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
|
|
|
+ if [[ $group == "" ]]; then
|
|
|
+ exit
|
|
|
+ else
|
|
|
+ new_name="$(listgpg | _rofi -dmenu -format 'f' -mesg "Copying to same Group. Please enter a name for the new entry" -p "> ")"
|
|
|
+ fi
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass cp "$selected_password" "${group}/${new_name}"
|
|
|
+ mainMenu
|
|
|
+ elif [[ "$1" == "delete" ]]; then
|
|
|
+ HELP="<span color='$help_color'>Selected entry: ${selected_password}</span>"
|
|
|
+ ask_content=("Yes"
|
|
|
+ "No"
|
|
|
+ )
|
|
|
+ ask=$(printf '%s\n' "${ask_content[@]}" | _rofi -mesg "${HELP}" -dmenu -p "Are You Sure? > ")
|
|
|
+ if [[ "$ask" == "Yes" ]]; then
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass rm --force "${selected_password}"
|
|
|
+ elif [[ "$ask" == "No" ]]; then
|
|
|
+ mainMenu
|
|
|
+ elif [[ -z "$ask" ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+ else
|
|
|
+ mainMenu
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+insertPass () {
|
|
|
+ url=$(xclip --selection clipboard -o)
|
|
|
+
|
|
|
+ if [[ "${url:0:4}" == "http" ]]; then
|
|
|
+ domain_name="$(printf '%s\n' "${url}" | awk -F / '{l=split($3,a,"."); print (a[l-1]=="com"?a[l-2] OFS:X) a[l-1] OFS a[l]}' OFS=".")"
|
|
|
+ help_content="Domain: ${domain_name}
|
|
|
+ Type name, make sure it is unique"
|
|
|
+ else
|
|
|
+ help_content="Hint: Copy URL to clipboard before calling this menu.
|
|
|
+ Type name, make sure it is unique"
|
|
|
+ fi
|
|
|
+
|
|
|
+ cd "${root}" || exit
|
|
|
+ group_array=(*/)
|
|
|
+ grouplist=$(printf '%s\n' "${group_array[@]%/}")
|
|
|
+ name="$(listgpg | _rofi -dmenu -format 'f' -filter "${domain_name}" -mesg "${help_content}" -p "> ")"
|
|
|
+ val=$?
|
|
|
+
|
|
|
+ if [[ $val -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+
|
|
|
+ user_content=("${default_user2}"
|
|
|
+ "${USER}"
|
|
|
+ "${default_user}"
|
|
|
+ )
|
|
|
+
|
|
|
+ user=$(printf '%s\n' "${user_content[@]}" | _rofi -dmenu -mesg "Chose Username or type" -p "> ")
|
|
|
+ val=$?
|
|
|
+
|
|
|
+ if [[ $val -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+
|
|
|
+ group_content=("No Group"
|
|
|
+ "---"
|
|
|
+ "${grouplist}"
|
|
|
+ )
|
|
|
+
|
|
|
+ group=$(printf '%s\n' "${group_content[@]}" | _rofi -dmenu -p "Choose Group > ")
|
|
|
+ val=$?
|
|
|
+
|
|
|
+ if [[ $val -eq 1 ]]; then
|
|
|
+ exit
|
|
|
+ fi
|
|
|
+
|
|
|
+ pw=$(printf '%s' "Generate" | _rofi -dmenu -password -p "Password > " -mesg "Type Password or hit Enter to generate one")
|
|
|
+
|
|
|
+ if [[ $pw == "Generate" ]]; then
|
|
|
+ pw=$(_pwgen "${password_length}")
|
|
|
+ fi
|
|
|
+
|
|
|
+ clear
|
|
|
+
|
|
|
+ if [[ "$group" == "No Group" ]]; then
|
|
|
+ if [[ $url == http* ]]; then
|
|
|
+ pass_content=("${pw}"
|
|
|
+ "---"
|
|
|
+ "${USERNAME_field}: ${user}"
|
|
|
+ "${URL_field}: ${url}"
|
|
|
+ )
|
|
|
+ printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && PASSWORD_STORE_DIR="${root}" pass edit "${name}"
|
|
|
+ else
|
|
|
+ pass_content=("${pw}"
|
|
|
+ "---"
|
|
|
+ "${USERNAME_field}: ${user}"
|
|
|
+ )
|
|
|
+ printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && PASSWORD_STORE_DIR="${root}" pass edit "${name}"
|
|
|
+ fi
|
|
|
+ else
|
|
|
+ if [[ $url == http* ]]; then
|
|
|
+ pass_content=("${pw}"
|
|
|
+ "---"
|
|
|
+ "${USERNAME_field}: ${user}"
|
|
|
+ "${URL_field}: ${url}"
|
|
|
+ )
|
|
|
+ printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && PASSWORD_STORE_DIR="${root}" pass edit "${group}/${name}"
|
|
|
+ else
|
|
|
+ pass_content=("${pw}"
|
|
|
+ "---"
|
|
|
+ "${USERNAME_field}: ${user}"
|
|
|
+ )
|
|
|
+ printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null
|
|
|
+ if [[ $edit_new_pass == "true" ]]; then
|
|
|
+ PASSWORD_STORE_DIR="${root}" pass edit "${group}/${name}"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+help_msg () {
|
|
|
+ cat <<'EOF'
|
|
|
+ Usage:
|
|
|
+ rofi-pass [command]
|
|
|
+
|
|
|
+ Commands:
|
|
|
+ --insert insert new entry to password store
|
|
|
+ --root set custom root directories (colon separated)
|
|
|
+ --last-used highlight last used item
|
|
|
+ --show-last show details of last used Entry
|
|
|
+ --bmarks start in bookmarks mode
|
|
|
+
|
|
|
+ rofi-pass version 1.5.3
|
|
|
+EOF
|
|
|
+}
|
|
|
+
|
|
|
+get_config_file () {
|
|
|
+ configs=("$ROFI_PASS_CONFIG"
|
|
|
+ "$config_dir/rofi-pass/config"
|
|
|
+ "/etc/rofi-pass.conf")
|
|
|
+
|
|
|
+ # return the first config file with a valid path
|
|
|
+ for config in "${configs[@]}"; do
|
|
|
+ # '-n' is needed in case ROFI_PASS_CONFIG is not set
|
|
|
+ if [[ -n "${config}" && -f "${config}" ]]; then
|
|
|
+ printf "%s" "$config"
|
|
|
+ return
|
|
|
+ fi
|
|
|
+ done
|
|
|
+}
|
|
|
+
|
|
|
+main () {
|
|
|
+ # load config file
|
|
|
+ config_file="$(get_config_file)"
|
|
|
+ [[ -n "$config_file" ]] && source "$config_file"
|
|
|
+
|
|
|
+ # create tmp dir
|
|
|
+ if [[ ! -d "$cache_dir/rofi-pass" ]]; then
|
|
|
+ mkdir -p "$cache_dir/rofi-pass"
|
|
|
+ fi
|
|
|
+
|
|
|
+ # fix keyboard layout if enabled in config
|
|
|
+ if [[ $fix_layout == "true" ]]; then
|
|
|
+ layout_cmd
|
|
|
+ fi
|
|
|
+
|
|
|
+ # set help color
|
|
|
+ if [[ $help_color == "" ]]; then
|
|
|
+ help_color=$(rofi -dump-xresources | grep 'rofi.color.normal' | gawk -F ',' '/,/{gsub(/ /, "", $2); print $2}')
|
|
|
+ fi
|
|
|
+
|
|
|
+ # check for BROWSER variable, use xdg-open as fallback
|
|
|
+ if [[ -z $BROWSER ]]; then
|
|
|
+ export BROWSER=xdg-open
|
|
|
+ fi
|
|
|
+
|
|
|
+ # check if alternative root directory was given on commandline
|
|
|
+ if [[ -r "$cache_dir/rofi-pass/last_used" ]] && [[ $1 == "--last-used" || $1 == "--show-last" ]]; then
|
|
|
+ roots=("$(awk -F ': ' '{ print $1 }' "$cache_dir/rofi-pass/last_used")")
|
|
|
+ elif [[ -n "$2" && "$1" == "--root" ]]; then
|
|
|
+ custom_root=true; IFS=: read -r -a roots <<< "$2"
|
|
|
+ elif [[ -n $root ]]; then
|
|
|
+ custom_root=true; IFS=: read -r -a roots <<< "${root}"
|
|
|
+ elif [[ -n ${PASSWORD_STORE_DIR} ]]; then
|
|
|
+ roots=("${PASSWORD_STORE_DIR}")
|
|
|
+ else
|
|
|
+ roots=("$HOME/.password-store")
|
|
|
+ fi
|
|
|
+ roots_index=0
|
|
|
+ roots_length=${#roots[@]}
|
|
|
+ export root=${roots[$roots_index]}
|
|
|
+ export PASSWORD_STORE_DIR="${root}"
|
|
|
+ case $1 in
|
|
|
+ --insert)
|
|
|
+ insertPass
|
|
|
+ ;;
|
|
|
+ --root)
|
|
|
+ mainMenu
|
|
|
+ ;;
|
|
|
+ --help)
|
|
|
+ help_msg
|
|
|
+ ;;
|
|
|
+ --last-used)
|
|
|
+ if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then
|
|
|
+ entry="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")"
|
|
|
+ fi
|
|
|
+ mainMenu
|
|
|
+ ;;
|
|
|
+ --show-last)
|
|
|
+ if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then
|
|
|
+ selected_password="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")" viewEntry
|
|
|
+ else
|
|
|
+ mainMenu
|
|
|
+ fi
|
|
|
+ ;;
|
|
|
+ --bmarks)
|
|
|
+ mainMenu --bmarks;
|
|
|
+ ;;
|
|
|
+ *)
|
|
|
+ mainMenu
|
|
|
+ ;;
|
|
|
+ esac
|
|
|
+}
|
|
|
+
|
|
|
+main "$@"
|