Created
November 21, 2020 18:48
-
-
Save yoshinari-nomura/8d9e0b193d48c3e027a3dc6446369b4d to your computer and use it in GitHub Desktop.
Mew to support IMAP XOAUTH2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; Mew XOAUTH2 | |
;; | |
;; * Setup | |
;; | |
;; 1) Install oauth2.el and GnuPG | |
;; | |
;; 2) In your .emacs: | |
;; | |
;; (with-eval-after-load 'mew | |
;; (require 'mew-xoauth2) | |
;; (setq mew-gmail-oauth-client-id "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com" | |
;; mew-gmail-oauth-client-secret "xxxxxxxxxxxxxxxxxxxxxxxx") | |
;; | |
;; 3) Patch mew-imap.el | |
;; | |
;; diff --git a/mew-imap.el b/mew-imap.el | |
;; index 0d4447c..7e604c8 100644 | |
;; --- a/mew-imap.el | |
;; +++ b/mew-imap.el | |
;; @@ -1477,8 +1477,12 @@ (defun mew-imap-filter (process string) | |
;; (if (string= status "greeting") | |
;; (setq next (mew-imap-fsm-next "greeting" "OK")) | |
;; (setq stay t))) | |
;; - ((and (goto-char (point-min)) (looking-at "\\+")) | |
;; - (setq next (mew-imap-fsm-next status "OK"))) | |
;; + ((and (goto-char (point-min)) (looking-at "\\+ *\\(.*\\)")) | |
;; + (setq next (mew-imap-fsm-next | |
;; + status | |
;; + (if (string= status "auth-xoauth2") | |
;; + (mew-imap-xoauth2-json-status (mew-match-string 1)) | |
;; + "OK")))) | |
;; ((and (goto-char (point-max)) (= (forward-line -1) 0) (looking-at eos)) | |
;; (mew-imap-set-tag pnm nil) | |
;; (setq code (mew-match-string 1)) | |
;; | |
;; | |
;; * Thanks to | |
;; OAuth related code is picked from: | |
;; https://github.com/ggervasio/gnus-gmail-oauth/blob/master/gnus-gmail-oauth.el | |
;; https://github.com/jd/google-contacts.el/blob/master/google-oauth.el | |
;; | |
(require 'oauth2) | |
(defvar mew-gmail-oauth-client-id nil | |
"Client ID for OAuth.") | |
(defvar mew-gmail-oauth-client-secret nil | |
"Mew secret key.") | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; OAuth | |
(defconst google-oauth-auth-url "https://accounts.google.com/o/oauth2/auth" | |
"Google OAuth2 server URL.") | |
(defconst google-oauth-token-url "https://accounts.google.com/o/oauth2/token" | |
"Google OAuth2 server URL.") | |
(defun google-oauth-auth (resource-url client-id client-secret) | |
"Request access to a resource." | |
(oauth2-auth google-oauth-auth-url google-oauth-token-url client-id client-secret resource-url)) | |
(defun google-oauth-auth-and-store (resource-url client-id client-secret) | |
"Request access to a Google resource and store it using `auth-source'." | |
(oauth2-auth-and-store google-oauth-auth-url google-oauth-token-url | |
resource-url client-id client-secret)) | |
(defconst mew-gmail-resource-url "https://mail.google.com/" | |
"URL used to request access to GMail.") | |
(defun mew-gmail-oauth-token () | |
"Get OAuth token for Mew to access GMail." | |
(let ((token (google-oauth-auth-and-store | |
mew-gmail-resource-url | |
mew-gmail-oauth-client-id | |
mew-gmail-oauth-client-secret))) | |
(oauth2-refresh-access token) | |
token)) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;; Mew | |
(setq mew-imap-auth-alist | |
'(("XOAUTH2" mew-imap-command-auth-xoauth2) | |
("CRAM-MD5" mew-imap-command-auth-cram-md5) | |
("LOGIN" mew-imap-command-auth-login))) | |
(setq mew-imap-auth-list | |
'("XOAUTH2" "LOGIN" "CRAM-MD5")) | |
(defun mew-imap-command-auth-xoauth2 (pro pnm) | |
(let* ((user (mew-imap-get-user pnm)) | |
(token (oauth2-token-access-token (mew-gmail-oauth-token))) | |
(token-string (mew-imap-xoauth2-token-string user token))) | |
(mew-imap-process-send-string pro pnm (format "AUTHENTICATE XOAUTH2 %s" token-string)) | |
(mew-imap-set-status pnm "auth-xoauth2"))) | |
(defun mew-imap-command-xoauth2-wpwd (pro pnm) | |
(mew-imap-set-done pnm t) | |
(mew-passwd-set-passwd (mew-imap-passtag pnm) nil) | |
(delete-process pro) | |
(error "IMAP password is wrong!")) | |
(defun mew-imap-xoauth2-token-string (user token) | |
;; base64([email protected]^Aauth=Bearer ya29vF9dft4...^A^A) | |
(base64-encode-string (format "user=%s\1auth=Bearer %s\1\1" user token) t)) | |
(defun mew-imap-xoauth2-json-status (status-string) | |
;; https://developers.google.com/gmail/imap/xoauth2-protocol#error_response_2 | |
(let ((json-status (json-parse-string (base64-decode-string status-string)))) | |
(if (string-match "^4" (gethash "status" json-status)) ;; 4XX | |
"NO" "OK"))) | |
(setq mew-imap-fsm | |
'(("greeting" ("OK" . "capability")) | |
("capability" ("OK" . "post-capability")) | |
("auth-xoauth2" ("OK" . "next") ("NO" . "xoauth2-wpwd")) | |
("auth-cram-md5" ("OK" . "pwd-cram-md5") ("NO" . "wpwd")) | |
("pwd-cram-md5" ("OK" . "next") ("NO" . "wpwd")) | |
("auth-login" ("OK" . "user-login") ("NO" . "wpwd")) | |
("user-login" ("OK" . "pwd-login") ("NO" . "wpwd")) | |
("pwd-login" ("OK" . "next") ("NO" . "wpwd")) | |
("login" ("OK" . "next") ("NO" . "wpwd")) | |
("select" ("OK" . "post-select")) | |
("flags" ("OK" . "flags")) ;; xxx NG but loop | |
("uid" ("OK" . "umsg")) | |
("copy" ("OK" . "copy") ("NO \\[TRYCREATE\\]" . "create") ("NO" . "wmbx")) | |
("create" ("OK" . "copy") ("NO" . "wmbx")) | |
("dels" ("OK" . "dels")) ;; xxx NG but loop | |
("expunge" ("OK" . "post-expunge")) | |
;; | |
("search" ("OK" . "post-search")) | |
;; | |
("fetch" ("OK" . "post-fetch")) | |
;; | |
("namespace" ("OK" . "post-namespace") ("NO\\|BAD" . "list")) | |
("list" ("OK" . "post-list")) | |
;; | |
("delete" ("OK" . "logout") ("NO" . "logout3")) | |
("rename" ("OK" . "logout") ("NO" . "logout3")) | |
;; | |
("logout" ("OK" . "noop")) | |
("logout2" ("OK" . "noop")) | |
("logout3" ("OK" . "noop")))) | |
(provide 'mew-xoauth2) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment