2021-08-29 18:53:53 +02:00
|
|
|
|
;;; mu4e-server.el -- part of mu4e -*- lexical-binding: t -*-
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2022-05-31 22:35:51 +02:00
|
|
|
|
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
|
|
|
|
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
|
|
|
|
|
|
|
|
|
;; This file is not part of GNU Emacs.
|
2020-02-11 12:23:40 +01:00
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is free software: you can redistribute it and/or modify
|
2011-09-12 19:52:32 +02:00
|
|
|
|
;; 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.
|
|
|
|
|
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; mu4e is distributed in the hope that it will be useful,
|
2011-09-12 19:52:32 +02:00
|
|
|
|
;; 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
|
2021-05-29 23:36:13 +02:00
|
|
|
|
;; along with mu4e. If not, see <http://www.gnu.org/licenses/>.
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
2020-02-11 15:10:35 +01:00
|
|
|
|
|
2021-08-28 21:29:03 +02:00
|
|
|
|
(require 'mu4e-helpers)
|
2012-04-15 13:21:59 +02:00
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
;;; Configuration
|
|
|
|
|
(defcustom mu4e-mu-home nil
|
|
|
|
|
"Location of an alternate mu home dir.
|
|
|
|
|
If not set, use the defaults, based on the XDG Base Directory
|
2021-10-30 14:46:03 +02:00
|
|
|
|
Specification.
|
|
|
|
|
|
2021-11-10 20:30:54 +01:00
|
|
|
|
Changes to this value only take effect after (re)starting the mu
|
|
|
|
|
session."
|
2021-08-29 16:30:10 +02:00
|
|
|
|
:group 'mu4e
|
|
|
|
|
:type '(choice (const :tag "Default location" nil)
|
|
|
|
|
(directory :tag "Specify location"))
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
|
|
|
|
(defcustom mu4e-mu-binary (executable-find "mu")
|
2021-11-10 20:30:54 +01:00
|
|
|
|
"Path to the mu-binary to use.
|
|
|
|
|
|
|
|
|
|
Changes to this value only take effect after (re)starting the mu
|
|
|
|
|
session."
|
2021-08-29 16:30:10 +02:00
|
|
|
|
:type 'file
|
|
|
|
|
:group 'mu4e
|
|
|
|
|
:safe 'stringp)
|
|
|
|
|
|
|
|
|
|
(defcustom mu4e-mu-debug nil
|
|
|
|
|
"Whether to run the mu binary in debug-mode.
|
2021-11-10 20:30:54 +01:00
|
|
|
|
Setting this to t increases the amount of information in the log.
|
|
|
|
|
|
|
|
|
|
Changes to this value only take effect after (re)starting the mu
|
|
|
|
|
session."
|
2021-08-29 16:30:10 +02:00
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'mu4e)
|
|
|
|
|
|
|
|
|
|
(defcustom mu4e-change-filenames-when-moving nil
|
|
|
|
|
"Change message file names when moving them.
|
2021-11-10 20:30:54 +01:00
|
|
|
|
|
2021-08-29 16:30:10 +02:00
|
|
|
|
When moving messages to different folders, normally mu/mu4e keep
|
|
|
|
|
the base filename the same (the flags-part of the filename may
|
|
|
|
|
change still). With this option set to non-nil, mu4e instead
|
2021-11-10 20:30:54 +01:00
|
|
|
|
changes the filename.
|
|
|
|
|
|
|
|
|
|
This latter behavior works better with some
|
2021-08-29 16:30:10 +02:00
|
|
|
|
IMAP-synchronization programs such as mbsync; the default works
|
|
|
|
|
better with e.g. offlineimap."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'mu4e
|
|
|
|
|
:safe 'booleanp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Handlers are not strictly internal, but are not meant
|
|
|
|
|
;; for overriding outside mu4e. The are mainly for breaking
|
|
|
|
|
;; dependency cycles.
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-error-func nil
|
|
|
|
|
"Function called for each error received.
|
|
|
|
|
The function is passed an error plist as argument. See
|
2021-08-29 18:53:53 +02:00
|
|
|
|
`mu4e--server-filter' for the format.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-update-func nil
|
|
|
|
|
"Function called for each :update sexp returned.
|
|
|
|
|
The function is passed a msg sexp as argument.
|
2021-08-29 18:53:53 +02:00
|
|
|
|
See `mu4e--server-filter' for the format.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-remove-func nil
|
|
|
|
|
"Function called for each :remove sexp returned.
|
|
|
|
|
This happens when some message has been deleted. The function is
|
|
|
|
|
passed the docid of the removed message.")
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-sent-func nil
|
|
|
|
|
"Function called for each :sent sexp received.
|
|
|
|
|
This happens when some message has been sent. The function is
|
|
|
|
|
passed the docid and the draft-path of the sent message.")
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-view-func nil
|
|
|
|
|
"Function called for each single-message sexp.
|
|
|
|
|
The function is passed a message sexp as argument. See
|
2021-08-29 18:53:53 +02:00
|
|
|
|
`mu4e--server-filter' for the format.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
2021-10-21 18:21:09 +02:00
|
|
|
|
(defvar mu4e-headers-append-func nil
|
|
|
|
|
"Function called with a list of headers to append.
|
|
|
|
|
The function is passed a list of message plists as argument. See
|
|
|
|
|
See `mu4e--server-filter' for the details.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-found-func nil
|
|
|
|
|
"Function called for when we received a :found sexp.
|
|
|
|
|
This happens after the headers have been returned, to report on
|
2021-08-29 18:53:53 +02:00
|
|
|
|
the number of matches. See `mu4e--server-filter' for the format.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-erase-func nil
|
|
|
|
|
"Function called we receive an :erase sexp.
|
|
|
|
|
This before new headers are displayed, to clear the current
|
2021-08-29 18:53:53 +02:00
|
|
|
|
headers buffer. See `mu4e--server-filter' for the format.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-compose-func nil
|
|
|
|
|
"Function called for each compose message received.
|
|
|
|
|
I.e., the original message that is used as basis for composing a
|
|
|
|
|
new message (i.e., either a reply or a forward); the function is
|
|
|
|
|
passed msg and a symbol (either reply or forward). See
|
2021-08-29 18:53:53 +02:00
|
|
|
|
`mu4e--server-filter' for the format of <msg-plist>.")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defvar mu4e-info-func nil
|
|
|
|
|
"Function called for each (:info type ....) sexp received.
|
|
|
|
|
from the server process.")
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-pong-func nil
|
|
|
|
|
"Function called for each (:pong type ....) sexp received.")
|
|
|
|
|
|
|
|
|
|
(defvar mu4e-contacts-func nil
|
2022-08-20 13:24:24 +02:00
|
|
|
|
"A function called for each (:contacts (<list-of-contacts>))
|
2021-08-29 16:30:10 +02:00
|
|
|
|
sexp received from the server process.")
|
|
|
|
|
|
|
|
|
|
|
2020-02-11 15:10:35 +01:00
|
|
|
|
;;; Internal vars
|
2012-04-19 07:30:42 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defvar mu4e--server-buf nil
|
2020-02-12 16:58:40 +01:00
|
|
|
|
"Buffer (string) for data received from the backend.")
|
2022-08-20 11:10:09 +02:00
|
|
|
|
(defconst mu4e--server-name " *mu4e-server*"
|
2012-06-14 08:58:13 +02:00
|
|
|
|
"Name of the server process, buffer.")
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defvar mu4e--server-process nil
|
2012-06-14 08:58:13 +02:00
|
|
|
|
"The mu-server process.")
|
2011-09-18 22:57:46 +02:00
|
|
|
|
|
2012-04-28 08:05:05 +02:00
|
|
|
|
;; dealing with the length cookie that precedes expressions
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defconst mu4e--server-cookie-pre "\376"
|
2019-04-29 05:25:43 +02:00
|
|
|
|
"Each expression starts with a length cookie:
|
2021-08-29 18:53:53 +02:00
|
|
|
|
<`mu4e--server-cookie-pre'><length-in-hex><`mu4e--server-cookie-post'>.")
|
|
|
|
|
(defconst mu4e--server-cookie-post "\377"
|
2020-02-11 12:00:46 +01:00
|
|
|
|
"Each expression starts with a length cookie:
|
2021-08-29 18:53:53 +02:00
|
|
|
|
<`mu4e--server-cookie-pre'><length-in-hex><`mu4e--server-cookie-post'>.")
|
|
|
|
|
(defconst mu4e--server-cookie-matcher-rx
|
|
|
|
|
(concat mu4e--server-cookie-pre "\\([[:xdigit:]]+\\)"
|
|
|
|
|
mu4e--server-cookie-post)
|
2012-11-10 14:01:17 +01:00
|
|
|
|
"Regular expression matching the length cookie.
|
|
|
|
|
Match 1 will be the length (in hex).")
|
2021-08-29 16:30:10 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e-running-p ()
|
|
|
|
|
"Whether mu4e is running.
|
|
|
|
|
Checks whether the server process is live."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(and mu4e--server-process
|
|
|
|
|
(memq (process-status mu4e--server-process)
|
2020-02-12 17:15:00 +01:00
|
|
|
|
'(run open listen connect stop))
|
|
|
|
|
t))
|
2012-04-26 16:59:34 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defsubst mu4e--server-eat-sexp-from-buf ()
|
|
|
|
|
"'Eat' the next s-expression from `mu4e--server-buf'.
|
|
|
|
|
Note: this is a string, not an emacs-buffer. `mu4e--server-buf gets
|
2012-11-10 14:01:17 +01:00
|
|
|
|
its contents from the mu-servers in the following form:
|
2021-08-29 18:53:53 +02:00
|
|
|
|
<`mu4e--server-cookie-pre'><length-in-hex><`mu4e--server-cookie-post'>
|
2012-11-10 14:01:17 +01:00
|
|
|
|
Function returns this sexp, or nil if there was none.
|
2021-08-29 18:53:53 +02:00
|
|
|
|
`mu4e--server-buf' is updated as well, with all processed sexp data
|
2012-11-10 14:01:17 +01:00
|
|
|
|
removed."
|
2012-09-28 16:11:58 +02:00
|
|
|
|
(ignore-errors ;; the server may die in the middle...
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(let ((b (string-match mu4e--server-cookie-matcher-rx mu4e--server-buf))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(sexp-len) (objcons))
|
2012-09-28 16:11:58 +02:00
|
|
|
|
(when b
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq sexp-len (string-to-number (match-string 1 mu4e--server-buf) 16))
|
|
|
|
|
;; does mu4e--server-buf contain the full sexp?
|
|
|
|
|
(when (>= (length mu4e--server-buf) (+ sexp-len (match-end 0)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; clear-up start
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq mu4e--server-buf (substring mu4e--server-buf (match-end 0)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
;; note: we read the input in binary mode -- here, we take the part
|
|
|
|
|
;; that is the sexp, and convert that to utf-8, before we interpret
|
|
|
|
|
;; it.
|
|
|
|
|
(setq objcons (read-from-string
|
|
|
|
|
(decode-coding-string
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(substring mu4e--server-buf 0 sexp-len)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
'utf-8 t)))
|
|
|
|
|
(when objcons
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq mu4e--server-buf (substring mu4e--server-buf sexp-len))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(car objcons)))))))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-filter (_proc str)
|
2019-04-29 05:25:43 +02:00
|
|
|
|
"Filter string STR from PROC.
|
2022-05-27 20:00:37 +02:00
|
|
|
|
This processes the \"mu server\" output. It accumulates the
|
|
|
|
|
strings into valid sexpsv and evaluating those.
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
2012-04-26 16:59:34 +02:00
|
|
|
|
The server output is as follows:
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
1. an error
|
2012-04-15 13:21:59 +02:00
|
|
|
|
(:error 2 :message \"unknown command\")
|
2011-09-18 13:39:36 +02:00
|
|
|
|
;; eox
|
2019-04-29 05:25:43 +02:00
|
|
|
|
=> passed to `mu4e-error-func'.
|
2011-09-12 19:52:32 +02:00
|
|
|
|
|
2021-10-20 21:41:48 +02:00
|
|
|
|
2a. a header exp looks something like:
|
2021-10-21 18:21:09 +02:00
|
|
|
|
(:headers
|
|
|
|
|
( ;; message 1
|
2021-10-20 21:41:48 +02:00
|
|
|
|
:docid 1585
|
|
|
|
|
:from ((\"Donald Duck\" . \"donald@example.com\"))
|
|
|
|
|
:to ((\"Mickey Mouse\" . \"mickey@example.com\"))
|
|
|
|
|
:subject \"Wicked stuff\"
|
|
|
|
|
:date (20023 26572 0)
|
|
|
|
|
:size 15165
|
|
|
|
|
:references (\"200208121222.g7CCMdb80690@msg.id\")
|
|
|
|
|
:in-reply-to \"200208121222.g7CCMdb80690@msg.id\"
|
|
|
|
|
:message-id \"foobar32423847ef23@pluto.net\"
|
|
|
|
|
:maildir: \"/archive\"
|
|
|
|
|
:path \"/home/mickey/Maildir/inbox/cur/1312_3.32282.pluto,4cd5bd4e9:2,\"
|
|
|
|
|
:priority high
|
2021-10-21 18:21:09 +02:00
|
|
|
|
:flags (new unread)
|
|
|
|
|
:meta <meta-data>
|
|
|
|
|
)
|
|
|
|
|
( .... more messages )
|
|
|
|
|
)
|
2011-09-12 19:52:32 +02:00
|
|
|
|
;; eox
|
2021-10-21 18:21:09 +02:00
|
|
|
|
=> this will be passed to `mu4e-headers-append-func'.
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2021-10-20 21:41:48 +02:00
|
|
|
|
2b. After the list of headers has been returned (see 2a.),
|
2011-10-25 07:43:24 +02:00
|
|
|
|
we'll receive a sexp that looks like
|
|
|
|
|
(:found <n>) with n the number of messages found. The <n> will be
|
2012-04-11 21:31:52 +02:00
|
|
|
|
passed to `mu4e-found-func'.
|
2011-10-25 07:43:24 +02:00
|
|
|
|
|
2011-09-18 13:39:36 +02:00
|
|
|
|
3. a view looks like:
|
|
|
|
|
(:view <msg-sexp>)
|
2012-04-11 21:31:52 +02:00
|
|
|
|
=> the <msg-sexp> (see 2.) will be passed to `mu4e-view-func'.
|
2021-10-21 18:21:09 +02:00
|
|
|
|
the <msg-sexp> also contains :body-txt and/or :body-html
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
4. a database update looks like:
|
|
|
|
|
(:update <msg-sexp> :move <nil-or-t>)
|
2021-10-20 21:41:48 +02:00
|
|
|
|
like :header
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
=> the <msg-sexp> (see 2.) will be passed to
|
2012-04-11 21:31:52 +02:00
|
|
|
|
`mu4e-update-func', :move tells us whether this is a move to
|
2011-09-18 13:39:36 +02:00
|
|
|
|
another maildir, or merely a flag change.
|
|
|
|
|
|
|
|
|
|
5. a remove looks like:
|
|
|
|
|
(:remove <docid>)
|
2012-04-11 21:31:52 +02:00
|
|
|
|
=> the docid will be passed to `mu4e-remove-func'
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
|
|
|
|
6. a compose looks like:
|
2012-04-15 13:21:59 +02:00
|
|
|
|
(:compose <reply|forward|edit|new> [:original<msg-sexp>] [:include <attach>])
|
2021-10-20 21:41:48 +02:00
|
|
|
|
`mu4e-compose-func'. :original looks like :view."
|
2012-04-16 17:31:48 +02:00
|
|
|
|
(mu4e-log 'misc "* Received %d byte(s)" (length str))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq mu4e--server-buf (concat mu4e--server-buf str)) ;; update our buffer
|
|
|
|
|
(let ((sexp (mu4e--server-eat-sexp-from-buf)))
|
2012-05-07 20:59:06 +02:00
|
|
|
|
(with-local-quit
|
|
|
|
|
(while sexp
|
2022-02-03 22:02:44 +01:00
|
|
|
|
(mu4e-log 'from-server "%s" sexp)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(cond
|
|
|
|
|
;; a header plist can be recognized by the existence of a :date field
|
2021-10-21 18:21:09 +02:00
|
|
|
|
((plist-get sexp :headers)
|
|
|
|
|
(funcall mu4e-headers-append-func (plist-get sexp :headers)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
|
|
|
|
|
;; the found sexp, we receive after getting all the headers
|
|
|
|
|
((plist-get sexp :found)
|
|
|
|
|
(funcall mu4e-found-func (plist-get sexp :found)))
|
|
|
|
|
|
|
|
|
|
;; viewing a specific message
|
|
|
|
|
((plist-get sexp :view)
|
2021-10-21 18:21:09 +02:00
|
|
|
|
(funcall mu4e-view-func (plist-get sexp :view)))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
|
|
|
|
|
;; receive an erase message
|
|
|
|
|
((plist-get sexp :erase)
|
|
|
|
|
(funcall mu4e-erase-func))
|
|
|
|
|
|
|
|
|
|
;; receive a :sent message
|
|
|
|
|
((plist-get sexp :sent)
|
|
|
|
|
(funcall mu4e-sent-func
|
|
|
|
|
(plist-get sexp :docid)
|
|
|
|
|
(plist-get sexp :path)))
|
|
|
|
|
|
|
|
|
|
;; received a pong message
|
|
|
|
|
((plist-get sexp :pong)
|
2021-08-28 21:29:03 +02:00
|
|
|
|
(setq mu4e--server-props (plist-get sexp :props))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(funcall mu4e-pong-func sexp))
|
|
|
|
|
|
|
|
|
|
;; received a contacts message
|
|
|
|
|
;; note: we use 'member', to match (:contacts nil)
|
|
|
|
|
((plist-member sexp :contacts)
|
|
|
|
|
(funcall mu4e-contacts-func
|
|
|
|
|
(plist-get sexp :contacts)
|
|
|
|
|
(plist-get sexp :tstamp)))
|
|
|
|
|
|
|
|
|
|
;; something got moved/flags changed
|
|
|
|
|
((plist-get sexp :update)
|
|
|
|
|
(funcall mu4e-update-func
|
2021-10-21 18:21:09 +02:00
|
|
|
|
(plist-get sexp :update)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(plist-get sexp :move)
|
|
|
|
|
(plist-get sexp :maybe-view)))
|
|
|
|
|
|
|
|
|
|
;; a message got removed
|
|
|
|
|
((plist-get sexp :remove)
|
|
|
|
|
(funcall mu4e-remove-func (plist-get sexp :remove)))
|
|
|
|
|
|
|
|
|
|
;; start composing a new message
|
|
|
|
|
((plist-get sexp :compose)
|
|
|
|
|
(funcall mu4e-compose-func
|
|
|
|
|
(plist-get sexp :compose)
|
2021-10-21 18:21:09 +02:00
|
|
|
|
(plist-get sexp :original)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(plist-get sexp :include)))
|
|
|
|
|
|
|
|
|
|
;; get some info
|
|
|
|
|
((plist-get sexp :info)
|
|
|
|
|
(funcall mu4e-info-func sexp))
|
|
|
|
|
|
|
|
|
|
;; receive an error
|
|
|
|
|
((plist-get sexp :error)
|
|
|
|
|
(funcall mu4e-error-func
|
|
|
|
|
(plist-get sexp :error)
|
|
|
|
|
(plist-get sexp :message)))
|
|
|
|
|
|
|
|
|
|
(t (mu4e-message "Unexpected data from server [%S]" sexp)))
|
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq sexp (mu4e--server-eat-sexp-from-buf))))))
|
2011-09-18 13:39:36 +02:00
|
|
|
|
|
2022-05-31 22:35:51 +02:00
|
|
|
|
(defun mu4e--kill-stale ()
|
|
|
|
|
"Kill stale mu4e server process.
|
|
|
|
|
As per issue #2198."
|
|
|
|
|
(seq-each
|
|
|
|
|
(lambda(proc)
|
|
|
|
|
(when (and (process-live-p proc)
|
|
|
|
|
(string-prefix-p mu4e--server-name (process-name proc)))
|
|
|
|
|
(mu4e-message "killing stale mu4e server")
|
|
|
|
|
(ignore-errors
|
|
|
|
|
(signal-process proc 'SIGINT) ;; nicely
|
|
|
|
|
(sit-for 1.0)
|
|
|
|
|
(signal-process proc 'SIGKILL)))) ;; forcefully
|
|
|
|
|
(process-list)))
|
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-start ()
|
2017-10-24 22:05:40 +02:00
|
|
|
|
"Start the mu server process."
|
2022-03-07 05:41:23 +01:00
|
|
|
|
(let ((default-directory temporary-file-directory)) ;;ensure it's local.
|
2022-05-31 22:35:51 +02:00
|
|
|
|
;; sanity-check 1
|
2020-05-05 20:22:35 +02:00
|
|
|
|
(unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary))
|
|
|
|
|
(mu4e-error
|
2020-10-17 09:14:37 +02:00
|
|
|
|
"Cannot find mu, please set `mu4e-mu-binary' to the mu executable path"))
|
|
|
|
|
;; sanity-check 2
|
2021-08-28 21:29:03 +02:00
|
|
|
|
(let ((version (let ((s (shell-command-to-string
|
|
|
|
|
(concat mu4e-mu-binary " --version"))))
|
2020-12-04 16:45:09 +01:00
|
|
|
|
(and (string-match "version \\([.0-9]+\\)" s)
|
|
|
|
|
(match-string 1 s)))))
|
2020-10-17 09:14:37 +02:00
|
|
|
|
(unless (string= version mu4e-mu-version)
|
|
|
|
|
(mu4e-error
|
|
|
|
|
(concat
|
2021-08-29 18:53:53 +02:00
|
|
|
|
"Found mu version %s, but mu4e needs version %s"
|
|
|
|
|
"; please set `mu4e-mu-binary' "
|
2020-10-17 09:14:37 +02:00
|
|
|
|
"accordingly") version mu4e-mu-version)))
|
2022-05-31 22:35:51 +02:00
|
|
|
|
;; kill old/stale servers, if any.
|
|
|
|
|
(mu4e--kill-stale)
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(let* ((process-connection-type nil) ;; use a pipe
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(args (when mu4e-mu-home `(,(format"--muhome=%s" mu4e-mu-home))))
|
2020-06-09 11:12:02 +02:00
|
|
|
|
(args (if mu4e-mu-debug (cons "--debug" args) args))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(args (cons "server" args)))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq mu4e--server-buf "")
|
|
|
|
|
(setq mu4e--server-process (apply 'start-process
|
|
|
|
|
mu4e--server-name mu4e--server-name
|
|
|
|
|
mu4e-mu-binary args))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
;; register a function for (:info ...) sexps
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(unless mu4e--server-process
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(mu4e-error "Failed to start the mu4e backend"))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(set-process-query-on-exit-flag mu4e--server-process nil)
|
|
|
|
|
(set-process-coding-system mu4e--server-process 'binary 'utf-8-unix)
|
|
|
|
|
(set-process-filter mu4e--server-process 'mu4e--server-filter)
|
2022-03-07 05:41:23 +01:00
|
|
|
|
(set-process-sentinel mu4e--server-process 'mu4e--server-sentinel))))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-kill ()
|
2017-10-24 22:05:40 +02:00
|
|
|
|
"Kill the mu server process."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(let* ((buf (get-buffer mu4e--server-name))
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(proc (and (buffer-live-p buf) (get-buffer-process buf))))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(when proc
|
2022-05-31 22:35:51 +02:00
|
|
|
|
(mu4e-message "shutting down")
|
2022-02-03 22:02:44 +01:00
|
|
|
|
(set-process-filter mu4e--server-process nil)
|
|
|
|
|
(set-process-sentinel mu4e--server-process nil)
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(let ((delete-exited-processes t))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu '(quit)))
|
2020-02-04 00:04:28 +01:00
|
|
|
|
;; try sending SIGINT (C-c) to process, so it can exit gracefully
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(ignore-errors
|
2020-02-04 00:04:28 +01:00
|
|
|
|
(signal-process proc 'SIGINT))))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(setq
|
2021-08-29 18:53:53 +02:00
|
|
|
|
mu4e--server-process nil
|
|
|
|
|
mu4e--server-buf nil))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
|
|
|
|
;; error codes are defined in src/mu-util
|
|
|
|
|
;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent")
|
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-sentinel (proc _msg)
|
2019-04-29 05:25:43 +02:00
|
|
|
|
"Function called when the server process PROC terminates with MSG."
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(let ((status (process-status proc)) (code (process-exit-status proc)))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(setq mu4e--server-process nil)
|
|
|
|
|
(setq mu4e--server-buf "") ;; clear any half-received sexps
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(cond
|
2020-02-11 12:00:46 +01:00
|
|
|
|
((eq status 'signal)
|
|
|
|
|
(cond
|
|
|
|
|
((or(eq code 9) (eq code 2)) (message nil))
|
|
|
|
|
;;(message "the mu server process has been stopped"))
|
|
|
|
|
(t (error (format "mu server process received signal %d" code)))))
|
|
|
|
|
((eq status 'exit)
|
|
|
|
|
(cond
|
|
|
|
|
((eq code 0)
|
|
|
|
|
(message nil)) ;; don't do anything
|
|
|
|
|
((eq code 19)
|
|
|
|
|
(error "Database is locked by another process"))
|
|
|
|
|
(t (error "Mu server process ended with exit code %d" code))))
|
|
|
|
|
(t
|
|
|
|
|
(error "Something bad happened to the mu server process")))))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-call-mu (form)
|
|
|
|
|
"Call the mu server with some command FORM."
|
|
|
|
|
(unless (mu4e-running-p) (mu4e--server-start))
|
2020-07-25 16:02:01 +02:00
|
|
|
|
(let* ((print-length nil) (print-level nil)
|
|
|
|
|
(cmd (format "%S" form)))
|
2020-01-19 16:23:24 +01:00
|
|
|
|
(mu4e-log 'to-server "%s" cmd)
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(process-send-string mu4e--server-process (concat cmd "\n"))))
|
2020-01-19 16:23:24 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-add (path)
|
2020-02-01 12:39:17 +01:00
|
|
|
|
"Add the message at PATH to the database.
|
|
|
|
|
On success, we receive `'(:info add :path <path> :docid <docid>)'
|
|
|
|
|
as well as `'(:update <msg-sexp>)`'; otherwise, we receive an error."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu `(add :path ,path)))
|
2020-02-01 12:39:17 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-compose (type decrypt &optional docid)
|
2020-02-01 12:39:17 +01:00
|
|
|
|
"Compose a message of TYPE, DECRYPT it and use DOCID.
|
|
|
|
|
TYPE is a symbol, either `forward', `reply', `edit', `resend' or
|
|
|
|
|
`new', based on an original message (ie, replying to, forwarding,
|
|
|
|
|
editing, resending) with DOCID or nil for type `new'.
|
|
|
|
|
|
|
|
|
|
The result is delivered to the function registered as
|
|
|
|
|
`mu4e-compose-func'."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(compose
|
|
|
|
|
:type ,type
|
|
|
|
|
:decrypt ,(and decrypt t)
|
|
|
|
|
:docid ,docid)))
|
2020-02-01 12:39:17 +01:00
|
|
|
|
|
2022-05-06 20:58:51 +02:00
|
|
|
|
(defun mu4e--server-contacts (personal after maxnum tstamp)
|
|
|
|
|
"Ask for contacts with PERSONAL AFTER MAXNUM TSTAMP.
|
|
|
|
|
|
|
|
|
|
S-expression (:contacts (<list>) :tstamp \"<tstamp>\")
|
|
|
|
|
is expected in response.
|
|
|
|
|
|
|
|
|
|
If PERSONAL is non-nil, only get personal contacts, if AFTER is
|
|
|
|
|
non-nil, get only contacts seen AFTER (the time_t value). If MAX is non-nil,
|
|
|
|
|
get at most MAX contacts."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(contacts
|
|
|
|
|
:personal ,(and personal t)
|
|
|
|
|
:after ,(or after nil)
|
2022-05-06 20:58:51 +02:00
|
|
|
|
:tstamp ,(or tstamp nil)
|
|
|
|
|
:maxnum ,(or maxnum nil))))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e--server-find (query threads sortfield sortdir maxnum skip-dups
|
|
|
|
|
include-related)
|
2019-04-29 05:25:43 +02:00
|
|
|
|
"Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED.
|
2022-05-27 20:00:37 +02:00
|
|
|
|
|
|
|
|
|
If THREADS is non-nil, show results in threaded fashion,
|
|
|
|
|
SORTFIELD is a symbol describing the field to sort by (or nil);
|
|
|
|
|
see `mu4e~headers-sortfield-choices'. If SORT is `descending',
|
|
|
|
|
sort Z->A, if it's `ascending', sort A->Z. MAXNUM determines the
|
|
|
|
|
maximum number of results to return, or nil for unlimited. If
|
2019-04-29 05:25:43 +02:00
|
|
|
|
SKIP-DUPS is non-nil, show only one of duplicate messages (see
|
|
|
|
|
`mu4e-headers-skip-duplicates'). If INCLUDE-RELATED is non-nil,
|
2017-10-24 22:05:40 +02:00
|
|
|
|
include messages related to the messages matching the search
|
|
|
|
|
query (see `mu4e-headers-include-related').
|
|
|
|
|
|
|
|
|
|
For each result found, a function is called, depending on the
|
|
|
|
|
kind of result. The variables `mu4e-error-func' contain the
|
2022-05-27 20:00:37 +02:00
|
|
|
|
function that to be be called for, resp., a message (header)
|
2017-10-24 22:05:40 +02:00
|
|
|
|
or an error."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(find
|
|
|
|
|
:query ,query
|
2022-08-02 06:21:15 +02:00
|
|
|
|
:threads ,(and threads t)
|
2021-08-29 18:53:53 +02:00
|
|
|
|
:sortfield ,sortfield
|
|
|
|
|
:descending ,(if (eq sortdir 'descending) t nil)
|
|
|
|
|
:maxnum ,maxnum
|
2022-08-02 06:21:15 +02:00
|
|
|
|
:skip-dups ,(and skip-dups t)
|
|
|
|
|
:include-related ,(and include-related t))))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
|
|
|
|
|
(defun mu4e--server-index (&optional cleanup lazy-check)
|
2022-08-02 06:21:15 +02:00
|
|
|
|
"Index messages.
|
|
|
|
|
If CLEANUP is non-nil, remove messages which are in the database
|
|
|
|
|
but no longer in the filesystem. If LAZY-CHECK is non-nil, only
|
|
|
|
|
consider messages for which the time stamp (ctime) of the
|
|
|
|
|
directory they reside in has not changed since the previous
|
|
|
|
|
indexing run. This is much faster than the non-lazy check, but
|
|
|
|
|
won't update messages that have change (rather than having been
|
|
|
|
|
added or removed), since merely editing a message does not update
|
|
|
|
|
the directory time stamp."
|
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(index :cleanup ,(and cleanup t)
|
|
|
|
|
:lazy-check ,(and lazy-check t))))
|
2020-02-01 12:39:17 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-mkdir (path)
|
2020-02-01 12:39:17 +01:00
|
|
|
|
"Create a new maildir-directory at filesystem PATH."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu `(mkdir :path ,path)))
|
2020-02-01 12:39:17 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-move (docid-or-msgid &optional maildir flags no-view)
|
2019-04-29 05:25:43 +02:00
|
|
|
|
"Move message identified by DOCID-OR-MSGID.
|
|
|
|
|
Optionally to MAILDIR and optionally setting FLAGS. If MAILDIR is
|
|
|
|
|
nil, message will be moved within the same maildir.
|
2017-11-04 14:06:45 +01:00
|
|
|
|
|
|
|
|
|
At least one of MAILDIR and FLAGS must be specified. Note that
|
|
|
|
|
even when MAILDIR is nil, this is still a filesystem move, since
|
|
|
|
|
a change in flags implies a change in message filename.
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
|
|
|
|
MAILDIR must be a maildir, that is, the part _without_ cur/ or new/
|
|
|
|
|
or the root-maildir-prefix. E.g. \"/archive\". This directory must
|
|
|
|
|
already exist.
|
|
|
|
|
|
|
|
|
|
The FLAGS parameter can have the following forms:
|
2022-05-27 20:00:37 +02:00
|
|
|
|
1. a list of flags such as `(passed replied seen)'
|
2017-10-24 22:05:40 +02:00
|
|
|
|
2. a string containing the one-char versions of the flags, e.g. \"PRS\"
|
|
|
|
|
3. a delta-string specifying the changes with +/- and the one-char flags,
|
|
|
|
|
e.g. \"+S-N\" to set Seen and remove New.
|
|
|
|
|
|
|
|
|
|
The flags are any of `deleted', `flagged', `new', `passed', `replied' `seen' or
|
|
|
|
|
`trashed', or the corresponding \"DFNPRST\" as defined in [1]. See
|
|
|
|
|
`mu4e-string-to-flags' and `mu4e-flags-to-string'.
|
|
|
|
|
The server reports the results for the operation through
|
|
|
|
|
`mu4e-update-func'.
|
|
|
|
|
|
|
|
|
|
If the variable `mu4e-change-filenames-when-moving' is
|
2018-08-12 12:46:55 +02:00
|
|
|
|
non-nil, moving to a different maildir generates new names forq
|
2017-10-24 22:05:40 +02:00
|
|
|
|
the target files; this helps certain tools (such as mbsync).
|
|
|
|
|
|
2022-05-27 20:00:37 +02:00
|
|
|
|
If NO-VIEW is non-nil, do not update the view.
|
2018-08-14 20:59:41 +02:00
|
|
|
|
|
|
|
|
|
Returns either (:update ... ) or (:error ) sexp, which are handled my
|
2018-08-12 12:46:55 +02:00
|
|
|
|
`mu4e-update-func' and `mu4e-error-func', respectively."
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(unless (or maildir flags)
|
|
|
|
|
(mu4e-error "At least one of maildir and flags must be specified"))
|
2017-11-02 06:51:00 +01:00
|
|
|
|
(unless (or (not maildir)
|
2020-02-11 12:00:46 +01:00
|
|
|
|
(file-exists-p (concat (mu4e-root-maildir) "/" maildir "/")))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
(mu4e-error "Target dir does not exist"))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(move
|
|
|
|
|
:docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
|
|
|
|
|
:msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
|
|
|
|
|
:flags ,(or flags nil)
|
|
|
|
|
:maildir ,(or maildir nil)
|
|
|
|
|
:rename ,(and maildir mu4e-change-filenames-when-moving t)
|
|
|
|
|
:no-view ,(and no-view t))))
|
|
|
|
|
|
|
|
|
|
(defun mu4e--server-ping (&optional queries)
|
2020-01-21 19:53:09 +01:00
|
|
|
|
"Sends a ping to the mu server, expecting a (:pong ...) in response.
|
2022-05-27 20:00:37 +02:00
|
|
|
|
QUERIES is a list of queries for the number of results with
|
|
|
|
|
read/unread status are returned in the pong-response."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu `(ping :queries ,queries)))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-remove (docid)
|
2020-02-01 12:39:17 +01:00
|
|
|
|
"Remove message with DOCID.
|
|
|
|
|
The results are reporter through either (:update ... )
|
|
|
|
|
or (:error) sexp, which are handled my `mu4e-error-func',
|
|
|
|
|
respectively."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu `(remove :docid ,docid)))
|
2020-02-01 12:39:17 +01:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-sent (path)
|
|
|
|
|
"Tell the mu server we sent a message at PATH.
|
|
|
|
|
If this works, we will receive (:info add :path <path> :docid
|
2020-02-01 12:39:17 +01:00
|
|
|
|
<docid> :fcc <path>)."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu `(sent :path ,path)))
|
2017-10-24 22:05:40 +02:00
|
|
|
|
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(defun mu4e--server-view (docid-or-msgid &optional mark-as-read)
|
2022-08-02 06:21:15 +02:00
|
|
|
|
"View a message referred to by DOCID-OR-MSGID.
|
2021-08-29 18:53:53 +02:00
|
|
|
|
Optionally, if MARK-AS-READ is non-nil, the backend marks the
|
2022-08-02 06:21:15 +02:00
|
|
|
|
message as \"read\" before returning, if not already. The result
|
|
|
|
|
will be delivered to the function registered as `mu4e-view-func'."
|
2021-08-29 18:53:53 +02:00
|
|
|
|
(mu4e--server-call-mu
|
|
|
|
|
`(view
|
|
|
|
|
:docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
|
|
|
|
|
:msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
|
2022-08-02 06:21:15 +02:00
|
|
|
|
:mark-as-read ,(and mark-as-read t)
|
2022-02-13 11:03:16 +01:00
|
|
|
|
;; when moving (due to mark-as-read), change filenames
|
2022-08-02 06:21:15 +02:00
|
|
|
|
;; if so configured. Note: currently this *ignored*
|
|
|
|
|
;; because mbsync seems to get confused.
|
|
|
|
|
:rename ,(and mu4e-change-filenames-when-moving t))))
|
2021-08-29 18:53:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(provide 'mu4e-server)
|
|
|
|
|
;;; mu4e-server.el ends here
|