unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#72992: 29.4; towards xoauth2 support in Emacs
@ 2024-09-02  8:34 Xiyue Deng
       [not found] ` <handler.72992.B.172532159013230.ack@debbugs.gnu.org>
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Xiyue Deng @ 2024-09-02  8:34 UTC (permalink / raw)
  To: 72992

[-- Attachment #1: Type: text/plain, Size: 72591 bytes --]

Now that bug#72358 is done, as promised, I'm posting my plugin for
auth-sources that enables oauth2 handling which you can find on
Gitlab[1] (also attached).  As the current approach tries to override
some existing handling in auth-source, I would like to gather some
comments on how to properly integrate this handling, and see if there is
any benefit on providing this as a separate package for older Emacs
versions.

In the comment section of the package I put notes on how xoauth2 is
enabled as well as existing restrictions in auth-source and how it
workarounds them.  I'll briefly explain below.

Currently, auth-source search requires that the result include `:secret'
most of the time, where when using xoauth2 it is actually the
access-token. Actually, auth-source has existing support for xoauth2
authentication, though it assumes that the password value actually
stores the access-token.  Because xoauth2 also makes use of
`secret'/`password', it makes it hard to determine whether to use
password-based or xoauth2-based authentication, which is why my plugin
asks users to set `auth' in auth-source to determine whether to use
xoauth2.  Another complication from this is that auth-source search
requires the entry contains a `secret' most of the time, where it does
not need to be set when using xoauth2.  Therefore I workaround this by
temporarily disables this check and try to retrieve access-token using
oauth2 and set the result as password.

Given the inconveniences of reusing password for access-token, I wonder
whether we can add support for a separate `:access-token' key in the
auth-source entry and use that instead of password when authenticating
using xoauth2.  This way, we can have both password and access-token in
an auth-source entry and nnimap and smtpmail can use either one.  More
specifically:

* When performing an auth-source search, if xoauth2 related fields are
  set (see the list of fields in the comments of my plugin), it will
  retrieve access-token using oauth2.

* The search should change to check for either `secret'/`password' or
  `access-token' is available.

* For `nnimap-login' and `smtpmail-try-auth-method', pass in both
  password and access-token, and for xoauth2 it should use access-token
  instead of password.

If this is an acceptable approach, I'll try to draft a patch to
implement this in Emacs.  Otherwise, it may still worth implement the
current approach directly in Emacs so as to avoid using hack like
advice.

Meanwhile, I wonder whether this may be worth release as a separate
package so that users of older versions can use xoauth2 as well.  I'd
like to make it compatible with the agreed-upon approach to minimize any
incompatibilities.

Thanks for reading, and any comments are appreciated.

[1] https://gitlab.com/xiyueden/auth-source-xoauth2-plugin


In GNU Emacs 29.4 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.38,
 cairo version 1.16.0) of 2024-07-02, modified by Debian built on
 debian-hx90
System Description: Debian GNU/Linux 12 (bookworm)

Configured using:
 'configure --build x86_64-linux-gnu --prefix=/usr
 --sharedstatedir=/var/lib --libexecdir=/usr/libexec
 --localstatedir=/var/lib --infodir=/usr/share/info
 --mandir=/usr/share/man --with-libsystemd --with-pop=yes
 --enable-locallisppath=/etc/emacs:/usr/local/share/emacs/29.4/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/29.4/site-lisp:/usr/share/emacs/site-lisp
 --with-sound=alsa --without-gconf --with-mailutils
 --with-native-compilation --build x86_64-linux-gnu --prefix=/usr
 --sharedstatedir=/var/lib --libexecdir=/usr/libexec
 --localstatedir=/var/lib --infodir=/usr/share/info
 --mandir=/usr/share/man --with-libsystemd --with-pop=yes
 --enable-locallisppath=/etc/emacs:/usr/local/share/emacs/29.4/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/29.4/site-lisp:/usr/share/emacs/site-lisp
 --with-sound=alsa --without-gconf --with-mailutils
 --with-native-compilation --with-cairo --with-x=yes
 --with-x-toolkit=gtk3 --with-toolkit-scroll-bars 'CFLAGS=-g -O2
 -ffile-prefix-map=/build/emacs-UNWIcy/emacs-29.4+1=. -fstack-protector-strong
 -Wformat -Werror=format-security -Wall' 'CPPFLAGS=-Wdate-time
 -D_FORTIFY_SOURCE=2' LDFLAGS=-Wl,-z,relro'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES
NATIVE_COMP NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3
THREADS TIFF TOOLKIT_SCROLL_BARS TREE_SITTER WEBP X11 XDBE XIM XINPUT2
XPM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Lisp Interaction

