+ (dolist (fallback '(nil t))
+ (let ((keys (loop for i below (length spec) by 2
+ collect (nth i spec)))
+ (default-session-fallback "login"))
+ (dolist (choice auth-sources)
+ (let* ((s (plist-get choice :source))
+ ;; this is only set for Secret Service API specs (see secrets.el)
+ (coll (plist-get s :secrets))
+ (score 0))
+ (cond
+ (coll ; use secrets.el here
+ (when (eq coll 'default)
+ (setq coll (secrets-get-alias "default"))
+ (unless coll
+ (auth-source-do-debug
+ "No 'default' alias. Trying collection '%s'."
+ default-session-fallback)
+ (setq coll default-session-fallback)))
+ (let* ((coll-search (cond
+ ((stringp coll) coll)
+
+ ;; when the collection is nil:
+ ;; in fallback mode, accept it as any
+ ;; otherwise, hope to fail
+ ((null coll) (if fallback
+ nil
+ " *fallback-fail*"))))
+ ;; assemble a search query for secrets-search-items
+ ;; in fallback mode, host and protocol are not checked
+ (other-search (loop for k
+ in (if fallback
+ (remove :host
+ (remove :protocol keys))
+ keys)
+ append (list
+ k
+ ;; convert symbols to a string
+ (let ((v (plist-get spec k)))
+ (if (stringp v)
+ v
+ (prin1-to-string v))))))
+ ;; the score is based on how exact the search was,
+ ;; plus base score = 1 for any match
+ (score (1+ (length other-search)))
+ (results (apply 'secrets-search-items
+ coll-search
+ other-search)))
+ (auth-source-do-debug
+ "auth-source-pick: got items %s in collection '%s' + %s"
+ results coll-search other-search)
+ ;; put the results in the choices variable
+ (dolist (result results)
+ (setq choices (cons (list score
+ `(:source secrets
+ :item ,result
+ :collection ,coll
+ :search ,coll-search
+ ,@other-search))
+ choices)))))
+ ;; this is any non-secrets spec (currently means a string filename)
+ (t
+ (let ((match t))
+ (dolist (k keys)
+ (let* ((v (plist-get spec k))
+ (choicev (plist-get choice k)))
+ (setq match
+ (and match
+ (or (eq t choicev) ; source always matches spec key
+ ;; source key gives regex to match against spec
+ (and (stringp choicev) (string-match choicev v))
+ ;; source key gives symbol to match against spec
+ (and (symbolp choicev) (eq choicev v))
+ ;; in fallback mode, missing source key is OK
+ fallback)))
+ (when match (incf score)))) ; increment the score for each match
+
+ ;; now if the whole iteration resulted in a match:
+ (when match
+ (setq choices (cons (list score choice) choices))))))))
+ ;; when there were matches, skip the second pass
+ (when choices (return choices))))
+
+ ;; return the results sorted by score
+ (mapcar 'cadr (sort choices (lambda (x y) (> (car x) (car y)))))))