rofi-pass.sh 21 KB

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