rofi-pass.sh 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. #!/usr/bin/env bash
  2. # set -euo pipefail # bash strict mode
  3. # rofi-pass
  4. # (c) 2015 Rasmus Steinke <rasi@xssn.at>
  5. basecommand="$0"
  6. # set default settings
  7. _rofi () {
  8. rofi -no-auto-select -i "$@"
  9. }
  10. _pwgen () {
  11. pwgen -y "$@"
  12. }
  13. _image_viewer () {
  14. feh -
  15. }
  16. _clip_in_primary() {
  17. xclip
  18. }
  19. _clip_in_clipboard() {
  20. xclip -selection clipboard
  21. }
  22. _clip_out_primary() {
  23. xclip -o
  24. }
  25. _clip_out_clipboard() {
  26. xclip --selection clipboard -o
  27. }
  28. config_dir=${XDG_CONFIG_HOME:-$HOME/.config}
  29. cache_dir=${XDG_CACHE_HOME:-$HOME/.cache}
  30. # We expect to find these fields in pass(1)'s output
  31. URL_field='url'
  32. USERNAME_field='user'
  33. AUTOTYPE_field='autotype'
  34. OTPmethod_field='otp_method'
  35. default_autotype="user :tab pass"
  36. delay=2
  37. wait=0.2
  38. xdotool_delay=12
  39. default_do='menu' # menu, copyPass, typeUser, typePass, copyUser, copyUrl, viewEntry, typeMenu, actionMenu, copyMenu, openUrl
  40. auto_enter='false'
  41. notify='false'
  42. help_color=""
  43. clip=primary
  44. clip_clear=45
  45. default_user="${ROFI_PASS_DEFAULT_USER-$(whoami)}"
  46. default_user2=john_doe
  47. password_length=12
  48. fix_layout=false
  49. # default shortcuts
  50. autotype="Alt+1"
  51. type_user="Alt+2"
  52. type_pass="Alt+3"
  53. open_url="Alt+4"
  54. copy_name="Alt+u"
  55. copy_url="Alt+l"
  56. copy_pass="Alt+p"
  57. show="Alt+o"
  58. copy_menu="Alt+c"
  59. action_menu="Alt+a"
  60. type_menu="Alt+t"
  61. help="Alt+h"
  62. switch="Alt+x"
  63. insert_pass="Alt+n"
  64. qrcode="Alt+q"
  65. previous_root="Shift+Left"
  66. next_root="Shift+Right"
  67. # Safe permissions
  68. umask 077
  69. has_qrencode() {
  70. command -v qrencode >/dev/null 2>&1
  71. }
  72. listgpg () {
  73. mapfile -d '' pw_list < <(find -L . -name '*.gpg' -print0)
  74. pw_list=("${pw_list[@]#./}")
  75. printf '%s\n' "${pw_list[@]}" | sort -n
  76. }
  77. # get all password files and output as newline-delimited text
  78. list_passwords() {
  79. cd "${root}" || exit
  80. mapfile -t pw_list < <(listgpg)
  81. printf '%s\n' "${pw_list[@]%.gpg}" | sort -n
  82. }
  83. doClip () {
  84. case "$clip" in
  85. "primary") _clip_in_primary ;;
  86. "clipboard") _clip_in_clipboard ;;
  87. "both") _clip_in_primary; _clip_out_primary | _clip_in_clipboard;;
  88. esac
  89. }
  90. checkIfPass () {
  91. printf '%s\n' "${root}: $selected_password" >| "$cache_dir/rofi-pass/last_used"
  92. }
  93. autopass () {
  94. x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
  95. xset r off
  96. rm -f "$cache_dir/rofi-pass/last_used"
  97. printf '%s\n' "${root}: $selected_password" > "$cache_dir/rofi-pass/last_used"
  98. for word in ${stuff["$AUTOTYPE_field"]}; do
  99. case "$word" in
  100. ":tab") xdotool key Tab;;
  101. ":space") xdotool key space;;
  102. ":delay") sleep "${delay}";;
  103. ":enter") xdotool key Return;;
  104. ":otp") printf '%s' "$(generateOTP)" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
  105. "pass") printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
  106. "path") printf '%s' "${selected_password}" | rev | cut -d'/' -f1 | rev | xdotool type --clearmodifiers --file -;;
  107. *) printf '%s' "${stuff[${word}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -;;
  108. esac
  109. done
  110. if [[ ${auto_enter} == "true" ]]; then
  111. xdotool key Return
  112. fi
  113. xset r "$x_repeat_enabled"
  114. unset x_repeat_enabled
  115. clearUp
  116. }
  117. generateQrCode() {
  118. has_qrencode
  119. if [[ $? -eq "1" ]]; then
  120. printf '%s\n' "qrencode not found" | _rofi -dmenu
  121. exit_code=$?
  122. if [[ $exit_code -eq "1" ]]; then
  123. exit
  124. else
  125. "${basecommand}"
  126. fi
  127. fi
  128. checkIfPass
  129. pass "$selected_password" | head -n 1 | qrencode -d 300 -v 8 -l H -o - | _image_viewer
  130. if [[ $? -eq "1" ]]; then
  131. printf '%s\n' "" | _rofi -dmenu -mesg "Image viewer not defined or cannot read from pipe"
  132. exit_value=$?
  133. if [[ $exit_value -eq "1" ]]; then
  134. exit
  135. else
  136. "${basecommand}"
  137. fi
  138. fi
  139. clearUp
  140. }
  141. openURL () {
  142. checkIfPass
  143. $BROWSER "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${URL_field}: " | gawk '{sub(/:/,"")}{print $2}1' | head -1)"; exit;
  144. clearUp
  145. }
  146. typeUser () {
  147. checkIfPass
  148. x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
  149. xset r off
  150. printf '%s' "${stuff[${USERNAME_field}]}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
  151. xset r "$x_repeat_enabled"
  152. unset x_repeat_enabled
  153. clearUp
  154. }
  155. typePass () {
  156. checkIfPass
  157. x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
  158. xset r off
  159. printf '%s' "${password}" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
  160. if [[ $notify == "true" ]]; then
  161. if [[ "${stuff[notify]}" == "false" ]]; then
  162. :
  163. else
  164. notify-send "rofi-pass" "finished typing password";
  165. fi
  166. elif [[ $notify == "false" ]]; then
  167. if [[ "${stuff[notify]}" == "true" ]]; then
  168. notify-send "rofi-pass" "finished typing password";
  169. else
  170. :
  171. fi
  172. fi
  173. xset r "$x_repeat_enabled"
  174. unset x_repeat_enabled
  175. clearUp
  176. }
  177. typeField () {
  178. checkIfPass
  179. local to_type
  180. x_repeat_enabled=$(xset q | awk '/auto repeat:/ {print $3}')
  181. xset r off
  182. case $typefield in
  183. "OTP") to_type="$(generateOTP)" ;;
  184. *) to_type="${stuff[${typefield}]}" ;;
  185. esac
  186. printf '%s' "$to_type" | xdotool type --delay ${xdotool_delay} --clearmodifiers --file -
  187. xset r "$x_repeat_enabled"
  188. unset x_repeat_enabled
  189. unset to_type
  190. clearUp
  191. }
  192. generateOTP () {
  193. checkIfPass
  194. # First, we check if there is a non-conventional OTP command in the pass file
  195. if PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep -q "${OTPmethod_field}: "; then
  196. # We execute the commands after otp_method: AS-IS
  197. bash -c "$(PASSWORD_STORE_DIR="${root}" pass "$selected_password" | grep "${OTPmethod_field}: " | cut -d' ' -f2-)"
  198. else
  199. # If there is no method defined, fallback to pass-otp
  200. PASSWORD_STORE_DIR="${root}" pass otp "$selected_password"
  201. fi
  202. clearUp
  203. }
  204. copyUser () {
  205. checkIfPass
  206. printf '%s' "${stuff[${USERNAME_field}]}" | doClip
  207. clearUp
  208. }
  209. copyField () {
  210. checkIfPass
  211. printf '%s' "${stuff[${copyfield}]}" | doClip
  212. clearUp
  213. }
  214. copyURL () {
  215. checkIfPass
  216. printf '%s' "${stuff[${URL_field}]}" | doClip
  217. clearUp
  218. }
  219. copyPass () {
  220. checkIfPass
  221. printf '%s' "$password" | doClip
  222. if [[ $notify == "true" ]]; then
  223. notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds"
  224. fi
  225. if [[ $notify == "true" ]]; then
  226. (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "rofi-pass" "Clipboard cleared") &
  227. elif [[ $notify == "false" ]]; then
  228. (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) &
  229. fi
  230. }
  231. viewEntry () {
  232. checkIfPass
  233. showEntry "${selected_password}"
  234. }
  235. generatePass () {
  236. askmenu_content=(
  237. "Yes"
  238. "No"
  239. )
  240. askGenMenu=$(printf '%s\n' "${askmenu_content[@]}" | _rofi -dmenu -p "Generate new Password for ${selected_password}? > ")
  241. askgen_exit=$?
  242. if [[ $askgen_exit -eq 1 ]]; then
  243. exit
  244. fi
  245. if [[ $askGenMenu == "Yes" ]]; then
  246. true
  247. elif [[ $askGenMenu == "No" ]]; then
  248. actionMenu
  249. fi
  250. checkIfPass
  251. symbols_content=(
  252. "0 Cancel"
  253. "1 Yes"
  254. "2 No"
  255. )
  256. symbols=$(printf '%s\n' "${symbols_content[@]}" | _rofi -dmenu -p "Use Symbols? > ")
  257. symbols_val=$?
  258. if [[ $symbols_val -eq 1 ]]; then
  259. exit
  260. fi
  261. if [[ $symbols == "0 Cancel" ]]; then
  262. mainMenu;
  263. elif [[ $symbols == "1 Yes" ]]; then
  264. symbols="";
  265. elif [[ $symbols == "2 No" ]]; then
  266. symbols="-n";
  267. fi
  268. HELP="<span color='$help_color'>Enter Number or hit Enter to use default length</span>"
  269. length=$(printf '%s' "" | _rofi -dmenu -mesg "${HELP}" -p "Password length? (Default: ${password_length}) > ")
  270. length_exit=$?
  271. if [[ $length_exit -eq 1 ]]; then
  272. exit
  273. fi
  274. if [[ $length == "" ]]; then
  275. PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${password_length}" > /dev/null;
  276. else
  277. PASSWORD_STORE_DIR="${root}" pass generate ${symbols} -i "$selected_password" "${length}" > /dev/null;
  278. fi
  279. }
  280. # main Menu
  281. mainMenu () {
  282. if [[ $1 == "--bmarks" ]]; then
  283. selected_password="$(list_passwords 2>/dev/null \
  284. | _rofi -mesg "Bookmarks Mode. ${switch} to switch" \
  285. -dmenu \
  286. -kb-custom-10 "${switch}" \
  287. -select "$entry" \
  288. -p "rofi-pass > ")"
  289. rofi_exit=$?
  290. if [[ $rofi_exit -eq 1 ]]; then
  291. exit
  292. elif [[ $rofi_exit -eq 19 ]]; then
  293. ${basecommand}
  294. elif [[ $rofi_exit -eq 0 ]]; then
  295. openURL
  296. fi
  297. else
  298. unset selected_password
  299. args=( -dmenu
  300. -kb-custom-1 "${autotype}"
  301. -kb-custom-2 "${type_user}"
  302. -kb-custom-3 "${type_pass}"
  303. -kb-custom-4 "${open_url}"
  304. -kb-custom-5 "${copy_name}"
  305. -kb-custom-6 "${copy_pass}"
  306. -kb-custom-7 "${show}"
  307. -kb-custom-8 "${copy_url}"
  308. -kb-custom-9 "${type_menu}"
  309. -kb-custom-10 "${previous_root}"
  310. -kb-custom-11 "${next_root}"
  311. -kb-custom-14 "${action_menu}"
  312. -kb-custom-15 "${copy_menu}"
  313. -kb-custom-16 "${help}"
  314. -kb-custom-17 "${switch}"
  315. -kb-custom-18 "${insert_pass}"
  316. -kb-custom-19 "${qrcode}"
  317. )
  318. args+=( -kb-mode-previous "" # These keyboard shortcut options are needed, because
  319. -kb-mode-next "" # Shift+<Left|Right> are otherwise taken by rofi.
  320. -select "$entry"
  321. -p "> " )
  322. if [[ ${#roots[@]} -gt "1" || $custom_root == "true" ]]; then
  323. args+=(-mesg "PW Store: ${root}")
  324. fi
  325. selected_password="$(list_passwords 2>/dev/null | _rofi "${args[@]}")"
  326. rofi_exit=$?
  327. if [[ $rofi_exit -eq 1 ]]; then
  328. exit
  329. fi
  330. # Actions based on exit code, which do not need the entry.
  331. # The exit code for -kb-custom-X is X+9.
  332. case "${rofi_exit}" in
  333. 19) roots_index=$(( (roots_index-1+roots_length) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
  334. 20) roots_index=$(( (roots_index+1) % roots_length)); root=${roots[$roots_index]}; mainMenu; return;;
  335. 25) helpMenu; return;;
  336. 26) ${basecommand} --bmarks; return;;
  337. esac
  338. mapfile -t password_temp < <(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
  339. password=${password_temp[0]}
  340. if [[ ${password} == "#FILE="* ]]; then
  341. pass_file="${password#*=}"
  342. mapfile -t password_temp2 < <(PASSWORD_STORE_DIR="${root}" pass show "${pass_file}")
  343. password=${password_temp2[0]}
  344. fi
  345. fields=$(printf '%s\n' "${password_temp[@]:1}" | awk '$1 ~ /:$/ || /otpauth:\/\// {$1=$1;print}')
  346. declare -A stuff
  347. stuff["pass"]=${password}
  348. if [[ -n $fields ]]; then
  349. while read -r LINE; do
  350. unset _id _val
  351. case "$LINE" in
  352. "otpauth://"*|"${OTPmethod_field}"*)
  353. _id="OTP"
  354. _val=""
  355. ;;
  356. *)
  357. _id="${LINE%%: *}"
  358. _val="${LINE#* }"
  359. ;;
  360. esac
  361. if [[ -n "$_id" ]]; then
  362. stuff["${_id}"]=${_val}
  363. fi
  364. done < <(printf '%s\n' "${fields}")
  365. if test "${stuff['autotype']+autotype}"; then
  366. :
  367. else
  368. stuff["autotype"]="${USERNAME_field} :tab pass"
  369. fi
  370. fi
  371. fi
  372. if [[ -z "${stuff["${AUTOTYPE_field}"]}" ]]; then
  373. if [[ -n $default_autotype ]]; then
  374. stuff["${AUTOTYPE_field}"]="${default_autotype}"
  375. fi
  376. fi
  377. if [[ -z "${stuff["${USERNAME_field}"]}" ]]; then
  378. if [[ -n $default_user ]]; then
  379. if [[ "$default_user" == ":filename" ]]; then
  380. stuff["${USERNAME_field}"]="$(basename "$selected_password")"
  381. else
  382. stuff["${USERNAME_field}"]="${default_user}"
  383. fi
  384. fi
  385. fi
  386. pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
  387. # actions based on keypresses
  388. # The exit code for -kb-custom-X is X+9.
  389. case "${rofi_exit}" in
  390. 0) typeMenu;;
  391. 10) sleep $wait; autopass;;
  392. 11) sleep $wait; typeUser;;
  393. 12) sleep $wait; typePass;;
  394. 13) openURL;;
  395. 14) copyUser;;
  396. 15) copyPass;;
  397. 16) viewEntry;;
  398. 17) copyURL;;
  399. 18) default_do="menu" typeMenu;;
  400. 23) actionMenu;;
  401. 24) copyMenu;;
  402. 27) insertPass;;
  403. 28) generateQrCode;;
  404. esac
  405. clearUp
  406. }
  407. clearUp () {
  408. password=''
  409. selected_password=''
  410. unset stuff
  411. unset password
  412. unset selected_password
  413. unset password_temp
  414. unset stuff
  415. }
  416. helpMenu () {
  417. _rofi -dmenu -mesg "Hint: All hotkeys are configurable in config file" -p "Help > " <<- EOM
  418. ${autotype}: Autotype
  419. ${type_user}: Type Username
  420. ${type_pass}: Type Password
  421. ${qrcode}: Generate and display qrcode
  422. ---
  423. ${copy_name}: Copy Username
  424. ${copy_pass}: Copy Password
  425. ${copy_url}: Copy URL
  426. ${open_url}: Open URL
  427. ${copy_menu}: Copy Custom Field
  428. ---
  429. ${action_menu}: Edit, Move, Delete, Re-generate Submenu
  430. ${show}: Show Password File
  431. ${insert_pass}: Insert new Pass Entry
  432. ${switch}: Switch Pass/Bookmark Mode
  433. ---
  434. ${previous_root}: Switch to previous password store (--root)
  435. ${next_root}: Switch to next password store (--root)
  436. EOM
  437. help_val=$?
  438. if [[ $help_val -eq 1 ]]; then
  439. exit;
  440. else
  441. unset helptext; mainMenu;
  442. fi
  443. }
  444. typeMenu () {
  445. if [[ -n $default_do ]]; then
  446. if [[ $default_do == "menu" ]]; then
  447. checkIfPass
  448. local -a keys=("${!stuff[@]}")
  449. keys=("${keys[@]/$AUTOTYPE_field}")
  450. typefield=$({ printf '%s' "${AUTOTYPE_field}" ; printf '%s\n' "${keys[@]}" | sort; } | _rofi -dmenu -p "Choose Field to type > ")
  451. typefield_exit=$?
  452. if [[ $typefield_exit -eq 1 ]]; then
  453. exit
  454. fi
  455. case "$typefield" in
  456. '') exit;;
  457. 'pass') sleep $wait; typePass;;
  458. "${AUTOTYPE_field}") sleep $wait; autopass;;
  459. *) sleep $wait; typeField
  460. esac
  461. clearUp
  462. elif [[ $default_do == "${AUTOTYPE_field}" ]]; then
  463. sleep $wait; autopass
  464. else
  465. ${default_do}
  466. fi
  467. fi
  468. }
  469. copyMenu () {
  470. checkIfPass
  471. copyfield=$(printf '%s\n' "${!stuff[@]}" | sort | _rofi -dmenu -p "Choose Field to copy > ")
  472. val=$?
  473. if [[ $val -eq 1 ]]; then
  474. exit;
  475. fi
  476. if [[ $copyfield == "pass" ]]; then
  477. copyPass;
  478. else
  479. copyField
  480. fi
  481. clearUp
  482. }
  483. actionMenu () {
  484. checkIfPass
  485. action_content=("< Return"
  486. "---"
  487. "1 Move Password File"
  488. "2 Copy Password File"
  489. "3 Delete Password File"
  490. "4 Edit Password File"
  491. "5 Generate New Password"
  492. )
  493. action=$(printf '%s\n' "${action_content[@]}" | _rofi -dmenu -p "Choose Action > ")
  494. if [[ ${action} == "1 Move Password File" ]]; then
  495. manageEntry move;
  496. elif [[ ${action} == "3 Delete Password File" ]]; then
  497. manageEntry delete;
  498. elif [[ ${action} == "2 Copy Password File" ]]; then
  499. manageEntry copy;
  500. elif [[ ${action} == "4 Edit Password File" ]]; then
  501. manageEntry edit;
  502. elif [[ ${action} == "5 Generate New Password" ]]; then
  503. generatePass;
  504. elif [[ ${action} == "< Return" ]]; then
  505. mainMenu;
  506. elif [[ ${action} == "" ]]; then
  507. exit
  508. fi
  509. }
  510. showEntry () {
  511. if [[ -z $pass_content ]]; then
  512. pass_temp=$(PASSWORD_STORE_DIR="${root}" pass show "$selected_password")
  513. password="${pass_temp%%$'\n'*}"
  514. pass_key_value=$(printf '%s\n' "${pass_temp}" | tail -n+2 | grep ': ')
  515. declare -A stuff
  516. while read -r LINE; do
  517. _id="${LINE%%: *}"
  518. _val="${LINE#* }"
  519. stuff["${_id}"]=${_val}
  520. done < <(printf '%s\n' "${pass_key_value}")
  521. stuff["pass"]=${password}
  522. if test "${stuff['autotype']+autotype}"; then
  523. :
  524. else
  525. stuff["autotype"]="${USERNAME_field} :tab pass"
  526. fi
  527. pass_content="$(for key in "${!stuff[@]}"; do printf '%s\n' "${key}: ${stuff[$key]}"; done)"
  528. fi
  529. bla_content=("< Return"
  530. "${pass_content}"
  531. )
  532. bla=$(printf '%s\n' "${bla_content[@]}" | _rofi -dmenu -mesg "Enter: Copy entry to clipboard" -p "> ")
  533. rofi_exit=$?
  534. word=$(printf '%s' "$bla" | gawk -F': ' '{print $1}')
  535. if [[ ${rofi_exit} -eq 1 ]]; then
  536. exit
  537. elif [[ ${rofi_exit} -eq 0 ]]; then
  538. if [[ ${bla} == "< Return" ]]; then
  539. mainMenu
  540. else
  541. if [[ -z $(printf '%s' "${stuff[${word}]}") ]]; then
  542. printf '%s' "$word" | doClip
  543. else
  544. printf '%s' "${stuff[${word}]}" | doClip
  545. fi
  546. if [[ $notify == "true" ]]; then
  547. notify-send "rofi-pass" "Copied Password\\nClearing in $clip_clear seconds"
  548. fi
  549. if [[ $notify == "true" ]]; then
  550. (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard | notify-send "rofi-pass" "Clipboard cleared") &
  551. elif [[ $notify == "false" ]]; then
  552. (sleep $clip_clear; printf '%s' "" | _clip_in_primary; printf '%s' "" | _clip_in_clipboard) &
  553. fi
  554. exit
  555. fi
  556. fi
  557. exit
  558. unset stuff
  559. unset password
  560. unset selected_password
  561. unset password_temp
  562. unset stuff
  563. exit
  564. }
  565. manageEntry () {
  566. if [[ "$1" == "edit" ]]; then
  567. EDITOR=$EDITOR PASSWORD_STORE_DIR="${root}" pass edit "${selected_password}"
  568. mainMenu
  569. elif [[ $1 == "move" ]]; then
  570. cd "${root}" || exit
  571. group_array=(*/)
  572. group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
  573. if [[ $group == "" ]]; then
  574. exit
  575. fi
  576. PASSWORD_STORE_DIR="${root}" pass mv "$selected_password" "${group}"
  577. mainMenu
  578. elif [[ $1 == "copy" ]]; then
  579. cd "${root}" || exit
  580. group_array=(*/)
  581. group=$(printf '%s\n' "${group_array[@]%/}" | _rofi -dmenu -p "Choose Group > ")
  582. if [[ $group == "" ]]; then
  583. exit
  584. else
  585. new_name="$(listgpg | _rofi -dmenu -format 'f' -mesg "Copying to same Group. Please enter a name for the new entry" -p "> ")"
  586. fi
  587. PASSWORD_STORE_DIR="${root}" pass cp "$selected_password" "${group}/${new_name}"
  588. mainMenu
  589. elif [[ "$1" == "delete" ]]; then
  590. HELP="<span color='$help_color'>Selected entry: ${selected_password}</span>"
  591. ask_content=("Yes"
  592. "No"
  593. )
  594. ask=$(printf '%s\n' "${ask_content[@]}" | _rofi -mesg "${HELP}" -dmenu -p "Are You Sure? > ")
  595. if [[ "$ask" == "Yes" ]]; then
  596. PASSWORD_STORE_DIR="${root}" pass rm --force "${selected_password}"
  597. elif [[ "$ask" == "No" ]]; then
  598. mainMenu
  599. elif [[ -z "$ask" ]]; then
  600. exit
  601. fi
  602. else
  603. mainMenu
  604. fi
  605. }
  606. edit_pass() {
  607. if [[ $edit_new_pass == "true" ]]; then
  608. PASSWORD_STORE_DIR="${root}" pass edit "${1}"
  609. fi
  610. }
  611. insertPass () {
  612. url=$(_clip_out_clipboard)
  613. if [[ "${url:0:4}" == "http" ]]; then
  614. 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=".")"
  615. help_content="Domain: ${domain_name}
  616. Type name, make sure it is unique"
  617. else
  618. help_content="Hint: Copy URL to clipboard before calling this menu.
  619. Type name, make sure it is unique"
  620. fi
  621. cd "${root}" || exit
  622. group_array=(*/)
  623. grouplist=$(printf '%s\n' "${group_array[@]%/}")
  624. name="$(listgpg | _rofi -dmenu -format 'f' -filter "${domain_name}" -mesg "${help_content}" -p "> ")"
  625. val=$?
  626. if [[ $val -eq 1 ]]; then
  627. exit
  628. fi
  629. user_content=("${default_user2}"
  630. "${USER}"
  631. "${default_user}"
  632. )
  633. user=$(printf '%s\n' "${user_content[@]}" | _rofi -dmenu -mesg "Chose Username or type" -p "> ")
  634. val=$?
  635. if [[ $val -eq 1 ]]; then
  636. exit
  637. fi
  638. group_content=("No Group"
  639. "---"
  640. "${grouplist}"
  641. )
  642. group=$(printf '%s\n' "${group_content[@]}" | _rofi -dmenu -p "Choose Group > ")
  643. val=$?
  644. if [[ $val -eq 1 ]]; then
  645. exit
  646. fi
  647. pw=$(printf '%s' "Generate" | _rofi -dmenu -password -p "Password > " -mesg "Type Password or hit Enter to generate one")
  648. if [[ $pw == "Generate" ]]; then
  649. pw=$(_pwgen "${password_length}")
  650. fi
  651. clear
  652. if [[ "$group" == "No Group" ]]; then
  653. if [[ $url == http* ]]; then
  654. pass_content=("${pw}"
  655. "---"
  656. "${USERNAME_field}: ${user}"
  657. "${URL_field}: ${url}"
  658. )
  659. printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}"
  660. else
  661. pass_content=("${pw}"
  662. "---"
  663. "${USERNAME_field}: ${user}"
  664. )
  665. printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${name}" > /dev/null && edit_pass "${name}"
  666. fi
  667. else
  668. if [[ $url == http* ]]; then
  669. pass_content=("${pw}"
  670. "---"
  671. "${USERNAME_field}: ${user}"
  672. "${URL_field}: ${url}"
  673. )
  674. printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}"
  675. else
  676. pass_content=("${pw}"
  677. "---"
  678. "${USERNAME_field}: ${user}"
  679. )
  680. printf '%s\n' "${pass_content[@]}" | PASSWORD_STORE_DIR="${root}" pass insert -m "${group}/${name}" > /dev/null && edit_pass "${group}/${name}"
  681. fi
  682. fi
  683. }
  684. help_msg () {
  685. cat <<'EOF'
  686. Usage:
  687. rofi-pass [command]
  688. Commands:
  689. --insert insert new entry to password store
  690. --root set custom root directories (colon separated)
  691. --last-used highlight last used item
  692. --show-last show details of last used Entry
  693. --bmarks start in bookmarks mode
  694. rofi-pass version 1.5.3
  695. EOF
  696. }
  697. get_config_file () {
  698. configs=("$ROFI_PASS_CONFIG"
  699. "$config_dir/rofi-pass/config"
  700. "/etc/rofi-pass.conf")
  701. # return the first config file with a valid path
  702. for config in "${configs[@]}"; do
  703. # '-n' is needed in case ROFI_PASS_CONFIG is not set
  704. if [[ -n "${config}" && -f "${config}" ]]; then
  705. printf "%s" "$config"
  706. return
  707. fi
  708. done
  709. }
  710. main () {
  711. # load config file
  712. config_file="$(get_config_file)"
  713. [[ -n "$config_file" ]] && source "$config_file"
  714. # create tmp dir
  715. if [[ ! -d "$cache_dir/rofi-pass" ]]; then
  716. mkdir -p "$cache_dir/rofi-pass"
  717. fi
  718. # fix keyboard layout if enabled in config
  719. if [[ $fix_layout == "true" ]]; then
  720. layout_cmd
  721. fi
  722. # set help color
  723. if [[ $help_color == "" ]]; then
  724. HELP_COLOR_ATTRIBUTE=""
  725. else
  726. HELP_COLOR_ATTRIBUTE=" color='$help_color'"
  727. fi
  728. HELP="<span${HELP_COLOR_ATTRIBUTE}>Selected entry: ${selected_password}</span>"
  729. # check for BROWSER variable, use xdg-open as fallback
  730. if [[ -z $BROWSER ]]; then
  731. export BROWSER=xdg-open
  732. fi
  733. # check if alternative root directory was given on commandline
  734. if [[ -r "$cache_dir/rofi-pass/last_used" ]] && [[ $1 == "--last-used" || $1 == "--show-last" ]]; then
  735. roots=("$(awk -F ': ' '{ print $1 }' "$cache_dir/rofi-pass/last_used")")
  736. elif [[ -n "$2" && "$1" == "--root" ]]; then
  737. custom_root=true; IFS=: read -r -a roots <<< "$2"
  738. elif [[ -n $root ]]; then
  739. custom_root=true; IFS=: read -r -a roots <<< "${root}"
  740. elif [[ -n ${PASSWORD_STORE_DIR} ]]; then
  741. roots=("${PASSWORD_STORE_DIR}")
  742. else
  743. roots=("$HOME/.password-store")
  744. fi
  745. roots_index=0
  746. roots_length=${#roots[@]}
  747. export root=${roots[$roots_index]}
  748. export PASSWORD_STORE_DIR="${root}"
  749. case $1 in
  750. --insert)
  751. insertPass
  752. ;;
  753. --root)
  754. mainMenu
  755. ;;
  756. --help)
  757. help_msg
  758. ;;
  759. --last-used)
  760. if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then
  761. entry="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")"
  762. fi
  763. mainMenu
  764. ;;
  765. --show-last)
  766. if [[ -r "$cache_dir/rofi-pass/last_used" ]]; then
  767. selected_password="$(awk -F ': ' '{ print $2 }' "$cache_dir/rofi-pass/last_used")" viewEntry
  768. else
  769. mainMenu
  770. fi
  771. ;;
  772. --bmarks)
  773. mainMenu --bmarks;
  774. ;;
  775. *)
  776. mainMenu
  777. ;;
  778. esac
  779. }
  780. main "$@"