mu4e: use the new command-parser

Update mu4e-proc to use the new mu4e <-> mu protocol
This commit is contained in:
Dirk-Jan C. Binnema 2020-01-19 17:23:24 +02:00
parent c71f683e39
commit d2ec85f01c
7 changed files with 116 additions and 104 deletions

View File

@ -445,6 +445,33 @@ Mu::size_to_string (const std::string& val, bool is_first)
} }
std::string
Mu::quoted (const std::string& str)
{
std::string res{"\""};
for (auto&& c : str) {
if (c == '\\' || c == '\"')
res += '\\';
res += c;
}
return res + '"';
}
// std::string
// Mu::quoted (const char* str)
// {
// if (!str)
// return str;
// char *s{g_strescape(str, NULL)};
// auto res = format("\"%s\"", s ? s : "");
// g_free(s);
// return res;
// }
void void
Mu::assert_equal(const std::string& s1, const std::string& s2) Mu::assert_equal(const std::string& s1, const std::string& s2)
{ {

View File

@ -93,6 +93,17 @@ std::string format (const char *frm, ...) __attribute__((format(printf, 1, 2)));
std::string format (const char *frm, va_list args) __attribute__((format(printf, 1, 0))); std::string format (const char *frm, va_list args) __attribute__((format(printf, 1, 0)));
/**
* Quote a string -- put in "" and escape any special characters by putting '\'
* in front of them.
*
* @param str
*
* @return
*/
std::string quoted (const std::string& str);
/** /**
* Convert an ISO date to the corresponding time expressed as a string * Convert an ISO date to the corresponding time expressed as a string
* with a 10-digit time_t * with a 10-digit time_t

View File

@ -696,7 +696,11 @@ mu_cmd_execute (MuConfig *opts, GError **err)
case MU_CONFIG_CMD_TICKLE: case MU_CONFIG_CMD_TICKLE:
merr = with_store (mu_cmd_tickle, opts, FALSE, err); break; merr = with_store (mu_cmd_tickle, opts, FALSE, err); break;
case MU_CONFIG_CMD_SERVER: case MU_CONFIG_CMD_SERVER:
merr = with_store (mu_cmd_server, opts, FALSE, err); break; if (opts->commands)
merr = mu_cmd_server (NULL, opts, err);
else
merr = with_store (mu_cmd_server, opts, FALSE, err);
break;
default: default:
merr = MU_ERROR_IN_PARAMETERS; break; merr = MU_ERROR_IN_PARAMETERS; break;
} }

View File

@ -441,8 +441,8 @@ config_options_group_server (void)
GOptionEntry entries[] = { GOptionEntry entries[] = {
{"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir, {"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir,
"top of the maildir", "<maildir>"}, "top of the maildir", "<maildir>"},
{"list-commands", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.list_commands, {"commands", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.commands,
"overwrite existing files (false)", NULL}, "list the available command and their parameters, then exit", NULL},
{NULL, 0, 0, 0, NULL, NULL, NULL} {NULL, 0, 0, 0, NULL, NULL, NULL}
}; };
@ -760,7 +760,6 @@ mu_config_uninit (MuConfig *opts)
memset (opts, 0, sizeof(MU_CONFIG)); memset (opts, 0, sizeof(MU_CONFIG));
} }
size_t size_t
mu_config_param_num (MuConfig *opts) mu_config_param_num (MuConfig *opts)
{ {

View File

@ -179,8 +179,8 @@ struct _MuConfig {
gboolean play; /* after saving, try to 'play' gboolean play; /* after saving, try to 'play'
* (open) the attmnt using xdgopen */ * (open) the attmnt using xdgopen */
/* for server */ /* for server */
gboolean list_commands; /* dump documentations for server gboolean commands; /* dump documentations for server
* commands */ * commands */
/* options for mu-script */ /* options for mu-script */
gchar *script; /* script to run */ gchar *script; /* script to run */

View File

@ -118,16 +118,9 @@ clicked."
"* " "* "
(propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face) (propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face)
(propertize mu4e-mu-version 'face 'mu4e-header-key-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face)
(propertize "; (in store: " 'face 'mu4e-title-face)
;; show some server properties; in this case; a big C when there's (propertize (format "%s" (plist-get mu4e~server-props :doccount)) 'face 'mu4e-header-key-face)
;; crypto support, a big G when there's Guile support (propertize " messages)" 'face 'mu4e-title-face)
" "
(propertize
(concat
(when (plist-get mu4e~server-props :crypto) "C")
(when (plist-get mu4e~server-props :guile) "G")
(when (plist-get mu4e~server-props :mux) "X"))
'face 'mu4e-title-face)
"\n\n" "\n\n"
(propertize " Basics\n\n" 'face 'mu4e-title-face) (propertize " Basics\n\n" 'face 'mu4e-title-face)

View File

@ -274,8 +274,7 @@ Start the process if needed."
(proc (and (buffer-live-p buf) (get-buffer-process buf)))) (proc (and (buffer-live-p buf) (get-buffer-process buf))))
(when proc (when proc
(let ((delete-exited-processes t)) (let ((delete-exited-processes t))
;; the mu server signal handler will make it quit after 'quit' (mu4e~call-mu '(quit)))
(mu4e~proc-send-command "cmd:quit"))
;; try sending SIGINT (C-c) to process, so it can exit gracefully ;; try sending SIGINT (C-c) to process, so it can exit gracefully
(ignore-errors (ignore-errors
(signal-process proc 'SIGINT)))) (signal-process proc 'SIGINT))))
@ -307,11 +306,18 @@ Start the process if needed."
(t (t
(error "Something bad happened to the mu server process"))))) (error "Something bad happened to the mu server process")))))
(defun mu4e~call-mu (form)
"Call 'mu' with some command."
(unless (mu4e~proc-running-p) (mu4e~proc-start))
(let ((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) (defun mu4e~docid-msgid-param (docid-or-msgid)
"Construct a backend parameter based on DOCID-OR-MSGID." "Construct a backend parameter based on DOCID-OR-MSGID."
(if (stringp docid-or-msgid) (if (stringp docid-or-msgid)
(concat "msgid:" (mu4e~escape docid-or-msgid)) `(:msgid ,(mu4e~escape docid-or-msgid))
(format "docid:%d" docid-or-msgid))) `(:docid ,docid-or-msgid)))
(defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups (defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups
include-related) include-related)
@ -330,20 +336,14 @@ For each result found, a function is called, depending on the
kind of result. The variables `mu4e-error-func' contain the kind of result. The variables `mu4e-error-func' contain the
function that will be called for, resp., a message (header row) function that will be called for, resp., a message (header row)
or an error." or an error."
(mu4e~proc-send-command (mu4e~call-mu `(find
(concat :query ,query
"cmd:find query:%s threads:%s sortfield:%s reverse:%s maxnum:%d " :threads ,threads
"skip-dups:%s include-related:%s") ;; :sortfield ,sortfield
(base64-encode-string (encode-coding-string query 'utf-8 t) t) :sortdir ,sortdir
(if threads "true" "false") :maxnum ,maxnum
;; sortfield is e.g. ':subject'; this removes the ':' :skip-dups ,skip-dups
(if (null sortfield) "nil" (substring (symbol-name sortfield) 1)) :include-related ,include-related)))
;; TODO: use ascending/descending in backend too (it's clearer than
;; 'reverse'
(if (eq sortdir 'descending) "true" "false")
(if maxnum maxnum -1)
(if skip-dups "true" "false")
(if include-related "true" "false")))
(defun mu4e~proc-move (docid-or-msgid &optional maildir flags no-view) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags no-view)
"Move message identified by DOCID-OR-MSGID. "Move message identified by DOCID-OR-MSGID.
@ -394,40 +394,32 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my
(rename (rename
(if (and maildir mu4e-change-filenames-when-moving) (if (and maildir mu4e-change-filenames-when-moving)
"true" "false"))) "true" "false")))
(mu4e~proc-send-command "cmd:move %s %s %s %s %s" (mu4e~call-mu `(move
idparam :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
(or flagstr "") :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
(or path "") :flags ,(or flags nil)
(format "newname:%s" rename) :maildir ,(or maildir nil)
(format "noview:%s" (if no-view "true" "false"))))) :rename ,(and maildir mu4e-change-filenames-when-moving)
:noview ,no-view))))
(defun mu4e~proc-index (path my-addresses cleanup lazy-check) (defun mu4e~proc-index (path my-addresses cleanup lazy-check)
"Index messages on PATH with possible CLEANUP and LAZY-CHECK. "Index messages on PATH with possible CLEANUP and LAZY-CHECK.
PATH should point to some maildir directory structure. PATH should point to some maildir directory structure.
MY-ADDRESSES is a list of 'my' email addresses (see MY-ADDRESSES is a list of 'my' email addresses (see
`mu4e-user-mail-address-list')." `mu4e-user-mail-address-list')."
(let ((path (mu4e~escape path)) (mu4e~call-mu `(index
(addrs (when my-addresses (mapconcat 'identity my-addresses ",")))) :my-addresses ,my-addresses
(if addrs :cleanup ,cleanup
(mu4e~proc-send-command :lazy-check ,lazy-check)))
"cmd:index path:%s my-addresses:%s cleanup:%s lazy-check:%s contacts:%s"
path
addrs
(if cleanup "true" : "false")
(if lazy-check "true")
(if mu4e-compose-complete-addresses "true"))
(mu4e~proc-send-command "cmd:index path:%s" path))))
(defun mu4e~proc-add (path maildir) (defun mu4e~proc-add (path maildir)
"Add the message at PATH to the database. "Add the message at PATH to the database.
With MAILDIR set to the maildir this message resides in, With MAILDIR set to the maildir this message resides in,
e.g. '/drafts'; if this works, we will receive (:info add :path e.g. '/drafts'; if this works, we will receive (:info add :path
<path> :docid <docid>) as well as (:update <msg-sexp>)." <path> :docid <docid>) as well as (:update <msg-sexp>)."
(mu4e~proc-send-command "cmd:add path:%s %s" (mu4e~call-mu `(add
(mu4e~escape path) :path ,path
(if maildir :maildir ,maildir)))
(format "maildir:%s" (mu4e~escape maildir))
"")))
(defun mu4e~proc-sent (path maildir) (defun mu4e~proc-sent (path maildir)
"Add the message at PATH to the database. "Add the message at PATH to the database.
@ -436,9 +428,9 @@ e.g. '/drafts'.
if this works, we will receive (:info add :path <path> :docid if this works, we will receive (:info add :path <path> :docid
<docid> :fcc <path>)." <docid> :fcc <path>)."
(mu4e~proc-send-command "cmd:sent path:%s maildir:%s" (mu4e~call-mu `(sent
(mu4e~escape path) (mu4e~escape maildir))) :path ,path
:maildir ,maildir)))
(defun mu4e~proc-compose (type decrypt &optional docid) (defun mu4e~proc-compose (type decrypt &optional docid)
"Compose a message of TYPE, DECRYPT it and use DOCID. "Compose a message of TYPE, DECRYPT it and use DOCID.
@ -448,49 +440,37 @@ editing, resending) with DOCID or nil for type `new'.
The result is delivered to the function registered as The result is delivered to the function registered as
`mu4e-compose-func'." `mu4e-compose-func'."
(unless (member type '(forward reply edit resend new)) (mu4e~call-mu `(compose
(mu4e-error "Unsupported compose-type %S" type)) :type ,type
(unless (eq (null docid) (eq type 'new)) :decrypt ,decrypt
(mu4e-error "`new' implies docid not-nil, and vice-versa")) :docid ,docid)))
(mu4e~proc-send-command
"cmd:compose type:%s docid:%d extract-encrypted:%s use-agent:true"
(symbol-name type) docid (if decrypt "true" "false")))
(defun mu4e~proc-mkdir (path) (defun mu4e~proc-mkdir (path)
"Create a new maildir-directory at filesystem PATH." "Create a new maildir-directory at filesystem PATH."
(mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~escape path))) ;;(mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~escape path))
(mu4e~call-mu `(mkdir :path ,path)))
(defun mu4e~proc-extract (action docid partidx decrypt (defun mu4e~proc-extract (action docid index decrypt
&optional path what param) &optional path what param)
"Perform ACTION on part with DOCID PARTIDX DECRYPT 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, Use a message with DOCID and perform ACTION on it (as symbol,
either `save', `open', `temp') which mean: * save: save the part either `save', `open', `temp') which mean: * save: save the part
to PATH (a path) (non-optional for save)$ * open: open the part to PATH (a path) (non-optional for save)$ * open: open the part
with the default application registered for doing so * temp: save with the default application registered for doing so * temp: save
to a temporary file, then respond with to a temporary file, then respond with
(:temp <path> :what <what> :param <param>)." (:temp <path> :what <what> :param <param>)."
(let ((cmd (mu4e~call-mu `(extract
(concat "cmd:extract " :action ,action
(cl-case action :docid ,docid
(save :index ,index
(format "action:save docid:%d index:%d path:%s extract-encrypted:%s use-agent:true" :decrypt ,decrypt
docid partidx (mu4e~escape path) (if decrypt "true" "false"))) :path ,path
(open (format "action:open docid:%d index:%d extract-encrypted:%s use-agent:true" :what ,what
docid partidx (if decrypt "true" "false"))) :param ,param)))
(temp
(format "action:temp docid:%d index:%d what:%s%s extract-encrypted:%s use-agent:true"
docid partidx what
(if param
(if (stringp param)
(format " param:%s" (mu4e~escape param))
(format " param:%S" param)) "") (if decrypt "true" "false")))
(otherwise (mu4e-error "Unsupported action %S" action))))
))
(mu4e~proc-send-command "%s" cmd)))
(defun mu4e~proc-ping () (defun mu4e~proc-ping ()
"Sends a ping to the mu server, expecting a (:pong ...) in response." "Sends a ping to the mu server, expecting a (:pong ...) in response."
(mu4e~proc-send-command "cmd:ping")) (mu4e~call-mu '(ping)))
(defun mu4e~proc-contacts (personal after tstamp) (defun mu4e~proc-contacts (personal after tstamp)
"Ask for contacts with PERSONAL AFTER TSTAMP. "Ask for contacts with PERSONAL AFTER TSTAMP.
@ -498,11 +478,10 @@ S-expression (:contacts (<list>) :tstamp \"<tstamp>\") is expected in
response. If PERSONAL is non-nil, only get personal contacts, if response. If PERSONAL is non-nil, only get personal contacts, if
AFTER is non-nil, get only contacts seen AFTER (the time_t AFTER is non-nil, get only contacts seen AFTER (the time_t
value)." value)."
(mu4e~proc-send-command (mu4e~call-mu `(contacts
"cmd:contacts personal:%s after:%d tstamp:%s" :personal ,personal
(if personal "true" "false") ;; :after ,(or after nil)
(or after 0) :tstamp ,(or tstamp nil))))
(or tstamp "0")))
(defun mu4e~proc-view (docid-or-msgid &optional images decrypt) (defun mu4e~proc-view (docid-or-msgid &optional images decrypt)
"Get a message DOCID-OR-MSGID. "Get a message DOCID-OR-MSGID.
@ -510,11 +489,11 @@ Optionally, if IMAGES is non-nil, backend will any images
attached to the message, and return them as temp files. DECRYPT attached to the message, and return them as temp files. DECRYPT
if necessary. The result will be delivered to the function if necessary. The result will be delivered to the function
registered as `mu4e-view-func'." registered as `mu4e-view-func'."
(mu4e~proc-send-command (mu4e~call-mu `(view
"cmd:view %s extract-images:%s extract-encrypted:%s use-agent:true" :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid)
(mu4e~docid-msgid-param docid-or-msgid) :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil)
(if images "true" "false") :extract-images ,images
(if decrypt "true" "false"))) :extract-encrypt ,decrypt)))
(defun mu4e~proc-view-path (path &optional images decrypt) (defun mu4e~proc-view-path (path &optional images decrypt)
"View message at PATH.. "View message at PATH..
@ -522,18 +501,17 @@ Optionally, if IMAGES is non-nil, backend will any images
attached to the message, and return them as temp files. The attached to the message, and return them as temp files. The
result will be delivered to the function registered as result will be delivered to the function registered as
`mu4e-view-func'. Optionally DECRYPT." `mu4e-view-func'. Optionally DECRYPT."
(mu4e~proc-send-command (mu4e~call-mu `(view-path
"cmd:view path:%s extract-images:%s extract-encrypted:%s use-agent:true" :path ,path
(mu4e~escape path) :images ,images
(if images "true" "false") :decrypt ,decrypt)))
(if decrypt "true" "false")))
(defun mu4e~proc-remove (docid) (defun mu4e~proc-remove (docid)
"Remove message with DOCID. "Remove message with DOCID.
The results are reporter through either (:update ... ) The results are reporter through either (:update ... )
or (:error) sexp, which are handled my `mu4e-error-func', or (:error) sexp, which are handled my `mu4e-error-func',
respectively." respectively."
(mu4e~proc-send-command "cmd:remove docid:%d" docid)) (mu4e~call-mu `(remove :docid ,docid)))
(provide 'mu4e-proc) (provide 'mu4e-proc)
;;; mu4e-proc.el ends here ;;; mu4e-proc.el ends here