forked from dimitri/mbsync-el
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mbsync.el
193 lines (159 loc) · 6.53 KB
/
mbsync.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
;;; mbsync.el --- run mbsync to fetch mails
;; Copyright (C) 2012-2017 Dimitri Fontaine
;; Author: Dimitri Fontaine <[email protected]>
;; Version: 0.1.2
;; URL: https://github.com/dimitri/mbsync-el
;; This file is NOT part of GNU Emacs.
;; mbsync-el is free software, see the file LICENSE.
;;; Commentary:
;;
;; Run mbsync to fetch mails
;;; News:
;;;; Changes since 0.0.1:
;;
;; - `mbsync-verbose' now has several levels of verbosity
;;
;; - Update status line regex and make it customizable. (#4, #10)
;; New defcustom mbsync-status-line-re – thanks Matthew Carter and
;; Ivan Stefanischin!
;;
;; - Ensure only one process runs at a time. (#8, #9)
;; If you wish to run several at a time (e.g. with different
;; configurations), let-bind `mbsync-buffer-name' around invocations
;; to keep them unique. Thanks Matthew Carter!
;;; Code:
(require 'cl-lib)
(defgroup mbsync nil "mbsync customization group"
:group 'convenience)
(defcustom mbsync-exit-hook nil
"Hook run after `mbsync' is done."
:group 'mbsync
:type 'hook)
(defcustom mbsync-executable (executable-find "mbsync")
"Where to find the `mbsync' utility."
:group 'mbsync
:type 'string)
(defcustom mbsync-args '("-a")
"List of options to pass to the `mbsync' command."
:group 'mbsync
:type '(repeat string))
(defcustom mbsync-auto-accept-certs nil
"Accept all certificates if true."
:group 'mbsync
:type 'boolean)
(defcustom mbsync-verbose 'normal
"How many messages to print to minibuffer. See `mbsync-log-levels'."
:group 'mbsync
:type 'boolean)
(defface mbsync-font-lock-error-face
'((t (:foreground "yellow" :background "red" :bold t)))
"Face description for all errors."
:group 'mbsync)
;; Newer versions of mbsync just report C:, B:, M:, or S: for progress.
(defcustom mbsync-status-line-re (rx (or "Channel "
(and (any ?m ?c ?b ?s) ": "))
(+ (any alnum ?/)))
;; (rx bol "Channel " (+ (any alnum)) eol)
"Regex which matches an output line to show it in the echo-area."
:group 'mbsync
:type 'string)
(defvar mbsync-process-filter-pos nil)
(defvar mbsync-buffer-name "*mbsync*")
(defun mbsync-elem-index (elt lst)
"Return index of ELT in LST, or nil if not found."
(let ((i 0))
(catch 'found
(dolist (e lst)
(if (eq e elt)
(throw 'found i)
(incf i))))))
(defvar mbsync-log-levels '(quiet normal verbose debug))
(defun mbsync-log-level-int (severity)
"Get the log level of SEVERITY as int."
(or (mbsync-elem-index severity mbsync-log-levels)
0))
(defun mbsync-log (severity &rest args)
"If SEVERITY is less than `mbsync-verbose', show user the message ARGS."
(when (>= (mbsync-log-level-int mbsync-verbose)
(mbsync-log-level-int severity))
(apply #'message args)))
(defun mbsync-process-filter (proc string)
"Filter for `mbsync', auto accepting certificates.
Arguments PROC, STRING as in `set-process-filter'."
(with-current-buffer (process-buffer proc)
(unless (bound-and-true-p mbsync-process-filter-pos)
(make-local-variable 'mbsync-process-filter-pos)
(setq mbsync-process-filter-pos (point-min)))
(save-excursion
(let ((inhibit-read-only t))
(goto-char (point-max))
(insert string)
;; accept certificates
(goto-char mbsync-process-filter-pos)
(while (re-search-forward "Accept certificate?" nil t)
(if mbsync-auto-accept-certs
(process-send-string proc "y\n")
(message "mbsync blocked, waiting for certificate acceptance")))))
(save-excursion
;; message progress
(goto-char mbsync-process-filter-pos)
(while (re-search-forward mbsync-status-line-re nil t)
(mbsync-log 'verbose "mbsync progress: %s" (match-string 0))))
(let (err-pos)
(save-excursion
;; errors
(goto-char mbsync-process-filter-pos)
(while (re-search-forward (rx (or
(and bol "Maildir error:" (* nonl) eol)
(and bol "Error:" (* nonl) eol)
(and (* nonl) ": unknown keyword " (* nonl) eol)
(and bol "Cannot connect to " (* nonl) eol)
(and bol "IMAP error:" (* nonl) eol)
(and bol "Error from" (* nonl) eol)
(and bol "No working address found for " (* nonl) eol)
(and bol "gpg: decryption failed: " (* nonl) eol)
(and bol "Skipping account " (* nonl) eol) ))
nil t)
(message "%s" (match-string 0))
(overlay-put (make-overlay (match-beginning 0)
(match-end 0))
'face 'mbsync-font-lock-error-face)
(switch-to-buffer-other-window (current-buffer))
(setq err-pos (match-beginning 0))))
(when err-pos
(goto-char err-pos)))
(setq mbsync-process-filter-pos (point-max))))
(defun mbsync-sentinel (proc change)
"Mail sync is over, message it then run `mbsync-exit-hook'.
Arguments PROC, CHANGE as in `set-process-sentinel'."
(when (eq (process-status proc) 'exit)
(mbsync-log 'normal (format "mbsync is done: %s" change))
(when (not (eq (process-exit-status proc) 0))
(switch-to-buffer-other-window (process-buffer proc)))
(run-hooks 'mbsync-exit-hook)))
(defun mbsync-get-proc ()
"Get the running mbsync process (or nil if no such)."
(let ((b (get-buffer "*mbsync*")))
(and (buffer-live-p b)
(get-buffer-process b))))
;;;###autoload
(defun mbsync (&optional show-buffer)
"Run the `mbsync' command, asynchronously, then run `mbsync-exit-hook'.
If SHOW-BUFFER, also show the *mbsync* output."
(interactive "P")
(if (mbsync-get-proc)
(message "Please wait, mbsync is already fetching, see buffer *mbsync* for details.")
(let* ((dummy (when (get-buffer mbsync-buffer-name)
(kill-buffer mbsync-buffer-name)))
(proc (apply 'start-process
mbsync-buffer-name
mbsync-buffer-name
mbsync-executable
mbsync-args)))
(set-process-filter proc 'mbsync-process-filter)
(set-process-sentinel proc 'mbsync-sentinel)))
(when show-buffer
(set-window-buffer (selected-window)
(process-buffer (mbsync-get-proc)))))
(provide 'mbsync)
;;; mbsync.el ends here