rofi-pass.sh 21 KB

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