neopassmenu.sh 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/env sh
  2. multi=false
  3. clipboard=false
  4. type=false
  5. add=false
  6. file=""
  7. dir=""
  8. checkmenu(){
  9. for i in "$@"; do
  10. if command -v "$i" >/dev/null 2>&1; then
  11. echo "$i";
  12. break;
  13. fi
  14. done
  15. }
  16. if [ -n "$WAYLAND_DISPLAY" ]; then
  17. x11=false
  18. if [ -z "$DMENU_COMMAND" ]; then
  19. dmenu="$(checkmenu dmenu-wl bemenu tofi)"
  20. case "$dmenu" in
  21. rofi)
  22. dmenu="rofi -dmenu"
  23. ;;
  24. esac
  25. else
  26. dmenu="$DMENU_COMMAND"
  27. fi
  28. else
  29. x11=true
  30. if [ -z "$DMENU_COMMAND" ]; then
  31. dmenu="$(checkmenu dmenu rofi bemenu)"
  32. else
  33. dmenu="$DMENU_COMMAND"
  34. fi
  35. fi
  36. if [ -z "$dmenu" ]; then
  37. echo "No menu found, specify menu with the DMENU_COMMAND environment variable or install a known menu"
  38. exit 1;
  39. fi
  40. usage="$(printf "USAGE:\nneopassmenu [OPTS] [Pass Entry]\n\nOptions:\n-a\t\tGenerate or modify a password\n-c\t\tCopy to clipboard instead of printing to stdout\n-t\t\tSimulate typing instead of printing to stdout\n-m\t\tSelect a specific line from a multiline file\n-h\\t\tPrint usage information\n\nCONFIGURATION:\nEnvironement variables:\nDMENU_COMMAND\tSpecify dmenu command (default: dmenu)")"
  41. cd "${PASSWORD_STORE_DIR:-"$HOME/.password-store"}" || exit 2
  42. while [ "$#" -gt 0 ]; do
  43. case "$1" in
  44. -a)
  45. add=true
  46. shift
  47. ;;
  48. -c)
  49. clipboard=true
  50. type=false
  51. shift
  52. ;;
  53. -t)
  54. clipboard=false
  55. type=true
  56. shift
  57. ;;
  58. -m)
  59. multi=true
  60. shift
  61. ;;
  62. -h)
  63. echo "$usage"
  64. exit 0
  65. ;;
  66. *)
  67. if [ -f "$1" ] || [ -f "${1}.gpg" ]; then
  68. file="$1"
  69. shift
  70. elif [ -d "$1" ]; then
  71. dir="$1"
  72. shift
  73. else
  74. echo "$usage" >&2
  75. exit 1
  76. fi
  77. ;;
  78. esac
  79. done
  80. if [ -z "$file" ]; then
  81. while true; do
  82. file="$(printf "..\n%s" "$(find "./$dir" -maxdepth 1)" | sed -e 's#^'"./$dir"'##;s#^/##;s/.gpg$//' -e '/^$/d; /^.gpg-id$/d; /^.git\(attributes\)\{0,1\}$/d' | $dmenu)"
  83. if [ -z "$dir" ]; then
  84. newdir="$file"
  85. else
  86. newdir="${dir%/}/$file"
  87. fi
  88. if [ -z "$file" ]; then
  89. exit 127
  90. elif [ "$file" = ".." ]; then
  91. dir="${dir%/}"
  92. if echo "$dir" | grep -q "/"; then
  93. dir="${dir%/*}"
  94. else
  95. dir=""
  96. fi
  97. elif [ -d "$newdir" ]; then
  98. dir="$newdir"
  99. elif [ -f "$newdir" ] || [ -f "${newdir}.gpg" ] || "$add"; then
  100. file="$newdir"
  101. break
  102. else
  103. break
  104. fi
  105. done
  106. fi
  107. if "$add"; then
  108. if [ -f "${file}.gpg" ]; then
  109. pass "$file" > /tmp/pass.txt
  110. fi
  111. printf "\n\n%s" "$(dd count=2 if=/dev/random of=/dev/stdout 2>/dev/null ibs=512 obs=512 | uuencode -m /dev/stdout | tail -n +2 | tr -d '\n')" >> /tmp/pass.txt
  112. fileopener /tmp/pass.txt
  113. if [ -n "$(cat /tmp/pass.txt)" ]; then
  114. save="$(printf "yes\nno" | $dmenu -p "Save changes? ")"
  115. if [ "$save" = "yes" ]; then
  116. cat /tmp/pass.txt | pass add -m -f "$file"
  117. fi
  118. rm /tmp/pass.txt
  119. exit 0
  120. fi
  121. rm /tmp/pass.txt
  122. exit 1
  123. fi
  124. if ! [ -f "$file" ] && ! [ -f "${file}.gpg" ]; then
  125. exit 127
  126. fi
  127. if $clipboard && ! $multi; then
  128. printf "%s" "$(pass "$file")" | cb copy
  129. exit 0
  130. elif $type && ! $multi; then
  131. if $x11; then
  132. printf "%s" "$(pass "$file")" | xdotool type --clearmodifiers --file -
  133. else
  134. printf "%s" "$(pass "$file")" | ydotool type --file -
  135. fi
  136. exit 0
  137. fi
  138. password_lines="$(pass "$file")"
  139. if ! $multi; then
  140. echo "$password_lines"
  141. exit 0
  142. fi
  143. password="$(echo "$password_lines" | sed '{ h; s/^.$//; s/^..//; s/./*/g; x; s/^\(..\).*/\1/; G; s/\n//; }' | $dmenu)"
  144. # Sed script:
  145. # Copy pattern space (line) to hold space. Pattern space: Foo Hold space: Foo
  146. # If the patter space is a single character, delete the character (prevents "a" to be outputted as "aa")
  147. # Delete the first two characters of the patter space. Pattern Space: o; Hold space: Foo
  148. # Replace all the characters in the pattern space with the literal "*". Pattern Space: *; Hold space: Foo
  149. # Swap pattern space with hold space. Pattern Space: Foo Hold space: *
  150. # Match the first two characters of the pattern space, then all the rest of the characters
  151. # substitute with the whole match (entire line) with the first submatch (first 2 characters). Pattern Space: Fo Hold Space: *
  152. # Append newline followed by the Hold Space to the Pattern Space. Pattern Space: Fo\n* Hold Space: *
  153. # Remove the (supposedly only) newline from the pattern space. Pattern Space: Fo* Hold Space: *
  154. # Sed automatically prints the pattern space at the end of a script (unless the -n option is given). Output: Fo*
  155. password="$(echo "$password_lines" | grep -xE "$(echo "$password" | sed 'y/*/./' )")"
  156. if "$clipboard"; then
  157. printf "%s" "$password" | cb copy
  158. exit 0
  159. elif "$type"; then
  160. if $x11; then
  161. printf "%s" "$password" | xdotool type --clearmodifiers --file -
  162. else
  163. printf "%s" "$password" | ydotool type --file -
  164. fi
  165. exit 0
  166. fi
  167. echo "$password"