Minor modes in effect:
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  shell-dirtrack-mode: t
  mu4e-modeline-mode: t
  windmove-mode: t
  rcirc-track-minor-mode: t
  server-mode: t
  subword-mode: t
  bug-reference-prog-mode: t
  whitespace-mode: t
  yas-minor-mode: t
  xclip-mode: t
  global-treesit-auto-mode: t
  treemacs-project-follow-mode: t
  treemacs-follow-mode: t
  treemacs-git-mode: t
  treemacs-fringe-indicator-mode: t
  corfu-terminal-mode: t
  corfu-popupinfo-mode: t
  corfu-echo-mode: t
  global-corfu-mode: t
  corfu-mode: t
  activities-tabs-mode: t
  activities-mode: t
  fido-vertical-mode: t
  icomplete-vertical-mode: t
  icomplete-mode: t
  fido-mode: t
  override-global-mode: t
  global-display-line-numbers-mode: t
  display-line-numbers-mode: t
  global-auto-revert-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  tab-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-autoloads hides /usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/debian-autoloads
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-section hides /usr/share/emacs/site-lisp/elpa/magit-section-3.3.0/magit-section
/usr/share/emacs/site-lisp/elpa/ace-window-0.10.0/ace-window-autoloads hides /usr/share/emacs/site-lisp/elpa-src/ace-window-0.10.0/ace-window-autoloads
/usr/share/emacs/site-lisp/elpa/ace-window-0.10.0/ace-window-pkg hides /usr/share/emacs/site-lisp/elpa-src/ace-window-0.10.0/ace-window-pkg
/usr/share/emacs/site-lisp/elpa/ace-window-0.10.0/ace-window hides /usr/share/emacs/site-lisp/elpa-src/ace-window-0.10.0/ace-window
/usr/share/emacs/site-lisp/elpa/activities-0.7.1/activities-autoloads hides /usr/share/emacs/site-lisp/elpa-src/activities-0.7.1/activities-autoloads
/usr/share/emacs/site-lisp/elpa/activities-0.7.1/activities-pkg hides /usr/share/emacs/site-lisp/elpa-src/activities-0.7.1/activities-pkg
/usr/share/emacs/site-lisp/elpa/activities-0.7.1/activities-list hides /usr/share/emacs/site-lisp/elpa-src/activities-0.7.1/activities-list
/usr/share/emacs/site-lisp/elpa/activities-0.7.1/activities hides /usr/share/emacs/site-lisp/elpa-src/activities-0.7.1/activities
/usr/share/emacs/site-lisp/elpa/activities-0.7.1/activities-tabs hides /usr/share/emacs/site-lisp/elpa-src/activities-0.7.1/activities-tabs
/usr/share/emacs/site-lisp/elpa/apache-mode-2.2.0/apache-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/apache-mode-2.2.0/apache-mode-autoloads
/usr/share/emacs/site-lisp/elpa/apache-mode-2.2.0/apache-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/apache-mode-2.2.0/apache-mode-pkg
/usr/share/emacs/site-lisp/elpa/apache-mode-2.2.0/apache-mode hides /usr/share/emacs/site-lisp/elpa-src/apache-mode-2.2.0/apache-mode
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-info hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-info
/usr/share/emacs/site-lisp/elpa/auctex-13.3/latex-flymake hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/latex-flymake
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-site hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-site
/usr/share/emacs/site-lisp/elpa/auctex-13.3/texmathp hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/texmathp
/usr/share/emacs/site-lisp/elpa/auctex-13.3/toolbar-x hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/toolbar-x
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-style hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-style
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-font hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-font
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-jp hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-jp
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-mik hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-mik
/usr/share/emacs/site-lisp/elpa/auctex-13.3/plain-tex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/plain-tex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-fold hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-fold
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-ispell hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-ispell
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-bar hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-bar
/usr/share/emacs/site-lisp/elpa/auctex-13.3/preview-latex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/preview-latex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/bib-cite hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/bib-cite
/usr/share/emacs/site-lisp/elpa/auctex-13.3/preview hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/preview
/usr/share/emacs/site-lisp/elpa/auctex-13.3/context-nl hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/context-nl
/usr/share/emacs/site-lisp/elpa/auctex-13.3/auto-loads hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/auto-loads
/usr/share/emacs/site-lisp/elpa/auctex-13.3/multi-prompt hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/multi-prompt
/usr/share/emacs/site-lisp/elpa/auctex-13.3/context-en hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/context-en
/usr/share/emacs/site-lisp/elpa/auctex-13.3/lpath hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/lpath
/usr/share/emacs/site-lisp/elpa/auctex-13.3/auctex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/auctex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/auctex-autoloads hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/auctex-autoloads
/usr/share/emacs/site-lisp/elpa/auctex-13.3/font-latex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/font-latex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/auctex-pkg hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/auctex-pkg
/usr/share/emacs/site-lisp/elpa/auctex-13.3/context hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/context
/usr/share/emacs/site-lisp/elpa/auctex-13.3/latex hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/latex
/usr/share/emacs/site-lisp/elpa/auctex-13.3/tex-wizard hides /usr/share/emacs/site-lisp/elpa-src/auctex-13.3/tex-wizard
/usr/share/emacs/site-lisp/elpa/avy-0.5.0/avy hides /usr/share/emacs/site-lisp/elpa-src/avy-0.5.0/avy
/usr/share/emacs/site-lisp/elpa/avy-0.5.0/avy-autoloads hides /usr/share/emacs/site-lisp/elpa-src/avy-0.5.0/avy-autoloads
/usr/share/emacs/site-lisp/elpa/avy-0.5.0/avy-pkg hides /usr/share/emacs/site-lisp/elpa-src/avy-0.5.0/avy-pkg
/usr/share/emacs/site-lisp/elpa/bazel-0/bazel-autoloads hides /usr/share/emacs/site-lisp/elpa-src/bazel-0/bazel-autoloads
/usr/share/emacs/site-lisp/elpa/bazel-0/bazel hides /usr/share/emacs/site-lisp/elpa-src/bazel-0/bazel
/usr/share/emacs/site-lisp/elpa/bazel-0/test hides /usr/share/emacs/site-lisp/elpa-src/bazel-0/test
/usr/share/emacs/site-lisp/elpa/bazel-0/bazel-pkg hides /usr/share/emacs/site-lisp/elpa-src/bazel-0/bazel-pkg
/usr/share/emacs/site-lisp/elpa/bison-mode-0.3/bison-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/bison-mode-0.3/bison-mode-pkg
/usr/share/emacs/site-lisp/elpa/bison-mode-0.3/bison-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/bison-mode-0.3/bison-mode-autoloads
/usr/share/emacs/site-lisp/elpa/bison-mode-0.3/bison-mode hides /usr/share/emacs/site-lisp/elpa-src/bison-mode-0.3/bison-mode
/usr/share/emacs/site-lisp/elpa/boxquote-2.3/boxquote hides /usr/share/emacs/site-lisp/elpa-src/boxquote-2.3/boxquote
/usr/share/emacs/site-lisp/elpa/boxquote-2.3/boxquote-autoloads hides /usr/share/emacs/site-lisp/elpa-src/boxquote-2.3/boxquote-autoloads
/usr/share/emacs/site-lisp/elpa/boxquote-2.3/boxquote-pkg hides /usr/share/emacs/site-lisp/elpa-src/boxquote-2.3/boxquote-pkg
/usr/share/emacs/site-lisp/elpa/buttercup-1.26/buttercup-pkg hides /usr/share/emacs/site-lisp/elpa-src/buttercup-1.26/buttercup-pkg
/usr/share/emacs/site-lisp/elpa/buttercup-1.26/buttercup hides /usr/share/emacs/site-lisp/elpa-src/buttercup-1.26/buttercup
/usr/share/emacs/site-lisp/elpa/buttercup-1.26/buttercup-autoloads hides /usr/share/emacs/site-lisp/elpa-src/buttercup-1.26/buttercup-autoloads
/usr/share/emacs/site-lisp/elpa/buttercup-1.26/buttercup-compat hides /usr/share/emacs/site-lisp/elpa-src/buttercup-1.26/buttercup-compat
/usr/share/emacs/site-lisp/elpa/cfrs-1.6.0/cfrs-pkg hides /usr/share/emacs/site-lisp/elpa-src/cfrs-1.6.0/cfrs-pkg
/usr/share/emacs/site-lisp/elpa/cfrs-1.6.0/cfrs hides /usr/share/emacs/site-lisp/elpa-src/cfrs-1.6.0/cfrs
/usr/share/emacs/site-lisp/elpa/cfrs-1.6.0/cfrs-autoloads hides /usr/share/emacs/site-lisp/elpa-src/cfrs-1.6.0/cfrs-autoloads
/usr/share/emacs/site-lisp/elpa/clojure-mode-5.19.0/clojure-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-5.19.0/clojure-mode-pkg
/usr/share/emacs/site-lisp/elpa/clojure-mode-5.19.0/clojure-mode hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-5.19.0/clojure-mode
/usr/share/emacs/site-lisp/elpa/clojure-mode-5.19.0/clojure-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-5.19.0/clojure-mode-autoloads
/usr/share/emacs/site-lisp/elpa/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking-pkg hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking-pkg
/usr/share/emacs/site-lisp/elpa/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking-autoloads hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking-autoloads
/usr/share/emacs/site-lisp/elpa/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking hides /usr/share/emacs/site-lisp/elpa-src/clojure-mode-extra-font-locking-3.0.0/clojure-mode-extra-font-locking
/usr/share/emacs/site-lisp/elpa/cmake-mode-3.29.0/cmake-mode hides /usr/share/emacs/site-lisp/elpa-src/cmake-mode-3.29.0/cmake-mode
/usr/share/emacs/site-lisp/elpa/cmake-mode-3.29.0/cmake-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/cmake-mode-3.29.0/cmake-mode-autoloads
/usr/share/emacs/site-lisp/elpa/cmake-mode-3.29.0/cmake-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/cmake-mode-3.29.0/cmake-mode-pkg
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-dabbrev hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-dabbrev
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-capf hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-capf
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-yasnippet hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-yasnippet
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-ispell hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-ispell
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-etags hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-etags
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-template hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-template
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-abbrev hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-abbrev
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-files hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-files
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-css hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-css
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-tests hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-tests
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-dabbrev-code hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-dabbrev-code
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-pkg hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-pkg
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-oddmuse hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-oddmuse
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-bbdb hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-bbdb
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-clang hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-clang
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-nxml hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-nxml
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-gtags hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-gtags
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-tempo hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-tempo
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-autoloads hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-autoloads
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-cmake hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-cmake
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-tng hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-tng
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-elisp hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-elisp
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-semantic hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-semantic
/usr/share/emacs/site-lisp/elpa/company-0.10.2/company-keywords hides /usr/share/emacs/site-lisp/elpa-src/company-0.10.2/company-keywords
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-26 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-26
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-28 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-28
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-macs hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-macs
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-autoloads hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-autoloads
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-pkg hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-pkg
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-25 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-25
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-30 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-30
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-29 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-29
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat-27 hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat-27
/usr/share/emacs/site-lisp/elpa/compat-30.0.0.0/compat hides /usr/share/emacs/site-lisp/elpa-src/compat-30.0.0.0/compat
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-quick hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-quick
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-info hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-info
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-history hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-history
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-popupinfo hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-popupinfo
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-indexed hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-indexed
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-pkg hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-pkg
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-echo hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-echo
/usr/share/emacs/site-lisp/elpa/corfu-1.5/corfu-autoloads hides /usr/share/emacs/site-lisp/elpa-src/corfu-1.5/corfu-autoloads
/usr/share/emacs/site-lisp/elpa/corfu-terminal-0.7/corfu-terminal-autoloads hides /usr/share/emacs/site-lisp/elpa-src/corfu-terminal-0.7/corfu-terminal-autoloads
/usr/share/emacs/site-lisp/elpa/corfu-terminal-0.7/corfu-terminal-pkg hides /usr/share/emacs/site-lisp/elpa-src/corfu-terminal-0.7/corfu-terminal-pkg
/usr/share/emacs/site-lisp/elpa/corfu-terminal-0.7/corfu-terminal hides /usr/share/emacs/site-lisp/elpa-src/corfu-terminal-0.7/corfu-terminal
/usr/share/emacs/site-lisp/elpa/csv-mode-1.23/csv-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/csv-mode-1.23/csv-mode-autoloads
/usr/share/emacs/site-lisp/elpa/csv-mode-1.23/csv-mode hides /usr/share/emacs/site-lisp/elpa-src/csv-mode-1.23/csv-mode
/usr/share/emacs/site-lisp/elpa/csv-mode-1.23/csv-mode-tests hides /usr/share/emacs/site-lisp/elpa-src/csv-mode-1.23/csv-mode-tests
/usr/share/emacs/site-lisp/elpa/csv-mode-1.23/csv-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/csv-mode-1.23/csv-mode-pkg
/usr/share/emacs/site-lisp/elpa/dart-mode-1.0.7/dart-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/dart-mode-1.0.7/dart-mode-autoloads
/usr/share/emacs/site-lisp/elpa/dart-mode-1.0.7/dart-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/dart-mode-1.0.7/dart-mode-pkg
/usr/share/emacs/site-lisp/elpa/dart-mode-1.0.7/dart-mode hides /usr/share/emacs/site-lisp/elpa-src/dart-mode-1.0.7/dart-mode
/usr/share/emacs/site-lisp/elpa/dash-2.19.1/dash hides /usr/share/emacs/site-lisp/elpa-src/dash-2.19.1/dash
/usr/share/emacs/site-lisp/elpa/dash-2.19.1/dash-pkg hides /usr/share/emacs/site-lisp/elpa-src/dash-2.19.1/dash-pkg
/usr/share/emacs/site-lisp/elpa/dash-2.19.1/dash-autoloads hides /usr/share/emacs/site-lisp/elpa-src/dash-2.19.1/dash-autoloads
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-el-autoloads hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/debian-el-autoloads
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/apt-sources hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/apt-sources
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-bug hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/debian-bug
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/apt-utils hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/apt-utils
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-el-pkg hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/debian-el-pkg
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-autoloads hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/debian-autoloads
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/gnus-BTS hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/gnus-BTS
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/deb-view hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/deb-view
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-el hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/debian-el
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/preseed hides /usr/share/emacs/site-lisp/elpa-src/debian-el-37.16/preseed
/usr/share/emacs/site-lisp/elpa/debpaste-0.1.5/debpaste hides /usr/share/emacs/site-lisp/elpa-src/debpaste-0.1.5/debpaste
/usr/share/emacs/site-lisp/elpa/debpaste-0.1.5/debpaste-pkg hides /usr/share/emacs/site-lisp/elpa-src/debpaste-0.1.5/debpaste-pkg
/usr/share/emacs/site-lisp/elpa/debpaste-0.1.5/debpaste-autoloads hides /usr/share/emacs/site-lisp/elpa-src/debpaste-0.1.5/debpaste-autoloads
/usr/share/emacs/site-lisp/elpa/devscripts-40/devscripts hides /usr/share/emacs/site-lisp/elpa-src/devscripts-40/devscripts
/usr/share/emacs/site-lisp/elpa/devscripts-40/devscripts-autoloads hides /usr/share/emacs/site-lisp/elpa-src/devscripts-40/devscripts-autoloads
/usr/share/emacs/site-lisp/elpa/devscripts-40/pbuilder-mode hides /usr/share/emacs/site-lisp/elpa-src/devscripts-40/pbuilder-mode
/usr/share/emacs/site-lisp/elpa/devscripts-40/devscripts-pkg hides /usr/share/emacs/site-lisp/elpa-src/devscripts-40/devscripts-pkg
/usr/share/emacs/site-lisp/elpa/devscripts-40/pbuilder-log-view-mode hides /usr/share/emacs/site-lisp/elpa-src/devscripts-40/pbuilder-log-view-mode
/usr/share/emacs/site-lisp/elpa/dockerfile-mode-1.7/dockerfile-mode hides /usr/share/emacs/site-lisp/elpa-src/dockerfile-mode-1.7/dockerfile-mode
/usr/share/emacs/site-lisp/elpa/dockerfile-mode-1.7/dockerfile-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/dockerfile-mode-1.7/dockerfile-mode-autoloads
/usr/share/emacs/site-lisp/elpa/dockerfile-mode-1.7/dockerfile-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/dockerfile-mode-1.7/dockerfile-mode-pkg
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/debian-bts-control hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/debian-bts-control
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/debian-changelog-mode hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/debian-changelog-mode
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/debian-autopkgtest-control-mode hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/debian-autopkgtest-control-mode
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/dpkg-dev-el-autoloads hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/dpkg-dev-el-autoloads
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/dpkg-dev-el-pkg hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/dpkg-dev-el-pkg
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/dpkg-dev-el hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/dpkg-dev-el
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/debian-control-mode hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/debian-control-mode
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/debian-copyright hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/debian-copyright
/usr/share/emacs/site-lisp/elpa/dpkg-dev-el-37.16/readme-debian hides /usr/share/emacs/site-lisp/elpa-src/dpkg-dev-el-37.16/readme-debian
/usr/share/emacs/site-lisp/elpa/exec-path-from-shell-2.1/exec-path-from-shell hides /usr/share/emacs/site-lisp/elpa-src/exec-path-from-shell-2.1/exec-path-from-shell
/usr/share/emacs/site-lisp/elpa/exec-path-from-shell-2.1/exec-path-from-shell-autoloads hides /usr/share/emacs/site-lisp/elpa-src/exec-path-from-shell-2.1/exec-path-from-shell-autoloads
/usr/share/emacs/site-lisp/elpa/exec-path-from-shell-2.1/exec-path-from-shell-pkg hides /usr/share/emacs/site-lisp/elpa-src/exec-path-from-shell-2.1/exec-path-from-shell-pkg
/usr/share/emacs/site-lisp/elpa/format-all-0.6.0/format-all hides /usr/share/emacs/site-lisp/elpa-src/format-all-0.6.0/format-all
/usr/share/emacs/site-lisp/elpa/format-all-0.6.0/format-all-pkg hides /usr/share/emacs/site-lisp/elpa-src/format-all-0.6.0/format-all-pkg
/usr/share/emacs/site-lisp/elpa/format-all-0.6.0/format-all-autoloads hides /usr/share/emacs/site-lisp/elpa-src/format-all-0.6.0/format-all-autoloads
/usr/share/emacs/site-lisp/elpa/git-commit-3.3.0/git-commit hides /usr/share/emacs/site-lisp/elpa-src/git-commit-3.3.0/git-commit
/usr/share/emacs/site-lisp/elpa/git-commit-3.3.0/git-commit-autoloads hides /usr/share/emacs/site-lisp/elpa-src/git-commit-3.3.0/git-commit-autoloads
/usr/share/emacs/site-lisp/elpa/git-commit-3.3.0/git-commit-pkg hides /usr/share/emacs/site-lisp/elpa-src/git-commit-3.3.0/git-commit-pkg
/usr/share/emacs/site-lisp/elpa/git-modes-1.4.2/git-modes hides /usr/share/emacs/site-lisp/elpa-src/git-modes-1.4.2/git-modes
/usr/share/emacs/site-lisp/elpa/git-modes-1.4.2/git-modes-pkg hides /usr/share/emacs/site-lisp/elpa-src/git-modes-1.4.2/git-modes-pkg
/usr/share/emacs/site-lisp/elpa/git-modes-1.4.2/git-modes-autoloads hides /usr/share/emacs/site-lisp/elpa-src/git-modes-1.4.2/git-modes-autoloads
/usr/share/emacs/site-lisp/elpa/gitattributes-mode-1.4.2/gitattributes-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/gitattributes-mode-1.4.2/gitattributes-mode-pkg
/usr/share/emacs/site-lisp/elpa/gitattributes-mode-1.4.2/gitattributes-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/gitattributes-mode-1.4.2/gitattributes-mode-autoloads
/usr/share/emacs/site-lisp/elpa/gitattributes-mode-1.4.2/gitattributes-mode hides /usr/share/emacs/site-lisp/elpa-src/gitattributes-mode-1.4.2/gitattributes-mode
/usr/share/emacs/site-lisp/elpa/gitconfig-mode-1.4.2/gitconfig-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/gitconfig-mode-1.4.2/gitconfig-mode-autoloads
/usr/share/emacs/site-lisp/elpa/gitconfig-mode-1.4.2/gitconfig-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/gitconfig-mode-1.4.2/gitconfig-mode-pkg
/usr/share/emacs/site-lisp/elpa/gitconfig-mode-1.4.2/gitconfig-mode hides /usr/share/emacs/site-lisp/elpa-src/gitconfig-mode-1.4.2/gitconfig-mode
/usr/share/emacs/site-lisp/elpa/gitignore-mode-1.4.2/gitignore-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/gitignore-mode-1.4.2/gitignore-mode-pkg
/usr/share/emacs/site-lisp/elpa/gitignore-mode-1.4.2/gitignore-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/gitignore-mode-1.4.2/gitignore-mode-autoloads
/usr/share/emacs/site-lisp/elpa/gitignore-mode-1.4.2/gitignore-mode hides /usr/share/emacs/site-lisp/elpa-src/gitignore-mode-1.4.2/gitignore-mode
/usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/gnuplot hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/gnuplot
/usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/gnuplot-autoloads hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/gnuplot-autoloads
/usr/share/emacs/site-lisp/elpa/debian-el-37.16/debian-autoloads hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/debian-autoloads
/usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/gnuplot-pkg hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/gnuplot-pkg
/usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/gnuplot-context hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/gnuplot-context
/usr/share/emacs/site-lisp/elpa/gnuplot-0.8.1/gnuplot-gui hides /usr/share/emacs/site-lisp/elpa-src/gnuplot-0.8.1/gnuplot-gui
/usr/share/emacs/site-lisp/elpa/go-mode-1.6.0/go-mode hides /usr/share/emacs/site-lisp/elpa-src/go-mode-1.6.0/go-mode
/usr/share/emacs/site-lisp/elpa/go-mode-1.6.0/go-guru hides /usr/share/emacs/site-lisp/elpa-src/go-mode-1.6.0/go-guru
/usr/share/emacs/site-lisp/elpa/go-mode-1.6.0/go-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/go-mode-1.6.0/go-mode-pkg
/usr/share/emacs/site-lisp/elpa/go-mode-1.6.0/go-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/go-mode-1.6.0/go-mode-autoloads
/usr/share/emacs/site-lisp/elpa/go-mode-1.6.0/go-rename hides /usr/share/emacs/site-lisp/elpa-src/go-mode-1.6.0/go-rename
/usr/share/emacs/site-lisp/elpa/graphviz-dot-mode-0.4.2/graphviz-dot-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/graphviz-dot-mode-0.4.2/graphviz-dot-mode-autoloads
/usr/share/emacs/site-lisp/elpa/graphviz-dot-mode-0.4.2/graphviz-dot-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/graphviz-dot-mode-0.4.2/graphviz-dot-mode-pkg
/usr/share/emacs/site-lisp/elpa/graphviz-dot-mode-0.4.2/graphviz-dot-mode hides /usr/share/emacs/site-lisp/elpa-src/graphviz-dot-mode-0.4.2/graphviz-dot-mode
/usr/share/emacs/site-lisp/elpa/ht-2.3/ht-pkg hides /usr/share/emacs/site-lisp/elpa-src/ht-2.3/ht-pkg
/usr/share/emacs/site-lisp/elpa/ht-2.3/ht-autoloads hides /usr/share/emacs/site-lisp/elpa-src/ht-2.3/ht-autoloads
/usr/share/emacs/site-lisp/elpa/ht-2.3/ht hides /usr/share/emacs/site-lisp/elpa-src/ht-2.3/ht
/usr/share/emacs/site-lisp/elpa/hydra-0.15.0/hydra-ox hides /usr/share/emacs/site-lisp/elpa-src/hydra-0.15.0/hydra-ox
/usr/share/emacs/site-lisp/elpa/hydra-0.15.0/hydra-autoloads hides /usr/share/emacs/site-lisp/elpa-src/hydra-0.15.0/hydra-autoloads
/usr/share/emacs/site-lisp/elpa/hydra-0.15.0/hydra-pkg hides /usr/share/emacs/site-lisp/elpa-src/hydra-0.15.0/hydra-pkg
/usr/share/emacs/site-lisp/elpa/hydra-0.15.0/hydra-examples hides /usr/share/emacs/site-lisp/elpa-src/hydra-0.15.0/hydra-examples
/usr/share/emacs/site-lisp/elpa/hydra-0.15.0/hydra hides /usr/share/emacs/site-lisp/elpa-src/hydra-0.15.0/hydra
/usr/share/emacs/site-lisp/elpa/inheritenv-0.2/inheritenv hides /usr/share/emacs/site-lisp/elpa-src/inheritenv-0.2/inheritenv
/usr/share/emacs/site-lisp/elpa/inheritenv-0.2/inheritenv-autoloads hides /usr/share/emacs/site-lisp/elpa-src/inheritenv-0.2/inheritenv-autoloads
/usr/share/emacs/site-lisp/elpa/inheritenv-0.2/inheritenv-pkg hides /usr/share/emacs/site-lisp/elpa-src/inheritenv-0.2/inheritenv-pkg
/usr/share/emacs/site-lisp/elpa/inheritenv-0.2/inheritenv-tests hides /usr/share/emacs/site-lisp/elpa-src/inheritenv-0.2/inheritenv-tests
/usr/share/emacs/site-lisp/elpa/language-id-0.20/language-id hides /usr/share/emacs/site-lisp/elpa-src/language-id-0.20/language-id
/usr/share/emacs/site-lisp/elpa/language-id-0.20/language-id-pkg hides /usr/share/emacs/site-lisp/elpa-src/language-id-0.20/language-id-pkg
/usr/share/emacs/site-lisp/elpa/language-id-0.20/language-id-autoloads hides /usr/share/emacs/site-lisp/elpa-src/language-id-0.20/language-id-autoloads
/usr/share/emacs/site-lisp/elpa/lintian-0.1/lintian-pkg hides /usr/share/emacs/site-lisp/elpa-src/lintian-0.1/lintian-pkg
/usr/share/emacs/site-lisp/elpa/lintian-0.1/lintian-autoloads hides /usr/share/emacs/site-lisp/elpa-src/lintian-0.1/lintian-autoloads
/usr/share/emacs/site-lisp/elpa/lintian-0.1/lintian hides /usr/share/emacs/site-lisp/elpa-src/lintian-0.1/lintian
/usr/share/emacs/site-lisp/elpa/lua-mode-20221027/lua-mode hides /usr/share/emacs/site-lisp/elpa-src/lua-mode-20221027/lua-mode
/usr/share/emacs/site-lisp/elpa/lua-mode-20221027/lua-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/lua-mode-20221027/lua-mode-autoloads
/usr/share/emacs/site-lisp/elpa/lua-mode-20221027/init-tryout hides /usr/share/emacs/site-lisp/elpa-src/lua-mode-20221027/init-tryout
/usr/share/emacs/site-lisp/elpa/lua-mode-20221027/lua-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/lua-mode-20221027/lua-mode-pkg
/usr/share/emacs/site-lisp/elpa/lv-0.15.0/lv-autoloads hides /usr/share/emacs/site-lisp/elpa-src/lv-0.15.0/lv-autoloads
/usr/share/emacs/site-lisp/elpa/lv-0.15.0/lv hides /usr/share/emacs/site-lisp/elpa-src/lv-0.15.0/lv
/usr/share/emacs/site-lisp/elpa/lv-0.15.0/lv-pkg hides /usr/share/emacs/site-lisp/elpa-src/lv-0.15.0/lv-pkg
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-remote hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-remote
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/git-rebase hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/git-rebase
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-bisect hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-bisect
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-margin hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-margin
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-merge hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-merge
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-section hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-section
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-patch hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-patch
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-commit hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-commit
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-autoloads hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-autoloads
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-files hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-files
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-stash hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-stash
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-bookmark hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-bookmark
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-submodule hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-submodule
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-apply hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-apply
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-repos hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-repos
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-core hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-core
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-subtree hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-subtree
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-autorevert hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-autorevert
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-gitignore hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-gitignore
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-transient hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-transient
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-extras hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-extras
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-git hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-git
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-notes hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-notes
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-reflog hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-reflog
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-mode hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-mode
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-push hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-push
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-tag hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-tag
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-process hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-process
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-ediff hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-ediff
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-imenu hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-imenu
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-diff hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-diff
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-clone hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-clone
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-log hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-log
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-utils hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-utils
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-wip hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-wip
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-branch hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-branch
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-pull hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-pull
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-reset hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-reset
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-sequence hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-sequence
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-status hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-status
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-refs hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-refs
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-obsolete hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-obsolete
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-fetch hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-fetch
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-worktree hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-worktree
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-blame hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-blame
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-pkg hides /usr/share/emacs/site-lisp/elpa-src/magit-3.3.0/magit-pkg
/usr/share/emacs/site-lisp/elpa/magit-section-3.3.0/magit-section-autoloads hides /usr/share/emacs/site-lisp/elpa-src/magit-section-3.3.0/magit-section-autoloads
/usr/share/emacs/site-lisp/elpa/magit-3.3.0/magit-section hides /usr/share/emacs/site-lisp/elpa-src/magit-section-3.3.0/magit-section
/usr/share/emacs/site-lisp/elpa/magit-section-3.3.0/magit-section-pkg hides /usr/share/emacs/site-lisp/elpa-src/magit-section-3.3.0/magit-section-pkg
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-cgen hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-cgen
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-scan hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-scan
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-publish hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-publish
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-complete hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-complete
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-syntax hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-syntax
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/tlc hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/tlc
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-netshell hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-netshell
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/semantic-matlab hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/semantic-matlab
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/mlint hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/mlint
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/company-matlab-shell hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/company-matlab-shell
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-shell-gud hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-shell-gud
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/mlgud hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/mlgud
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/srecode-matlab hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/srecode-matlab
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-compat hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-compat
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-mode-pkg
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-maint hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-maint
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/cedet-matlab hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/cedet-matlab
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-topic hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-topic
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/linemark hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/linemark
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-shell hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-shell
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/semanticdb-matlab hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/semanticdb-matlab
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-load hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-load
/usr/share/emacs/site-lisp/elpa/matlab-mode-4.0/matlab-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/matlab-mode-4.0/matlab-mode-autoloads
/usr/share/emacs/site-lisp/elpa/meson-mode-0.2/meson-mode hides /usr/share/emacs/site-lisp/elpa-src/meson-mode-0.2/meson-mode
/usr/share/emacs/site-lisp/elpa/meson-mode-0.2/utils hides /usr/share/emacs/site-lisp/elpa-src/meson-mode-0.2/utils
/usr/share/emacs/site-lisp/elpa/meson-mode-0.2/meson-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/meson-mode-0.2/meson-mode-autoloads
/usr/share/emacs/site-lisp/elpa/meson-mode-0.2/meson-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/meson-mode-0.2/meson-mode-pkg
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-draft hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-draft
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-modeline hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-modeline
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-view hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-view
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-message hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-message
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-helpers hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-helpers
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-pkg hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-pkg
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-bookmarks hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-bookmarks
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-thread hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-thread
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-mime-parts hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-mime-parts
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-server hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-server
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-query-items hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-query-items
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-contrib hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-contrib
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-window hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-window
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-config hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-config
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-autoloads hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-autoloads
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-icalendar hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-icalendar
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-mark hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-mark
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-headers hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-headers
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-org hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-org
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-contacts hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-contacts
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-speedbar hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-speedbar
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-obsolete hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-obsolete
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-vars hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-vars
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-actions hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-actions
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-main hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-main
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-search hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-search
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-notification hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-notification
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-context hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-context
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-compose hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-compose
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-lists hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-lists
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-folders hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-folders
/usr/share/emacs/site-lisp/elpa/mu4e-1.12.6/mu4e-update hides /usr/share/emacs/site-lisp/elpa-src/mu4e-1.12.6/mu4e-update
/usr/share/emacs/site-lisp/elpa/nginx-mode-1.1.9/nginx-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/nginx-mode-1.1.9/nginx-mode-autoloads
/usr/share/emacs/site-lisp/elpa/nginx-mode-1.1.9/nginx-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/nginx-mode-1.1.9/nginx-mode-pkg
/usr/share/emacs/site-lisp/elpa/nginx-mode-1.1.9/nginx-mode hides /usr/share/emacs/site-lisp/elpa-src/nginx-mode-1.1.9/nginx-mode
/usr/share/emacs/site-lisp/elpa/paredit-26/paredit-autoloads hides /usr/share/emacs/site-lisp/elpa-src/paredit-26/paredit-autoloads
/usr/share/emacs/site-lisp/elpa/paredit-26/paredit-pkg hides /usr/share/emacs/site-lisp/elpa-src/paredit-26/paredit-pkg
/usr/share/emacs/site-lisp/elpa/paredit-26/paredit hides /usr/share/emacs/site-lisp/elpa-src/paredit-26/paredit
/usr/share/emacs/site-lisp/elpa/persist-0.6.1/persist hides /usr/share/emacs/site-lisp/elpa-src/persist-0.6.1/persist
/usr/share/emacs/site-lisp/elpa/persist-0.6.1/persist-pkg hides /usr/share/emacs/site-lisp/elpa-src/persist-0.6.1/persist-pkg
/usr/share/emacs/site-lisp/elpa/persist-0.6.1/persist-autoloads hides /usr/share/emacs/site-lisp/elpa-src/persist-0.6.1/persist-autoloads
/usr/share/emacs/site-lisp/elpa/pfuture-1.9/pfuture hides /usr/share/emacs/site-lisp/elpa-src/pfuture-1.9/pfuture
/usr/share/emacs/site-lisp/elpa/pfuture-1.9/pfuture-autoloads hides /usr/share/emacs/site-lisp/elpa-src/pfuture-1.9/pfuture-autoloads
/usr/share/emacs/site-lisp/elpa/pfuture-1.9/pfuture-pkg hides /usr/share/emacs/site-lisp/elpa-src/pfuture-1.9/pfuture-pkg
/usr/share/emacs/site-lisp/elpa/po-mode-0.21/po-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/po-mode-0.21/po-mode-pkg
/usr/share/emacs/site-lisp/elpa/po-mode-0.21/po-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/po-mode-0.21/po-mode-autoloads
/usr/share/emacs/site-lisp/elpa/po-mode-0.21/po-mode hides /usr/share/emacs/site-lisp/elpa-src/po-mode-0.21/po-mode
/usr/share/emacs/site-lisp/elpa/popon-0.13/popon hides /usr/share/emacs/site-lisp/elpa-src/popon-0.13/popon
/usr/share/emacs/site-lisp/elpa/popon-0.13/popon-autoloads hides /usr/share/emacs/site-lisp/elpa-src/popon-0.13/popon-autoloads
/usr/share/emacs/site-lisp/elpa/popon-0.13/popon-pkg hides /usr/share/emacs/site-lisp/elpa-src/popon-0.13/popon-pkg
/usr/share/emacs/site-lisp/elpa/posframe-1.1.7/posframe-pkg hides /usr/share/emacs/site-lisp/elpa-src/posframe-1.1.7/posframe-pkg
/usr/share/emacs/site-lisp/elpa/posframe-1.1.7/posframe-autoloads hides /usr/share/emacs/site-lisp/elpa-src/posframe-1.1.7/posframe-autoloads
/usr/share/emacs/site-lisp/elpa/posframe-1.1.7/posframe hides /usr/share/emacs/site-lisp/elpa-src/posframe-1.1.7/posframe
/usr/share/emacs/site-lisp/elpa/projectile-2.8.0/projectile-pkg hides /usr/share/emacs/site-lisp/elpa-src/projectile-2.8.0/projectile-pkg
/usr/share/emacs/site-lisp/elpa/projectile-2.8.0/projectile-autoloads hides /usr/share/emacs/site-lisp/elpa-src/projectile-2.8.0/projectile-autoloads
/usr/share/emacs/site-lisp/elpa/projectile-2.8.0/projectile hides /usr/share/emacs/site-lisp/elpa-src/projectile-2.8.0/projectile
/usr/share/emacs/site-lisp/elpa/py-isort-2016.1/py-isort hides /usr/share/emacs/site-lisp/elpa-src/py-isort-2016.1/py-isort
/usr/share/emacs/site-lisp/elpa/py-isort-2016.1/py-isort-autoloads hides /usr/share/emacs/site-lisp/elpa-src/py-isort-2016.1/py-isort-autoloads
/usr/share/emacs/site-lisp/elpa/py-isort-2016.1/py-isort-pkg hides /usr/share/emacs/site-lisp/elpa-src/py-isort-2016.1/py-isort-pkg
/usr/share/emacs/site-lisp/elpa/pyvenv-1.21/pyvenv hides /usr/share/emacs/site-lisp/elpa-src/pyvenv-1.21/pyvenv
/usr/share/emacs/site-lisp/elpa/pyvenv-1.21/pyvenv-pkg hides /usr/share/emacs/site-lisp/elpa-src/pyvenv-1.21/pyvenv-pkg
/usr/share/emacs/site-lisp/elpa/pyvenv-1.21/pyvenv-autoloads hides /usr/share/emacs/site-lisp/elpa-src/pyvenv-1.21/pyvenv-autoloads
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-common hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-common
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-mode-tests hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-mode-tests
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-mode-treesitter hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-mode-treesitter
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-cargo hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-cargo
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-mode-autoloads
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-utils hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-utils
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-rustfmt hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-rustfmt
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-mode hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-mode
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-playpen hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-playpen
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-prog-mode hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-prog-mode
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-mode-pkg
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-cargo-tests hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-cargo-tests
/usr/share/emacs/site-lisp/elpa/rust-mode-1.0.6/rust-compile hides /usr/share/emacs/site-lisp/elpa-src/rust-mode-1.0.6/rust-compile
/usr/share/emacs/site-lisp/elpa/s-1.12.0/s-autoloads hides /usr/share/emacs/site-lisp/elpa-src/s-1.12.0/s-autoloads
/usr/share/emacs/site-lisp/elpa/s-1.12.0/s-pkg hides /usr/share/emacs/site-lisp/elpa-src/s-1.12.0/s-pkg
/usr/share/emacs/site-lisp/elpa/s-1.12.0/s hides /usr/share/emacs/site-lisp/elpa-src/s-1.12.0/s
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-map hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-map
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-indent hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-indent
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-syntax hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-syntax
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-lib hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-lib
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-pkg
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-prettify-symbols hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-prettify-symbols
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-compile hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-compile
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-organise hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-organise
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-autoloads
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-paragraph hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-paragraph
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-imenu hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-imenu
/usr/share/emacs/site-lisp/elpa/scala-mode-1.1.0/scala-mode-fontlock hides /usr/share/emacs/site-lisp/elpa-src/scala-mode-1.1.0/scala-mode-fontlock
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq-24 hides /usr/share/emacs/site-lisp/elpa-src/seq-2.24/seq-24
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq-autoloads hides /usr/share/emacs/site-lisp/elpa-src/seq-2.24/seq-autoloads
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq hides /usr/share/emacs/site-lisp/elpa-src/seq-2.24/seq
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq-pkg hides /usr/share/emacs/site-lisp/elpa-src/seq-2.24/seq-pkg
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq-25 hides /usr/share/emacs/site-lisp/elpa-src/seq-2.24/seq-25
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-project-follow-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-project-follow-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-dom hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-dom
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-follow-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-follow-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-logging hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-logging
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-autoloads hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-autoloads
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-compatibility hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-compatibility
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-async hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-async
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-themes hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-themes
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-mouse-interface hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-mouse-interface
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-tag-follow-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-tag-follow-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-bookmarks hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-bookmarks
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-fringe-indicator hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-fringe-indicator
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-filewatch-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-filewatch-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-interface hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-interface
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-tags hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-tags
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-visuals hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-visuals
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-core-utils hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-core-utils
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-peek-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-peek-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-annotations hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-annotations
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-icons hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-icons
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-persistence hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-persistence
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-diagnostics hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-diagnostics
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-scope hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-scope
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-extensions hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-extensions
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-rendering hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-rendering
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-customization hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-customization
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-file-management hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-file-management
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-faces hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-faces
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-macros hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-macros
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-git-commit-diff-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-git-commit-diff-mode
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-treelib hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-treelib
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-hydras hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-hydras
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-pkg hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-pkg
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-header-line hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-header-line
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-icons-dired hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-icons-dired
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-workspaces hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-workspaces
/usr/share/emacs/site-lisp/elpa/treemacs-3.1/treemacs-mode hides /usr/share/emacs/site-lisp/elpa-src/treemacs-3.1/treemacs-mode
/usr/share/emacs/site-lisp/elpa/treemacs-magit-3.1/treemacs-magit hides /usr/share/emacs/site-lisp/elpa-src/treemacs-magit-3.1/treemacs-magit
/usr/share/emacs/site-lisp/elpa/treemacs-magit-3.1/treemacs-magit-pkg hides /usr/share/emacs/site-lisp/elpa-src/treemacs-magit-3.1/treemacs-magit-pkg
/usr/share/emacs/site-lisp/elpa/treemacs-magit-3.1/treemacs-magit-autoloads hides /usr/share/emacs/site-lisp/elpa-src/treemacs-magit-3.1/treemacs-magit-autoloads
/usr/share/emacs/site-lisp/elpa/treemacs-projectile-3.1/treemacs-projectile-pkg hides /usr/share/emacs/site-lisp/elpa-src/treemacs-projectile-3.1/treemacs-projectile-pkg
/usr/share/emacs/site-lisp/elpa/treemacs-projectile-3.1/treemacs-projectile-autoloads hides /usr/share/emacs/site-lisp/elpa-src/treemacs-projectile-3.1/treemacs-projectile-autoloads
/usr/share/emacs/site-lisp/elpa/treemacs-projectile-3.1/treemacs-projectile hides /usr/share/emacs/site-lisp/elpa-src/treemacs-projectile-3.1/treemacs-projectile
/usr/share/emacs/site-lisp/elpa/vterm-0.0.2/vterm-load-path hides /usr/share/emacs/site-lisp/elpa-src/vterm-0.0.2/vterm-load-path
/usr/share/emacs/site-lisp/elpa/vterm-0.0.2/vterm hides /usr/share/emacs/site-lisp/elpa-src/vterm-0.0.2/vterm
/usr/share/emacs/site-lisp/elpa/vterm-0.0.2/vterm-pkg hides /usr/share/emacs/site-lisp/elpa-src/vterm-0.0.2/vterm-pkg
/usr/share/emacs/site-lisp/elpa/vterm-0.0.2/vterm-autoloads hides /usr/share/emacs/site-lisp/elpa-src/vterm-0.0.2/vterm-autoloads
/usr/share/emacs/site-lisp/elpa/web-mode-17.3.13/web-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/web-mode-17.3.13/web-mode-pkg
/usr/share/emacs/site-lisp/elpa/web-mode-17.3.13/web-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/web-mode-17.3.13/web-mode-autoloads
/usr/share/emacs/site-lisp/elpa/web-mode-17.3.13/web-mode hides /usr/share/emacs/site-lisp/elpa-src/web-mode-17.3.13/web-mode
/usr/share/emacs/site-lisp/elpa/with-editor-3.3.2/with-editor-pkg hides /usr/share/emacs/site-lisp/elpa-src/with-editor-3.3.2/with-editor-pkg
/usr/share/emacs/site-lisp/elpa/with-editor-3.3.2/with-editor-autoloads hides /usr/share/emacs/site-lisp/elpa-src/with-editor-3.3.2/with-editor-autoloads
/usr/share/emacs/site-lisp/elpa/with-editor-3.3.2/with-editor hides /usr/share/emacs/site-lisp/elpa-src/with-editor-3.3.2/with-editor
/usr/share/emacs/site-lisp/elpa/xml-rpc-1.6.17/xml-rpc hides /usr/share/emacs/site-lisp/elpa-src/xml-rpc-1.6.17/xml-rpc
/usr/share/emacs/site-lisp/elpa/xml-rpc-1.6.17/xml-rpc-pkg hides /usr/share/emacs/site-lisp/elpa-src/xml-rpc-1.6.17/xml-rpc-pkg
/usr/share/emacs/site-lisp/elpa/xml-rpc-1.6.17/xml-rpc-autoloads hides /usr/share/emacs/site-lisp/elpa-src/xml-rpc-1.6.17/xml-rpc-autoloads
/usr/share/emacs/site-lisp/elpa/yaml-mode-0.0.16/yaml-mode-pkg hides /usr/share/emacs/site-lisp/elpa-src/yaml-mode-0.0.16/yaml-mode-pkg
/usr/share/emacs/site-lisp/elpa/yaml-mode-0.0.16/yaml-mode hides /usr/share/emacs/site-lisp/elpa-src/yaml-mode-0.0.16/yaml-mode
/usr/share/emacs/site-lisp/elpa/yaml-mode-0.0.16/yaml-mode-autoloads hides /usr/share/emacs/site-lisp/elpa-src/yaml-mode-0.0.16/yaml-mode-autoloads
/usr/share/emacs/site-lisp/elpa/yasnippet-0.14.1/yasnippet-autoloads hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-0.14.1/yasnippet-autoloads
/usr/share/emacs/site-lisp/elpa/yasnippet-0.14.1/yasnippet-pkg hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-0.14.1/yasnippet-pkg
/usr/share/emacs/site-lisp/elpa/yasnippet-0.14.1/yasnippet hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-0.14.1/yasnippet
/usr/share/emacs/site-lisp/elpa/yasnippet-snippets-20220713/yasnippet-snippets hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-snippets-20220713/yasnippet-snippets
/usr/share/emacs/site-lisp/elpa/yasnippet-snippets-20220713/yasnippet-snippets-pkg hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-snippets-20220713/yasnippet-snippets-pkg
/usr/share/emacs/site-lisp/elpa/yasnippet-snippets-20220713/yasnippet-snippets-autoloads hides /usr/share/emacs/site-lisp/elpa-src/yasnippet-snippets-20220713/yasnippet-snippets-autoloads
/usr/share/emacs/site-lisp/elpa/zenburn-theme-2.8.0/zenburn-theme hides /usr/share/emacs/site-lisp/elpa-src/zenburn-theme-2.8.0/zenburn-theme
/usr/share/emacs/site-lisp/elpa/zenburn-theme-2.8.0/zenburn-theme-pkg hides /usr/share/emacs/site-lisp/elpa-src/zenburn-theme-2.8.0/zenburn-theme-pkg
/usr/share/emacs/site-lisp/elpa/zenburn-theme-2.8.0/zenburn-theme-autoloads hides /usr/share/emacs/site-lisp/elpa-src/zenburn-theme-2.8.0/zenburn-theme-autoloads
/usr/share/emacs/site-lisp/elpa/seq-2.24/seq hides /usr/share/emacs/29.4/lisp/emacs-lisp/seq

