mirror of https://github.com/djcb/mu.git
mu4e: use the new command-parser
Update mu4e-proc to use the new mu4e <-> mu protocol
This commit is contained in:
parent
c71f683e39
commit
d2ec85f01c
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -179,7 +179,7 @@ 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 */
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue