auth-source.el 77 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808
  1. ;;; auth-source.el --- authentication sources for Gnus and Emacs
  2. ;; Copyright (C) 2008-2012 Free Software Foundation, Inc.
  3. ;; Author: Ted Zlatanov <tzz@lifelogs.com>
  4. ;; Keywords: news
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This is the auth-source.el package. It lets users tell Gnus how to
  18. ;; authenticate in a single place. Simplicity is the goal. Instead
  19. ;; of providing 5000 options, we'll stick to simple, easy to
  20. ;; understand options.
  21. ;; See the auth.info Info documentation for details.
  22. ;; TODO:
  23. ;; - never decode the backend file unless it's necessary
  24. ;; - a more generic way to match backends and search backend contents
  25. ;; - absorb netrc.el and simplify it
  26. ;; - protect passwords better
  27. ;; - allow creating and changing netrc lines (not files) e.g. change a password
  28. ;;; Code:
  29. (require 'password-cache)
  30. (require 'mm-util)
  31. (require 'gnus-util)
  32. (require 'assoc)
  33. (eval-when-compile (require 'cl))
  34. (require 'eieio)
  35. (autoload 'secrets-create-item "secrets")
  36. (autoload 'secrets-delete-item "secrets")
  37. (autoload 'secrets-get-alias "secrets")
  38. (autoload 'secrets-get-attributes "secrets")
  39. (autoload 'secrets-get-secret "secrets")
  40. (autoload 'secrets-list-collections "secrets")
  41. (autoload 'secrets-search-items "secrets")
  42. (autoload 'rfc2104-hash "rfc2104")
  43. (autoload 'plstore-open "plstore")
  44. (autoload 'plstore-find "plstore")
  45. (autoload 'plstore-put "plstore")
  46. (autoload 'plstore-delete "plstore")
  47. (autoload 'plstore-save "plstore")
  48. (autoload 'plstore-get-file "plstore")
  49. (autoload 'epg-make-context "epg")
  50. (autoload 'epg-context-set-passphrase-callback "epg")
  51. (autoload 'epg-decrypt-string "epg")
  52. (autoload 'epg-context-set-armor "epg")
  53. (autoload 'epg-encrypt-string "epg")
  54. (autoload 'help-mode "help-mode" nil t)
  55. (defvar secrets-enabled)
  56. (defgroup auth-source nil
  57. "Authentication sources."
  58. :version "23.1" ;; No Gnus
  59. :group 'gnus)
  60. ;;;###autoload
  61. (defcustom auth-source-cache-expiry 7200
  62. "How many seconds passwords are cached, or nil to disable
  63. expiring. Overrides `password-cache-expiry' through a
  64. let-binding."
  65. :version "24.1"
  66. :group 'auth-source
  67. :type '(choice (const :tag "Never" nil)
  68. (const :tag "All Day" 86400)
  69. (const :tag "2 Hours" 7200)
  70. (const :tag "30 Minutes" 1800)
  71. (integer :tag "Seconds")))
  72. ;;; The slots below correspond with the `auth-source-search' spec,
  73. ;;; so a backend with :host set, for instance, would match only
  74. ;;; searches for that host. Normally they are nil.
  75. (defclass auth-source-backend ()
  76. ((type :initarg :type
  77. :initform 'netrc
  78. :type symbol
  79. :custom symbol
  80. :documentation "The backend type.")
  81. (source :initarg :source
  82. :type string
  83. :custom string
  84. :documentation "The backend source.")
  85. (host :initarg :host
  86. :initform t
  87. :type t
  88. :custom string
  89. :documentation "The backend host.")
  90. (user :initarg :user
  91. :initform t
  92. :type t
  93. :custom string
  94. :documentation "The backend user.")
  95. (port :initarg :port
  96. :initform t
  97. :type t
  98. :custom string
  99. :documentation "The backend protocol.")
  100. (data :initarg :data
  101. :initform nil
  102. :documentation "Internal backend data.")
  103. (create-function :initarg :create-function
  104. :initform ignore
  105. :type function
  106. :custom function
  107. :documentation "The create function.")
  108. (search-function :initarg :search-function
  109. :initform ignore
  110. :type function
  111. :custom function
  112. :documentation "The search function.")))
  113. (defcustom auth-source-protocols '((imap "imap" "imaps" "143" "993")
  114. (pop3 "pop3" "pop" "pop3s" "110" "995")
  115. (ssh "ssh" "22")
  116. (sftp "sftp" "115")
  117. (smtp "smtp" "25"))
  118. "List of authentication protocols and their names"
  119. :group 'auth-source
  120. :version "23.2" ;; No Gnus
  121. :type '(repeat :tag "Authentication Protocols"
  122. (cons :tag "Protocol Entry"
  123. (symbol :tag "Protocol")
  124. (repeat :tag "Names"
  125. (string :tag "Name")))))
  126. ;;; generate all the protocols in a format Customize can use
  127. ;;; TODO: generate on the fly from auth-source-protocols
  128. (defconst auth-source-protocols-customize
  129. (mapcar (lambda (a)
  130. (let ((p (car-safe a)))
  131. (list 'const
  132. :tag (upcase (symbol-name p))
  133. p)))
  134. auth-source-protocols))
  135. (defvar auth-source-creation-defaults nil
  136. "Defaults for creating token values. Usually let-bound.")
  137. (defvar auth-source-creation-prompts nil
  138. "Default prompts for token values. Usually let-bound.")
  139. (make-obsolete 'auth-source-hide-passwords nil "Emacs 24.1")
  140. (defcustom auth-source-save-behavior 'ask
  141. "If set, auth-source will respect it for save behavior."
  142. :group 'auth-source
  143. :version "23.2" ;; No Gnus
  144. :type `(choice
  145. :tag "auth-source new token save behavior"
  146. (const :tag "Always save" t)
  147. (const :tag "Never save" nil)
  148. (const :tag "Ask" ask)))
  149. ;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car (symbol-value 'epa-file-auto-mode-alist-entry)) "\\.gpg\\'") never) (t gpg)))
  150. ;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
  151. (defcustom auth-source-netrc-use-gpg-tokens 'never
  152. "Set this to tell auth-source when to create GPG password
  153. tokens in netrc files. It's either an alist or `never'.
  154. Note that if EPA/EPG is not available, this should NOT be used."
  155. :group 'auth-source
  156. :version "23.2" ;; No Gnus
  157. :type `(choice
  158. (const :tag "Always use GPG password tokens" (t gpg))
  159. (const :tag "Never use GPG password tokens" never)
  160. (repeat :tag "Use a lookup list"
  161. (list
  162. (choice :tag "Matcher"
  163. (const :tag "Match anything" t)
  164. (const :tag "The EPA encrypted file extensions"
  165. ,(if (boundp 'epa-file-auto-mode-alist-entry)
  166. (car (symbol-value
  167. 'epa-file-auto-mode-alist-entry))
  168. "\\.gpg\\'"))
  169. (regexp :tag "Regular expression"))
  170. (choice :tag "What to do"
  171. (const :tag "Save GPG-encrypted password tokens" gpg)
  172. (const :tag "Don't encrypt tokens" never))))))
  173. (defvar auth-source-magic "auth-source-magic ")
  174. (defcustom auth-source-do-cache t
  175. "Whether auth-source should cache information with `password-cache'."
  176. :group 'auth-source
  177. :version "23.2" ;; No Gnus
  178. :type `boolean)
  179. (defcustom auth-source-debug nil
  180. "Whether auth-source should log debug messages.
  181. If the value is nil, debug messages are not logged.
  182. If the value is t, debug messages are logged with `message'. In
  183. that case, your authentication data will be in the clear (except
  184. for passwords).
  185. If the value is a function, debug messages are logged by calling
  186. that function using the same arguments as `message'."
  187. :group 'auth-source
  188. :version "23.2" ;; No Gnus
  189. :type `(choice
  190. :tag "auth-source debugging mode"
  191. (const :tag "Log using `message' to the *Messages* buffer" t)
  192. (const :tag "Log all trivia with `message' to the *Messages* buffer"
  193. trivia)
  194. (function :tag "Function that takes arguments like `message'")
  195. (const :tag "Don't log anything" nil)))
  196. (defcustom auth-sources '("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")
  197. "List of authentication sources.
  198. The default will get login and password information from
  199. \"~/.authinfo.gpg\", which you should set up with the EPA/EPG
  200. packages to be encrypted. If that file doesn't exist, it will
  201. try the unencrypted version \"~/.authinfo\" and the famous
  202. \"~/.netrc\" file.
  203. See the auth.info manual for details.
  204. Each entry is the authentication type with optional properties.
  205. It's best to customize this with `M-x customize-variable' because the choices
  206. can get pretty complex."
  207. :group 'auth-source
  208. :version "24.1" ;; No Gnus
  209. :type `(repeat :tag "Authentication Sources"
  210. (choice
  211. (string :tag "Just a file")
  212. (const :tag "Default Secrets API Collection" 'default)
  213. (const :tag "Login Secrets API Collection" "secrets:Login")
  214. (const :tag "Temp Secrets API Collection" "secrets:session")
  215. (list :tag "Source definition"
  216. (const :format "" :value :source)
  217. (choice :tag "Authentication backend choice"
  218. (string :tag "Authentication Source (file)")
  219. (list
  220. :tag "Secret Service API/KWallet/GNOME Keyring"
  221. (const :format "" :value :secrets)
  222. (choice :tag "Collection to use"
  223. (string :tag "Collection name")
  224. (const :tag "Default" 'default)
  225. (const :tag "Login" "Login")
  226. (const
  227. :tag "Temporary" "session"))))
  228. (repeat :tag "Extra Parameters" :inline t
  229. (choice :tag "Extra parameter"
  230. (list
  231. :tag "Host"
  232. (const :format "" :value :host)
  233. (choice :tag "Host (machine) choice"
  234. (const :tag "Any" t)
  235. (regexp
  236. :tag "Regular expression")))
  237. (list
  238. :tag "Protocol"
  239. (const :format "" :value :port)
  240. (choice
  241. :tag "Protocol"
  242. (const :tag "Any" t)
  243. ,@auth-source-protocols-customize))
  244. (list :tag "User" :inline t
  245. (const :format "" :value :user)
  246. (choice
  247. :tag "Personality/Username"
  248. (const :tag "Any" t)
  249. (string
  250. :tag "Name")))))))))
  251. (defcustom auth-source-gpg-encrypt-to t
  252. "List of recipient keys that `authinfo.gpg' encrypted to.
  253. If the value is not a list, symmetric encryption will be used."
  254. :group 'auth-source
  255. :version "24.1" ;; No Gnus
  256. :type '(choice (const :tag "Symmetric encryption" t)
  257. (repeat :tag "Recipient public keys"
  258. (string :tag "Recipient public key"))))
  259. ;; temp for debugging
  260. ;; (unintern 'auth-source-protocols)
  261. ;; (unintern 'auth-sources)
  262. ;; (customize-variable 'auth-sources)
  263. ;; (setq auth-sources nil)
  264. ;; (format "%S" auth-sources)
  265. ;; (customize-variable 'auth-source-protocols)
  266. ;; (setq auth-source-protocols nil)
  267. ;; (format "%S" auth-source-protocols)
  268. ;; (auth-source-pick nil :host "a" :port 'imap)
  269. ;; (auth-source-user-or-password "login" "imap.myhost.com" 'imap)
  270. ;; (auth-source-user-or-password "password" "imap.myhost.com" 'imap)
  271. ;; (auth-source-user-or-password-imap "login" "imap.myhost.com")
  272. ;; (auth-source-user-or-password-imap "password" "imap.myhost.com")
  273. ;; (auth-source-protocol-defaults 'imap)
  274. ;; (let ((auth-source-debug 'debug)) (auth-source-do-debug "hello"))
  275. ;; (let ((auth-source-debug t)) (auth-source-do-debug "hello"))
  276. ;; (let ((auth-source-debug nil)) (auth-source-do-debug "hello"))
  277. (defun auth-source-do-debug (&rest msg)
  278. (when auth-source-debug
  279. (apply 'auth-source-do-warn msg)))
  280. (defun auth-source-do-trivia (&rest msg)
  281. (when (or (eq auth-source-debug 'trivia)
  282. (functionp auth-source-debug))
  283. (apply 'auth-source-do-warn msg)))
  284. (defun auth-source-do-warn (&rest msg)
  285. (apply
  286. ;; set logger to either the function in auth-source-debug or 'message
  287. ;; note that it will be 'message if auth-source-debug is nil
  288. (if (functionp auth-source-debug)
  289. auth-source-debug
  290. 'message)
  291. msg))
  292. ;;; (auth-source-read-char-choice "enter choice? " '(?a ?b ?q))
  293. (defun auth-source-read-char-choice (prompt choices)
  294. "Read one of CHOICES by `read-char-choice', or `read-char'.
  295. `dropdown-list' support is disabled because it doesn't work reliably.
  296. Only one of CHOICES will be returned. The PROMPT is augmented
  297. with \"[a/b/c] \" if CHOICES is '\(?a ?b ?c\)."
  298. (when choices
  299. (let* ((prompt-choices
  300. (apply 'concat (loop for c in choices
  301. collect (format "%c/" c))))
  302. (prompt-choices (concat "[" (substring prompt-choices 0 -1) "] "))
  303. (full-prompt (concat prompt prompt-choices))
  304. k)
  305. (while (not (memq k choices))
  306. (setq k (cond
  307. ((fboundp 'read-char-choice)
  308. (read-char-choice full-prompt choices))
  309. (t (message "%s" full-prompt)
  310. (setq k (read-char))))))
  311. k)))
  312. ;; (auth-source-pick nil :host "any" :port 'imap :user "joe")
  313. ;; (auth-source-pick t :host "any" :port 'imap :user "joe")
  314. ;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
  315. ;; (:source (:secrets "session") :host t :port t :user "joe")
  316. ;; (:source (:secrets "Login") :host t :port t)
  317. ;; (:source "~/.authinfo.gpg" :host t :port t)))
  318. ;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
  319. ;; (:source (:secrets "session") :host t :port t :user "joe")
  320. ;; (:source (:secrets "Login") :host t :port t)
  321. ;; ))
  322. ;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :port t)))
  323. ;; (auth-source-backend-parse "myfile.gpg")
  324. ;; (auth-source-backend-parse 'default)
  325. ;; (auth-source-backend-parse "secrets:Login")
  326. (defun auth-source-backend-parse (entry)
  327. "Creates an auth-source-backend from an ENTRY in `auth-sources'."
  328. (auth-source-backend-parse-parameters
  329. entry
  330. (cond
  331. ;; take 'default and recurse to get it as a Secrets API default collection
  332. ;; matching any user, host, and protocol
  333. ((eq entry 'default)
  334. (auth-source-backend-parse '(:source (:secrets default))))
  335. ;; take secrets:XYZ and recurse to get it as Secrets API collection "XYZ"
  336. ;; matching any user, host, and protocol
  337. ((and (stringp entry) (string-match "^secrets:\\(.+\\)" entry))
  338. (auth-source-backend-parse `(:source (:secrets ,(match-string 1 entry)))))
  339. ;; take just a file name and recurse to get it as a netrc file
  340. ;; matching any user, host, and protocol
  341. ((stringp entry)
  342. (auth-source-backend-parse `(:source ,entry)))
  343. ;; a file name with parameters
  344. ((stringp (plist-get entry :source))
  345. (if (equal (file-name-extension (plist-get entry :source)) "plist")
  346. (auth-source-backend
  347. (plist-get entry :source)
  348. :source (plist-get entry :source)
  349. :type 'plstore
  350. :search-function 'auth-source-plstore-search
  351. :create-function 'auth-source-plstore-create
  352. :data (plstore-open (plist-get entry :source)))
  353. (auth-source-backend
  354. (plist-get entry :source)
  355. :source (plist-get entry :source)
  356. :type 'netrc
  357. :search-function 'auth-source-netrc-search
  358. :create-function 'auth-source-netrc-create)))
  359. ;; the Secrets API. We require the package, in order to have a
  360. ;; defined value for `secrets-enabled'.
  361. ((and
  362. (not (null (plist-get entry :source))) ; the source must not be nil
  363. (listp (plist-get entry :source)) ; and it must be a list
  364. (require 'secrets nil t) ; and we must load the Secrets API
  365. secrets-enabled) ; and that API must be enabled
  366. ;; the source is either the :secrets key in ENTRY or
  367. ;; if that's missing or nil, it's "session"
  368. (let ((source (or (plist-get (plist-get entry :source) :secrets)
  369. "session")))
  370. ;; if the source is a symbol, we look for the alias named so,
  371. ;; and if that alias is missing, we use "Login"
  372. (when (symbolp source)
  373. (setq source (or (secrets-get-alias (symbol-name source))
  374. "Login")))
  375. (if (featurep 'secrets)
  376. (auth-source-backend
  377. (format "Secrets API (%s)" source)
  378. :source source
  379. :type 'secrets
  380. :search-function 'auth-source-secrets-search
  381. :create-function 'auth-source-secrets-create)
  382. (auth-source-do-warn
  383. "auth-source-backend-parse: no Secrets API, ignoring spec: %S" entry)
  384. (auth-source-backend
  385. (format "Ignored Secrets API (%s)" source)
  386. :source ""
  387. :type 'ignore))))
  388. ;; none of them
  389. (t
  390. (auth-source-do-warn
  391. "auth-source-backend-parse: invalid backend spec: %S" entry)
  392. (auth-source-backend
  393. "Empty"
  394. :source ""
  395. :type 'ignore)))))
  396. (defun auth-source-backend-parse-parameters (entry backend)
  397. "Fills in the extra auth-source-backend parameters of ENTRY.
  398. Using the plist ENTRY, get the :host, :port, and :user search
  399. parameters."
  400. (let ((entry (if (stringp entry)
  401. nil
  402. entry))
  403. val)
  404. (when (setq val (plist-get entry :host))
  405. (oset backend host val))
  406. (when (setq val (plist-get entry :user))
  407. (oset backend user val))
  408. (when (setq val (plist-get entry :port))
  409. (oset backend port val)))
  410. backend)
  411. ;; (mapcar 'auth-source-backend-parse auth-sources)
  412. (defun* auth-source-search (&rest spec
  413. &key type max host user port secret
  414. require create delete
  415. &allow-other-keys)
  416. "Search or modify authentication backends according to SPEC.
  417. This function parses `auth-sources' for matches of the SPEC
  418. plist. It can optionally create or update an authentication
  419. token if requested. A token is just a standard Emacs property
  420. list with a :secret property that can be a function; all the
  421. other properties will always hold scalar values.
  422. Typically the :secret property, if present, contains a password.
  423. Common search keys are :max, :host, :port, and :user. In
  424. addition, :create specifies how tokens will be or created.
  425. Finally, :type can specify which backend types you want to check.
  426. A string value is always matched literally. A symbol is matched
  427. as its string value, literally. All the SPEC values can be
  428. single values (symbol or string) or lists thereof (in which case
  429. any of the search terms matches).
  430. :create t means to create a token if possible.
  431. A new token will be created if no matching tokens were found.
  432. The new token will have only the keys the backend requires. For
  433. the netrc backend, for instance, that's the user, host, and
  434. port keys.
  435. Here's an example:
  436. \(let ((auth-source-creation-defaults '((user . \"defaultUser\")
  437. (A . \"default A\"))))
  438. (auth-source-search :host \"mine\" :type 'netrc :max 1
  439. :P \"pppp\" :Q \"qqqq\"
  440. :create t))
  441. which says:
  442. \"Search for any entry matching host 'mine' in backends of type
  443. 'netrc', maximum one result.
  444. Create a new entry if you found none. The netrc backend will
  445. automatically require host, user, and port. The host will be
  446. 'mine'. We prompt for the user with default 'defaultUser' and
  447. for the port without a default. We will not prompt for A, Q,
  448. or P. The resulting token will only have keys user, host, and
  449. port.\"
  450. :create '(A B C) also means to create a token if possible.
  451. The behavior is like :create t but if the list contains any
  452. parameter, that parameter will be required in the resulting
  453. token. The value for that parameter will be obtained from the
  454. search parameters or from user input. If any queries are needed,
  455. the alist `auth-source-creation-defaults' will be checked for the
  456. default value. If the user, host, or port are missing, the alist
  457. `auth-source-creation-prompts' will be used to look up the
  458. prompts IN THAT ORDER (so the 'user prompt will be queried first,
  459. then 'host, then 'port, and finally 'secret). Each prompt string
  460. can use %u, %h, and %p to show the user, host, and port.
  461. Here's an example:
  462. \(let ((auth-source-creation-defaults '((user . \"defaultUser\")
  463. (A . \"default A\")))
  464. (auth-source-creation-prompts
  465. '((password . \"Enter IMAP password for %h:%p: \"))))
  466. (auth-source-search :host '(\"nonesuch\" \"twosuch\") :type 'netrc :max 1
  467. :P \"pppp\" :Q \"qqqq\"
  468. :create '(A B Q)))
  469. which says:
  470. \"Search for any entry matching host 'nonesuch'
  471. or 'twosuch' in backends of type 'netrc', maximum one result.
  472. Create a new entry if you found none. The netrc backend will
  473. automatically require host, user, and port. The host will be
  474. 'nonesuch' and Q will be 'qqqq'. We prompt for the password
  475. with the shown prompt. We will not prompt for Q. The resulting
  476. token will have keys user, host, port, A, B, and Q. It will not
  477. have P with any value, even though P is used in the search to
  478. find only entries that have P set to 'pppp'.\"
  479. When multiple values are specified in the search parameter, the
  480. user is prompted for which one. So :host (X Y Z) would ask the
  481. user to choose between X, Y, and Z.
  482. This creation can fail if the search was not specific enough to
  483. create a new token (it's up to the backend to decide that). You
  484. should `catch' the backend-specific error as usual. Some
  485. backends (netrc, at least) will prompt the user rather than throw
  486. an error.
  487. :require (A B C) means that only results that contain those
  488. tokens will be returned. Thus for instance requiring :secret
  489. will ensure that any results will actually have a :secret
  490. property.
  491. :delete t means to delete any found entries. nil by default.
  492. Use `auth-source-delete' in ELisp code instead of calling
  493. `auth-source-search' directly with this parameter.
  494. :type (X Y Z) will check only those backend types. 'netrc and
  495. 'secrets are the only ones supported right now.
  496. :max N means to try to return at most N items (defaults to 1).
  497. When 0 the function will return just t or nil to indicate if any
  498. matches were found. More than N items may be returned, depending
  499. on the search and the backend.
  500. :host (X Y Z) means to match only hosts X, Y, or Z according to
  501. the match rules above. Defaults to t.
  502. :user (X Y Z) means to match only users X, Y, or Z according to
  503. the match rules above. Defaults to t.
  504. :port (P Q R) means to match only protocols P, Q, or R.
  505. Defaults to t.
  506. :K (V1 V2 V3) for any other key K will match values V1, V2, or
  507. V3 (note the match rules above).
  508. The return value is a list with at most :max tokens. Each token
  509. is a plist with keys :backend :host :port :user, plus any other
  510. keys provided by the backend (notably :secret). But note the
  511. exception for :max 0, which see above.
  512. The token can hold a :save-function key. If you call that, the
  513. user will be prompted to save the data to the backend. You can't
  514. request that this should happen right after creation, because
  515. `auth-source-search' has no way of knowing if the token is
  516. actually useful. So the caller must arrange to call this function.
  517. The token's :secret key can hold a function. In that case you
  518. must call it to obtain the actual value."
  519. (let* ((backends (mapcar 'auth-source-backend-parse auth-sources))
  520. (max (or max 1))
  521. (ignored-keys '(:require :create :delete :max))
  522. (keys (loop for i below (length spec) by 2
  523. unless (memq (nth i spec) ignored-keys)
  524. collect (nth i spec)))
  525. (cached (auth-source-remembered-p spec))
  526. ;; note that we may have cached results but found is still nil
  527. ;; (there were no results from the search)
  528. (found (auth-source-recall spec))
  529. filtered-backends accessor-key backend)
  530. (if (and cached auth-source-do-cache)
  531. (auth-source-do-debug
  532. "auth-source-search: found %d CACHED results matching %S"
  533. (length found) spec)
  534. (assert
  535. (or (eq t create) (listp create)) t
  536. "Invalid auth-source :create parameter (must be t or a list): %s %s")
  537. (assert
  538. (listp require) t
  539. "Invalid auth-source :require parameter (must be a list): %s")
  540. (setq filtered-backends (copy-sequence backends))
  541. (dolist (backend backends)
  542. (dolist (key keys)
  543. ;; ignore invalid slots
  544. (condition-case signal
  545. (unless (eval `(auth-source-search-collection
  546. (plist-get spec key)
  547. (oref backend ,key)))
  548. (setq filtered-backends (delq backend filtered-backends))
  549. (return))
  550. (invalid-slot-name))))
  551. (auth-source-do-trivia
  552. "auth-source-search: found %d backends matching %S"
  553. (length filtered-backends) spec)
  554. ;; (debug spec "filtered" filtered-backends)
  555. ;; First go through all the backends without :create, so we can
  556. ;; query them all.
  557. (setq found (auth-source-search-backends filtered-backends
  558. spec
  559. ;; to exit early
  560. max
  561. ;; create is always nil here
  562. nil delete
  563. require))
  564. (auth-source-do-debug
  565. "auth-source-search: found %d results (max %d) matching %S"
  566. (length found) max spec)
  567. ;; If we didn't find anything, then we allow the backend(s) to
  568. ;; create the entries.
  569. (when (and create
  570. (not found))
  571. (setq found (auth-source-search-backends filtered-backends
  572. spec
  573. ;; to exit early
  574. max
  575. create delete
  576. require))
  577. (auth-source-do-debug
  578. "auth-source-search: CREATED %d results (max %d) matching %S"
  579. (length found) max spec))
  580. ;; note we remember the lack of result too, if it's applicable
  581. (when auth-source-do-cache
  582. (auth-source-remember spec found)))
  583. found))
  584. (defun auth-source-search-backends (backends spec max create delete require)
  585. (let (matches)
  586. (dolist (backend backends)
  587. (when (> max (length matches)) ; when we need more matches...
  588. (let* ((bmatches (apply
  589. (slot-value backend 'search-function)
  590. :backend backend
  591. ;; note we're overriding whatever the spec
  592. ;; has for :require, :create, and :delete
  593. :require require
  594. :create create
  595. :delete delete
  596. spec)))
  597. (when bmatches
  598. (auth-source-do-trivia
  599. "auth-source-search-backend: got %d (max %d) in %s:%s matching %S"
  600. (length bmatches) max
  601. (slot-value backend :type)
  602. (slot-value backend :source)
  603. spec)
  604. (setq matches (append matches bmatches))))))
  605. matches))
  606. ;;; (auth-source-search :max 1)
  607. ;;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret))
  608. ;;; (auth-source-search :host "nonesuch" :type 'netrc :K 1)
  609. ;;; (auth-source-search :host "nonesuch" :type 'secrets)
  610. (defun* auth-source-delete (&rest spec
  611. &key delete
  612. &allow-other-keys)
  613. "Delete entries from the authentication backends according to SPEC.
  614. Calls `auth-source-search' with the :delete property in SPEC set to t.
  615. The backend may not actually delete the entries.
  616. Returns the deleted entries."
  617. (auth-source-search (plist-put spec :delete t)))
  618. (defun auth-source-search-collection (collection value)
  619. "Returns t is VALUE is t or COLLECTION is t or contains VALUE."
  620. (when (and (atom collection) (not (eq t collection)))
  621. (setq collection (list collection)))
  622. ;; (debug :collection collection :value value)
  623. (or (eq collection t)
  624. (eq value t)
  625. (equal collection value)
  626. (member value collection)))
  627. (defvar auth-source-netrc-cache nil)
  628. (defun auth-source-forget-all-cached ()
  629. "Forget all cached auth-source data."
  630. (interactive)
  631. (loop for sym being the symbols of password-data
  632. ;; when the symbol name starts with auth-source-magic
  633. when (string-match (concat "^" auth-source-magic)
  634. (symbol-name sym))
  635. ;; remove that key
  636. do (password-cache-remove (symbol-name sym)))
  637. (setq auth-source-netrc-cache nil))
  638. (defun auth-source-format-cache-entry (spec)
  639. "Format SPEC entry to put it in the password cache."
  640. (concat auth-source-magic (format "%S" spec)))
  641. (defun auth-source-remember (spec found)
  642. "Remember FOUND search results for SPEC."
  643. (let ((password-cache-expiry auth-source-cache-expiry))
  644. (password-cache-add
  645. (auth-source-format-cache-entry spec) found)))
  646. (defun auth-source-recall (spec)
  647. "Recall FOUND search results for SPEC."
  648. (password-read-from-cache (auth-source-format-cache-entry spec)))
  649. (defun auth-source-remembered-p (spec)
  650. "Check if SPEC is remembered."
  651. (password-in-cache-p
  652. (auth-source-format-cache-entry spec)))
  653. (defun auth-source-forget (spec)
  654. "Forget any cached data matching SPEC exactly.
  655. This is the same SPEC you passed to `auth-source-search'.
  656. Returns t or nil for forgotten or not found."
  657. (password-cache-remove (auth-source-format-cache-entry spec)))
  658. ;;; (loop for sym being the symbols of password-data when (string-match (concat "^" auth-source-magic) (symbol-name sym)) collect (symbol-name sym))
  659. ;;; (auth-source-remember '(:host "wedd") '(4 5 6))
  660. ;;; (auth-source-remembered-p '(:host "wedd"))
  661. ;;; (auth-source-remember '(:host "xedd") '(1 2 3))
  662. ;;; (auth-source-remembered-p '(:host "xedd"))
  663. ;;; (auth-source-remembered-p '(:host "zedd"))
  664. ;;; (auth-source-recall '(:host "xedd"))
  665. ;;; (auth-source-recall '(:host t))
  666. ;;; (auth-source-forget+ :host t)
  667. (defun* auth-source-forget+ (&rest spec &allow-other-keys)
  668. "Forget any cached data matching SPEC. Returns forgotten count.
  669. This is not a full `auth-source-search' spec but works similarly.
  670. For instance, \(:host \"myhost\" \"yourhost\") would find all the
  671. cached data that was found with a search for those two hosts,
  672. while \(:host t) would find all host entries."
  673. (let ((count 0)
  674. sname)
  675. (loop for sym being the symbols of password-data
  676. ;; when the symbol name matches with auth-source-magic
  677. when (and (setq sname (symbol-name sym))
  678. (string-match (concat "^" auth-source-magic "\\(.+\\)")
  679. sname)
  680. ;; and the spec matches what was stored in the cache
  681. (auth-source-specmatchp spec (read (match-string 1 sname))))
  682. ;; remove that key
  683. do (progn
  684. (password-cache-remove sname)
  685. (incf count)))
  686. count))
  687. (defun auth-source-specmatchp (spec stored)
  688. (let ((keys (loop for i below (length spec) by 2
  689. collect (nth i spec))))
  690. (not (eq
  691. (dolist (key keys)
  692. (unless (auth-source-search-collection (plist-get stored key)
  693. (plist-get spec key))
  694. (return 'no)))
  695. 'no))))
  696. ;;; (auth-source-pick-first-password :host "z.lifelogs.com")
  697. ;;; (auth-source-pick-first-password :port "imap")
  698. (defun auth-source-pick-first-password (&rest spec)
  699. "Pick the first secret found from applying SPEC to `auth-source-search'."
  700. (let* ((result (nth 0 (apply 'auth-source-search (plist-put spec :max 1))))
  701. (secret (plist-get result :secret)))
  702. (if (functionp secret)
  703. (funcall secret)
  704. secret)))
  705. ;; (auth-source-format-prompt "test %u %h %p" '((?u "user") (?h "host")))
  706. (defun auth-source-format-prompt (prompt alist)
  707. "Format PROMPT using %x (for any character x) specifiers in ALIST."
  708. (dolist (cell alist)
  709. (let ((c (nth 0 cell))
  710. (v (nth 1 cell)))
  711. (when (and c v)
  712. (setq prompt (replace-regexp-in-string (format "%%%c" c)
  713. (format "%s" v)
  714. prompt)))))
  715. prompt)
  716. (defun auth-source-ensure-strings (values)
  717. (unless (listp values)
  718. (setq values (list values)))
  719. (mapcar (lambda (value)
  720. (if (numberp value)
  721. (format "%s" value)
  722. value))
  723. values))
  724. ;;; Backend specific parsing: netrc/authinfo backend
  725. ;;; (auth-source-netrc-parse "~/.authinfo.gpg")
  726. (defun* auth-source-netrc-parse (&rest
  727. spec
  728. &key file max host user port delete require
  729. &allow-other-keys)
  730. "Parse FILE and return a list of all entries in the file.
  731. Note that the MAX parameter is used so we can exit the parse early."
  732. (if (listp file)
  733. ;; We got already parsed contents; just return it.
  734. file
  735. (when (file-exists-p file)
  736. (setq port (auth-source-ensure-strings port))
  737. (with-temp-buffer
  738. (let* ((tokens '("machine" "host" "default" "login" "user"
  739. "password" "account" "macdef" "force"
  740. "port" "protocol"))
  741. (max (or max 5000)) ; sanity check: default to stop at 5K
  742. (modified 0)
  743. (cached (cdr-safe (assoc file auth-source-netrc-cache)))
  744. (cached-mtime (plist-get cached :mtime))
  745. (cached-secrets (plist-get cached :secret))
  746. alist elem result pair)
  747. (if (and (functionp cached-secrets)
  748. (equal cached-mtime
  749. (nth 5 (file-attributes file))))
  750. (progn
  751. (auth-source-do-trivia
  752. "auth-source-netrc-parse: using CACHED file data for %s"
  753. file)
  754. (insert (funcall cached-secrets)))
  755. (insert-file-contents file)
  756. ;; cache all netrc files (used to be just .gpg files)
  757. ;; Store the contents of the file heavily encrypted in memory.
  758. ;; (note for the irony-impaired: they are just obfuscated)
  759. (aput 'auth-source-netrc-cache file
  760. (list :mtime (nth 5 (file-attributes file))
  761. :secret (lexical-let ((v (mapcar '1+ (buffer-string))))
  762. (lambda () (apply 'string (mapcar '1- v)))))))
  763. (goto-char (point-min))
  764. ;; Go through the file, line by line.
  765. (while (and (not (eobp))
  766. (> max 0))
  767. (narrow-to-region (point) (point-at-eol))
  768. ;; For each line, get the tokens and values.
  769. (while (not (eobp))
  770. (skip-chars-forward "\t ")
  771. ;; Skip lines that begin with a "#".
  772. (if (eq (char-after) ?#)
  773. (goto-char (point-max))
  774. (unless (eobp)
  775. (setq elem
  776. (if (= (following-char) ?\")
  777. (read (current-buffer))
  778. (buffer-substring
  779. (point) (progn (skip-chars-forward "^\t ")
  780. (point)))))
  781. (cond
  782. ((equal elem "macdef")
  783. ;; We skip past the macro definition.
  784. (widen)
  785. (while (and (zerop (forward-line 1))
  786. (looking-at "$")))
  787. (narrow-to-region (point) (point)))
  788. ((member elem tokens)
  789. ;; Tokens that don't have a following value are ignored,
  790. ;; except "default".
  791. (when (and pair (or (cdr pair)
  792. (equal (car pair) "default")))
  793. (push pair alist))
  794. (setq pair (list elem)))
  795. (t
  796. ;; Values that haven't got a preceding token are ignored.
  797. (when pair
  798. (setcdr pair elem)
  799. (push pair alist)
  800. (setq pair nil)))))))
  801. (when (and alist
  802. (> max 0)
  803. (auth-source-search-collection
  804. host
  805. (or
  806. (aget alist "machine")
  807. (aget alist "host")
  808. t))
  809. (auth-source-search-collection
  810. user
  811. (or
  812. (aget alist "login")
  813. (aget alist "account")
  814. (aget alist "user")
  815. t))
  816. (auth-source-search-collection
  817. port
  818. (or
  819. (aget alist "port")
  820. (aget alist "protocol")
  821. t))
  822. (or
  823. ;; the required list of keys is nil, or
  824. (null require)
  825. ;; every element of require is in the normalized list
  826. (let ((normalized (nth 0 (auth-source-netrc-normalize
  827. (list alist) file))))
  828. (loop for req in require
  829. always (plist-get normalized req)))))
  830. (decf max)
  831. (push (nreverse alist) result)
  832. ;; to delete a line, we just comment it out
  833. (when delete
  834. (goto-char (point-min))
  835. (insert "#")
  836. (incf modified)))
  837. (setq alist nil
  838. pair nil)
  839. (widen)
  840. (forward-line 1))
  841. (when (< 0 modified)
  842. (when auth-source-gpg-encrypt-to
  843. ;; (see bug#7487) making `epa-file-encrypt-to' local to
  844. ;; this buffer lets epa-file skip the key selection query
  845. ;; (see the `local-variable-p' check in
  846. ;; `epa-file-write-region').
  847. (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
  848. (make-local-variable 'epa-file-encrypt-to))
  849. (if (listp auth-source-gpg-encrypt-to)
  850. (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
  851. ;; ask AFTER we've successfully opened the file
  852. (when (y-or-n-p (format "Save file %s? (%d deletions)"
  853. file modified))
  854. (write-region (point-min) (point-max) file nil 'silent)
  855. (auth-source-do-debug
  856. "auth-source-netrc-parse: modified %d lines in %s"
  857. modified file)))
  858. (nreverse result))))))
  859. (defvar auth-source-passphrase-alist nil)
  860. (defun auth-source-token-passphrase-callback-function (context key-id file)
  861. (let* ((file (file-truename file))
  862. (entry (assoc file auth-source-passphrase-alist))
  863. passphrase)
  864. ;; return the saved passphrase, calling a function if needed
  865. (or (copy-sequence (if (functionp (cdr entry))
  866. (funcall (cdr entry))
  867. (cdr entry)))
  868. (progn
  869. (unless entry
  870. (setq entry (list file))
  871. (push entry auth-source-passphrase-alist))
  872. (setq passphrase
  873. (read-passwd
  874. (format "Passphrase for %s tokens: " file)
  875. t))
  876. (setcdr entry (lexical-let ((p (copy-sequence passphrase)))
  877. (lambda () p)))
  878. passphrase))))
  879. ;; (auth-source-epa-extract-gpg-token "gpg:LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEdudVBHIHYxLjQuMTEgKEdOVS9MaW51eCkKCmpBMEVBd01DT25qMjB1ak9rZnRneVI3K21iNm9aZWhuLzRad3cySkdlbnVaKzRpeEswWDY5di9icDI1U1dsQT0KPS9yc2wKLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo=" "~/.netrc")
  880. (defun auth-source-epa-extract-gpg-token (secret file)
  881. "Pass either the decoded SECRET or the gpg:BASE64DATA version.
  882. FILE is the file from which we obtained this token."
  883. (when (string-match "^gpg:\\(.+\\)" secret)
  884. (setq secret (base64-decode-string (match-string 1 secret))))
  885. (let ((context (epg-make-context 'OpenPGP))
  886. plain)
  887. (epg-context-set-passphrase-callback
  888. context
  889. (cons #'auth-source-token-passphrase-callback-function
  890. file))
  891. (epg-decrypt-string context secret)))
  892. ;; (insert (auth-source-epa-make-gpg-token "mysecret" "~/.netrc"))
  893. (defun auth-source-epa-make-gpg-token (secret file)
  894. (let ((context (epg-make-context 'OpenPGP))
  895. (pp-escape-newlines nil)
  896. cipher)
  897. (epg-context-set-armor context t)
  898. (epg-context-set-passphrase-callback
  899. context
  900. (cons #'auth-source-token-passphrase-callback-function
  901. file))
  902. (setq cipher (epg-encrypt-string context secret nil))
  903. (with-temp-buffer
  904. (insert cipher)
  905. (base64-encode-region (point-min) (point-max) t)
  906. (concat "gpg:" (buffer-substring-no-properties
  907. (point-min)
  908. (point-max))))))
  909. (defun auth-source-netrc-normalize (alist filename)
  910. (mapcar (lambda (entry)
  911. (let (ret item)
  912. (while (setq item (pop entry))
  913. (let ((k (car item))
  914. (v (cdr item)))
  915. ;; apply key aliases
  916. (setq k (cond ((member k '("machine")) "host")
  917. ((member k '("login" "account")) "user")
  918. ((member k '("protocol")) "port")
  919. ((member k '("password")) "secret")
  920. (t k)))
  921. ;; send back the secret in a function (lexical binding)
  922. (when (equal k "secret")
  923. (setq v (lexical-let ((lexv v)
  924. (token-decoder nil))
  925. (when (string-match "^gpg:" lexv)
  926. ;; it's a GPG token: create a token decoder
  927. ;; which unsets itself once
  928. (setq token-decoder
  929. (lambda (val)
  930. (prog1
  931. (auth-source-epa-extract-gpg-token
  932. val
  933. filename)
  934. (setq token-decoder nil)))))
  935. (lambda ()
  936. (when token-decoder
  937. (setq lexv (funcall token-decoder lexv)))
  938. lexv))))
  939. (setq ret (plist-put ret
  940. (intern (concat ":" k))
  941. v))))
  942. ret))
  943. alist))
  944. ;;; (setq secret (plist-get (nth 0 (auth-source-search :host t :type 'netrc :K 1 :max 1)) :secret))
  945. ;;; (funcall secret)
  946. (defun* auth-source-netrc-search (&rest
  947. spec
  948. &key backend require create delete
  949. type max host user port
  950. &allow-other-keys)
  951. "Given a property list SPEC, return search matches from the :backend.
  952. See `auth-source-search' for details on SPEC."
  953. ;; just in case, check that the type is correct (null or same as the backend)
  954. (assert (or (null type) (eq type (oref backend type)))
  955. t "Invalid netrc search: %s %s")
  956. (let ((results (auth-source-netrc-normalize
  957. (auth-source-netrc-parse
  958. :max max
  959. :require require
  960. :delete delete
  961. :file (oref backend source)
  962. :host (or host t)
  963. :user (or user t)
  964. :port (or port t))
  965. (oref backend source))))
  966. ;; if we need to create an entry AND none were found to match
  967. (when (and create
  968. (not results))
  969. ;; create based on the spec and record the value
  970. (setq results (or
  971. ;; if the user did not want to create the entry
  972. ;; in the file, it will be returned
  973. (apply (slot-value backend 'create-function) spec)
  974. ;; if not, we do the search again without :create
  975. ;; to get the updated data.
  976. ;; the result will be returned, even if the search fails
  977. (apply 'auth-source-netrc-search
  978. (plist-put spec :create nil)))))
  979. results))
  980. (defun auth-source-netrc-element-or-first (v)
  981. (if (listp v)
  982. (nth 0 v)
  983. v))
  984. ;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t)
  985. ;;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t :create-extra-keys '((A "default A") (B)))
  986. (defun* auth-source-netrc-create (&rest spec
  987. &key backend
  988. secret host user port create
  989. &allow-other-keys)
  990. (let* ((base-required '(host user port secret))
  991. ;; we know (because of an assertion in auth-source-search) that the
  992. ;; :create parameter is either t or a list (which includes nil)
  993. (create-extra (if (eq t create) nil create))
  994. (current-data (car (auth-source-search :max 1
  995. :host host
  996. :port port)))
  997. (required (append base-required create-extra))
  998. (file (oref backend source))
  999. (add "")
  1000. ;; `valist' is an alist
  1001. valist
  1002. ;; `artificial' will be returned if no creation is needed
  1003. artificial)
  1004. ;; only for base required elements (defined as function parameters):
  1005. ;; fill in the valist with whatever data we may have from the search
  1006. ;; we complete the first value if it's a list and use the value otherwise
  1007. (dolist (br base-required)
  1008. (when (symbol-value br)
  1009. (let ((br-choice (cond
  1010. ;; all-accepting choice (predicate is t)
  1011. ((eq t (symbol-value br)) nil)
  1012. ;; just the value otherwise
  1013. (t (symbol-value br)))))
  1014. (when br-choice
  1015. (aput 'valist br br-choice)))))
  1016. ;; for extra required elements, see if the spec includes a value for them
  1017. (dolist (er create-extra)
  1018. (let ((name (concat ":" (symbol-name er)))
  1019. (keys (loop for i below (length spec) by 2
  1020. collect (nth i spec))))
  1021. (dolist (k keys)
  1022. (when (equal (symbol-name k) name)
  1023. (aput 'valist er (plist-get spec k))))))
  1024. ;; for each required element
  1025. (dolist (r required)
  1026. (let* ((data (aget valist r))
  1027. ;; take the first element if the data is a list
  1028. (data (or (auth-source-netrc-element-or-first data)
  1029. (plist-get current-data
  1030. (intern (format ":%s" r) obarray))))
  1031. ;; this is the default to be offered
  1032. (given-default (aget auth-source-creation-defaults r))
  1033. ;; the default supplementals are simple:
  1034. ;; for the user, try `given-default' and then (user-login-name);
  1035. ;; otherwise take `given-default'
  1036. (default (cond
  1037. ((and (not given-default) (eq r 'user))
  1038. (user-login-name))
  1039. (t given-default)))
  1040. (printable-defaults (list
  1041. (cons 'user
  1042. (or
  1043. (auth-source-netrc-element-or-first
  1044. (aget valist 'user))
  1045. (plist-get artificial :user)
  1046. "[any user]"))
  1047. (cons 'host
  1048. (or
  1049. (auth-source-netrc-element-or-first
  1050. (aget valist 'host))
  1051. (plist-get artificial :host)
  1052. "[any host]"))
  1053. (cons 'port
  1054. (or
  1055. (auth-source-netrc-element-or-first
  1056. (aget valist 'port))
  1057. (plist-get artificial :port)
  1058. "[any port]"))))
  1059. (prompt (or (aget auth-source-creation-prompts r)
  1060. (case r
  1061. (secret "%p password for %u@%h: ")
  1062. (user "%p user name for %h: ")
  1063. (host "%p host name for user %u: ")
  1064. (port "%p port for %u@%h: "))
  1065. (format "Enter %s (%%u@%%h:%%p): " r)))
  1066. (prompt (auth-source-format-prompt
  1067. prompt
  1068. `((?u ,(aget printable-defaults 'user))
  1069. (?h ,(aget printable-defaults 'host))
  1070. (?p ,(aget printable-defaults 'port))))))
  1071. ;; Store the data, prompting for the password if needed.
  1072. (setq data (or data
  1073. (if (eq r 'secret)
  1074. ;; Special case prompt for passwords.
  1075. ;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car (symbol-value 'epa-file-auto-mode-alist-entry)) "\\.gpg\\'") nil) (t gpg)))
  1076. ;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
  1077. (let* ((ep (format "Use GPG password tokens in %s?" file))
  1078. (gpg-encrypt
  1079. (cond
  1080. ((eq auth-source-netrc-use-gpg-tokens 'never)
  1081. 'never)
  1082. ((listp auth-source-netrc-use-gpg-tokens)
  1083. (let ((check (copy-sequence
  1084. auth-source-netrc-use-gpg-tokens))
  1085. item ret)
  1086. (while check
  1087. (setq item (pop check))
  1088. (when (or (eq (car item) t)
  1089. (string-match (car item) file))
  1090. (setq ret (cdr item))
  1091. (setq check nil)))))
  1092. (t 'never)))
  1093. (plain (or (eval default) (read-passwd prompt))))
  1094. ;; ask if we don't know what to do (in which case
  1095. ;; auth-source-netrc-use-gpg-tokens must be a list)
  1096. (unless gpg-encrypt
  1097. (setq gpg-encrypt (if (y-or-n-p ep) 'gpg 'never))
  1098. ;; TODO: save the defcustom now? or ask?
  1099. (setq auth-source-netrc-use-gpg-tokens
  1100. (cons `(,file ,gpg-encrypt)
  1101. auth-source-netrc-use-gpg-tokens)))
  1102. (if (eq gpg-encrypt 'gpg)
  1103. (auth-source-epa-make-gpg-token plain file)
  1104. plain))
  1105. (if (stringp default)
  1106. (read-string (if (string-match ": *\\'" prompt)
  1107. (concat (substring prompt 0 (match-beginning 0))
  1108. " (default " default "): ")
  1109. (concat prompt "(default " default ") "))
  1110. nil nil default)
  1111. (eval default)))))
  1112. (when data
  1113. (setq artificial (plist-put artificial
  1114. (intern (concat ":" (symbol-name r)))
  1115. (if (eq r 'secret)
  1116. (lexical-let ((data data))
  1117. (lambda () data))
  1118. data))))
  1119. ;; When r is not an empty string...
  1120. (when (and (stringp data)
  1121. (< 0 (length data)))
  1122. ;; this function is not strictly necessary but I think it
  1123. ;; makes the code clearer -tzz
  1124. (let ((printer (lambda ()
  1125. ;; append the key (the symbol name of r)
  1126. ;; and the value in r
  1127. (format "%s%s %s"
  1128. ;; prepend a space
  1129. (if (zerop (length add)) "" " ")
  1130. ;; remap auth-source tokens to netrc
  1131. (case r
  1132. (user "login")
  1133. (host "machine")
  1134. (secret "password")
  1135. (port "port") ; redundant but clearer
  1136. (t (symbol-name r)))
  1137. (if (string-match "[\"# ]" data)
  1138. (format "%S" data)
  1139. data)))))
  1140. (setq add (concat add (funcall printer)))))))
  1141. (plist-put
  1142. artificial
  1143. :save-function
  1144. (lexical-let ((file file)
  1145. (add add))
  1146. (lambda () (auth-source-netrc-saver file add))))
  1147. (list artificial)))
  1148. ;;(funcall (plist-get (nth 0 (auth-source-search :host '("nonesuch2") :user "tzz" :port "imap" :create t :max 1)) :save-function))
  1149. (defun auth-source-netrc-saver (file add)
  1150. "Save a line ADD in FILE, prompting along the way.
  1151. Respects `auth-source-save-behavior'. Uses
  1152. `auth-source-netrc-cache' to avoid prompting more than once."
  1153. (let* ((key (format "%s %s" file (rfc2104-hash 'md5 64 16 file add)))
  1154. (cached (assoc key auth-source-netrc-cache)))
  1155. (if cached
  1156. (auth-source-do-trivia
  1157. "auth-source-netrc-saver: found previous run for key %s, returning"
  1158. key)
  1159. (with-temp-buffer
  1160. (when (file-exists-p file)
  1161. (insert-file-contents file))
  1162. (when auth-source-gpg-encrypt-to
  1163. ;; (see bug#7487) making `epa-file-encrypt-to' local to
  1164. ;; this buffer lets epa-file skip the key selection query
  1165. ;; (see the `local-variable-p' check in
  1166. ;; `epa-file-write-region').
  1167. (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
  1168. (make-local-variable 'epa-file-encrypt-to))
  1169. (if (listp auth-source-gpg-encrypt-to)
  1170. (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
  1171. ;; we want the new data to be found first, so insert at beginning
  1172. (goto-char (point-min))
  1173. ;; Ask AFTER we've successfully opened the file.
  1174. (let ((prompt (format "Save auth info to file %s? " file))
  1175. (done (not (eq auth-source-save-behavior 'ask)))
  1176. (bufname "*auth-source Help*")
  1177. k)
  1178. (while (not done)
  1179. (setq k (auth-source-read-char-choice prompt '(?y ?n ?N ?e ??)))
  1180. (case k
  1181. (?y (setq done t))
  1182. (?? (save-excursion
  1183. (with-output-to-temp-buffer bufname
  1184. (princ
  1185. (concat "(y)es, save\n"
  1186. "(n)o but use the info\n"
  1187. "(N)o and don't ask to save again\n"
  1188. "(e)dit the line\n"
  1189. "(?) for help as you can see.\n"))
  1190. ;; Why? Doesn't with-output-to-temp-buffer already do
  1191. ;; the exact same thing anyway? --Stef
  1192. (set-buffer standard-output)
  1193. (help-mode))))
  1194. (?n (setq add ""
  1195. done t))
  1196. (?N
  1197. (setq add ""
  1198. done t)
  1199. (customize-save-variable 'auth-source-save-behavior nil))
  1200. (?e (setq add (read-string "Line to add: " add)))
  1201. (t nil)))
  1202. (when (get-buffer-window bufname)
  1203. (delete-window (get-buffer-window bufname)))
  1204. ;; Make sure the info is not saved.
  1205. (when (null auth-source-save-behavior)
  1206. (setq add ""))
  1207. (when (< 0 (length add))
  1208. (progn
  1209. (unless (bolp)
  1210. (insert "\n"))
  1211. (insert add "\n")
  1212. (write-region (point-min) (point-max) file nil 'silent)
  1213. ;; Make the .authinfo file non-world-readable.
  1214. (set-file-modes file #o600)
  1215. (auth-source-do-debug
  1216. "auth-source-netrc-create: wrote 1 new line to %s"
  1217. file)
  1218. (message "Saved new authentication information to %s" file)
  1219. nil))))
  1220. (aput 'auth-source-netrc-cache key "ran"))))
  1221. ;;; Backend specific parsing: Secrets API backend
  1222. ;;; (let ((auth-sources '(default))) (auth-source-search :max 1 :create t))
  1223. ;;; (let ((auth-sources '(default))) (auth-source-search :max 1 :delete t))
  1224. ;;; (let ((auth-sources '(default))) (auth-source-search :max 1))
  1225. ;;; (let ((auth-sources '(default))) (auth-source-search))
  1226. ;;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1))
  1227. ;;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1 :signon_realm "https://git.gnus.org/Git"))
  1228. (defun* auth-source-secrets-search (&rest
  1229. spec
  1230. &key backend create delete label
  1231. type max host user port
  1232. &allow-other-keys)
  1233. "Search the Secrets API; spec is like `auth-source'.
  1234. The :label key specifies the item's label. It is the only key
  1235. that can specify a substring. Any :label value besides a string
  1236. will allow any label.
  1237. All other search keys must match exactly. If you need substring
  1238. matching, do a wider search and narrow it down yourself.
  1239. You'll get back all the properties of the token as a plist.
  1240. Here's an example that looks for the first item in the 'Login'
  1241. Secrets collection:
  1242. \(let ((auth-sources '(\"secrets:Login\")))
  1243. (auth-source-search :max 1)
  1244. Here's another that looks for the first item in the 'Login'
  1245. Secrets collection whose label contains 'gnus':
  1246. \(let ((auth-sources '(\"secrets:Login\")))
  1247. (auth-source-search :max 1 :label \"gnus\")
  1248. And this one looks for the first item in the 'Login' Secrets
  1249. collection that's a Google Chrome entry for the git.gnus.org site
  1250. authentication tokens:
  1251. \(let ((auth-sources '(\"secrets:Login\")))
  1252. (auth-source-search :max 1 :signon_realm \"https://git.gnus.org/Git\"))
  1253. "
  1254. ;; TODO
  1255. (assert (not create) nil
  1256. "The Secrets API auth-source backend doesn't support creation yet")
  1257. ;; TODO
  1258. ;; (secrets-delete-item coll elt)
  1259. (assert (not delete) nil
  1260. "The Secrets API auth-source backend doesn't support deletion yet")
  1261. (let* ((coll (oref backend source))
  1262. (max (or max 5000)) ; sanity check: default to stop at 5K
  1263. (ignored-keys '(:create :delete :max :backend :label))
  1264. (search-keys (loop for i below (length spec) by 2
  1265. unless (memq (nth i spec) ignored-keys)
  1266. collect (nth i spec)))
  1267. ;; build a search spec without the ignored keys
  1268. ;; if a search key is nil or t (match anything), we skip it
  1269. (search-spec (apply 'append (mapcar
  1270. (lambda (k)
  1271. (if (or (null (plist-get spec k))
  1272. (eq t (plist-get spec k)))
  1273. nil
  1274. (list k (plist-get spec k))))
  1275. search-keys)))
  1276. ;; needed keys (always including host, login, port, and secret)
  1277. (returned-keys (mm-delete-duplicates (append
  1278. '(:host :login :port :secret)
  1279. search-keys)))
  1280. (items (loop for item in (apply 'secrets-search-items coll search-spec)
  1281. unless (and (stringp label)
  1282. (not (string-match label item)))
  1283. collect item))
  1284. ;; TODO: respect max in `secrets-search-items', not after the fact
  1285. (items (butlast items (- (length items) max)))
  1286. ;; convert the item name to a full plist
  1287. (items (mapcar (lambda (item)
  1288. (append
  1289. ;; make an entry for the secret (password) element
  1290. (list
  1291. :secret
  1292. (lexical-let ((v (secrets-get-secret coll item)))
  1293. (lambda () v)))
  1294. ;; rewrite the entry from ((k1 v1) (k2 v2)) to plist
  1295. (apply 'append
  1296. (mapcar (lambda (entry)
  1297. (list (car entry) (cdr entry)))
  1298. (secrets-get-attributes coll item)))))
  1299. items))
  1300. ;; ensure each item has each key in `returned-keys'
  1301. (items (mapcar (lambda (plist)
  1302. (append
  1303. (apply 'append
  1304. (mapcar (lambda (req)
  1305. (if (plist-get plist req)
  1306. nil
  1307. (list req nil)))
  1308. returned-keys))
  1309. plist))
  1310. items)))
  1311. items))
  1312. (defun* auth-source-secrets-create (&rest
  1313. spec
  1314. &key backend type max host user port
  1315. &allow-other-keys)
  1316. ;; TODO
  1317. ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec)
  1318. (debug spec))
  1319. ;;; Backend specific parsing: PLSTORE backend
  1320. (defun* auth-source-plstore-search (&rest
  1321. spec
  1322. &key backend create delete label
  1323. type max host user port
  1324. &allow-other-keys)
  1325. "Search the PLSTORE; spec is like `auth-source'."
  1326. (let* ((store (oref backend data))
  1327. (max (or max 5000)) ; sanity check: default to stop at 5K
  1328. (ignored-keys '(:create :delete :max :backend :require))
  1329. (search-keys (loop for i below (length spec) by 2
  1330. unless (memq (nth i spec) ignored-keys)
  1331. collect (nth i spec)))
  1332. ;; build a search spec without the ignored keys
  1333. ;; if a search key is nil or t (match anything), we skip it
  1334. (search-spec (apply 'append (mapcar
  1335. (lambda (k)
  1336. (let ((v (plist-get spec k)))
  1337. (if (or (null v)
  1338. (eq t v))
  1339. nil
  1340. (if (stringp v)
  1341. (setq v (list v)))
  1342. (list k v))))
  1343. search-keys)))
  1344. ;; needed keys (always including host, login, port, and secret)
  1345. (returned-keys (mm-delete-duplicates (append
  1346. '(:host :login :port :secret)
  1347. search-keys)))
  1348. (items (plstore-find store search-spec))
  1349. (item-names (mapcar #'car items))
  1350. (items (butlast items (- (length items) max)))
  1351. ;; convert the item to a full plist
  1352. (items (mapcar (lambda (item)
  1353. (let* ((plist (copy-tree (cdr item)))
  1354. (secret (plist-member plist :secret)))
  1355. (if secret
  1356. (setcar
  1357. (cdr secret)
  1358. (lexical-let ((v (car (cdr secret))))
  1359. (lambda () v))))
  1360. plist))
  1361. items))
  1362. ;; ensure each item has each key in `returned-keys'
  1363. (items (mapcar (lambda (plist)
  1364. (append
  1365. (apply 'append
  1366. (mapcar (lambda (req)
  1367. (if (plist-get plist req)
  1368. nil
  1369. (list req nil)))
  1370. returned-keys))
  1371. plist))
  1372. items)))
  1373. (cond
  1374. ;; if we need to create an entry AND none were found to match
  1375. ((and create
  1376. (not items))
  1377. ;; create based on the spec and record the value
  1378. (setq items (or
  1379. ;; if the user did not want to create the entry
  1380. ;; in the file, it will be returned
  1381. (apply (slot-value backend 'create-function) spec)
  1382. ;; if not, we do the search again without :create
  1383. ;; to get the updated data.
  1384. ;; the result will be returned, even if the search fails
  1385. (apply 'auth-source-plstore-search
  1386. (plist-put spec :create nil)))))
  1387. ((and delete
  1388. item-names)
  1389. (dolist (item-name item-names)
  1390. (plstore-delete store item-name))
  1391. (plstore-save store)))
  1392. items))
  1393. (defun* auth-source-plstore-create (&rest spec
  1394. &key backend
  1395. secret host user port create
  1396. &allow-other-keys)
  1397. (let* ((base-required '(host user port secret))
  1398. (base-secret '(secret))
  1399. ;; we know (because of an assertion in auth-source-search) that the
  1400. ;; :create parameter is either t or a list (which includes nil)
  1401. (create-extra (if (eq t create) nil create))
  1402. (current-data (car (auth-source-search :max 1
  1403. :host host
  1404. :port port)))
  1405. (required (append base-required create-extra))
  1406. (file (oref backend source))
  1407. (add "")
  1408. ;; `valist' is an alist
  1409. valist
  1410. ;; `artificial' will be returned if no creation is needed
  1411. artificial
  1412. secret-artificial)
  1413. ;; only for base required elements (defined as function parameters):
  1414. ;; fill in the valist with whatever data we may have from the search
  1415. ;; we complete the first value if it's a list and use the value otherwise
  1416. (dolist (br base-required)
  1417. (when (symbol-value br)
  1418. (let ((br-choice (cond
  1419. ;; all-accepting choice (predicate is t)
  1420. ((eq t (symbol-value br)) nil)
  1421. ;; just the value otherwise
  1422. (t (symbol-value br)))))
  1423. (when br-choice
  1424. (aput 'valist br br-choice)))))
  1425. ;; for extra required elements, see if the spec includes a value for them
  1426. (dolist (er create-extra)
  1427. (let ((name (concat ":" (symbol-name er)))
  1428. (keys (loop for i below (length spec) by 2
  1429. collect (nth i spec))))
  1430. (dolist (k keys)
  1431. (when (equal (symbol-name k) name)
  1432. (aput 'valist er (plist-get spec k))))))
  1433. ;; for each required element
  1434. (dolist (r required)
  1435. (let* ((data (aget valist r))
  1436. ;; take the first element if the data is a list
  1437. (data (or (auth-source-netrc-element-or-first data)
  1438. (plist-get current-data
  1439. (intern (format ":%s" r) obarray))))
  1440. ;; this is the default to be offered
  1441. (given-default (aget auth-source-creation-defaults r))
  1442. ;; the default supplementals are simple:
  1443. ;; for the user, try `given-default' and then (user-login-name);
  1444. ;; otherwise take `given-default'
  1445. (default (cond
  1446. ((and (not given-default) (eq r 'user))
  1447. (user-login-name))
  1448. (t given-default)))
  1449. (printable-defaults (list
  1450. (cons 'user
  1451. (or
  1452. (auth-source-netrc-element-or-first
  1453. (aget valist 'user))
  1454. (plist-get artificial :user)
  1455. "[any user]"))
  1456. (cons 'host
  1457. (or
  1458. (auth-source-netrc-element-or-first
  1459. (aget valist 'host))
  1460. (plist-get artificial :host)
  1461. "[any host]"))
  1462. (cons 'port
  1463. (or
  1464. (auth-source-netrc-element-or-first
  1465. (aget valist 'port))
  1466. (plist-get artificial :port)
  1467. "[any port]"))))
  1468. (prompt (or (aget auth-source-creation-prompts r)
  1469. (case r
  1470. (secret "%p password for %u@%h: ")
  1471. (user "%p user name for %h: ")
  1472. (host "%p host name for user %u: ")
  1473. (port "%p port for %u@%h: "))
  1474. (format "Enter %s (%%u@%%h:%%p): " r)))
  1475. (prompt (auth-source-format-prompt
  1476. prompt
  1477. `((?u ,(aget printable-defaults 'user))
  1478. (?h ,(aget printable-defaults 'host))
  1479. (?p ,(aget printable-defaults 'port))))))
  1480. ;; Store the data, prompting for the password if needed.
  1481. (setq data (or data
  1482. (if (eq r 'secret)
  1483. (or (eval default) (read-passwd prompt))
  1484. (if (stringp default)
  1485. (read-string (if (string-match ": *\\'" prompt)
  1486. (concat (substring prompt 0 (match-beginning 0))
  1487. " (default " default "): ")
  1488. (concat prompt "(default " default ") "))
  1489. nil nil default)
  1490. (eval default)))))
  1491. (when data
  1492. (if (member r base-secret)
  1493. (setq secret-artificial
  1494. (plist-put secret-artificial
  1495. (intern (concat ":" (symbol-name r)))
  1496. data))
  1497. (setq artificial (plist-put artificial
  1498. (intern (concat ":" (symbol-name r)))
  1499. data))))))
  1500. (plstore-put (oref backend data)
  1501. (sha1 (format "%s@%s:%s"
  1502. (plist-get artificial :user)
  1503. (plist-get artificial :host)
  1504. (plist-get artificial :port)))
  1505. artificial secret-artificial)
  1506. (if (y-or-n-p (format "Save auth info to file %s? "
  1507. (plstore-get-file (oref backend data))))
  1508. (plstore-save (oref backend data)))))
  1509. ;;; older API
  1510. ;;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" t "tzz")
  1511. ;; deprecate the old interface
  1512. (make-obsolete 'auth-source-user-or-password
  1513. 'auth-source-search "Emacs 24.1")
  1514. (make-obsolete 'auth-source-forget-user-or-password
  1515. 'auth-source-forget "Emacs 24.1")
  1516. (defun auth-source-user-or-password
  1517. (mode host port &optional username create-missing delete-existing)
  1518. "Find MODE (string or list of strings) matching HOST and PORT.
  1519. DEPRECATED in favor of `auth-source-search'!
  1520. USERNAME is optional and will be used as \"login\" in a search
  1521. across the Secret Service API (see secrets.el) if the resulting
  1522. items don't have a username. This means that if you search for
  1523. username \"joe\" and it matches an item but the item doesn't have
  1524. a :user attribute, the username \"joe\" will be returned.
  1525. A non nil DELETE-EXISTING means deleting any matching password
  1526. entry in the respective sources. This is useful only when
  1527. CREATE-MISSING is non nil as well; the intended use case is to
  1528. remove wrong password entries.
  1529. If no matching entry is found, and CREATE-MISSING is non nil,
  1530. the password will be retrieved interactively, and it will be
  1531. stored in the password database which matches best (see
  1532. `auth-sources').
  1533. MODE can be \"login\" or \"password\"."
  1534. (auth-source-do-debug
  1535. "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s"
  1536. mode host port username)
  1537. (let* ((listy (listp mode))
  1538. (mode (if listy mode (list mode)))
  1539. (cname (if username
  1540. (format "%s %s:%s %s" mode host port username)
  1541. (format "%s %s:%s" mode host port)))
  1542. (search (list :host host :port port))
  1543. (search (if username (append search (list :user username)) search))
  1544. (search (if create-missing
  1545. (append search (list :create t))
  1546. search))
  1547. (search (if delete-existing
  1548. (append search (list :delete t))
  1549. search))
  1550. ;; (found (if (not delete-existing)
  1551. ;; (gethash cname auth-source-cache)
  1552. ;; (remhash cname auth-source-cache)
  1553. ;; nil)))
  1554. (found nil))
  1555. (if found
  1556. (progn
  1557. (auth-source-do-debug
  1558. "auth-source-user-or-password: DEPRECATED cached %s=%s for %s (%s) + %s"
  1559. mode
  1560. ;; don't show the password
  1561. (if (and (member "password" mode) t)
  1562. "SECRET"
  1563. found)
  1564. host port username)
  1565. found) ; return the found data
  1566. ;; else, if not found, search with a max of 1
  1567. (let ((choice (nth 0 (apply 'auth-source-search
  1568. (append '(:max 1) search)))))
  1569. (when choice
  1570. (dolist (m mode)
  1571. (cond
  1572. ((equal "password" m)
  1573. (push (if (plist-get choice :secret)
  1574. (funcall (plist-get choice :secret))
  1575. nil) found))
  1576. ((equal "login" m)
  1577. (push (plist-get choice :user) found)))))
  1578. (setq found (nreverse found))
  1579. (setq found (if listy found (car-safe found)))))
  1580. found))
  1581. (defun auth-source-user-and-password (host &optional user)
  1582. (let* ((auth-info (car
  1583. (if user
  1584. (auth-source-search
  1585. :host host
  1586. :user "yourusername"
  1587. :max 1
  1588. :require '(:user :secret)
  1589. :create nil)
  1590. (auth-source-search
  1591. :host host
  1592. :max 1
  1593. :require '(:user :secret)
  1594. :create nil))))
  1595. (user (plist-get auth-info :user))
  1596. (password (plist-get auth-info :secret)))
  1597. (when (functionp password)
  1598. (setq password (funcall password)))
  1599. (list user password auth-info)))
  1600. (provide 'auth-source)
  1601. ;;; auth-source.el ends here