Features:
(shadow emacsbug url-queue magit-extras magit-bookmark magit-submodule
magit-obsolete magit-blame magit-stash magit-reflog magit-bisect
magit-push magit-pull magit-fetch magit-clone magit-remote magit-commit
magit-sequence magit-notes magit-worktree magit-tag magit-merge
magit-branch magit-reset magit-files magit-refs magit-status magit
magit-repos magit-apply magit-wip magit-log which-func imenu magit-diff
smerge-mode diff git-commit log-edit add-log magit-core magit-autorevert
magit-margin magit-transient magit-process with-editor shell magit-mode
transient magit-git magit-section etags fileloop generator xref
shr-color help-fns radix-tree cl-print debug backtrace magit-utils crm
dired-aux mailalias po face-remap matlab matlab-scan matlab-syntax
matlab-compat qp mm-archive sort gnus-cite mail-extr textsec uni-scripts
idna-mapping ucs-normalize uni-confusable textsec-check gnus-async
gnus-bcklg gnus-ml gnus-topic cursor-sensor utf-7 url-cache nnfolder
gnus-demon nnml ezgnus gnus-delay gnus-draft gnus-agent gnus-srvr
gnus-score score-mode nnvirtual nntp gnus-cache nndraft nnmh mu4e
mu4e-org org ob ob-tangle ob-ref ob-lob ob-table ob-exp org-macro
org-src ob-comint org-pcomplete pcomplete org-list org-footnote
org-faces org-entities noutline outline ob-emacs-lisp ob-core ob-eval
org-cycle org-table ol org-fold org-fold-core org-keys oc org-loaddefs
find-func org-version org-compat org-macs format-spec mu4e-notification
notifications mu4e-main mu4e-view mu4e-mime-parts cal-menu calendar
cal-loaddefs mu4e-headers mu4e-thread mu4e-actions mu4e-compose
mu4e-draft gnus-msg gnus-art mm-uu mml2015 mm-view mml-smime smime dig
gnus-sum gnus-group gnus-undo gnus-start gnus-dbus dbus gnus-cloud
nnimap nnmail mail-source utf7 nnoo gnus-spec gnus-int gnus-range
gnus-win gnus nnheader range mu4e-search mu4e-lists mu4e-bookmarks
mu4e-mark mu4e-message shr pixel-fill kinsoku url-file svg xml dom
flow-fill mu4e-contacts mu4e-update mu4e-folders mu4e-context
mu4e-query-items mu4e-server mu4e-modeline mu4e-vars mu4e-helpers
mu4e-config mu4e-window ido message yank-media rfc822 mml mml-sec
gnus-util mm-decode mm-bodies mm-encode mailabbrev gmm-utils mailheader
mu4e-obsolete mule-util jka-compr windmove flyspell ispell gnutls
network-stream epa-file epa derived rcirc parse-time iso8601 time-date
term/xterm xterm comp comp-cstr server cap-words superword subword vc-hg
vc-git diff-mode vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs log-view
pcvs-util vc vc-dispatcher bug-reference disp-table whitespace
yasnippet-snippets yasnippet cus-edit cus-start wid-edit init
mu4e-debian-hx90 zenburn-theme xclip treesit-auto treesit
treemacs-project-follow-mode treemacs-follow-mode treemacs-rendering
treemacs-annotations treemacs-async treemacs-visuals
treemacs-fringe-indicator pulse color treemacs-workspaces treemacs-dom
treemacs-icons treemacs-themes treemacs-scope treemacs-core-utils
treemacs-logging treemacs-customization pfuture inline ht s hl-line dash
keychain-environment exec-path-from-shell corfu-terminal popon
corfu-popupinfo corfu-echo corfu compat compat-30 activities-tabs
activities persist bookmark pp edmacro kmacro auth-source-xoauth2-plugin
smtpmail sendmail mail-utils oauth2 url-http url-auth mail-parse rfc2231
rfc2047 rfc2045 mm-util ietf-drums mail-prsvr url-gw nsm puny plstore
epg rfc6068 epg-config advice icomplete cus-load flymake-proc flymake
project compile text-property-search comint ansi-osc ansi-color ring
warnings icons thingatpt cl-extra help-mode use-package
use-package-ensure use-package-delight use-package-diminish
use-package-bind-key bind-key easy-mmode use-package-core
display-line-numbers autorevert filenotify
keychain-environment-autoloads treesit-auto-autoloads xclip-autoloads rx
info debian-el dired dired-loaddefs finder-inf package browse-url url
url-proxy url-privacy url-expand url-methods url-history url-cookie
generate-lisp-file url-domsuf url-util mailcap url-handlers url-parse
auth-source cl-seq eieio eieio-core cl-macs password-cache json subr-x
map byte-opt gv bytecomp byte-compile url-vars cl-loaddefs cl-lib rmc
iso-transl tooltip cconv eldoc paren electric uniquify ediff-hook
vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode lisp-mode prog-mode register
page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads dbusbind inotify lcms2 dynamic-setting system-font-setting
font-render-setting cairo move-toolbar gtk x-toolkit xinput2 x multi-tty
make-network-process native-compile emacs)

