org-passwords.el 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  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.
  22. ;; A basic setup needs to indicate a passwords file, and a dictionary
  23. ;; for the random words:
  24. ;; (require org-passwords)
  25. ;; (setq org-passwords-file "~/documents/passwords.gpg")
  26. ;; (setq org-passwords-random-words-dictionary "/etc/dictionaries-common/words")
  27. ;; Basic usage:
  28. ;; `M-x org-passwords' opens the passwords file in
  29. ;; `org-passwords-mode'.
  30. ;; `M-x org-passwords-generate-password' generates a random string
  31. ;; of numbers, lowercase letters and uppercase letters.
  32. ;; `C-u M-x org-passwords-generate-password' generates a random
  33. ;; string of numbers, lowercase letters, uppercase letters and
  34. ;; symbols.
  35. ;; `M-x org-passwords-random-words' concatenates random words from
  36. ;; the dictionary defined by `org-passwords-random-words-dictionary'
  37. ;; into a string, each word separated by the string defined in
  38. ;; `org-passwords-random-words-separator'.
  39. ;; `C-u M-x org-passwords-random-words' does the same as above, and
  40. ;; also makes substitutions according to
  41. ;; `org-passwords-random-words-substitutions'.
  42. ;; It is also useful to set up keybindings for the functions
  43. ;; `org-passwords-copy-username' and
  44. ;; `org-passwords-copy-password' in the
  45. ;; `org-passwords-mode', to easily make the passwords and usernames
  46. ;; available to the facility for pasting text of the window system
  47. ;; (clipboard on X and MS-Windows, pasteboard on Nextstep/Mac OS,
  48. ;; etc.), without inserting them in the kill-ring. You can set for
  49. ;; example:
  50. ;; (eval-after-load "org-passwords"
  51. ;; '(progn
  52. ;; (define-key org-passwords-mode-map
  53. ;; (kbd "C-c u")
  54. ;; 'org-passwords-copy-username)
  55. ;; (define-key org-passwords-mode-map
  56. ;; (kbd "C-c p")
  57. ;; 'org-passwords-copy-password)))
  58. ;; Finally, to enter new passwords, you can use `org-capture' and a minimal template like:
  59. ;; ("p" "password" entry (file "~/documents/passwords.gpg")
  60. ;; "* %^{Title}\n %^{PASSWORD}p %^{USERNAME}p")
  61. ;; When asked for the password you can then call either
  62. ;; `org-passwords-generate-password' or `org-passwords-random-words'.
  63. ;; Be sure to enable recursive minibuffers to call those functions
  64. ;; from the minibuffer:
  65. ;; (setq enable-recursive-minibuffers t)
  66. ;;; Code:
  67. (require 'org)
  68. (define-derived-mode org-passwords-mode org-mode
  69. "org-passwords-mode"
  70. "Mode for storing passwords"
  71. nil)
  72. (defgroup org-passwords nil
  73. "Options for password management."
  74. :group 'org)
  75. (defcustom org-passwords-password-property "PASSWORD"
  76. "Name of the property for password entry password."
  77. :type 'string
  78. :group 'org-passwords)
  79. (defcustom org-passwords-username-property "USERNAME"
  80. "Name of the property for password entry user name."
  81. :type 'string
  82. :group 'org-passwords)
  83. (defcustom org-passwords-file nil
  84. "Default file name for the file that contains the passwords."
  85. :type 'file
  86. :group 'org-passwords)
  87. (defcustom org-passwords-time-opened "1 min"
  88. "Time that the password file will remain open. It has to be a
  89. string, a number followed by units."
  90. :type 'str
  91. :group 'org-passwords)
  92. (defcustom org-passwords-random-words-dictionary nil
  93. "Default file name for the file that contains a dictionary of
  94. words for `org-passwords-random-words'. Each non-empty line in
  95. the file is considered a word."
  96. :type 'file
  97. :group 'org-passwords)
  98. (defvar org-passwords-random-words-separator "-"
  99. "A string to separate words in `org-passwords-random-words'.")
  100. (defvar org-passwords-random-words-substitutions
  101. '(("a" . "@")
  102. ("e" . "3")
  103. ("o" . "0"))
  104. "A list of substitutions to be made with
  105. `org-passwords-random-words' if it is called with
  106. `universal-argument'. Each element is pair of
  107. strings (SUBSTITUTE-THIS . BY-THIS).")
  108. (defun org-passwords-copy-password ()
  109. "Makes the password available to other programs. Puts the
  110. password of the entry at the location of the cursor in the
  111. facility for pasting text of the window system (clipboard on X
  112. and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
  113. putting it in the kill ring."
  114. (interactive)
  115. (save-excursion
  116. (search-backward-regexp "^\\*")
  117. (search-forward-regexp (concat "^[[:space:]]*:"
  118. org-passwords-password-property
  119. ":[[:space:]]*"))
  120. (funcall interprogram-cut-function
  121. (buffer-substring-no-properties (point)
  122. (funcall (lambda ()
  123. (end-of-line)
  124. (point)))))))
  125. (defun org-passwords-copy-username ()
  126. "Makes the password available to other programs. Puts the
  127. username of the entry at the location of the cursor in the
  128. facility for pasting text of the window system (clipboard on X
  129. and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
  130. putting it in the kill ring."
  131. (interactive)
  132. (save-excursion
  133. (search-backward-regexp "^\\*")
  134. (search-forward-regexp (concat "^[[:space:]]*:"
  135. org-passwords-username-property
  136. ":[[:space:]]*"))
  137. (funcall interprogram-cut-function
  138. (buffer-substring-no-properties (point)
  139. (funcall (lambda ()
  140. (end-of-line)
  141. (point)))))))
  142. (defun org-passwords ()
  143. "Open the password file. Open the password file defined by the
  144. variable `org-password-file' in read-only mode and kill that
  145. buffer later according to the value of the variable
  146. `org-passwords-time-opened'. It also adds the `org-password-file'
  147. to the auto-mode-alist so that it is opened with its mode being
  148. `org-passwords-mode'."
  149. (interactive)
  150. (if org-passwords-file
  151. (progn
  152. (add-to-list 'auto-mode-alist
  153. (cons
  154. (regexp-quote
  155. (expand-file-name org-passwords-file))
  156. 'org-passwords-mode))
  157. (find-file-read-only org-passwords-file)
  158. (org-passwords-set-up-kill-password-buffer))
  159. (minibuffer-message "No default password file defined. Set the variable `org-password-file'.")))
  160. (defun org-passwords-set-up-kill-password-buffer ()
  161. (run-at-time org-passwords-time-opened
  162. nil
  163. '(lambda ()
  164. (if (get-file-buffer org-passwords-file)
  165. (kill-buffer
  166. (get-file-buffer org-passwords-file))))))
  167. ;;; Password generator
  168. ;; Set random number seed from current time and pid. Otherwise
  169. ;; `random' gives the same results every time emacs restarts.
  170. (random t)
  171. (defun org-passwords-generate-password (arg)
  172. "Ask a number of characters and insert a password of that size.
  173. Password has a random string of numbers, lowercase letters, and
  174. uppercase letters. Argument ARG include symbols."
  175. (interactive "P")
  176. (let ((number-of-chars
  177. (string-to-number
  178. (read-from-minibuffer "Number of Characters: "))))
  179. (if arg
  180. (insert (org-passwords-generate-password-with-symbols "" number-of-chars))
  181. (insert (org-passwords-generate-password-without-symbols "" number-of-chars)))))
  182. (defun org-passwords-generate-password-with-symbols (previous-string nums-of-chars)
  183. "Return a string consisting of PREVIOUS-STRING and
  184. NUMS-OF-CHARS random characters."
  185. (if (eq nums-of-chars 0) previous-string
  186. (insert (org-passwords-generate-password-with-symbols
  187. (concat previous-string
  188. (char-to-string
  189. ;; symbols, letters, numbers are from 33 to 126
  190. (+ (random (- 127 33)) 33)))
  191. (1- nums-of-chars)))))
  192. (defun org-passwords-generate-password-without-symbols (previous-string nums-of-chars)
  193. "Return string consisting of PREVIOUS-STRING and NUMS-OF-CHARS
  194. random numbers, lowercase letters, and numbers."
  195. (if (eq nums-of-chars 0)
  196. previous-string
  197. ; There are 10 numbers, 26 lowercase letters and 26 uppercase
  198. ; letters. 10 + 26 + 26 = 62. The number characters go from 48
  199. ; to 57, the uppercase letters from 65 to 90, and the lowercase
  200. ; from 97 to 122. The following makes each equally likely.
  201. (let ((temp-value (random 62)))
  202. (cond ((< temp-value 10)
  203. ; If temp-value<10, then add a number
  204. (org-passwords-generate-password-without-symbols
  205. (concat previous-string
  206. (char-to-string (+ 48 temp-value)))
  207. (1- nums-of-chars)))
  208. ((and (> temp-value 9) (< temp-value 36))
  209. ; If 9<temp-value<36, then add an uppercase letter
  210. (org-passwords-generate-password-without-symbols
  211. (concat previous-string
  212. (char-to-string (+ 65 (- temp-value 10))))
  213. (1- nums-of-chars)))
  214. ((> temp-value 35)
  215. ; If temp-value>35, then add a lowecase letter
  216. (org-passwords-generate-password-without-symbols
  217. (concat previous-string
  218. (char-to-string (+ 97 (- temp-value 36))))
  219. (1- nums-of-chars)))))))
  220. ;;; Random words
  221. (defun org-passwords-random-words (arg)
  222. "Ask for a number of words and inserts a sequence of that many
  223. random words from the list in the file
  224. `org-passwords-random-words-dictionary' separated by
  225. `org-passwords-random-words-separator'. ARG make substitutions in
  226. the words as defined by
  227. `org-passwords-random-words-substitutions'."
  228. (interactive "P")
  229. (if org-passwords-random-words-dictionary
  230. (let ((number-of-words
  231. (string-to-number
  232. (read-from-minibuffer "Number of words: ")))
  233. (list-of-words
  234. (with-temp-buffer
  235. (insert-file-contents
  236. org-passwords-random-words-dictionary)
  237. (split-string (buffer-string) "\n" t))))
  238. (insert
  239. (org-passwords-substitute
  240. (org-passwords-random-words-attach-number-of-words
  241. (nth (random (length list-of-words))
  242. list-of-words)
  243. (1- number-of-words)
  244. list-of-words
  245. org-passwords-random-words-separator)
  246. (if arg
  247. org-passwords-random-words-substitutions
  248. nil))))
  249. (minibuffer-message
  250. "No default dictionary file defined. Set the variable `org-passwords-random-words-dictionary'.")))
  251. (defun org-passwords-random-words-attach-number-of-words
  252. (previous-string number-of-words list-of-words separator)
  253. "Returns a string consisting of PREVIOUS-STRING followed by a
  254. succession of NUMBER-OF-WORDS random words from the list LIST-OF-WORDS
  255. separated SEPARATOR."
  256. (if (eq number-of-words 0)
  257. previous-string
  258. (org-passwords-random-words-attach-number-of-words
  259. (concat previous-string
  260. separator
  261. (nth (random (length list-of-words)) list-of-words))
  262. (1- number-of-words)
  263. list-of-words
  264. separator)))
  265. (defun org-passwords-substitute (string-to-change list-of-substitutions)
  266. "Substitutes each appearence in STRING-TO-CHANGE of the `car' of
  267. each element of LIST-OF-SUBSTITUTIONS by the `cdr' of that
  268. element. For example:
  269. (org-passwords-substitute \"ab\" \'((\"a\" . \"b\") (\"b\" . \"c\")))
  270. => \"bc\"
  271. Substitutions are made in order of the list, so for example:
  272. (org-passwords-substitute \"ab\" \'((\"ab\" . \"c\") (\"b\" . \"d\")))
  273. => \"c\""
  274. (if list-of-substitutions
  275. (concat (org-passwords-concat-this-with-string
  276. (cdar list-of-substitutions)
  277. (mapcar (lambda (x)
  278. (org-passwords-substitute
  279. x
  280. (cdr list-of-substitutions)))
  281. (split-string string-to-change
  282. (caar list-of-substitutions)))))
  283. string-to-change))
  284. (defun org-passwords-concat-this-with-string (this list-of-strings)
  285. "Put the string THIS in between every string in LIST-OF-STRINGS. For example:
  286. (org-passwords-concat-this-with-string \"Here\" \'(\"First\" \"Second\" \"Third\"))
  287. => \"FirstHereSencondHereThird\""
  288. (if (cdr list-of-strings)
  289. (concat (car list-of-strings)
  290. this
  291. (org-passwords-concat-this-with-string
  292. (cdr list-of-strings)
  293. this))
  294. (car list-of-strings)))
  295. (provide 'org-passwords)
  296. ;;; org-passwords.el ends here