Gnus Emacs as email client in IMAP with ProtonMail

2021-10-13, updated 2024-03-01 next - previous

In this post, my .gnus.el file and my .authfile to help ProtonMail users.

1. Gnus setup via .gnus.el

Your .gnus.el must be in ~/ . If you are using ProtonMail maybe my .gnus.el can help you:

;; for gnus as email reader
(require 'gnus)
(require 'imap)
(require 'gnus-registry)
(setq gnus-registry-max-entries 500)
(gnus-registry-initialize)
(require 'gnus-x-gm-raw)

(setq user-mail-address "joseph@vidal-rosset.net"
      user-full-name "Joseph Vidal-Rosset")

(setq gnus-select-method '(nnimap "localhost"
                                  (nnimap-stream plain)
                                  (nnimap-address "127.0.0.1")
                                   (nnimap-server-port 1143)))
(setq smtpmail-default-smtp-server "127.0.0.1")

(setq mail-sources '((imap :server "127.0.0.1"
                         :user "joseph@vidal-rosset.net"
                         :password "t*****M_U******"))) ;;;  Proton bridge's password (see the documentation online).

(setq nnmail-expiry-wait 'immediate)
(require 'smtpmail)
(setq smtpmail-stream-type 'starttls)
(setq message-send-mail-function 'message-smtpmail-send-it)

;; Choose account label to feed msmtp -a option based on From header in Message buffer;
;; This function must be added to message-send-mail-hook for on-the-fly change of From address
;; before sending message since message-send-mail-hook is processed right before sending message.

(defun fs-change-smtp ()
  "Change the SMTP server according to the current from line."
  (save-excursion
    (let ((from
      (save-restriction
      (message-narrow-to-headers)
      (message-fetch-field "From"))))
      (cond
       ((string-match "joseph@vidal-rosset.net" from)
      (setq smtpmail-smtp-server "127.0.0.1"
            smtpmail-smtp-service 1025
            starttls-use-gnutls t
            send-mail-function 'smtpmail-send-it
            message-send-mail-function 'smtpmail-send-it
            mail-from-style 'angles
            smtpmail-debug-info t
            smtpmail-debug-verb t
            ))
       ((string-match "joseph.vidal-rosset@univ-lorraine.fr" from)
      (setq smtpmail-smtp-server "smtp.univ-lorraine.fr"
             smtpmail-smtp-service 587
             starttls-use-gnutls t
             send-mail-function 'smtpmail-send-it
             message-send-mail-function 'smtpmail-send-it
             mail-from-style 'angles
             smtpmail-debug-info t
             smtpmail-debug-verb t ))
       ((string-match "philoso@free.fr" from)
      (setq smtpmail-smtp-server "smtp.free.fr"
             smtpmail-smtp-service 25
             ; starttls-use-gnutls t
             send-mail-function 'smtpmail-send-it
             message-send-mail-function 'smtpmail-send-it
            ; mail-from-style 'angles
             smtpmail-debug-info t
             smtpmail-debug-verb t ))
      ))))
(add-hook 'message-send-hook 'fs-change-smtp)

;; Storing sent mail in the server

(setq gnus-posting-styles
      '((".*"
       (address "joseph@vidal-rosset.net")
       ("X-Message-SMTP-Method" "smtp 127.0.0.1 1025"))
      ("nnml:univ-lorraine.fr"
       (address "joseph.vidal-rosset@univ-lorraine.fr")
       ("X-Message-SMTP-Method" "smtp smtp.univ-lorraine.fr 587"))
      ("nnml:free.fr"
       (address "philoso@free.fr")
       ("X-Message-SMTP-Method" "smtp smtp.free.fr 25"))
      ))

;;Reply-to with the same address as it was sent to
(setq gnus-posting-styles
      '(((header "to" "joseph@vidal-rosset.net")
       (address "joseph@vidal-rosset.net"))
      ((header "to" "joseph.vidal-rosset@univ-lorraine.fr")
       (address "joseph.vidal-rosset@univ-lorraine.fr"))
      ((header "to" "philoso@free.fr")
       (address "philoso@free.fr"))
      ((header "cc" "joseph@vidal-rosset.net")
       (address "joseph@vidal-rosset.net"))
      ((header "cc" "joseph.vidal-rosset@univ-lorraine.fr")
       (address "joseph.vidal-rosset@univ-lorraine.fr"))))


;; set renderer for html mail to w3m in emacs
(setq mm-text-html-renderer 'w3m)
(setq gnus-inhibit-images nil)
(setq gnus-large-newsgroup nil)
(setq gnus-summary-line-format ":%U%R %B %s %-60=|%4L |%-20,20f |%&user-date; \n")

;; mailcrypt
                                      ;(add-hook 'gnus-summary-mode-hook 'mc-install-read-mode)
(add-hook 'news-reply-mode-hook 'mc-install-write-mode)
;; MIME
(setq gnus-show-mime t)
;;
(require 'gnus-alias)
;; An alternative to `gnus-posting-styles', if you want to change accounts
;; on the fly while composing messages.
(autoload 'gnus-alias-determine-identity "gnus-alias" nil t)
(add-hook 'message-setup-hook 'gnus-alias-determine-identity)

(with-eval-after-load "gnus-alias"
  ;; ;; Add gnus-alias call to message mode hook.
  (gnus-alias-init)
  ;; Added one key binding.
  (define-key message-mode-map
    (kbd "C-c i") 'gnus-alias-select-identity)
  ;; set up my identities
  (setq gnus-alias-identity-alist
      '(("default-ID"
         nil
         "Joseph Vidal-Rosset <joseph@vidal-rosset.net>"
         nil
         nil
         nil
         "Joseph "
         )
        ("univ-ID"
         nil                        ; Does not refer to any other identity.
         "Joseph Vidal-Rosset <joseph.vidal-rosset@univ-lorraine.fr>" ; Sender address.
         "Université de Lorraine"            ; Organization header.
         (("X-Url" . "http://philo.shs-nancy.univ-lorraine.fr/131/membre/joseph-vidal-rosset")) ; Extra headers.
         nil                       ; body text
         "Joseph Vidal-Rosset")                ; Signature.
        ("biblio-ID"
         nil                        ; Does not refer to any other identity.
         "Joseph Vidal-Rosset <joseph@vidal-rosset.net>" ; Sender address.
         "Université de Lorraine"            ; Organization header.
         (("X-Url" . "http://philo.shs-nancy.univ-lorraine.fr/131/membre/joseph-vidal-rosset")) ; Extra headers.
         "#+LaTeX_CLASS: smfart-fr \n\ #+CSL_STYLE: ~/MEGA/org/bjps.csl\n\\n\n\nbibliography:/home/joseph/Dropbox/Orgzly/reforg.bib\n bibliographystyle:apalike\n"                        ; body text
         "Joseph Vidal-Rosset")
        ("educasup-ID"
         nil                      ; Does not refer to any other identity.
         "<philoso@free.fr>" ; Sender address.
         nil
         nil
           )                            ; Signature.
        ))
  ;; Automatically choose an identity given the message context.
  (setq gnus-alias-identity-rules
      '(
        ("send mails to @univ-lorraine with identity univ-ID"
         ("to" "@univ-lorraine\\.fr" current) "univ-ID")
        )
      )
  ;; Use "home" identity by default
  (setq gnus-alias-default-identity "default-ID")
  ;; Define rules to match work identity
                                      ;  (setq gnus-alias-identity-rules
                                      ;       '(("univ-ID" ("any" "<\\\\(.+\\\\)@univ-lorraine.fr" both) "univ-ID")))
  ;; Determine identity when message-mode loads
  (add-hook 'message-setup-hook 'gnus-alias-determine-identity)
  ;; Identity to use when gnus-alias finds an unknown identity.
  (setq gnus-alias-unknown-identity-rule 'error)

  ;; Old identity is completely removed before the new one is added.
  (setq gnus-alias-overlay-identities nil)

  ;; Allow your `Return-Path' to be set properly.
  (setq gnus-alias-override-user-mail-address t)

  ;; After an Identity is used, where should point be moved to?
  (setq gnus-alias-point-position 'start-of-sig)

  ;; `From' header becomes a button that you can click on.
  (setq gnus-alias-use-buttonized-from t)

  ;; Level of verbosity -- message output only (see `*gnus-alias debug*'
  ;; buffer, when maximum verbosity).
  (setq gnus-alias-verbosity 1)

  ;; Set message envelope to content of `From'.
  ;; XXX see `mail-specify-envelope-from'
  (defun leuven-set-msg-envelope-from()
    "Set `mail-envelope-from' to the value in the \"From\" field."
    (let* ((from (message-fetch-field "From" t))
         (first (1+ (string-match "<" from)))
         (last (string-match ">" from)))
      (setq mail-envelope-from (substring from first last))))

  (add-hook 'message-setup-hook 'leuven-set-msg-envelope-from)
  )

;; reply with quotation
(setq message-citation-line-function 'message-insert-formatted-citation-line)
(setq message-citation-line-format "Le %a %D %b %Y à %r, %f a envoyé ce message:")
;;toujours voir les groupes!
;;(setq gnus-permanently-visible-groups "^nnml\\|^nnfolder\\|^inbox\\|")
                                      ;(setq gnus-permanently-visible-groups "|^inbox\\|")
;; bbdb  : ne pas laisser bbdb ralentir gnus
(setq bbdb-allow-duplicates t)
;; répondre avec la citation datée
(setq message-citation-line-function 'message-insert-formatted-citation-line)
(setq message-citation-line-format "Le %a %D %b %Y à %r, %f a envoyé ce message:")

(setq gnus-summary-mark-as-read-forward t) ; Mark the current article as read

;;; Nicolas Berthier gnus.el
;;;;;
;; MIME settings:
;; - see `http://gconnan.free.fr/les_sources/point_gnus.html'
(require 'mm-util)
(setq gnus-article-decode-mime-words t
      gnus-article-decode-charset 1
      gnus-mime-view-all-parts t        ; View all the MIME parts in current
                                      ; article
      gnus-ignored-mime-types '("text/x-vcard")
      gnus-buttonized-mime-types '("multipart/encrypted" "multipart/signed")
      gnus-unbuttonized-mime-types '("text/plain")

      gnus-treat-buttonize t               ; Add buttons
      gnus-treat-buttonize-head 'head      ; Add buttons to the head
      gnus-treat-emphasize t               ; Emphasize text
      gnus-treat-fill-article nil          ; Fill the article
      gnus-treat-strip-cr 'last            ; Remove carriage returns
      gnus-treat-hide-headers 'head        ; Hide headers
      gnus-treat-hide-boring-headers 'head ; -Hide boring headers
      gnus-treat-hide-signature nil        ; Hide the signature
      gnus-treat-hide-citation nil         ; Hide cited text
      ;; Deprecated.
      ;; gnus-treat-strip-pgp 'last     ; Strip PGP signatures
      gnus-treat-strip-pem 'last           ; Strip PEM signatures
      gnus-treat-highlight-headers 'head   ; Highlight the headers
      gnus-treat-highlight-citation 'last  ; Highlight cited text
      gnus-treat-highlight-signature 'last ; Highlight the signature
      gnus-treat-date-ut nil               ; Display the Date in UT (GMT)
      gnus-treat-date-local t           ; Display the Date in the local timezone
      gnus-treat-date-original t        ; Display the date in the original
                                      ; timezone
      gnus-treat-display-x-face t
      gnus-treat-display-face t
      gnus-treat-display-smileys t
      gnus-treat-strip-trailing-blank-lines 'last ; Strip trailing blank lines
      gnus-treat-strip-leading-blank-lines 'last  ; Strip leading blank lines
      gnus-treat-strip-multiple-blank-lines 'last ; Strip multiple blank lines
      ;; gnus-treat-strip-blank-lines nil             ; Strip all blank lines
      gnus-treat-overstrike 'last)

;; Some display and misc. options (note the whole file is messed up for these):
(setq gnus-visible-headers
      '("^From:" "^Newsgroups:" "^Subject:" "^Date:" "^Followup-To:" "^Reply-To:"
      "^Organization:" "^Summary:" "^Keywords:" "^To:" "^[BGF]?Cc:" "^Posted-To:"
      "^Mail-Copies-To:" "^Apparently-To:" "^X-Gnus-Warning:" "^Resent-From:"
      "^X-Sent:" "^X-Mailer:" "^X-Newsreader:" "^X-User-Agent:" "^User-Agent:"
      "^X-Diary" )                      ;Provient du manuel Gnus (Node
                                      ;3.16.7)

      gnus-signature-separator
      '("^-- $"                         ;The standard
      "^-- *$"                  ;A common mangling
      "^-------*$"                      ;Many people just use a looong line of
                                      ;dashes.  Shame!
      "^ *--------*$"                   ;Double-shame!
      "^________*$"                     ;Underscores are also popular
      "^========*$"
      "^ *-----* *Original Message *-----* *$")

      gnus-always-read-dribble-file nil
      gnus-use-dribble-file nil
      gnus-save-newsrc-file nil         ;I don't use any other newsreader.
      gnus-read-newsrc-file nil
      gnus-read-active-file 'some)
                                      ;gnus-large-newsgroup 40)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Summary display options.
(setq gnus-user-date-format-alist                       ;date en relatif (de gyom)
      '(((gnus-seconds-today) . "     %k:%M")           ;dans la journée = 14:39
      ((+ 86400 (gnus-seconds-today)) . "Yest %k:%M") ;hier = hier 14:39
      ((+ 604800 (gnus-seconds-today)) . "%a %k:%M") ;dans la semaine = sam 14:39
      ((gnus-seconds-month) . "%a %D")         ;ce mois  = sam 28
      ((gnus-seconds-year) . "%D %b")                  ;durant l'année = mai 28
      (t . "%D %b '%y"))                               ;le reste = mai 28 '05

      gnus-thread-indent-level 2        ;threads indentation

      ;; gnus-article-sort-functions '((not gnus-article-sort-by-date)) ;sorting
      ;; gnus-thread-sort-functions  '((not gnus-thread-sort-by-date))
      ;;;using numbers is faster and similar:
      gnus-article-sort-functions '((gnus-article-sort-by-number t))
      gnus-thread-sort-functions  '((gnus-thread-sort-by-number t)
                                  (gnus-thread-sort-by-most-recent-date t))
      ;; gnus-summary-line-format "%U%R%z %12&user-date; %(%[%-30,30f%]%) %B %s\n"
      ;; gnus-summary-line-format "%U%R│%B%(%s%-80=%) │ %f %110=│ %6&user-date;\n"
                                      ; gnus-summary-line-format ":%U%R %B %s %-60=|%4L |%-20,20f |%&user-date; \n"
      gnus-summary-same-subject ""
                                      ;gnus-permanently-visible-groups "^.*"

      gnus-use-trees nil                ;no thread tree buffers
      gnus-generate-tree-function 'gnus-generate-horizontal-tree
      gnus-tree-minimize-window t
      gnus-thread-hide-subtree t  ;auto collapse
      gnus-sum-thread-tree-indent          "  "
      gnus-sum-thread-tree-root            "● "
      gnus-sum-thread-tree-false-root      "◎ "
      gnus-sum-thread-tree-single-indent   "◯ "
      gnus-sum-thread-tree-leaf-with-other "├─► "
      gnus-sum-thread-tree-vertical        "│ "
      gnus-sum-thread-tree-single-leaf     "╰─► "

      ;; Yay (seen here: `https://github.com/cofi/dotfiles/blob/master/gnus.el')
      ;; gnus-cached-mark ?☍
      ;; gnus-canceled-mark ?↗
      gnus-del-mark ?✗
      ;; gnus-dormant-mark ?⚐
      gnus-expirable-mark ?♻
      gnus-forwarded-mark ?↪
      ;; gnus-killed-mark ?☠
      ;; gnus-process-mark ?⚙
      gnus-read-mark ?✓
      gnus-recent-mark ?✩
      gnus-replied-mark ?↺
      gnus-unread-mark ?✉
      ;; gnus-unseen-mark ?★
      ;; gnus-ticked-mark ?⚑
      )

(define-key gnus-summary-mode-map "-" 'gnus-summary-hide-thread)
(define-key gnus-summary-mode-map "+" 'gnus-summary-show-thread)

;; Enable mailinglist support
(when (fboundp 'turn-on-gnus-mailing-list-mode)
  (add-hook 'gnus-summary-mode-hook 'turn-on-gnus-mailing-list-mode))

;; En plus :  trier les threads en fonctions de la  date la plus récente
(setq gnus-thread-sort-functions
      '(gnus-thread-sort-by-number gnus-thread-sort-by-most-recent-date))

(setq gnus-subthread-sort-functions
      '(gnus-thread-sort-by-number gnus-thread-sort-by-date))

(setq gnus-sort-gathered-threads-function 'gnus-thread-sort-by-date)

;; Adding: I do not prefer to see only the top level message.  If a message has
;; several replies or is part of a thread, do not only show the first
;; message.  'gnus-thread-ignore-subject' will ignore the subject and
;; look at 'In-Reply-To:' and 'References:' headers.
(setq gnus-thread-hide-subtree nil)
(setq gnus-thread-ignore-subject nil)

(defun rs-gnus-get-label (header)
  "Returns label from X-Label header"
  (let
      ((lbl (or (cdr (assq 'X-Label (mail-header-extra header))) "")))
    lbl))

(defalias 'gnus-user-format-function-r 'rs-gnus-get-label)

(setq nnmail-extra-headers '(To X-Label Newsgroups Content-Type))

(copy-face 'default 'face-label)
(set-face-foreground 'face-label "red")
(setq gnus-face-6 'face-label)

(defun
    rs-gnus-summary-limit-to-label (regexp &optional not-matching)
  "Limit the summary buffer to articles that match a label."
  (interactive
   (list (read-string
        (format "%s label (regexp): "
                (if current-prefix-arg "Exclude" "Limit to")))  current-prefix-arg))
  (gnus-summary-limit-to-extra 'X-Label regexp not-matching))

(setq gnus-thread-sort-functions
      '(lambda (t1 t2) (not (gnus-thread-sort-by-date t1 t2))))
;;;
(run-hooks 'gnus-prepare-article-hook)
(setq message-kill-buffer-on-exit t)
;;

(setq gnus-parameters
      '((".*"
       (display . all)
       (gnus-use-scoring nil))))
;;(setq gnus-permanently-visible-groups "INBOX")

(setq gnus-thread-sort-functions
      '(gnus-thread-sort-by-most-recent-number))

(add-hook 'gnus-group-mode-hook 'gnus-topic-mode)

;; Raccourcis claviers TRÈS UTILES
;; Jette à la corbeille via la touche "Suppr"
(define-key gnus-summary-mode-map (kbd "<delete>")
  (lambda () (interactive)
    (gnus-summary-move-article nil "Trash" nil)))
;; Archive avec touche "Inser":
(define-key gnus-summary-mode-map (kbd "<insert>")
  (lambda () (interactive)
    (gnus-summary-move-article nil "Archive" nil)))
;; Redépose le courriel dans "INBOX":
(define-key gnus-summary-mode-map "I"
  (lambda () (interactive)
    (gnus-summary-move-article nil "INBOX" nil)))

(defun stemacs-switch-close ()
  "Switch window and close it."
  (interactive)
  (progn
    (other-window 1)
    (delete-window)))

(defun stemacs-mouse-close (event)
  "Switch to the clicked window and close it."
  (interactive "e")
  (let ((w (posn-window (event-start event))))
    (if (window-valid-p w)
      (delete-window (select-window w))
      nil)))

(add-hook 'gnus-group-mode-hook
        (lambda ()
          (progn
            (define-key gnus-group-mode-map (kbd "n") 'gnus-group-next-group)
            (define-key gnus-group-mode-map (kbd "p") 'gnus-group-prev-group))))
(add-hook 'gnus-summary-mode-hook
        (lambda ()
          (progn
            (define-key gnus-summary-mode-map (kbd "v x") 'stemacs-switch-close)
            (define-key gnus-summary-mode-map (kbd "n") 'next-line)
            (define-key gnus-summary-mode-map (kbd "p") 'previous-line)
            ;(define-key gnus-summary-mode-map (kbd "<insert>") 'gnus-summary-delete-article)
            (define-key gnus-summary-mode-map (kbd "g") 'gnus-summary-insert-new-articles))))
(add-hook 'gnus-article-mode-hook
        (lambda ()
          (progn
            (define-key gnus-article-mode-map [down-mouse-2] 'stemacs-mouse-close)
            ;(define-key gnus-article-mode-map (kbd "<insert>") 'gnus-summary-delete-article)
            )))

(setq gnus-novice-user nil)

(setq gnus-interactive-exit nil)
(add-hook 'kill-emacs-hook (lambda ()
                           (when (boundp 'gnus-group-exit)
                             (gnus-group-exit))))

;;; bbdb
(require 'bbdb)
(bbdb-initialize 'gnus 'message)
(setq bbdb-allow-duplicates t)
(setq
 bbdb-file "~/.bbdb"
 bbdb-offer-save 'auto
 bbdb-notice-auto-save-file t
 bbdb-expand-mail-aliases t
 bbdb-canonicalize-redundant-nets-p t
 bbdb-always-add-addresses t
 bbdb-complete-name-allow-cycling nil
 )
(setq bbdb-allow-duplicates t)

(bbdb-initialize 'gnus 'message)
(bbdb-mua-auto-update-init 'message) ;; use 'gnus for incoming messages too
(setq bbdb-mua-auto-update-p 'query) ;; or 'create to create without asking

(setq browse-url-browser-function 'browse-url-firefox
      browse-url-new-window-flag nil
      browse-url-firefox-new-window-is-tab t)

(define-key gnus-article-mode-map [?M] 'browse-url-firefox)
;;; .gnus.el ends here

2. .authinfo file

You must now adding in your home a file that is called .authinfo:

machine 127.0.0.1 login joseph@vidal-rosset.net port 1143 password  ProtonMail bridge's password
machine 127.0.0.1 login joseph@vidal-rosset.net port 1025 password  ProtonMail bridge's password
machine imap.univ-lorraine.fr login vidalros5@univ-lorraine.fr port 993 password **********
machine smtp.univ-lorraine.fr login vidalros5@univ-lorraine.fr port 587 password **********
machine mx login root port sudo password ********
 

Made with Emacs 28.2 (Org mode 9.5.5) and with org-export-head, a blog exporter.

The css file of this blog is mainly the result of Zhitao Gong's work.

orgmode emacs isso debian mxlinux