Memory information:
((conses 16 1284383 173587)
 (symbols 48 38751 38)
 (strings 32 161682 13995)
 (string-bytes 1 5119905)
 (vectors 16 102981)
 (vector-slots 8 2547120 157205)
 (floats 8 1011 1550)
 (intervals 56 48502 7145)
 (buffers 984 76))

-- 
Xiyue Deng

[-- Attachment #2: auth-source-xoauth2-plugin.el --]
[-- Type: application/emacs-lisp, Size: 6506 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
       [not found] ` <handler.72992.B.172532159013230.ack@debbugs.gnu.org>
@ 2024-09-11  0:27   ` Xiyue Deng
  2024-09-17 17:33     ` Xiyue Deng
  0 siblings, 1 reply; 12+ messages in thread
From: Xiyue Deng @ 2024-09-11  0:27 UTC (permalink / raw)
  To: 72992

Friendly ping for feedback.
-- 
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-11  0:27   ` Xiyue Deng
@ 2024-09-17 17:33     ` Xiyue Deng
  0 siblings, 0 replies; 12+ messages in thread
From: Xiyue Deng @ 2024-09-17 17:33 UTC (permalink / raw)
  To: 72992, Philip Kaludercic

Xiyue Deng <manphiz@gmail.com> writes:

> Friendly ping for feedback.
> -- 
> Xiyue Deng

Another friendly ping.  Also CCing Philip who kindly provided feedback
for the previous bug.

-- 
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-02  8:34 bug#72992: 29.4; towards xoauth2 support in Emacs Xiyue Deng
       [not found] ` <handler.72992.B.172532159013230.ack@debbugs.gnu.org>
@ 2024-09-17 19:12 ` Philip Kaludercic
  2024-09-18  6:24   ` Xiyue Deng
  2024-09-17 21:33 ` Stefan Kangas
  2 siblings, 1 reply; 12+ messages in thread
From: Philip Kaludercic @ 2024-09-17 19:12 UTC (permalink / raw)
  To: Xiyue Deng; +Cc: 72992


Xiyue Deng <manphiz@gmail.com> writes:

> Now that bug#72358 is done, as promised, I'm posting my plugin for
> auth-sources that enables oauth2 handling which you can find on
> Gitlab[1] (also attached).

Once again I just want to be sure: When you say "plugin", you mean
package, right?  You are proposing to add this to GNU ELPA?

[...]

>
> Thanks for reading, and any comments are appreciated.

I'll be honest, I have no idea about OAuth, so my comments are just
related to Elisp for now.  Hope that's OK!

> [1] https://gitlab.com/xiyueden/auth-source-xoauth2-plugin

> ;; Copyright (C) 2024 Xiyue Deng <manphiz@gmail.com>
>
> ;; Author: Xiyue Deng <manphiz@gmail.com>
> ;; Version: 0.1-git
> ;; Package-Requires: ((emacs "28.1") (oauth2 "0.17"))
>
> ;; This file is not part of GNU Emacs.
>
> ;; GNU Emacs is free software: you can redistribute it and/or modify
> ;; it under the terms of the GNU General Public License as published by
> ;; the Free Software Foundation, either version 3 of the License, or
> ;; (at your option) any later version.
>
> ;; GNU Emacs is distributed in the hope that it will be useful,
> ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
> ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> ;; GNU General Public License for more details.
>
> ;; You should have received a copy of the GNU General Public License
> ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
>
> ;;; Commentary:
>
> ;; This package enables support for xoauth2 authentication with
> ;; auth-source.  To set up, please put this file in the `load-path' of
> ;; Emacs, and add the following lines in your Emacs configuration:
>
> ;;     (require 'auth-source-xoauth2-plugin)
> ;;     (auth-source-xoauth2-plugin-enable)
>
> ;; or with use-package:
>
> ;;     (use-package auth-source-xoauth2-plugin
> ;;       :config
> ;;       (auth-source-xoauth2-plugin-enable))
>
> ;; After enabling, smtpmail should be supported.  To enable this in Gnus
> ;; nnimap, you should also set `(nnimap-authenticator xoauth2)' in the
> ;; corresponding account settings in `gnus-secondary-select-methods'
>
> ;; auth-source uses the `secret' field in auth-source file as password
> ;; for authentication, including xoauth2.  To decide which
> ;; authentication method to use (e.g. plain password vs xoauth2), it
> ;; inspects the `auth' field from the auth-source entry, and if the
> ;; value is `xaouth2', it will try to gather data and get the access
> ;; token for use of xoauth2 authentication; otherwise, it will fallback
> ;; to the default authentication method.
>
> ;; When xoauth2 authentication is enabled, it will try to get the
> ;; following data from the auth-source entry: `auth-url', `token-url',
> ;; `scope', `client-id', `client-secret', `redirect-uri', and optionally
> ;; `state'.  These information will be used by oauth2 to retrieve the
> ;; access-token.  This package uses an advice to switch the auth-source
> ;; search result from the `password' to the `access-token' it got, which
> ;; in turn will be used to construct the xoauth2 authentication string,
> ;; currently in nnimap-login and smtpmail-try-auth-method.  To really
> ;; enable xoauth2 in smtpmail, it will add 'xoauth2 to
> ;; 'smtpmail-auth-supported (if it is not already in the list) using
> ;; `add-to-list' so that xoauth2 is tried first.
>
> ;; Note that currently the auth-source requires the searched entry must
> ;; have `secret' field set in the entry, which is not necessary when
> ;; using xoauth2.  Therefore in the advice it temporarily disables
> ;; checking for `:secret' if set and perform the search, and check the
> ;; result before returning.
>
> ;;; Code:
>
> (require 'auth-source)
> (require 'cl-lib)
> (require 'oauth2)
> (require 'smtpmail)
>
> (defun auth-source-xoauth2-plugin--search-backends (orig-fun &rest args)
>   "Perform auth-source-search and set password as access-token when requested.
> The substitution only happens if one sets `auth' to `xoauth2' in
> your auth-source-entry.  It is expected that `token_url',
> `client_id', `client_secret', and `refresh_token' are properly
> set along `host', `user', and `port' (note the snake_case)."
>   (auth-source-do-trivia "Advising auth-source-search")
>   (let (check-secret)
>     (when (memq :secret (nth 5 args))
>       (auth-source-do-trivia
>        "Required fields include :secret.  As we are requesting access token to replace the secret, we'll temporary remove :secret from the require list and check that it's properly set to a valid access token later.")
>       (setf (nth 5 args) (remove :secret (nth 5 args)))
>       (setq check-secret t))
>     (let ((orig-res (apply orig-fun args))
>           res)
>       (dolist (auth-data orig-res)
>         (auth-source-do-trivia "Matched auth data: %s" (pp-to-string auth-data))

Is "trivia" the right debug level for all the messages in this function?
I feel like some of them should at least be "debug".

>         (let ((auth (plist-get auth-data :auth)))
>           (when (and auth
>                      (stringp auth)
>                      (string= auth "xoauth2"))

You can simplify the check by just doing (equal auth "xoauth2"), as this
implies all of the above (if it is `equal' to a string, it must be a
string and hence also non-nil).

>             (auth-source-do-debug
>              ":auth set to `xoauth2'.  Will get access token.")
>             (let ((auth-url (plist-get auth-data :auth-url))
>                   (token-url (plist-get auth-data :token-url))
>                   (scope (plist-get auth-data :scope))
>                   (client-id (plist-get auth-data :client-id))
>                   (client-secret (plist-get auth-data :client-secret))
>                   (redirect-uri (plist-get auth-data :redirect-uri))
>                   (state (plist-get auth-data :state)))

I feel like this could be simplified with a map-let expression.  

  (map-let (:auth-url :token-url ...) auth-data
    ...)
                                                                 
You should be able to require 'map at compile time, so that the
expansions don't occur an overhead during run-time.

>               (auth-source-do-trivia "Using oauth2 to auth and store token...")
>               (let ((token (oauth2-auth-and-store
>                             auth-url token-url scope client-id client-secret
>                             redirect-uri state)))
>                 (auth-source-do-trivia "oauth2 token: %s" (pp-to-string token))
>                 (auth-source-do-trivia "Refreshing token...")
>                 (oauth2-refresh-access token)
>                 (auth-source-do-trivia "oauth2 token after refresh: %s"
>                                        (pp-to-string token))
>                 (let ((access-token (oauth2-token-access-token token)))
>                   (auth-source-do-trivia
>                    "Updating :secret with access-token: %s" access-token)
>                   (plist-put auth-data :secret access-token))))))

