From a6514fd6836c169043b939dc054bbfd326a143e0 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sun, 29 Aug 2021 19:53:53 +0300 Subject: mu4e-server: rename from mu4e-proc Rename mu4e-proc.el into mu4e-server.el Rename mu4e~proc... into mu4e--server... Update users, too. --- mu4e/Makefile.am | 2 +- mu4e/meson.build | 2 +- mu4e/mu4e-actions.el | 4 +- mu4e/mu4e-compose.el | 22 +- mu4e/mu4e-contacts.el | 4 +- mu4e/mu4e-folders.el | 6 +- mu4e/mu4e-headers.el | 10 +- mu4e/mu4e-mark.el | 22 +- mu4e/mu4e-proc.el | 598 -------------------------------------------------- mu4e/mu4e-server.el | 553 ++++++++++++++++++++++++++++++++++++++++++++++ mu4e/mu4e-update.el | 4 +- mu4e/mu4e-view.el | 3 +- mu4e/mu4e.el | 6 +- mu4e/mu4e.texi | 8 +- 14 files changed, 598 insertions(+), 646 deletions(-) delete mode 100644 mu4e/mu4e-proc.el create mode 100644 mu4e/mu4e-server.el diff --git a/mu4e/Makefile.am b/mu4e/Makefile.am index d441a3a..790a0e6 100644 --- a/mu4e/Makefile.am +++ b/mu4e/Makefile.am @@ -38,7 +38,7 @@ dist_lisp_LISP= \ mu4e-message.el \ mu4e-meta.el \ mu4e-org.el \ - mu4e-proc.el \ + mu4e-server.el \ mu4e-speedbar.el \ mu4e-update.el \ mu4e-vars.el \ diff --git a/mu4e/meson.build b/mu4e/meson.build index b6d8744..ea82af3 100644 --- a/mu4e/meson.build +++ b/mu4e/meson.build @@ -45,7 +45,7 @@ mu4e_srcs=[ 'mu4e-message.el', join_paths(meson.current_build_dir(), 'mu4e-meta.el'), 'mu4e-org.el', - 'mu4e-proc.el', + 'mu4e-server.el', 'mu4e-speedbar.el', 'mu4e-update.el', 'mu4e-vars.el', diff --git a/mu4e/mu4e-actions.el b/mu4e/mu4e-actions.el index 41e6d0b..e8c1916 100644 --- a/mu4e/mu4e-actions.el +++ b/mu4e/mu4e-actions.el @@ -170,13 +170,13 @@ Otherwise return nil." (if (re-search-forward regexp nil t) (replace-match to-string nil nil))))) -(declare-function mu4e~proc-add "mu4e-proc") +(declare-function mu4e--server-add "mu4e-server") (defun mu4e--refresh-message (path) "Re-parse message at PATH. if this works, we will receive (:info add :path :docid ) as well as (:update )." - (mu4e~proc-add path)) + (mu4e--server-add path)) (defun mu4e-action-retag-message (msg &optional retag-arg) "Change tags of MSG with RETAG-ARG. diff --git a/mu4e/mu4e-compose.el b/mu4e/mu4e-compose.el index ca469fe..3b94636 100644 --- a/mu4e/mu4e-compose.el +++ b/mu4e/mu4e-compose.el @@ -72,7 +72,7 @@ (require 'smtpmail) (require 'rfc2368) -(require 'mu4e-proc) +(require 'mu4e-server) (require 'mu4e-actions) (require 'mu4e-message) (require 'mu4e-draft) @@ -153,12 +153,12 @@ If needed, set the Fcc header, and register the handler function." (setq message-fcc-handler-function old-handler) ;; reset the fcc handler (let ((mdir-path (concat (mu4e-root-maildir) maildir))) ;; Create the full maildir structure for the sent folder if it doesn't exist. - ;; `mu4e~proc-mkdir` runs asynchronously but no matter whether it runs before or after + ;; `mu4e--server-mkdir` runs asynchronously but no matter whether it runs before or after ;; `write-file`, the sent maildir ends up in the correct state. (unless (file-exists-p mdir-path) - (mu4e~proc-mkdir mdir-path))) + (mu4e--server-mkdir mdir-path))) (write-file file) ;; writing maildirs files is easy - (mu4e~proc-add file))))))) ;; update the database + (mu4e--server-add file))))))) ;; update the database (defvar mu4e-compose-hidden-headers `("^References:" "^Face:" "^X-Face:" @@ -206,7 +206,7 @@ Message-ID." (set-buffer-modified-p nil) (mu4e-message "Saved (%d lines)" (count-lines (point-min) (point-max))) ;; update the file on disk -- ie., without the separator - (mu4e~proc-add (buffer-file-name)))) + (mu4e--server-add (buffer-file-name)))) ;;; address completion @@ -432,7 +432,7 @@ buffers; lets remap its faces so it uses the ones for mu4e." (defun mu4e~set-sent-handler-message-sent-hook-fn () ;; mu4e~compose-mark-after-sending (setq mu4e-sent-func 'mu4e-sent-handler) - (mu4e~proc-sent (buffer-file-name))) + (mu4e--server-sent (buffer-file-name))) (defun mu4e-send-harden-newlines () "Set the hard property to all newlines." @@ -660,7 +660,7 @@ when the buffer is in `mu4e-compose-mode': ;; Remove the <> (when (and msg-id (string-match "<\\(.*\\)>" msg-id)) (save-window-excursion - (mu4e~proc-move (match-string 1 msg-id) mu4e-drafts-folder nil t) + (mu4e--server-move (match-string 1 msg-id) mu4e-drafts-folder nil t) (kill-buffer buf))))) ;; Kill previous buffer which points to wrong file ;; No file, just change the buffer file name (setq buffer-file-name @@ -676,7 +676,7 @@ For Forwarded ('Passed') and Replied messages, try to set the appropriate flag at the message forwarded or replied-to." (mu4e~compose-set-parent-flag path) (when (file-exists-p path) ;; maybe the draft was not saved at all - (mu4e~proc-remove docid)) + (mu4e--server-remove docid)) ;; kill any remaining buffers for the draft file, or they will hang around... ;; this seems a bit hamfisted... (when message-kill-buffer-on-exit @@ -738,9 +738,9 @@ buffer." (setq forwarded-from (cl-first refs)))))) ;; remove the <> (when (and in-reply-to (string-match "<\\(.*\\)>" in-reply-to)) - (mu4e~proc-move (match-string 1 in-reply-to) nil "+R-N")) + (mu4e--server-move (match-string 1 in-reply-to) nil "+R-N")) (when (and forwarded-from (string-match "<\\(.*\\)>" forwarded-from)) - (mu4e~proc-move (match-string 1 forwarded-from) nil "+P-N"))))))) + (mu4e--server-move (match-string 1 forwarded-from) nil "+P-N"))))))) (defun mu4e-compose (compose-type) "Start composing a message of COMPOSE-TYPE. @@ -778,7 +778,7 @@ Symbol `edit' is only allowed for draft messages." (when (window-live-p viewwin) (select-window viewwin)))) ;; talk to the backend - (mu4e~proc-compose compose-type decrypt docid))))) + (mu4e--server-compose compose-type decrypt docid))))) (defun mu4e-compose-reply () "Compose a reply for the message at point in the headers buffer." diff --git a/mu4e/mu4e-contacts.el b/mu4e/mu4e-contacts.el index dcb434a..65d32e2 100644 --- a/mu4e/mu4e-contacts.el +++ b/mu4e/mu4e-contacts.el @@ -215,14 +215,14 @@ For testing/debugging." (pop-to-buffer "*mu4e-contacts-info*"))) -(declare-function mu4e~proc-contacts "mu4e-proc") +(declare-function mu4e--server-contacts "mu4e-server") (defun mu4e--request-contacts-maybe () "If `mu4e-compose-complete-addresses' is non-nil, get/update the list of contacts we use for autocompletion; otherwise, do nothing." (when mu4e-compose-complete-addresses - (mu4e~proc-contacts + (mu4e--server-contacts mu4e-compose-complete-only-personal mu4e-compose-complete-only-after mu4e--contacts-tstamp))) diff --git a/mu4e/mu4e-folders.el b/mu4e/mu4e-folders.el index 0e7e5c1..a2780d0 100644 --- a/mu4e/mu4e-folders.el +++ b/mu4e/mu4e-folders.el @@ -28,7 +28,7 @@ (require 'cl-lib) (require 'mu4e-helpers) (require 'mu4e-context) -(require 'mu4e-proc) +(require 'mu4e-server) ;;; Customization (defgroup mu4e-folders nil @@ -234,7 +234,7 @@ an absolute path." (cond ((file-directory-p dir) t) ((yes-or-no-p (mu4e-format "%s does not exist yet. Create now?" dir)) - (mu4e~proc-mkdir dir) t) + (mu4e--server-mkdir dir) t) (t nil))) (defun mu4e~get-maildirs-1 (path mdir) @@ -326,7 +326,7 @@ Offer to create it if it does not exist yet." (unless (file-directory-p fullpath) (and (yes-or-no-p (mu4e-format "%s does not exist. Create now?" fullpath)) - (mu4e~proc-mkdir fullpath))) + (mu4e--server-mkdir fullpath))) mdir)) ;; mu4e-attachment-dir is either a string or a function that takes a diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index 74e68c6..d6afb0e 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -37,7 +37,7 @@ (require 'mu4e-update) (require 'mu4e-utils) ;; utility functions -(require 'mu4e-proc) +(require 'mu4e-server) (require 'mu4e-vars) (require 'mu4e-mark) (require 'mu4e-context) @@ -784,7 +784,7 @@ true, do *not* update the query history stack." (switch-to-buffer buf)) (run-hook-with-args 'mu4e-search-hook expr) (mu4e~headers-clear mu4e~search-message) - (mu4e~proc-find + (mu4e--server-find rewritten-expr mu4e-search-threads mu4e-headers-sort-field @@ -1546,8 +1546,6 @@ window . " (if (functionp mu4e-view-auto-mark-as-read) (funcall mu4e-view-auto-mark-as-read msg) mu4e-view-auto-mark-as-read)) - (decrypt nil) ;; XXX remove - (verify nil) ;; XXX remove (viewwin (mu4e~headers-redraw-get-view-window))) (unless (window-live-p viewwin) (mu4e-error "Cannot get a message view")) @@ -1568,8 +1566,8 @@ window . " ;; (if mu4e-view-use-gnus ;; (mu4e-view msg) - ;; (mu4e~proc-view dowcid decrypt)) - (mu4e~proc-view docid mark-as-read decrypt verify))) + ;; (mu4e--server-view dowcid decrypt)) + (mu4e--server-view docid mark-as-read))) (defun mu4e~headers-move (lines) diff --git a/mu4e/mu4e-mark.el b/mu4e/mu4e-mark.el index dca3e7b..8a44bfc 100644 --- a/mu4e/mu4e-mark.el +++ b/mu4e/mu4e-mark.el @@ -28,7 +28,7 @@ ;;; Code: (require 'cl-lib) -(require 'mu4e-proc) +(require 'mu4e-server) (require 'mu4e-utils) (require 'mu4e-message) (require 'mu4e-folders) @@ -133,50 +133,50 @@ The current buffer must be either a headers or view buffer." :prompt "refile" :dyn-target (lambda (target msg) (mu4e-get-refile-folder msg)) :action (lambda (docid msg target) - (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) + (mu4e--server-move docid (mu4e~mark-check-target target) "-N"))) (delete :char ("D" . "x") :prompt "Delete" :show-target (lambda (target) "delete") - :action (lambda (docid msg target) (mu4e~proc-remove docid))) + :action (lambda (docid msg target) (mu4e--server-remove docid))) (flag :char ("+" . "✚") :prompt "+flag" :show-target (lambda (target) "flag") :action (lambda (docid msg target) - (mu4e~proc-move docid nil "+F-u-N"))) + (mu4e--server-move docid nil "+F-u-N"))) (move :char ("m" . "▷") :prompt "move" :ask-target mu4e~mark-get-move-target :action (lambda (docid msg target) - (mu4e~proc-move docid (mu4e~mark-check-target target) "-N"))) + (mu4e--server-move docid (mu4e~mark-check-target target) "-N"))) (read :char ("!" . "◼") :prompt "!read" :show-target (lambda (target) "read") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "+S-u-N"))) + :action (lambda (docid msg target) (mu4e--server-move docid nil "+S-u-N"))) (trash :char ("d" . "▼") :prompt "dtrash" :dyn-target (lambda (target msg) (mu4e-get-trash-folder msg)) - :action (lambda (docid msg target) (mu4e~proc-move docid + :action (lambda (docid msg target) (mu4e--server-move docid (mu4e~mark-check-target target) "+T-N"))) (unflag :char ("-" . "➖") :prompt "-unflag" :show-target (lambda (target) "unflag") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-F-N"))) + :action (lambda (docid msg target) (mu4e--server-move docid nil "-F-N"))) (untrash :char ("=" . "▲") :prompt "=untrash" :show-target (lambda (target) "untrash") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-T"))) + :action (lambda (docid msg target) (mu4e--server-move docid nil "-T"))) (unread :char ("?" . "◻") :prompt "?unread" :show-target (lambda (target) "unread") - :action (lambda (docid msg target) (mu4e~proc-move docid nil "-S+u-N"))) + :action (lambda (docid msg target) (mu4e--server-move docid nil "-S+u-N"))) (unmark :char " " :prompt "unmark" @@ -299,7 +299,7 @@ The following marks are available, and the corresponding props: (when (or (file-directory-p fulltarget) (and (yes-or-no-p (format "%s does not exist. Create now?" fulltarget)) - (mu4e~proc-mkdir fulltarget))) + (mu4e--server-mkdir fulltarget))) target))) (defun mu4e~mark-ask-target (mark) diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el deleted file mode 100644 index 0dcd556..0000000 --- a/mu4e/mu4e-proc.el +++ /dev/null @@ -1,598 +0,0 @@ -;;; mu4e-proc.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- - -;; Copyright (C) 2011-2020 Dirk-Jan C. Binnema - -;; Author: Dirk-Jan C. Binnema -;; Maintainer: Dirk-Jan C. Binnema - -;; This file is not part of GNU Emacs. - -;; mu4e 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. - -;; mu4e 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 mu4e. If not, see . - -;;; Commentary: - -;;; Code: - -(require 'mu4e-helpers) -(require 'mu4e-meta) - - -;;; 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 -Specification." - :group 'mu4e - :type '(choice (const :tag "Default location" nil) - (directory :tag "Specify location")) - :safe 'stringp) - -(defcustom mu4e-mu-binary (executable-find "mu") - "Name of the mu-binary to use. -If it cannot be found in your PATH, you can specify the full -path." - :type 'file - :group 'mu4e - :safe 'stringp) - -(defcustom mu4e-mu-debug nil - "Whether to run the mu binary in debug-mode. -Setting this to t increases the amount of information in the log." - :type 'boolean - :group 'mu4e) - -(make-obsolete-variable - 'mu4e-maildir - "determined by server; see `mu4e-root-maildir'." "1.3.8") - -(defcustom mu4e-change-filenames-when-moving nil - "Change message file names when moving them. -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 -changes the filename. This latter behavior works better with some -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 -`mu4e~proc-filter' for the format.") - -(defvar mu4e-update-func nil - "Function called for each :update sexp returned. -The function is passed a msg sexp as argument. -See `mu4e~proc-filter' for the format.") - -(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 -`mu4e~proc-filter' for the format.") - -(defvar mu4e-header-func nil - "Function called for each message-header received. -The function is passed a msg plist as argument. See -`mu4e~proc-filter' for the format.") - -(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 -the number of matches. See `mu4e~proc-filter' for the format.") - -(defvar mu4e-erase-func nil - "Function called we receive an :erase sexp. -This before new headers are displayed, to clear the current -headers buffer. See `mu4e~proc-filter' for the format.") - -(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 -`mu4e~proc-filter' for the format of .") - -(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 - "A function called for each (:contacts () -sexp received from the server process.") - -(make-obsolete-variable 'mu4e-temp-func "No longer used" "1.7.0") - -;;; Internal vars - -(defvar mu4e~proc-buf nil - "Buffer (string) for data received from the backend.") -(defconst mu4e~proc-name " *mu4e-proc*" - "Name of the server process, buffer.") -(defvar mu4e~proc-process nil - "The mu-server process.") - -;; dealing with the length cookie that precedes expressions -(defconst mu4e~cookie-pre "\376" - "Each expression starts with a length cookie: -<`mu4e~cookie-pre'><`mu4e~cookie-post'>.") -(defconst mu4e~cookie-post "\377" - "Each expression starts with a length cookie: -<`mu4e~cookie-pre'><`mu4e~cookie-post'>.") -(defconst mu4e~cookie-matcher-rx - (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post) - "Regular expression matching the length cookie. -Match 1 will be the length (in hex).") - -(defun mu4e-running-p () - "Whether mu4e is running. -Checks whether the server process is live." - (and mu4e~proc-process - (memq (process-status mu4e~proc-process) - '(run open listen connect stop)) - t)) - -(defsubst mu4e~proc-eat-sexp-from-buf () - "'Eat' the next s-expression from `mu4e~proc-buf'. -Note: this is a string, not an emacs-buffer. `mu4e~proc-buf gets -its contents from the mu-servers in the following form: - <`mu4e~cookie-pre'><`mu4e~cookie-post'> -Function returns this sexp, or nil if there was none. -`mu4e~proc-buf' is updated as well, with all processed sexp data -removed." - (ignore-errors ;; the server may die in the middle... - ;; mu4e~cookie-matcher-rx: - ;; (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)]" mu4e~cookie-post) - (let ((b (string-match mu4e~cookie-matcher-rx mu4e~proc-buf)) - (sexp-len) (objcons)) - (when b - (setq sexp-len (string-to-number (match-string 1 mu4e~proc-buf) 16)) - ;; does mu4e~proc-buf contain the full sexp? - (when (>= (length mu4e~proc-buf) (+ sexp-len (match-end 0))) - ;; clear-up start - (setq mu4e~proc-buf (substring mu4e~proc-buf (match-end 0))) - ;; 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 - (substring mu4e~proc-buf 0 sexp-len) - 'utf-8 t))) - (when objcons - (setq mu4e~proc-buf (substring mu4e~proc-buf sexp-len)) - (car objcons))))))) - - -(defun mu4e~proc-filter (_proc str) - "Filter string STR from PROC. -This processes the 'mu server' output. It accumulates the -strings into valid sexps by checking of the ';;eox' `end-of-sexp' -marker, and then evaluating them. - -The server output is as follows: - - 1. an error - (:error 2 :message \"unknown command\") - ;; eox - => passed to `mu4e-error-func'. - - 2a. a message sexp looks something like: - \( - :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/1312254065_3.32282.pluto,4cd5bd4e9:2,\" - :priority high - :flags (new unread) - :attachments ((2 \"hello.jpg\" \"image/jpeg\") (3 \"laah.mp3\" \"audio/mp3\")) - :body-txt \" \" -\) -;; eox - => this will be passed to `mu4e-header-func'. - - 2b. After the list of message sexps has been returned (see 2a.), - we'll receive a sexp that looks like - (:found ) with n the number of messages found. The will be - passed to `mu4e-found-func'. - - 3. a view looks like: - (:view ) - => the (see 2.) will be passed to `mu4e-view-func'. - - 4. a database update looks like: - (:update :move ) - - => the (see 2.) will be passed to - `mu4e-update-func', :move tells us whether this is a move to - another maildir, or merely a flag change. - - 5. a remove looks like: - (:remove ) - => the docid will be passed to `mu4e-remove-func' - - 6. a compose looks like: - (:compose [:original] [:include ]) - `mu4e-compose-func'." - (mu4e-log 'misc "* Received %d byte(s)" (length str)) - (setq mu4e~proc-buf (concat mu4e~proc-buf str)) ;; update our buffer - (let ((sexp (mu4e~proc-eat-sexp-from-buf))) - (with-local-quit - (while sexp - (mu4e-log 'from-server "%S" sexp) - (cond - ;; a header plist can be recognized by the existence of a :date field - ((plist-get sexp :date) - (funcall mu4e-header-func sexp)) - - ;; 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) - (funcall mu4e-view-func (plist-get sexp :view))) - - ;; 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) - (setq mu4e--server-props (plist-get sexp :props)) - (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 - (plist-get sexp :update) - (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) - (plist-get sexp :original) - (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))) - - (setq sexp (mu4e~proc-eat-sexp-from-buf)))))) - -(defun mu4e~escape (str) - "Escape string STR for transport. -Put it in quotes, and escape existing quotation. In particular, -backslashes and double-quotes." - (let ((esc (replace-regexp-in-string "\\\\" "\\\\\\\\" str))) - (format "\"%s\"" (replace-regexp-in-string "\"" "\\\\\"" esc)))) - -(defun mu4e~proc-start () - "Start the mu server process." - - ;; sanity-check 1 - (unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary)) - (mu4e-error - "Cannot find mu, please set `mu4e-mu-binary' to the mu executable path")) - - ;; sanity-check 2 - (let ((version (let ((s (shell-command-to-string - (concat mu4e-mu-binary " --version")))) - (and (string-match "version \\([.0-9]+\\)" s) - (match-string 1 s))))) - (unless (string= version mu4e-mu-version) - (mu4e-error - (concat - "Found mu version %s, but mu4e needs version %s; please set `mu4e-mu-binary' " - "accordingly") version mu4e-mu-version))) - - (let* ((process-connection-type nil) ;; use a pipe - (args (when mu4e-mu-home `(,(format"--muhome=%s" mu4e-mu-home)))) - (args (if mu4e-mu-debug (cons "--debug" args) args)) - (args (cons "server" args))) - (setq mu4e~proc-buf "") - (setq mu4e~proc-process (apply 'start-process - mu4e~proc-name mu4e~proc-name - mu4e-mu-binary args)) - ;; register a function for (:info ...) sexps - (unless mu4e~proc-process - (mu4e-error "Failed to start the mu4e backend")) - (set-process-query-on-exit-flag mu4e~proc-process nil) - (set-process-coding-system mu4e~proc-process 'binary 'utf-8-unix) - (set-process-filter mu4e~proc-process 'mu4e~proc-filter) - (set-process-sentinel mu4e~proc-process 'mu4e~proc-sentinel))) - -(defun mu4e~proc-kill () - "Kill the mu server process." - (let* ((buf (get-buffer mu4e~proc-name)) - (proc (and (buffer-live-p buf) (get-buffer-process buf)))) - (when proc - (let ((delete-exited-processes t)) - (mu4e~call-mu '(quit))) - ;; try sending SIGINT (C-c) to process, so it can exit gracefully - (ignore-errors - (signal-process proc 'SIGINT)))) - (setq - mu4e~proc-process nil - mu4e~proc-buf nil)) - -;; error codes are defined in src/mu-util -;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") - -(defun mu4e~proc-sentinel (proc _msg) - "Function called when the server process PROC terminates with MSG." - (let ((status (process-status proc)) (code (process-exit-status proc))) - (setq mu4e~proc-process nil) - (setq mu4e~proc-buf "") ;; clear any half-received sexps - (cond - ((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"))))) - -(defun mu4e~call-mu (form) - "Call 'mu' with some command." - (unless (mu4e-running-p) (mu4e~proc-start)) - (let* ((print-length nil) (print-level nil) - (cmd (format "%S" form))) - (mu4e-log 'to-server "%s" cmd) - (process-send-string mu4e~proc-process (concat cmd "\n")))) - -(defun mu4e~docid-msgid-param (docid-or-msgid) - "Construct a backend parameter based on DOCID-OR-MSGID." - (if (stringp docid-or-msgid) - `(:msgid ,(mu4e~escape docid-or-msgid)) - `(:docid ,docid-or-msgid))) - -(defun mu4e~proc-add (path) - "Add the message at PATH to the database. -On success, we receive `'(:info add :path :docid )' -as well as `'(:update )`'; otherwise, we receive an error." - (mu4e~call-mu `(add :path ,path))) - -(defun mu4e~proc-compose (type decrypt &optional docid) - "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'." - (mu4e~call-mu `(compose - :type ,type - :decrypt ,(and decrypt t) - :docid ,docid))) - -(defun mu4e~proc-contacts (personal after tstamp) - "Ask for contacts with PERSONAL AFTER TSTAMP. -S-expression (:contacts () :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)." - (mu4e~call-mu `(contacts - :personal ,(and personal t) - :after ,(or after nil) - :tstamp ,(or tstamp nil)))) - -(defun mu4e~proc-extract (action docid index decrypt - &optional path what param) - "Perform ACTION on part with DOCID INDEX DECRYPT PATH WHAT PARAM. -Use a message with DOCID and perform ACTION on it (as symbol, -either `save', `open', `temp') which mean: -* save: save the part to PATH (a path) (non-optional for save) -* open: open the part with the default application registered for doing so -* temp: save to a temporary file, then respond with - (:temp :what :param )." - (mu4e~call-mu `(extract - :action ,action - :docid ,docid - :index ,index - :decrypt ,(and decrypt t) - :path ,path - :what ,what - :param ,param))) - -(defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups - include-related) - "Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED. -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 -SKIP-DUPS is non-nil, show only one of duplicate messages (see -`mu4e-headers-skip-duplicates'). If INCLUDE-RELATED is non-nil, -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 -function that will be called for, resp., a message (header row) -or an error." - (mu4e~call-mu `(find - :query ,query - :threads ,threads - :sortfield ,sortfield - :descending ,(if (eq sortdir 'descending) t nil) - :maxnum ,maxnum - :skip-dups ,skip-dups - :include-related ,include-related))) - -(defun mu4e~proc-index (&optional cleanup lazy-check) - "Index messages with possible CLEANUP and LAZY-CHECK." - (mu4e~call-mu `(index :cleanup ,cleanup :lazy-check ,lazy-check))) - -(defun mu4e~proc-mkdir (path) - "Create a new maildir-directory at filesystem PATH." - ;;(mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~escape path)) - (mu4e~call-mu `(mkdir :path ,path))) - - -(defun mu4e~proc-move (docid-or-msgid &optional maildir flags no-view) - "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. - -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. - -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: - 1. a list of flags such as '(passed replied seen) - 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 -non-nil, moving to a different maildir generates new names forq -the target files; this helps certain tools (such as mbsync). - -If NO-VIEW is non-nil, don't update the view. - -Returns either (:update ... ) or (:error ) sexp, which are handled my -`mu4e-update-func' and `mu4e-error-func', respectively." - (unless (or maildir flags) - (mu4e-error "At least one of maildir and flags must be specified")) - (unless (or (not maildir) - (file-exists-p (concat (mu4e-root-maildir) "/" maildir "/"))) - (mu4e-error "Target dir does not exist")) - (mu4e~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~proc-ping (&optional queries) - "Sends a ping to the mu server, expecting a (:pong ...) in response. -QUERIES is a list of queries for the number of results with read/unread status -are returned in the 'pong' response." - (mu4e~call-mu `(ping :queries ,queries))) - -(defun mu4e~proc-remove (docid) - "Remove message with DOCID. -The results are reporter through either (:update ... ) -or (:error) sexp, which are handled my `mu4e-error-func', -respectively." - (mu4e~call-mu `(remove :docid ,docid))) - -(defun mu4e~proc-sent (path) - "Add the message at PATH to the database. - - if this works, we will receive (:info add :path :docid - :fcc )." - (mu4e~call-mu `(sent :path ,path))) - -(defun mu4e~proc-view (docid-or-msgid &optional mark-as-read decrypt verify) - "Get a message DOCID-OR-MSGID. -Optionally, if MARK-AS-READ is non-nil, the backend marks the message as -read before returning, if it was not already unread. - DECRYPT and VERIFY if necessary. The result will be delivered to -the function registered as `mu4e-view-func'." - (mu4e~call-mu `(view - :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) - :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) - :mark-as-read ,mark-as-read - :extract-images nil ;; XXX remove - :decrypt ,(and decrypt t) ;; XXX remove - :verify ,(and verify t)))) ;; XXX remove - -(defun mu4e~proc-view-path (path &optional images decrypt verify) - "View message at PATH.. -Optionally, if IMAGES is non-nil, backend will any images -attached to the message, and return them as temp files. The -result will be delivered to the function registered as -`mu4e-view-func'. Optionally DECRYPT and VERIFY." - (mu4e~call-mu `(view - :path ,path - :extract-images ,(if images t nil) - :decrypt ,(and decrypt t) - :verify ,(and verify t)))) - -;;; _ -(provide 'mu4e-proc) -;;; mu4e-proc.el ends here diff --git a/mu4e/mu4e-server.el b/mu4e/mu4e-server.el new file mode 100644 index 0000000..c0aff6e --- /dev/null +++ b/mu4e/mu4e-server.el @@ -0,0 +1,553 @@ +;;; mu4e-server.el -- part of mu4e -*- lexical-binding: t -*- + +;; Copyright (C) 2011-2021 Dirk-Jan C. Binnema + +;; Author: Dirk-Jan C. Binnema +;; Maintainer: Dirk-Jan C. Binnema + +;; This file is not part of GNU Emacs. + +;; mu4e 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. + +;; mu4e 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 mu4e. If not, see . + +;;; Commentary: + +;;; Code: + +(require 'mu4e-helpers) +(require 'mu4e-meta) + + +;;; 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 +Specification." + :group 'mu4e + :type '(choice (const :tag "Default location" nil) + (directory :tag "Specify location")) + :safe 'stringp) + +(defcustom mu4e-mu-binary (executable-find "mu") + "Name of the mu-binary to use. +If it cannot be found in your PATH, you can specify the full +path." + :type 'file + :group 'mu4e + :safe 'stringp) + +(defcustom mu4e-mu-debug nil + "Whether to run the mu binary in debug-mode. +Setting this to t increases the amount of information in the log." + :type 'boolean + :group 'mu4e) + +(make-obsolete-variable + 'mu4e-maildir + "determined by server; see `mu4e-root-maildir'." "1.3.8") + +(defcustom mu4e-change-filenames-when-moving nil + "Change message file names when moving them. +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 +changes the filename. This latter behavior works better with some +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 +`mu4e--server-filter' for the format.") + +(defvar mu4e-update-func nil + "Function called for each :update sexp returned. +The function is passed a msg sexp as argument. +See `mu4e--server-filter' for the format.") + +(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 +`mu4e--server-filter' for the format.") + +(defvar mu4e-header-func nil + "Function called for each message-header received. +The function is passed a msg plist as argument. See +`mu4e--server-filter' for the format.") + +(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 +the number of matches. See `mu4e--server-filter' for the format.") + +(defvar mu4e-erase-func nil + "Function called we receive an :erase sexp. +This before new headers are displayed, to clear the current +headers buffer. See `mu4e--server-filter' for the format.") + +(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 +`mu4e--server-filter' for the format of .") + +(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 + "A function called for each (:contacts () +sexp received from the server process.") + +(make-obsolete-variable 'mu4e-temp-func "No longer used" "1.7.0") + +;;; Internal vars + +(defvar mu4e--server-buf nil + "Buffer (string) for data received from the backend.") +(defconst mu4e--server-name " *mu4e-server*" + "Name of the server process, buffer.") +(defvar mu4e--server-process nil + "The mu-server process.") + +;; dealing with the length cookie that precedes expressions +(defconst mu4e--server-cookie-pre "\376" + "Each expression starts with a length cookie: +<`mu4e--server-cookie-pre'><`mu4e--server-cookie-post'>.") +(defconst mu4e--server-cookie-post "\377" + "Each expression starts with a length cookie: +<`mu4e--server-cookie-pre'><`mu4e--server-cookie-post'>.") +(defconst mu4e--server-cookie-matcher-rx + (concat mu4e--server-cookie-pre "\\([[:xdigit:]]+\\)" + mu4e--server-cookie-post) + "Regular expression matching the length cookie. +Match 1 will be the length (in hex).") + +(defun mu4e-running-p () + "Whether mu4e is running. +Checks whether the server process is live." + (and mu4e--server-process + (memq (process-status mu4e--server-process) + '(run open listen connect stop)) + t)) + +(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 +its contents from the mu-servers in the following form: + <`mu4e--server-cookie-pre'><`mu4e--server-cookie-post'> +Function returns this sexp, or nil if there was none. +`mu4e--server-buf' is updated as well, with all processed sexp data +removed." + (ignore-errors ;; the server may die in the middle... + (let ((b (string-match mu4e--server-cookie-matcher-rx mu4e--server-buf)) + (sexp-len) (objcons)) + (when b + (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))) + ;; clear-up start + (setq mu4e--server-buf (substring mu4e--server-buf (match-end 0))) + ;; 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 + (substring mu4e--server-buf 0 sexp-len) + 'utf-8 t))) + (when objcons + (setq mu4e--server-buf (substring mu4e--server-buf sexp-len)) + (car objcons))))))) + + +(defun mu4e--server-filter (_proc str) + "Filter string STR from PROC. +This processes the 'mu server' output. It accumulates the +strings into valid sexps by checking of the ';;eox' `end-of-sexp' +marker, and then evaluating them. + +The server output is as follows: + + 1. an error + (:error 2 :message \"unknown command\") + ;; eox + => passed to `mu4e-error-func'. + + 2a. a message sexp looks something like: + \( + :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/1312254065_3.32282.pluto,4cd5bd4e9:2,\" + :priority high + :flags (new unread) + :attachments ((2 \"hello.jpg\" \"image/jpeg\") (3 \"laah.mp3\" \"audio/mp3\")) + :body-txt \" \" +\) +;; eox + => this will be passed to `mu4e-header-func'. + + 2b. After the list of message sexps has been returned (see 2a.), + we'll receive a sexp that looks like + (:found ) with n the number of messages found. The will be + passed to `mu4e-found-func'. + + 3. a view looks like: + (:view ) + => the (see 2.) will be passed to `mu4e-view-func'. + + 4. a database update looks like: + (:update :move ) + + => the (see 2.) will be passed to + `mu4e-update-func', :move tells us whether this is a move to + another maildir, or merely a flag change. + + 5. a remove looks like: + (:remove ) + => the docid will be passed to `mu4e-remove-func' + + 6. a compose looks like: + (:compose [:original] [:include ]) + `mu4e-compose-func'." + (mu4e-log 'misc "* Received %d byte(s)" (length str)) + (setq mu4e--server-buf (concat mu4e--server-buf str)) ;; update our buffer + (let ((sexp (mu4e--server-eat-sexp-from-buf))) + (with-local-quit + (while sexp + (mu4e-log 'from-server "%S" sexp) + (cond + ;; a header plist can be recognized by the existence of a :date field + ((plist-get sexp :date) + (funcall mu4e-header-func sexp)) + + ;; 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) + (funcall mu4e-view-func (plist-get sexp :view))) + + ;; 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) + (setq mu4e--server-props (plist-get sexp :props)) + (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 + (plist-get sexp :update) + (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) + (plist-get sexp :original) + (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))) + + (setq sexp (mu4e--server-eat-sexp-from-buf)))))) + +(defun mu4e--server-start () + "Start the mu server process." + ;; sanity-check 1 + (unless (and mu4e-mu-binary (file-executable-p mu4e-mu-binary)) + (mu4e-error + "Cannot find mu, please set `mu4e-mu-binary' to the mu executable path")) + + ;; sanity-check 2 + (let ((version (let ((s (shell-command-to-string + (concat mu4e-mu-binary " --version")))) + (and (string-match "version \\([.0-9]+\\)" s) + (match-string 1 s))))) + (unless (string= version mu4e-mu-version) + (mu4e-error + (concat + "Found mu version %s, but mu4e needs version %s" + "; please set `mu4e-mu-binary' " + "accordingly") version mu4e-mu-version))) + + (let* ((process-connection-type nil) ;; use a pipe + (args (when mu4e-mu-home `(,(format"--muhome=%s" mu4e-mu-home)))) + (args (if mu4e-mu-debug (cons "--debug" args) args)) + (args (cons "server" args))) + (setq mu4e--server-buf "") + (setq mu4e--server-process (apply 'start-process + mu4e--server-name mu4e--server-name + mu4e-mu-binary args)) + ;; register a function for (:info ...) sexps + (unless mu4e--server-process + (mu4e-error "Failed to start the mu4e backend")) + (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) + (set-process-sentinel mu4e--server-process 'mu4e--server-sentinel))) + +(defun mu4e--server-kill () + "Kill the mu server process." + (let* ((buf (get-buffer mu4e--server-name)) + (proc (and (buffer-live-p buf) (get-buffer-process buf)))) + (when proc + (let ((delete-exited-processes t)) + (mu4e--server-call-mu '(quit))) + ;; try sending SIGINT (C-c) to process, so it can exit gracefully + (ignore-errors + (signal-process proc 'SIGINT)))) + (setq + mu4e--server-process nil + mu4e--server-buf nil)) + +;; error codes are defined in src/mu-util +;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") + +(defun mu4e--server-sentinel (proc _msg) + "Function called when the server process PROC terminates with MSG." + (let ((status (process-status proc)) (code (process-exit-status proc))) + (setq mu4e--server-process nil) + (setq mu4e--server-buf "") ;; clear any half-received sexps + (cond + ((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"))))) + +(defun mu4e--server-call-mu (form) + "Call the mu server with some command FORM." + (unless (mu4e-running-p) (mu4e--server-start)) + (let* ((print-length nil) (print-level nil) + (cmd (format "%S" form))) + (mu4e-log 'to-server "%s" cmd) + (process-send-string mu4e--server-process (concat cmd "\n")))) + +(defun mu4e--server-add (path) + "Add the message at PATH to the database. +On success, we receive `'(:info add :path :docid )' +as well as `'(:update )`'; otherwise, we receive an error." + (mu4e--server-call-mu `(add :path ,path))) + +(defun mu4e--server-compose (type decrypt &optional docid) + "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'." + (mu4e--server-call-mu + `(compose + :type ,type + :decrypt ,(and decrypt t) + :docid ,docid))) + +(defun mu4e--server-contacts (personal after tstamp) + "Ask for contacts with PERSONAL AFTER TSTAMP. +S-expression (:contacts () :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)." + (mu4e--server-call-mu + `(contacts + :personal ,(and personal t) + :after ,(or after nil) + :tstamp ,(or tstamp nil)))) + +(defun mu4e--server-find (query threads sortfield sortdir maxnum skip-dups + include-related) + "Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED. +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 +SKIP-DUPS is non-nil, show only one of duplicate messages (see +`mu4e-headers-skip-duplicates'). If INCLUDE-RELATED is non-nil, +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 +function that will be called for, resp., a message (header row) +or an error." + (mu4e--server-call-mu + `(find + :query ,query + :threads ,threads + :sortfield ,sortfield + :descending ,(if (eq sortdir 'descending) t nil) + :maxnum ,maxnum + :skip-dups ,skip-dups + :include-related ,include-related))) + +(defun mu4e--server-index (&optional cleanup lazy-check) + "Index messages with possible CLEANUP and LAZY-CHECK." + (mu4e--server-call-mu `(index :cleanup ,cleanup :lazy-check ,lazy-check))) + +(defun mu4e--server-mkdir (path) + "Create a new maildir-directory at filesystem PATH." + (mu4e--server-call-mu `(mkdir :path ,path))) + +(defun mu4e--server-move (docid-or-msgid &optional maildir flags no-view) + "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. + +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. + +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: + 1. a list of flags such as '(passed replied seen) + 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 +non-nil, moving to a different maildir generates new names forq +the target files; this helps certain tools (such as mbsync). + +If NO-VIEW is non-nil, don't update the view. + +Returns either (:update ... ) or (:error ) sexp, which are handled my +`mu4e-update-func' and `mu4e-error-func', respectively." + (unless (or maildir flags) + (mu4e-error "At least one of maildir and flags must be specified")) + (unless (or (not maildir) + (file-exists-p (concat (mu4e-root-maildir) "/" maildir "/"))) + (mu4e-error "Target dir does not exist")) + (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) + "Sends a ping to the mu server, expecting a (:pong ...) in response. +QUERIES is a list of queries for the number of results with read/unread status +are returned in the 'pong' response." + (mu4e--server-call-mu `(ping :queries ,queries))) + +(defun mu4e--server-remove (docid) + "Remove message with DOCID. +The results are reporter through either (:update ... ) +or (:error) sexp, which are handled my `mu4e-error-func', +respectively." + (mu4e--server-call-mu `(remove :docid ,docid))) + +(defun mu4e--server-sent (path) + "Tell the mu server we sent a message at PATH. +If this works, we will receive (:info add :path :docid + :fcc )." + (mu4e--server-call-mu `(sent :path ,path))) + +(defun mu4e--server-view (docid-or-msgid &optional mark-as-read) + "Get a message DOCID-OR-MSGID. +Optionally, if MARK-AS-READ is non-nil, the backend marks the +message as read before returning, if it was not already unread. +The result will be delivered to the function registered as +`mu4e-view-func'." + (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) + :mark-as-read ,mark-as-read))) + + +(provide 'mu4e-server) +;;; mu4e-server.el ends here diff --git a/mu4e/mu4e-update.el b/mu4e/mu4e-update.el index 967f46c..82a56b1 100644 --- a/mu4e/mu4e-update.el +++ b/mu4e/mu4e-update.el @@ -28,7 +28,7 @@ ;;; Code: (require 'mu4e-helpers) -(require 'mu4e-proc) +(require 'mu4e-server) ;;; Customization @@ -194,7 +194,7 @@ However, if `mu4e-hide-index-messages' is non-nil, do not display anything." (defun mu4e-update-index () "Update the mu4e index." (interactive) - (mu4e~proc-index mu4e-index-cleanup mu4e-index-lazy-check)) + (mu4e--server-index mu4e-index-cleanup mu4e-index-lazy-check)) (defun mu4e-update-index-nonlazy () "Update the mu4e index non-lazily. diff --git a/mu4e/mu4e-view.el b/mu4e/mu4e-view.el index 38aca93..6ee96ff 100644 --- a/mu4e/mu4e-view.el +++ b/mu4e/mu4e-view.el @@ -43,7 +43,7 @@ (require 'mu4e-headers) (require 'mu4e-mark) (require 'mu4e-message) -(require 'mu4e-proc) +(require 'mu4e-server) (require 'mu4e-search) (require 'mu4e-utils) ;; utility functions (require 'mu4e-contacts) @@ -201,7 +201,6 @@ Then, display the results." (let ((path (mu4e-message-field (mu4e-message-at-point) :path))) (mu4e-process-file-through-pipe path cmd))) - (defmacro mu4e~view-in-headers-context (&rest body) "Evaluate BODY in the context of the headers buffer connected to this view." diff --git a/mu4e/mu4e.el b/mu4e/mu4e.el index f7f60cd..9421757 100644 --- a/mu4e/mu4e.el +++ b/mu4e/mu4e.el @@ -36,7 +36,7 @@ (require 'mu4e-bookmarks) (require 'mu4e-update) (require 'mu4e-main) -(require 'mu4e-proc) ;; communication with backend +(require 'mu4e-server) ;; communication with backend @@ -136,7 +136,7 @@ successful, call FUNC (if non-nil) afterwards." (unless (mu4e-context-current) (mu4e--context-autoswitch nil mu4e-context-policy)) (setq mu4e-pong-func (lambda (info) (mu4e--pong-handler info func))) - (mu4e~proc-ping + (mu4e--server-ping (mapcar ;; send it a list of queries we'd like to see read/unread info for (lambda (bm) (funcall (or mu4e-search-query-rewrite-function #'identity) @@ -158,7 +158,7 @@ successful, call FUNC (if non-nil) afterwards." (cancel-timer mu4e--update-timer) (setq mu4e--update-timer nil)) (mu4e-clear-caches) - (mu4e~proc-kill) + (mu4e--server-kill) ;; kill all mu4e buffers (mapc (lambda (buf) diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index 43ccce2..7965407 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -2287,7 +2287,7 @@ loading @t{mu4e}): ;; must come before proc-move since retag runs ;; 'sed' on the file (mu4e-action-retag-message msg "-\\Inbox") - (mu4e~proc-move docid nil "+S-u-N")))) + (mu4e--server-move docid nil "+S-u-N")))) @end lisp Adding to @code{mu4e-marks} list allows to use the mark in bulk operations @@ -5058,15 +5058,15 @@ to provide this information (this is implemented in @file{mu-cmd-server.c}). We start this sequence when @t{mu4e} is invoked (when the program is -started). It calls @t{mu4e-proc-ping}, and registers a (lambda) function for -@t{mu4e-proc-pong-func}, to handle the response. +started). It calls @t{mu4e-server-ping}, and registers a (lambda) function for +@t{mu4e-server-pong-func}, to handle the response. @verbatim -> (ping) <-(:pong "mu" :props (:version "x.x.x" :doccount 78545)) @end verbatim -When we receive such a @t{pong} (in @file{mu4e-proc.el}), the lambda +When we receive such a @t{pong} (in @file{mu4e-server.el}), the lambda function we registered is called, and it compares the version we got from the @t{pong} with the version we expected, and raises an error if they differ. -- cgit v1.0