org-passwords.el 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. ;;; org-passwords.el --- org derived mode for managing passwords
  2. ;; Author: Jorge A. Alfaro-Murillo <jorge.alfaro-murillo@yale.edu>
  3. ;; Created: December 26, 2012
  4. ;; Keywords: passwords, password
  5. ;; This file is NOT part of GNU Emacs.
  6. ;;
  7. ;; This program is free software: you can redistribute it and/or modify
  8. ;; it under the terms of the GNU General Public License as published by
  9. ;; the Free Software Foundation, either version 3 of the License, or
  10. ;; (at your option) any later version.
  11. ;; This program is distributed in the hope that it will be useful,
  12. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;; GNU General Public License for more details.
  15. ;; You should have received a copy of the GNU General Public License
  16. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  17. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  18. ;;
  19. ;;; Commentary:
  20. ;; This file contains the code for managing your passwords with
  21. ;; Org-mode. It is part of org/contrib (see https://orgmode.org/). If
  22. ;; you want to contribute with development, or have a problem, do it
  23. ;; here: https://bitbucket.org/alfaromurillo/org-passwords.el
  24. ;; A basic setup needs to indicate a passwords file, and a dictionary
  25. ;; for the random words:
  26. ;; (require 'org-passwords)
  27. ;; (setq org-passwords-file "~/documents/passwords.gpg")
  28. ;; (setq org-passwords-random-words-dictionary "/etc/dictionaries-common/words")
  29. ;; Basic usage:
  30. ;; `M-x org-passwords' opens the passwords file in
  31. ;; `org-passwords-mode'.
  32. ;; `M-x org-passwords-generate-password' generates a random string
  33. ;; of numbers, lowercase letters and uppercase letters.
  34. ;; `C-u M-x org-passwords-generate-password' generates a random
  35. ;; string of numbers, lowercase letters, uppercase letters and
  36. ;; symbols.
  37. ;; `M-x org-passwords-random-words' concatenates random words from
  38. ;; the dictionary defined by `org-passwords-random-words-dictionary'
  39. ;; into a string, each word separated by the string defined in
  40. ;; `org-passwords-random-words-separator'.
  41. ;; `C-u M-x org-passwords-random-words' does the same as above, and
  42. ;; also makes substitutions according to
  43. ;; `org-passwords-random-words-substitutions'.
  44. ;; It is also useful to set up keybindings for the functions
  45. ;; `org-passwords-copy-username', `org-passwords-copy-password' and
  46. ;; `org-passwords-open-url' in the `org-passwords-mode', to easily
  47. ;; make the passwords and usernames available to the facility for
  48. ;; pasting text of the window system (clipboard on X and MS-Windows,
  49. ;; pasteboard on Nextstep/Mac OS, etc.), without inserting them in the
  50. ;; kill-ring. You can set for example:
  51. ;; (eval-after-load "org-passwords"
  52. ;; '(progn
  53. ;; (define-key org-passwords-mode-map
  54. ;; (kbd "C-c u")
  55. ;; 'org-passwords-copy-username)
  56. ;; (define-key org-passwords-mode-map
  57. ;; (kbd "C-c p")
  58. ;; 'org-passwords-copy-password)
  59. ;; (kbd "C-c o")
  60. ;; 'org-passwords-open-url)))
  61. ;; Finally, to enter new passwords, you can use `org-capture' and a
  62. ;; minimal template like:
  63. ;; ("p" "password" entry (file "~/documents/passwords.gpg")
  64. ;; "* %^{Title}\n %^{URL}p %^{USERNAME}p %^{PASSWORD}p")
  65. ;; When asked for the password you can then call either
  66. ;; `org-passwords-generate-password' or `org-passwords-random-words'.
  67. ;; Be sure to enable recursive minibuffers to call those functions
  68. ;; from the minibuffer:
  69. ;; (setq enable-recursive-minibuffers t)
  70. ;;; Code:
  71. (require 'org)
  72. ;;;###autoload
  73. (define-derived-mode org-passwords-mode org-mode
  74. "org-passwords-mode"
  75. "Mode for storing passwords"
  76. nil)
  77. (defgroup org-passwords nil
  78. "Options for password management."
  79. :group 'org)
  80. (defcustom org-passwords-password-property "PASSWORD"
  81. "Name of the property for password entry."
  82. :type 'string
  83. :group 'org-passwords)
  84. (defcustom org-passwords-username-property "USERNAME"
  85. "Name of the property for user name entry."
  86. :type 'string
  87. :group 'org-passwords)
  88. (defcustom org-passwords-url-property "URL"
  89. "Name of the property for URL entry."
  90. :type 'string
  91. :group 'org-passwords)
  92. (defcustom org-passwords-file nil
  93. "Default file name for the file that contains the passwords."
  94. :type 'file
  95. :group 'org-passwords)
  96. (defcustom org-passwords-time-opened "1 min"
  97. "Time that the password file will remain open. It has to be a
  98. string, a number followed by units."
  99. :type 'str
  100. :group 'org-passwords)
  101. (defcustom org-passwords-default-password-size "20"
  102. "Default number of characters to use in
  103. org-passwords-generate-password. It has to be a string."
  104. :type 'str
  105. :group 'org-passwords)
  106. (defcustom org-passwords-random-words-dictionary nil
  107. "Default file name for the file that contains a dictionary of
  108. words for `org-passwords-random-words'. Each non-empty line in
  109. the file is considered a word."
  110. :type 'file
  111. :group 'org-passwords)
  112. (defcustom org-passwords-default-random-words-number "5"
  113. "Default number of words to use in org-passwords-random-words.
  114. It has to be a string."
  115. :type 'str
  116. :group 'org-passwords)
  117. (defvar org-passwords-random-words-separator "-"
  118. "A string to separate words in `org-passwords-random-words'.")
  119. (defvar org-passwords-random-words-substitutions
  120. '(("a" . "@")
  121. ("e" . "3")
  122. ("o" . "0"))
  123. "A list of substitutions to be made with
  124. `org-passwords-random-words' if it is called with
  125. `universal-argument'. Each element is pair of
  126. strings (SUBSTITUTE-THIS . BY-THIS).")
  127. (defun org-passwords-copy-password ()
  128. "Makes the password available to other programs. Puts the
  129. password of the entry at the location of the cursor in the
  130. facility for pasting text of the window system (clipboard on X
  131. and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
  132. putting it in the kill ring."
  133. (interactive)
  134. (funcall interprogram-cut-function
  135. (org-entry-get (point)
  136. org-passwords-password-property)))
  137. (defun org-passwords-copy-username ()
  138. "Makes the password available to other programs. Puts the
  139. username of the entry at the location of the cursor in the
  140. facility for pasting text of the window system (clipboard on X
  141. and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
  142. putting it in the kill ring."
  143. (interactive)
  144. (funcall interprogram-cut-function
  145. (org-entry-get (point)
  146. org-passwords-username-property
  147. t)))
  148. (defun org-passwords-open-url ()
  149. "Browse the URL associated with the entry at the location of
  150. the cursor."
  151. (interactive)
  152. (browse-url (org-entry-get (point)
  153. org-passwords-url-property
  154. t)))
  155. ;;;###autoload
  156. (defun org-passwords (&optional arg)
  157. "Open the password file. Open the password file defined by the
  158. variable `org-password-file' in read-only mode and kill that
  159. buffer later according to the value of the variable
  160. `org-passwords-time-opened'. It also adds the `org-password-file'
  161. to the auto-mode-alist so that it is opened with its mode being
  162. `org-passwords-mode'.
  163. With prefix arg ARG, the command does not set up a timer to kill the buffer.
  164. With a double prefix arg \\[universal-argument] \\[universal-argument], open the file for editing.
  165. "
  166. (interactive "P")
  167. (if org-passwords-file
  168. (progn
  169. (add-to-list 'auto-mode-alist
  170. (cons
  171. (regexp-quote
  172. (expand-file-name org-passwords-file))
  173. 'org-passwords-mode))
  174. (if (equal arg '(4))
  175. (find-file-read-only org-passwords-file)
  176. (if (equal arg '(16))
  177. (find-file org-passwords-file)
  178. (progn
  179. (find-file-read-only org-passwords-file)
  180. (org-passwords-set-up-kill-password-buffer)))))
  181. (minibuffer-message "No default password file defined. Set the variable `org-password-file'.")))
  182. (defun org-passwords-set-up-kill-password-buffer ()
  183. (run-at-time org-passwords-time-opened
  184. nil
  185. '(lambda ()
  186. (if (get-file-buffer org-passwords-file)
  187. (kill-buffer
  188. (get-file-buffer org-passwords-file))))))
  189. ;;; Password generator
  190. ;; Set random number seed from current time and pid. Otherwise
  191. ;; `random' gives the same results every time emacs restarts.
  192. (random t)
  193. (defun org-passwords-generate-password (arg)
  194. "Ask a number of characters and insert a password of that size.
  195. Password has a random string of numbers, lowercase letters, and
  196. uppercase letters. Argument ARG include symbols."
  197. (interactive "P")
  198. (let ((number-of-chars
  199. (read-from-minibuffer
  200. (concat "Number of characters (default "
  201. org-passwords-default-password-size
  202. "): ")
  203. nil
  204. nil
  205. t
  206. nil
  207. org-passwords-default-password-size)))
  208. (if arg
  209. (insert (org-passwords-generate-password-with-symbols "" number-of-chars))
  210. (insert (org-passwords-generate-password-without-symbols "" number-of-chars)))))
  211. (defun org-passwords-generate-password-with-symbols (previous-string nums-of-chars)
  212. "Return a string consisting of PREVIOUS-STRING and
  213. NUMS-OF-CHARS random characters."
  214. (if (eq nums-of-chars 0) previous-string
  215. (org-passwords-generate-password-with-symbols
  216. (concat previous-string
  217. (char-to-string
  218. ;; symbols, letters, numbers are from 33 to 126
  219. (+ (random (- 127 33)) 33)))
  220. (1- nums-of-chars))))
  221. (defun org-passwords-generate-password-without-symbols (previous-string nums-of-chars)
  222. "Return string consisting of PREVIOUS-STRING and NUMS-OF-CHARS
  223. random numbers, lowercase letters, and numbers."
  224. (if (eq nums-of-chars 0)
  225. previous-string
  226. ; There are 10 numbers, 26 lowercase letters and 26 uppercase
  227. ; letters. 10 + 26 + 26 = 62. The number characters go from 48
  228. ; to 57, the uppercase letters from 65 to 90, and the lowercase
  229. ; from 97 to 122. The following makes each equally likely.
  230. (let ((temp-value (random 62)))
  231. (cond ((< temp-value 10)
  232. ; If temp-value<10, then add a number
  233. (org-passwords-generate-password-without-symbols
  234. (concat previous-string
  235. (char-to-string (+ 48 temp-value)))
  236. (1- nums-of-chars)))
  237. ((and (> temp-value 9) (< temp-value 36))
  238. ; If 9<temp-value<36, then add an uppercase letter
  239. (org-passwords-generate-password-without-symbols
  240. (concat previous-string
  241. (char-to-string (+ 65 (- temp-value 10))))
  242. (1- nums-of-chars)))
  243. ((> temp-value 35)
  244. ; If temp-value>35, then add a lowecase letter
  245. (org-passwords-generate-password-without-symbols
  246. (concat previous-string
  247. (char-to-string (+ 97 (- temp-value 36))))
  248. (1- nums-of-chars)))))))
  249. ;;; Random words
  250. (defun org-passwords-random-words (arg)
  251. "Ask for a number of words and inserts a sequence of that many
  252. random words from the list in the file
  253. `org-passwords-random-words-dictionary' separated by
  254. `org-passwords-random-words-separator'. ARG make substitutions in
  255. the words as defined by
  256. `org-passwords-random-words-substitutions'."
  257. (interactive "P")
  258. (if org-passwords-random-words-dictionary
  259. (let ((number-of-words
  260. (read-from-minibuffer
  261. (concat "Number of words (default "
  262. org-passwords-default-random-words-number
  263. "): ")
  264. nil
  265. nil
  266. t
  267. nil
  268. org-passwords-default-random-words-number))
  269. (list-of-words
  270. (with-temp-buffer
  271. (insert-file-contents
  272. org-passwords-random-words-dictionary)
  273. (split-string (buffer-string) "\n" t))))
  274. (insert
  275. (org-passwords-substitute
  276. (org-passwords-random-words-attach-number-of-words
  277. (nth (random (length list-of-words))
  278. list-of-words)
  279. (1- number-of-words)
  280. list-of-words
  281. org-passwords-random-words-separator)
  282. (if arg
  283. org-passwords-random-words-substitutions
  284. nil))))
  285. (minibuffer-message
  286. "No default dictionary file defined. Set the variable `org-passwords-random-words-dictionary'.")))
  287. (defun org-passwords-random-words-attach-number-of-words
  288. (previous-string number-of-words list-of-words separator)
  289. "Returns a string consisting of PREVIOUS-STRING followed by a
  290. succession of NUMBER-OF-WORDS random words from the list LIST-OF-WORDS
  291. separated SEPARATOR."
  292. (if (eq number-of-words 0)
  293. previous-string
  294. (org-passwords-random-words-attach-number-of-words
  295. (concat previous-string
  296. separator
  297. (nth (random (length list-of-words)) list-of-words))
  298. (1- number-of-words)
  299. list-of-words
  300. separator)))
  301. (defun org-passwords-substitute (string-to-change list-of-substitutions)
  302. "Substitutes each appearance in STRING-TO-CHANGE of the `car' of
  303. each element of LIST-OF-SUBSTITUTIONS by the `cdr' of that
  304. element. For example:
  305. (org-passwords-substitute \"ab\" \'((\"a\" . \"b\") (\"b\" . \"c\")))
  306. => \"bc\"
  307. Substitutions are made in order of the list, so for example:
  308. (org-passwords-substitute \"ab\" \'((\"ab\" . \"c\") (\"b\" . \"d\")))
  309. => \"c\""
  310. (if list-of-substitutions
  311. (concat (org-passwords-concat-this-with-string
  312. (cdar list-of-substitutions)
  313. (mapcar (lambda (x)
  314. (org-passwords-substitute
  315. x
  316. (cdr list-of-substitutions)))
  317. (split-string string-to-change
  318. (caar list-of-substitutions)))))
  319. string-to-change))
  320. (defun org-passwords-concat-this-with-string (this list-of-strings)
  321. "Put the string THIS in between every string in LIST-OF-STRINGS. For example:
  322. (org-passwords-concat-this-with-string \"Here\" \'(\"First\" \"Second\" \"Third\"))
  323. => \"FirstHereSencondHereThird\""
  324. (if (cdr list-of-strings)
  325. (concat (car list-of-strings)
  326. this
  327. (org-passwords-concat-this-with-string
  328. this
  329. (cdr list-of-strings)))
  330. (car list-of-strings)))
  331. (provide 'org-passwords)
  332. ;;; org-passwords.el ends here