The documentation for plist-put warns:

  The new plist is returned;
  use ‘(setq x (plist-put x prop val))’ to be sure to use the new value.
  The PLIST is modified by side effects.

Alternatively, you should also be able to do:

  (setf (plist-get auth-data :secret) access-token)

>
>         (unless (and check-secret
>                      (not (plist-get auth-data :secret)))
>           (auth-source-do-trivia "Updating auth-source-search results.")
>           (add-to-list 'res auth-data t)))

This should definitely be a `push', as `res' is lexically scoped (see
docstring for `add-to-list').

>       res)))
>
> ;;;###autoload
> (defun auth-source-xoauth2-plugin-enable ()
>   "Enable auth-source-xoauth2-plugin."
>   (unless (memq 'xoauth2 smtpmail-auth-supported)
>     (add-to-list 'smtpmail-auth-supported 'xoauth2))

In functions, it would be more conventional to use push.  Especially
because `add-to-list' checks for duplicates, which you have already
done.

>
>   (advice-add 'auth-source-search-backends :around

There's no reason not to sharp-quote the function here as well?

>               #'auth-source-xoauth2-plugin--search-backends))

I would recommend turning this into a global minor mode instead, so that
it is easy to disable, if a user just wants to try it out.

>
> (provide 'auth-source-xoauth2-plugin)
>
> ;;; auth-source-xoauth2-plugin.el ends here

-- 
	Philip Kaludercic on siskin





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-02  8:34 bug#72992: 29.4; towards xoauth2 support in Emacs Xiyue Deng
       [not found] ` <handler.72992.B.172532159013230.ack@debbugs.gnu.org>
  2024-09-17 19:12 ` Philip Kaludercic
@ 2024-09-17 21:33 ` Stefan Kangas
  2024-09-18 19:43   ` Xiyue Deng
  2 siblings, 1 reply; 12+ messages in thread
From: Stefan Kangas @ 2024-09-17 21:33 UTC (permalink / raw)
  To: Xiyue Deng, 72992; +Cc: Ted Zlatanov

Xiyue Deng <manphiz@gmail.com> writes:

> Now that bug#72358 is done, as promised, I'm posting my plugin for
> auth-sources that enables oauth2 handling which you can find on
> Gitlab[1] (also attached).  As the current approach tries to override
> some existing handling in auth-source, I would like to gather some
> comments on how to properly integrate this handling, and see if there is
> any benefit on providing this as a separate package for older Emacs
> versions.
>
> In the comment section of the package I put notes on how xoauth2 is
> enabled as well as existing restrictions in auth-source and how it
> workarounds them.  I'll briefly explain below.

I think it would be good if you could add to your package some general
explanation of what xoauth2 is, and what are its use cases both in a
general sense, and specifically together with the auth-source
package. Don't assume that people already know what xoauth2 is, how it
is different from oauth2, which services use it, etc. Explain it. I
would add such general information to the beginning of the "Commentary"
section. Nothing long is needed, just a general introduction and perhaps
links for where to read more.

Some examples of when it would be used, preferably with example code for
some use cases, would also go a long way.

> Currently, auth-source search requires that the result include `:secret'
> most of the time, where when using xoauth2 it is actually the
> access-token. Actually, auth-source has existing support for xoauth2
> authentication, though it assumes that the password value actually
> stores the access-token.

Where can we find this "existing support"?  Do you mean the
'auth-source-xoauth2' package on GNU ELPA?

> Because xoauth2 also makes use of
> `secret'/`password', it makes it hard to determine whether to use
> password-based or xoauth2-based authentication, which is why my plugin
> asks users to set `auth' in auth-source to determine whether to use
> xoauth2.  Another complication from this is that auth-source search
> requires the entry contains a `secret' most of the time, where it does
> not need to be set when using xoauth2.  Therefore I workaround this by
> temporarily disables this check and try to retrieve access-token using
> oauth2 and set the result as password.
>
> Given the inconveniences of reusing password for access-token, I wonder
> whether we can add support for a separate `:access-token' key in the
> auth-source entry and use that instead of password when authenticating
> using xoauth2.  This way, we can have both password and access-token in
> an auth-source entry and nnimap and smtpmail can use either one.  More
> specifically:
>
> * When performing an auth-source search, if xoauth2 related fields are
>   set (see the list of fields in the comments of my plugin), it will
>   retrieve access-token using oauth2.
>
> * The search should change to check for either `secret'/`password' or
>   `access-token' is available.
>
> * For `nnimap-login' and `smtpmail-try-auth-method', pass in both
>   password and access-token, and for xoauth2 it should use access-token
>   instead of password.
>
> If this is an acceptable approach, I'll try to draft a patch to
> implement this in Emacs.  Otherwise, it may still worth implement the
> current approach directly in Emacs so as to avoid using hack like
> advice.

I'm not very familiar with auth-source.el, but on a general level the
above makes sense to me.  I've also Cc:ed Ted Zlatanov, the author of
auth-source.el

> Meanwhile, I wonder whether this may be worth release as a separate
> package so that users of older versions can use xoauth2 as well.  I'd
> like to make it compatible with the agreed-upon approach to minimize any
> incompatibilities.
>
> Thanks for reading, and any comments are appreciated.

Are you proposing to include this in Emacs core, on GNU ELPA, or
something else?

Thanks.

> [1] https://gitlab.com/xiyueden/auth-source-xoauth2-plugin





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-17 19:12 ` Philip Kaludercic
@ 2024-09-18  6:24   ` Xiyue Deng
  2024-09-18 14:11     ` Philip Kaludercic
  0 siblings, 1 reply; 12+ messages in thread
From: Xiyue Deng @ 2024-09-18  6:24 UTC (permalink / raw)
  To: Philip Kaludercic; +Cc: 72992

Philip Kaludercic <philipk@posteo.net> writes:

> Xiyue Deng <manphiz@gmail.com> writes:
>
>> Now that bug#72358 is done, as promised, I'm posting my plugin for
>> auth-sources that enables oauth2 handling which you can find on
>> Gitlab[1] (also attached).
>
> Once again I just want to be sure: When you say "plugin", you mean
> package, right?

Yes, though it's not really an independent package but a "plugin" for
auth-source, a.k.a. a hack (the advice) to make auth-source to work with
xoauth2.

> You are proposing to add this to GNU ELPA?
>

Actually I would like to see which of my proposed changes to auth-source
is acceptable and update auth-source in core accordingly.  I think
Stefan's reply gave some suggestions in this regard and I'll follow-up
in a reply there.

Meanwhile, it may still worth adding this package to ELPA to support
older Emacs versions if desired.

> [...]
>
>>
>> Thanks for reading, and any comments are appreciated.
>
> I'll be honest, I have no idea about OAuth, so my comments are just
> related to Elisp for now.  Hope that's OK!
>

Your comments are greatly helpful and much appreciated!  Please see my
replies below.

>> [1] https://gitlab.com/xiyueden/auth-source-xoauth2-plugin
>
>> ;; Copyright (C) 2024 Xiyue Deng <manphiz@gmail.com>
>>
>> ;; Author: Xiyue Deng <manphiz@gmail.com>
>> ;; Version: 0.1-git
>> ;; Package-Requires: ((emacs "28.1") (oauth2 "0.17"))
>>
>> ;; This file is not part of GNU Emacs.
>>
>> ;; GNU Emacs is free software: you can redistribute it and/or modify
>> ;; it under the terms of the GNU General Public License as published by
>> ;; the Free Software Foundation, either version 3 of the License, or
>> ;; (at your option) any later version.
>>
>> ;; GNU Emacs is distributed in the hope that it will be useful,
>> ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
>> ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> ;; GNU General Public License for more details.
>>
>> ;; You should have received a copy of the GNU General Public License
>> ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
>>
>> ;;; Commentary:
>>
>> ;; This package enables support for xoauth2 authentication with
>> ;; auth-source.  To set up, please put this file in the `load-path' of
>> ;; Emacs, and add the following lines in your Emacs configuration:
>>
>> ;;     (require 'auth-source-xoauth2-plugin)
>> ;;     (auth-source-xoauth2-plugin-enable)
>>
>> ;; or with use-package:
>>
>> ;;     (use-package auth-source-xoauth2-plugin
>> ;;       :config
>> ;;       (auth-source-xoauth2-plugin-enable))
>>
>> ;; After enabling, smtpmail should be supported.  To enable this in Gnus
>> ;; nnimap, you should also set `(nnimap-authenticator xoauth2)' in the
>> ;; corresponding account settings in `gnus-secondary-select-methods'
>>
>> ;; auth-source uses the `secret' field in auth-source file as password
>> ;; for authentication, including xoauth2.  To decide which
>> ;; authentication method to use (e.g. plain password vs xoauth2), it
>> ;; inspects the `auth' field from the auth-source entry, and if the
>> ;; value is `xaouth2', it will try to gather data and get the access
>> ;; token for use of xoauth2 authentication; otherwise, it will fallback
>> ;; to the default authentication method.
>>
>> ;; When xoauth2 authentication is enabled, it will try to get the
>> ;; following data from the auth-source entry: `auth-url', `token-url',
>> ;; `scope', `client-id', `client-secret', `redirect-uri', and optionally
>> ;; `state'.  These information will be used by oauth2 to retrieve the
>> ;; access-token.  This package uses an advice to switch the auth-source
>> ;; search result from the `password' to the `access-token' it got, which
>> ;; in turn will be used to construct the xoauth2 authentication string,
>> ;; currently in nnimap-login and smtpmail-try-auth-method.  To really
>> ;; enable xoauth2 in smtpmail, it will add 'xoauth2 to
>> ;; 'smtpmail-auth-supported (if it is not already in the list) using
>> ;; `add-to-list' so that xoauth2 is tried first.
>>
>> ;; Note that currently the auth-source requires the searched entry must
>> ;; have `secret' field set in the entry, which is not necessary when
>> ;; using xoauth2.  Therefore in the advice it temporarily disables
>> ;; checking for `:secret' if set and perform the search, and check the
>> ;; result before returning.
>>
>> ;;; Code:
>>
>> (require 'auth-source)
>> (require 'cl-lib)
>> (require 'oauth2)
>> (require 'smtpmail)
>>
>> (defun auth-source-xoauth2-plugin--search-backends (orig-fun &rest args)
>>   "Perform auth-source-search and set password as access-token when requested.
>> The substitution only happens if one sets `auth' to `xoauth2' in
>> your auth-source-entry.  It is expected that `token_url',
>> `client_id', `client_secret', and `refresh_token' are properly
>> set along `host', `user', and `port' (note the snake_case)."
>>   (auth-source-do-trivia "Advising auth-source-search")
>>   (let (check-secret)
>>     (when (memq :secret (nth 5 args))
>>       (auth-source-do-trivia
>>        "Required fields include :secret.  As we are requesting access token to replace the secret, we'll temporary remove :secret from the require list and check that it's properly set to a valid access token later.")
>>       (setf (nth 5 args) (remove :secret (nth 5 args)))
>>       (setq check-secret t))
>>     (let ((orig-res (apply orig-fun args))
>>           res)
>>       (dolist (auth-data orig-res)
>>         (auth-source-do-trivia "Matched auth data: %s" (pp-to-string auth-data))
>
> Is "trivia" the right debug level for all the messages in this function?
> I feel like some of them should at least be "debug".
>

Indeed.  I have changed logs without token to be "debug"-level.

>>         (let ((auth (plist-get auth-data :auth)))
>>           (when (and auth
>>                      (stringp auth)
>>                      (string= auth "xoauth2"))
>
> You can simplify the check by just doing (equal auth "xoauth2"), as this
> implies all of the above (if it is `equal' to a string, it must be a
> string and hence also non-nil).
>

Done.  Nice tip!  Coming from strong-typed languages I always want to do
type-checks first in fear of any aborting error :)

>>             (auth-source-do-debug
>>              ":auth set to `xoauth2'.  Will get access token.")
>>             (let ((auth-url (plist-get auth-data :auth-url))
>>                   (token-url (plist-get auth-data :token-url))
>>                   (scope (plist-get auth-data :scope))
>>                   (client-id (plist-get auth-data :client-id))
>>                   (client-secret (plist-get auth-data :client-secret))
>>                   (redirect-uri (plist-get auth-data :redirect-uri))
>>                   (state (plist-get auth-data :state)))
>
> I feel like this could be simplified with a map-let expression.  
>
>   (map-let (:auth-url :token-url ...) auth-data
>     ...)
>                                                                  
> You should be able to require 'map at compile time, so that the
> expansions don't occur an overhead during run-time.
>

Another nice TIL!

>>               (auth-source-do-trivia "Using oauth2 to auth and store token...")
>>               (let ((token (oauth2-auth-and-store
>>                             auth-url token-url scope client-id client-secret
>>                             redirect-uri state)))
>>                 (auth-source-do-trivia "oauth2 token: %s" (pp-to-string token))
>>                 (auth-source-do-trivia "Refreshing token...")
>>                 (oauth2-refresh-access token)
>>                 (auth-source-do-trivia "oauth2 token after refresh: %s"
>>                                        (pp-to-string token))
>>                 (let ((access-token (oauth2-token-access-token token)))
>>                   (auth-source-do-trivia
>>                    "Updating :secret with access-token: %s" access-token)
>>                   (plist-put auth-data :secret access-token))))))
>
> The documentation for plist-put warns:
>
>   The new plist is returned;
>   use ‘(setq x (plist-put x prop val))’ to be sure to use the new value.
>   The PLIST is modified by side effects.
>
> Alternatively, you should also be able to do:
>
>   (setf (plist-get auth-data :secret) access-token)
>

Ah didn't know this as I learned the usage of plist-put from searching.
Changed to your `setq' version.  Though I'd also expect that the side
effect is not going away anytime soon either ;)

>>
>>         (unless (and check-secret
>>                      (not (plist-get auth-data :secret)))
>>           (auth-source-do-trivia "Updating auth-source-search results.")
>>           (add-to-list 'res auth-data t)))
>
> This should definitely be a `push', as `res' is lexically scoped (see
> docstring for `add-to-list').
>

Indeed.  Guess I got lucky here that it behaves the same.

>>       res)))
>>
>> ;;;###autoload
>> (defun auth-source-xoauth2-plugin-enable ()
>>   "Enable auth-source-xoauth2-plugin."
>>   (unless (memq 'xoauth2 smtpmail-auth-supported)
>>     (add-to-list 'smtpmail-auth-supported 'xoauth2))
>
> In functions, it would be more conventional to use push.  Especially
> because `add-to-list' checks for duplicates, which you have already
> done.
>

Ack.

>>
>>   (advice-add 'auth-source-search-backends :around
>
> There's no reason not to sharp-quote the function here as well?
>

Indeed.  Added.

>>               #'auth-source-xoauth2-plugin--search-backends))
>
> I would recommend turning this into a global minor mode instead, so that
> it is easy to disable, if a user just wants to try it out.
>

This is an interesting suggestion and sounds like a good idea.  Though
as a matter of fact the oauth2 support in auth-source in Emacs core
actually doesn't work without those hack as of now, so I don't think
it's of interest to support turning off.  But of course it would be
great if auth-source can be changed to support all this out-of-the-box.
Will continue the discussion in my reply to Stefan.

I have updated the source code on GitLab[1] based on your review.
Please check it out.  Thanks very much!

>>
>> (provide 'auth-source-xoauth2-plugin)
>>
>> ;;; auth-source-xoauth2-plugin.el ends here
>
> -- 
> 	Philip Kaludercic on siskin

-- 
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-18  6:24   ` Xiyue Deng
@ 2024-09-18 14:11     ` Philip Kaludercic
  0 siblings, 0 replies; 12+ messages in thread
From: Philip Kaludercic @ 2024-09-18 14:11 UTC (permalink / raw)
  To: Xiyue Deng; +Cc: 72992

Xiyue Deng <manphiz@gmail.com> writes:

> Philip Kaludercic <philipk@posteo.net> writes:
>
>> Xiyue Deng <manphiz@gmail.com> writes:
>>
>>> Now that bug#72358 is done, as promised, I'm posting my plugin for
>>> auth-sources that enables oauth2 handling which you can find on
>>> Gitlab[1] (also attached).
>>
>> Once again I just want to be sure: When you say "plugin", you mean
>> package, right?
>
> Yes, though it's not really an independent package but a "plugin" for
> auth-source, a.k.a. a hack (the advice) to make auth-source to work with
> xoauth2.

Just to clarify: When I say package, I mean something to add to ELPA.

>> You are proposing to add this to GNU ELPA?
>
> Actually I would like to see which of my proposed changes to auth-source
> is acceptable and update auth-source in core accordingly.  

Sure it's acceptable, but in that case it would better to submit a patch
modifying. auth-source.el

>                                                            I think
> Stefan's reply gave some suggestions in this regard and I'll follow-up
> in a reply there.  

I just want to second Stefan's point that some clarification as to what
xoauth2 is.

>                    Meanwhile, it may still worth adding this package
> to ELPA to support older Emacs versions if desired.

In that case it might be better not to merge your changes into
auth-source.el directly, as that would make it more difficult to
automatically pull your changes out of the core to ELPA.

An alternative is that ELPA mirrors your repository, and then we
manually synchronise the changes into the core, whenever there is a new
release.


[...]

>>>         (let ((auth (plist-get auth-data :auth)))
>>>           (when (and auth
>>>                      (stringp auth)
>>>                      (string= auth "xoauth2"))
>>
>> You can simplify the check by just doing (equal auth "xoauth2"), as this
>> implies all of the above (if it is `equal' to a string, it must be a
>> string and hence also non-nil).
>>
>
> Done.  Nice tip!  Coming from strong-typed languages I always want to do
> type-checks first in fear of any aborting error :)

If you want strong typing, then string= is the right thing to use,
because if you want to assume that auth is always a string, then an
error will be signalled.  That being said, if auth has the type "Maybe
String", then checking the values explicitly or implicitly using equal
is the right approach.


[...]

>>>               (auth-source-do-trivia "Using oauth2 to auth and store token...")
>>>               (let ((token (oauth2-auth-and-store
>>>                             auth-url token-url scope client-id client-secret
>>>                             redirect-uri state)))
>>>                 (auth-source-do-trivia "oauth2 token: %s" (pp-to-string token))
>>>                 (auth-source-do-trivia "Refreshing token...")
>>>                 (oauth2-refresh-access token)
>>>                 (auth-source-do-trivia "oauth2 token after refresh: %s"
>>>                                        (pp-to-string token))
>>>                 (let ((access-token (oauth2-token-access-token token)))
>>>                   (auth-source-do-trivia
>>>                    "Updating :secret with access-token: %s" access-token)
>>>                   (plist-put auth-data :secret access-token))))))
>>
>> The documentation for plist-put warns:
>>
>>   The new plist is returned;
>>   use ‘(setq x (plist-put x prop val))’ to be sure to use the new value.
>>   The PLIST is modified by side effects.
>>
>> Alternatively, you should also be able to do:
>>
>>   (setf (plist-get auth-data :secret) access-token)
>>
>
> Ah didn't know this as I learned the usage of plist-put from searching.
> Changed to your `setq' version.  Though I'd also expect that the side
> effect is not going away anytime soon either ;)

I am not sure what you mean?  The crux of the issue is demonstrated
here:

(let (plist)
  (list (plist-put plist :foo 1) plist))
;; ((:foo 1) nil)

I.e. the plist was not modified, because there was no cons-cell to
modify.


[...]

>>>               #'auth-source-xoauth2-plugin--search-backends))
>>
>> I would recommend turning this into a global minor mode instead, so that
>> it is easy to disable, if a user just wants to try it out.
>>
>
> This is an interesting suggestion and sounds like a good idea.  Though
> as a matter of fact the oauth2 support in auth-source in Emacs core
> actually doesn't work without those hack as of now, so I don't think
> it's of interest to support turning off.  

I regard it as a matter of good style to allow the user to disable
anything then can enable, if anything then just to allow better
experimentation.

>                                           But of course it would be
> great if auth-source can be changed to support all this out-of-the-box.
> Will continue the discussion in my reply to Stefan.

Ack.

> I have updated the source code on GitLab[1] based on your review.
> Please check it out.  Thanks very much!

For anyone following the thread, it seem the footnote was missing:

[1]https://gitlab.com/xiyueden/auth-source-xoauth2-plugin/-/blob/main/auth-source-xoauth2-plugin.el

Watch out, in

  (unless (memq 'xoauth2 smtpmail-auth-supported)
    (push 'smtpmail-auth-supported 'xoauth2))

the push expression is malformed, as 'xoauth2 is not a place.  I'm
guessing that you want to write

  (... (push 'xoauth2 smtpmail-auth-supported))

Also, checkdoc complains about
`auth-source-xoauth2-plugin--search-backends's docstring.  I'd try to
address the issues it mentions.

The (and auth (equal auth "xoauth2")) can be further simplified to just
(equal auth "xauth2"), as if auth is equal to "xauth2" is cannot be nil.

>>>
>>> (provide 'auth-source-xoauth2-plugin)
>>>
>>> ;;; auth-source-xoauth2-plugin.el ends here
>>
>> -- 
>> 	Philip Kaludercic on siskin

-- 
	Philip Kaludercic on siskin





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-17 21:33 ` Stefan Kangas
@ 2024-09-18 19:43   ` Xiyue Deng
  2024-09-19  5:13     ` Andrew Cohen
  0 siblings, 1 reply; 12+ messages in thread
From: Xiyue Deng @ 2024-09-18 19:43 UTC (permalink / raw)
  To: Stefan Kangas, 72992; +Cc: Ted Zlatanov, Philip Kaludercic

Hi Stefan,

Stefan Kangas <stefankangas@gmail.com> writes:

> Xiyue Deng <manphiz@gmail.com> writes:
>
>> Now that bug#72358 is done, as promised, I'm posting my plugin for
>> auth-sources that enables oauth2 handling which you can find on
>> Gitlab[1] (also attached).  As the current approach tries to override
>> some existing handling in auth-source, I would like to gather some
>> comments on how to properly integrate this handling, and see if there is
>> any benefit on providing this as a separate package for older Emacs
>> versions.
>>
>> In the comment section of the package I put notes on how xoauth2 is
>> enabled as well as existing restrictions in auth-source and how it
>> workarounds them.  I'll briefly explain below.
>
> I think it would be good if you could add to your package some general
> explanation of what xoauth2 is, and what are its use cases both in a
> general sense, and specifically together with the auth-source
> package. Don't assume that people already know what xoauth2 is, how it
> is different from oauth2, which services use it, etc. Explain it. I
> would add such general information to the beginning of the "Commentary"
> section. Nothing long is needed, just a general introduction and perhaps
> links for where to read more.
>

Good suggestions.  Added to the comments part.

> Some examples of when it would be used, preferably with example code for
> some use cases, would also go a long way.
>

I have added some examples on how to set it up and use it in Gnus and
smtpmail after sending the earlier version.  Please check it out at [1]
(with the changes above.)

>> Currently, auth-source search requires that the result include `:secret'
>> most of the time, where when using xoauth2 it is actually the
>> access-token. Actually, auth-source has existing support for xoauth2
>> authentication, though it assumes that the password value actually
>> stores the access-token.
>
> Where can we find this "existing support"?  Do you mean the
> 'auth-source-xoauth2' package on GNU ELPA?
>

The basic support is actually in the Emacs core already, e.g. for Gnus
nnimap[2] and smtpmail[3].  However, this assumes one to put the
access_token in place of `:secret' in the auth-source file as Emacs uses
password as the access_token in both places.  However, access_token
expires quite frequently (e.g. about 1 hour for Gmail) and without
refreshing it automatically it is practically impossible to use
conveniently.  Hence the propose hack and the following suggestion.

>> Because xoauth2 also makes use of
>> `secret'/`password', it makes it hard to determine whether to use
>> password-based or xoauth2-based authentication, which is why my plugin
>> asks users to set `auth' in auth-source to determine whether to use
>> xoauth2.  Another complication from this is that auth-source search
>> requires the entry contains a `secret' most of the time, where it does
>> not need to be set when using xoauth2.  Therefore I workaround this by
>> temporarily disables this check and try to retrieve access-token using
>> oauth2 and set the result as password.
>>
>> Given the inconveniences of reusing password for access-token, I wonder
>> whether we can add support for a separate `:access-token' key in the
>> auth-source entry and use that instead of password when authenticating
>> using xoauth2.  This way, we can have both password and access-token in
>> an auth-source entry and nnimap and smtpmail can use either one.  More
>> specifically:
>>
>> * When performing an auth-source search, if xoauth2 related fields are
>>   set (see the list of fields in the comments of my plugin), it will
>>   retrieve access-token using oauth2.
>>
>> * The search should change to check for either `secret'/`password' or
>>   `access-token' is available.
>>
>> * For `nnimap-login' and `smtpmail-try-auth-method', pass in both
>>   password and access-token, and for xoauth2 it should use access-token
>>   instead of password.
>>
>> If this is an acceptable approach, I'll try to draft a patch to
>> implement this in Emacs.  Otherwise, it may still worth implement the
>> current approach directly in Emacs so as to avoid using hack like
>> advice.
>
> I'm not very familiar with auth-source.el, but on a general level the
> above makes sense to me.  I've also Cc:ed Ted Zlatanov, the author of
> auth-source.el
>

Thanks!  Would also like to hear Ted's opinion on this and decide which
route to take.

>> Meanwhile, I wonder whether this may be worth release as a separate
>> package so that users of older versions can use xoauth2 as well.  I'd
>> like to make it compatible with the agreed-upon approach to minimize any
>> incompatibilities.
>>
>> Thanks for reading, and any comments are appreciated.
>
> Are you proposing to include this in Emacs core, on GNU ELPA, or
> something else?
>

I'd like to contribute to Emacs core once a direction is decided.  Maybe
also put this plugin in ELPA to support older emacs versions if both are
compatible.

> Thanks.
>

[1] https://gitlab.com/xiyueden/auth-source-xoauth2-plugin
[2] https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/gnus/nnimap.el#n616
[3] https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/mail/smtpmail.el#n640

--
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-18 19:43   ` Xiyue Deng
@ 2024-09-19  5:13     ` Andrew Cohen
  2024-09-19  8:22       ` Xiyue Deng
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Cohen @ 2024-09-19  5:13 UTC (permalink / raw)
  To: Xiyue Deng; +Cc: Ted Zlatanov, Philip Kaludercic, 72992, Stefan Kangas

>>>>> "XD" == Xiyue Deng <dengxiyue@gmail.com> writes:

    XD> Hi Stefan, Stefan Kangas <stefankangas@gmail.com> writes:

    >> Xiyue Deng <manphiz@gmail.com> writes:
    >> 

[...]


    >>> Currently, auth-source search requires that the result include
    >>> `:secret' most of the time, where when using xoauth2 it is
    >>> actually the access-token. Actually, auth-source has existing
    >>> support for xoauth2 authentication, though it assumes that the
    >>> password value actually stores the access-token.
    >> 
    >> Where can we find this "existing support"?  Do you mean the
    >> 'auth-source-xoauth2' package on GNU ELPA?
    >> 

    XD> The basic support is actually in the Emacs core already,
    XD> e.g. for Gnus nnimap[2] and smtpmail[3].  However, this assumes
    XD> one to put the access_token in place of `:secret' in the
    XD> auth-source file as Emacs uses password as the access_token in
    XD> both places.  However, access_token expires quite frequently
    XD> (e.g. about 1 hour for Gmail) and without refreshing it
    XD> automatically it is practically impossible to use conveniently.
    XD> Hence the propose hack and the following suggestion.


This isn't actually true. When I added the support many years ago, I
updated auth-source so that the :secret field can be a function, and
this is how you should be using the current xoauth support. On the bug
thread I posted a suitable function that handles token refreshing (and
its on my list of changes to emacs that I expect to push at some
point). So everything necessary to use xoauth for nnimap and smtpmail
with auth-source, including automatic token refreshing, is already
present in emacs. 

Having said that, I think some of the ideas in Xiyue's code would be
useful. However I think it would be best to base this on the existing
code which works very well and is in use by at least me (and I think
some others as well).

Best,
Andy


-- 
Andrew Cohen





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-19  5:13     ` Andrew Cohen
@ 2024-09-19  8:22       ` Xiyue Deng
  2024-09-19  9:06         ` Andrew Cohen
  0 siblings, 1 reply; 12+ messages in thread
From: Xiyue Deng @ 2024-09-19  8:22 UTC (permalink / raw)
  To: Andrew Cohen; +Cc: Ted Zlatanov, Philip Kaludercic, 72992, Stefan Kangas

Hi Andrew,

Andrew Cohen <acohen@ust.hk> writes:

>>>>>> "XD" == Xiyue Deng <dengxiyue@gmail.com> writes:
>
>     XD> Hi Stefan, Stefan Kangas <stefankangas@gmail.com> writes:
>
>     >> Xiyue Deng <manphiz@gmail.com> writes:
>     >> 
>
> [...]
>
>
>     >>> Currently, auth-source search requires that the result include
>     >>> `:secret' most of the time, where when using xoauth2 it is
>     >>> actually the access-token. Actually, auth-source has existing
>     >>> support for xoauth2 authentication, though it assumes that the
>     >>> password value actually stores the access-token.
>     >> 
>     >> Where can we find this "existing support"?  Do you mean the
>     >> 'auth-source-xoauth2' package on GNU ELPA?
>     >> 
>
>     XD> The basic support is actually in the Emacs core already,
>     XD> e.g. for Gnus nnimap[2] and smtpmail[3].  However, this assumes
>     XD> one to put the access_token in place of `:secret' in the
>     XD> auth-source file as Emacs uses password as the access_token in
>     XD> both places.  However, access_token expires quite frequently
>     XD> (e.g. about 1 hour for Gmail) and without refreshing it
>     XD> automatically it is practically impossible to use conveniently.
>     XD> Hence the propose hack and the following suggestion.
>
>
> This isn't actually true. When I added the support many years ago, I
> updated auth-source so that the :secret field can be a function, and
> this is how you should be using the current xoauth support.

Thanks for pointing this out!  I found the place where `:secret' is
handled as a function[1].  However, this requires a user to implement
the oauth2 logic oneself, which I'm afraid is a bit too low-level and
error-prone.  (Actually, can I actually put a lisp function in
auth-source.gpg?)  Maybe auth-source source can host a helper function
that checks if `:secret' is not set and xaouth2 is preferred
(e.g. `:auth' is `xoauth2') and all required credentials are available
it will get the access_token and put it `:secret' (or basically my hacky
advice :)

> On the bug thread I posted a suitable function that handles token
> refreshing (and its on my list of changes to emacs that I expect to
> push at some point).  So everything necessary to use xoauth for nnimap
> and smtpmail with auth-source, including automatic token refreshing,
> is already present in emacs.
>
> Having said that, I think some of the ideas in Xiyue's code would be
> useful. However I think it would be best to base this on the existing
> code which works very well and is in use by at least me (and I think
> some others as well).
>

Just remembered your comment in Bug#72358[2].  And of course if your
proposals can be part of auth-source that would be great for the users.
Still would be great to have a unified plan and make it happen.

P.S. Is your set up mentioned in Bug#72358 still working for outlook.com
emails?  After reaching out to an MS representative they mentioned that
token refresh was disabled[3] for outlook.com so I just gave up.  Maybe
it still works for Outlook Org emails?

> Best,
> Andy
>
>
> -- 
> Andrew Cohen

[1] https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/auth-source.el#n872
[2] https://debbugs.gnu.org/cgi/bugreport.cgi?bug=72358#47
[3] https://stackoverflow.com/questions/78787763/getting-aadsts65001-error-invalid-grant-when-trying-to-refresh-access-token-fo

-- 
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-19  8:22       ` Xiyue Deng
@ 2024-09-19  9:06         ` Andrew Cohen
  2024-09-19 22:37           ` Xiyue Deng
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Cohen @ 2024-09-19  9:06 UTC (permalink / raw)
  To: Xiyue Deng; +Cc: Ted Zlatanov, Philip Kaludercic, 72992, Stefan Kangas

>>>>> "XD" == Xiyue Deng <manphiz@gmail.com> writes:

    XD> Hi Andrew, Andrew Cohen <acohen@ust.hk> writes:

    >>>>>>> "XD" == Xiyue Deng <dengxiyue@gmail.com> writes:
    >> 

[...]

    XD> The basic support is actually in the Emacs core already,
    XD> e.g. for Gnus nnimap[2] and smtpmail[3].  However, this assumes
    XD> one to put the access_token in place of `:secret' in the
    XD> auth-source file as Emacs uses password as the access_token in
    XD> both places.  However, access_token expires quite frequently
    XD> (e.g. about 1 hour for Gmail) and without refreshing it
    XD> automatically it is practically impossible to use conveniently.
    XD> Hence the propose hack and the following suggestion.
    >> 
    >> 
    >> This isn't actually true. When I added the support many years
    >> ago, I updated auth-source so that the :secret field can be a
    >> function, and this is how you should be using the current xoauth
    >> support.

    XD> Thanks for pointing this out!  I found the place where `:secret'
    XD> is handled as a function[1].  However, this requires a user to
    XD> implement the oauth2 logic oneself, which I'm afraid is a bit
    XD> too low-level and error-prone.  (Actually, can I actually put a
    XD> lisp function in auth-source.gpg?)  

I don't think you have to do anything low level, and I don't think there
is anything error prone here; you can use the functions from oauth
themselves (oauth2.el can create its own plstores, but I prefer to use
auth-source.el to manage the stores).  The only things needed are a call
to oauth2-refresh-access to get a new token, and then
oauth2-token-access-token to return the new access token.

The function I wrote computes the refresh time to decide when to create
a new token. This logic could easily be put into oauth2 instead.

And yes, you can put the lisp function in auth-source.gpg (this is what
I do).

By the way there are some significant bugs in auth-source.el which I
have fixed in my personal tree but haven't yet pushed. I have so little
time for emacs at the moment, but I'll try to get around to it. And
there is one major deficiency in auth-source.el that I want to deal
with: obfuscation of the :secret. When Ted originally wrote
auth-source.el he wrapped the :secret in a closure so that the secret
itself wasn't visible in memory. At the time he did this, closures
weren't fully part of emacs, and their implementation at the time didn't
expose the contents of the closure in bytecode. But the current official
implementation does, so this obfuscation trick no longer works. I want
to remove it since it no longer works and might lead to confusion. 

    XD> Maybe auth-source source can host a helper function that checks
    XD> if `:secret' is not set and xaouth2 is preferred (e.g. `:auth'
    XD> is `xoauth2') and all required credentials are available it will
    XD> get the access_token and put it `:secret' (or basically my hacky
    XD> advice :)

I think this isn't the right way to go. Currently xoauth2 is one of
several supported SASL methods.  The logic is supposed to be to try them
in a certain order, but this hasn't worked properly for some
time. Nobody has noticed since almost everyone uses only the basic
method. In gnus there has always been a server variable,
nnimap-authenticator, that chooses the preferred sasl method, which is
how the current support for xaouth2 is designed to work.  I think this
is the right way to handle this (rather than relying on some specific
form of the auth-source entry) but it would be good to fix the logic in
nnimap.el to allow multiple methods to be tried.

[...]

    XD> P.S. Is your set up mentioned in Bug#72358 still working for
    XD> outlook.com emails?  After reaching out to an MS representative
    XD> they mentioned that token refresh was disabled[3] for
    XD> outlook.com so I just gave up.  Maybe it still works for Outlook
    XD> Org emails?

Yes, it still works perfectly. I suspect that the information they gave
you isn't fully accurate :)
-- 
Andrew Cohen





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#72992: 29.4; towards xoauth2 support in Emacs
  2024-09-19  9:06         ` Andrew Cohen
@ 2024-09-19 22:37           ` Xiyue Deng
  0 siblings, 0 replies; 12+ messages in thread
From: Xiyue Deng @ 2024-09-19 22:37 UTC (permalink / raw)
  To: Andrew Cohen; +Cc: Ted Zlatanov, Philip Kaludercic, 72992, Stefan Kangas

Andrew Cohen <acohen@ust.hk> writes:

>>>>>> "XD" == Xiyue Deng <manphiz@gmail.com> writes:
>
>     XD> Hi Andrew, Andrew Cohen <acohen@ust.hk> writes:
>
>     >>>>>>> "XD" == Xiyue Deng <dengxiyue@gmail.com> writes:
>     >> 
>
> [...]
>
>     XD> The basic support is actually in the Emacs core already,
>     XD> e.g. for Gnus nnimap[2] and smtpmail[3].  However, this assumes
>     XD> one to put the access_token in place of `:secret' in the
>     XD> auth-source file as Emacs uses password as the access_token in
>     XD> both places.  However, access_token expires quite frequently
>     XD> (e.g. about 1 hour for Gmail) and without refreshing it
>     XD> automatically it is practically impossible to use conveniently.
>     XD> Hence the propose hack and the following suggestion.
>     >> 
>     >> 
>     >> This isn't actually true. When I added the support many years
>     >> ago, I updated auth-source so that the :secret field can be a
>     >> function, and this is how you should be using the current xoauth
>     >> support.
>
>     XD> Thanks for pointing this out!  I found the place where `:secret'
>     XD> is handled as a function[1].  However, this requires a user to
>     XD> implement the oauth2 logic oneself, which I'm afraid is a bit
>     XD> too low-level and error-prone.  (Actually, can I actually put a
>     XD> lisp function in auth-source.gpg?)  
>
> I don't think you have to do anything low level, and I don't think there
> is anything error prone here; you can use the functions from oauth
> themselves (oauth2.el can create its own plstores, but I prefer to use
> auth-source.el to manage the stores).  The only things needed are a call
> to oauth2-refresh-access to get a new token, and then
> oauth2-token-access-token to return the new access token.
>

Yes, I'm not worried about power users.  I just think that the average
Emacs user would be hesitant on writing ELisp themselves to enable
xoauth2 login (hence low-level), especially when they don't have
anything to copy from (yet).  Many Gnus users are not programmers and
would prefer writing "(nnimap-authenticator 'xoauth2)" and expect it to
work.  But I believe you don't object providing that convenience OOTB
either.

> The function I wrote computes the refresh time to decide when to create
> a new token. This logic could easily be put into oauth2 instead.
>

I am planning on adding this to oauth2 as well.  Will ask for your
review when that happens.

> And yes, you can put the lisp function in auth-source.gpg (this is what
> I do).
>

TIL!  (I used to have a handwritten script to get the values for
offlineimap.  Guess we should all be using `auth-info-password')

> By the way there are some significant bugs in auth-source.el which I
> have fixed in my personal tree but haven't yet pushed. I have so little
> time for emacs at the moment, but I'll try to get around to it. And
> there is one major deficiency in auth-source.el that I want to deal
> with: obfuscation of the :secret. When Ted originally wrote
> auth-source.el he wrapped the :secret in a closure so that the secret
> itself wasn't visible in memory. At the time he did this, closures
> weren't fully part of emacs, and their implementation at the time didn't
> expose the contents of the closure in bytecode. But the current official
> implementation does, so this obfuscation trick no longer works. I want
> to remove it since it no longer works and might lead to confusion. 
>

Looking forward to it!

>     XD> Maybe auth-source source can host a helper function that checks
>     XD> if `:secret' is not set and xaouth2 is preferred (e.g. `:auth'
>     XD> is `xoauth2') and all required credentials are available it will
>     XD> get the access_token and put it `:secret' (or basically my hacky
>     XD> advice :)
>
> I think this isn't the right way to go. Currently xoauth2 is one of
> several supported SASL methods.  The logic is supposed to be to try them
> in a certain order, but this hasn't worked properly for some
> time. Nobody has noticed since almost everyone uses only the basic
> method. In gnus there has always been a server variable,
> nnimap-authenticator, that chooses the preferred sasl method, which is
> how the current support for xaouth2 is designed to work.  I think this
> is the right way to handle this (rather than relying on some specific
> form of the auth-source entry) but it would be good to fix the logic in
> nnimap.el to allow multiple methods to be tried.
>

Right.  The `:auth' trick I did is just to workaround the restriction
that `nnimap-login' chooses basic method over other methods, and I'd
prefer a better built-in support in auth-source myself.  As you
mentioned, maybe it can be remodeled after `smtpmail-try-auth-method' to
so that the login method is chosen on demand instead of trial-and-error.

> [...]
>
>     XD> P.S. Is your set up mentioned in Bug#72358 still working for
>     XD> outlook.com emails?  After reaching out to an MS representative
>     XD> they mentioned that token refresh was disabled[3] for
>     XD> outlook.com so I just gave up.  Maybe it still works for Outlook
>     XD> Org emails?
>
> Yes, it still works perfectly. I suspect that the information they gave
> you isn't fully accurate :)

Thanks for confirming!  I'll follow-up in private to try to figure this
out if you don't mind.

> -- 
> Andrew Cohen

-- 
Xiyue Deng





^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2024-09-19 22:37 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-02  8:34 bug#72992: 29.4; towards xoauth2 support in Emacs Xiyue Deng
     [not found] ` <handler.72992.B.172532159013230.ack@debbugs.gnu.org>
2024-09-11  0:27   ` Xiyue Deng
2024-09-17 17:33     ` Xiyue Deng
2024-09-17 19:12 ` Philip Kaludercic
2024-09-18  6:24   ` Xiyue Deng
2024-09-18 14:11     ` Philip Kaludercic
2024-09-17 21:33 ` Stefan Kangas
2024-09-18 19:43   ` Xiyue Deng
2024-09-19  5:13     ` Andrew Cohen
2024-09-19  8:22       ` Xiyue Deng
2024-09-19  9:06         ` Andrew Cohen
2024-09-19 22:37           ` Xiyue Deng

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).