diff --git a/lib/utils/mu-utils.cc b/lib/utils/mu-utils.cc index 9ebe801d..5c8e59e0 100644 --- a/lib/utils/mu-utils.cc +++ b/lib/utils/mu-utils.cc @@ -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 Mu::assert_equal(const std::string& s1, const std::string& s2) { diff --git a/lib/utils/mu-utils.hh b/lib/utils/mu-utils.hh index f5b2e61e..e9880657 100644 --- a/lib/utils/mu-utils.hh +++ b/lib/utils/mu-utils.hh @@ -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))); +/** + * 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 * with a 10-digit time_t diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index 0146bc99..703db945 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -696,7 +696,11 @@ mu_cmd_execute (MuConfig *opts, GError **err) case MU_CONFIG_CMD_TICKLE: merr = with_store (mu_cmd_tickle, opts, FALSE, err); break; 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: merr = MU_ERROR_IN_PARAMETERS; break; } diff --git a/mu/mu-config.c b/mu/mu-config.c index e5244387..cc8c92dc 100644 --- a/mu/mu-config.c +++ b/mu/mu-config.c @@ -441,8 +441,8 @@ config_options_group_server (void) GOptionEntry entries[] = { {"maildir", 'm', 0, G_OPTION_ARG_FILENAME, &MU_CONFIG.maildir, "top of the maildir", ""}, - {"list-commands", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.list_commands, - "overwrite existing files (false)", NULL}, + {"commands", 0, 0, G_OPTION_ARG_NONE, &MU_CONFIG.commands, + "list the available command and their parameters, then exit", NULL}, {NULL, 0, 0, 0, NULL, NULL, NULL} }; @@ -760,7 +760,6 @@ mu_config_uninit (MuConfig *opts) memset (opts, 0, sizeof(MU_CONFIG)); } - size_t mu_config_param_num (MuConfig *opts) { diff --git a/mu/mu-config.h b/mu/mu-config.h index 3d4f2814..d643676c 100644 --- a/mu/mu-config.h +++ b/mu/mu-config.h @@ -179,8 +179,8 @@ struct _MuConfig { gboolean play; /* after saving, try to 'play' * (open) the attmnt using xdgopen */ /* for server */ - gboolean list_commands; /* dump documentations for server - * commands */ + gboolean commands; /* dump documentations for server + * commands */ /* options for mu-script */ gchar *script; /* script to run */ diff --git a/mu4e/mu4e-main.el b/mu4e/mu4e-main.el index 9580c78d..68ce1a74 100644 --- a/mu4e/mu4e-main.el +++ b/mu4e/mu4e-main.el @@ -118,16 +118,9 @@ clicked." "* " (propertize "mu4e - mu for emacs version " 'face 'mu4e-title-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face) - - ;; show some server properties; in this case; a big C when there's - ;; crypto support, a big G when there's Guile support - " " - (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) + (propertize "; (in store: " 'face 'mu4e-title-face) + (propertize (format "%s" (plist-get mu4e~server-props :doccount)) 'face 'mu4e-header-key-face) + (propertize " messages)" 'face 'mu4e-title-face) "\n\n" (propertize " Basics\n\n" 'face 'mu4e-title-face) diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el index f395b961..1bf2b550 100644 --- a/mu4e/mu4e-proc.el +++ b/mu4e/mu4e-proc.el @@ -274,8 +274,7 @@ Start the process if needed." (proc (and (buffer-live-p buf) (get-buffer-process buf)))) (when proc (let ((delete-exited-processes t)) - ;; the mu server signal handler will make it quit after 'quit' - (mu4e~proc-send-command "cmd:quit")) + (mu4e~call-mu '(quit))) ;; try sending SIGINT (C-c) to process, so it can exit gracefully (ignore-errors (signal-process proc 'SIGINT)))) @@ -307,11 +306,18 @@ Start the process if needed." (t (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) "Construct a backend parameter based on DOCID-OR-MSGID." (if (stringp docid-or-msgid) - (concat "msgid:" (mu4e~escape docid-or-msgid)) - (format "docid:%d" docid-or-msgid))) + `(:msgid ,(mu4e~escape docid-or-msgid)) + `(:docid ,docid-or-msgid))) (defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups 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 function that will be called for, resp., a message (header row) or an error." - (mu4e~proc-send-command - (concat - "cmd:find query:%s threads:%s sortfield:%s reverse:%s maxnum:%d " - "skip-dups:%s include-related:%s") - (base64-encode-string (encode-coding-string query 'utf-8 t) t) - (if threads "true" "false") - ;; sortfield is e.g. ':subject'; this removes the ':' - (if (null sortfield) "nil" (substring (symbol-name sortfield) 1)) - ;; 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"))) + (mu4e~call-mu `(find + :query ,query + :threads ,threads +;; :sortfield ,sortfield + :sortdir ,sortdir + :maxnum ,maxnum + :skip-dups ,skip-dups + :include-related ,include-related))) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags no-view) "Move message identified by DOCID-OR-MSGID. @@ -394,40 +394,32 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my (rename (if (and maildir mu4e-change-filenames-when-moving) "true" "false"))) - (mu4e~proc-send-command "cmd:move %s %s %s %s %s" - idparam - (or flagstr "") - (or path "") - (format "newname:%s" rename) - (format "noview:%s" (if no-view "true" "false"))))) + (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) + :noview ,no-view)))) (defun mu4e~proc-index (path my-addresses cleanup lazy-check) "Index messages on PATH with possible CLEANUP and LAZY-CHECK. PATH should point to some maildir directory structure. MY-ADDRESSES is a list of 'my' email addresses (see `mu4e-user-mail-address-list')." - (let ((path (mu4e~escape path)) - (addrs (when my-addresses (mapconcat 'identity my-addresses ",")))) - (if addrs - (mu4e~proc-send-command - "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)))) + (mu4e~call-mu `(index + :my-addresses ,my-addresses + :cleanup ,cleanup + :lazy-check ,lazy-check))) (defun mu4e~proc-add (path maildir) "Add the message at PATH to the database. With MAILDIR set to the maildir this message resides in, e.g. '/drafts'; if this works, we will receive (:info add :path :docid ) as well as (:update )." - (mu4e~proc-send-command "cmd:add path:%s %s" - (mu4e~escape path) - (if maildir - (format "maildir:%s" (mu4e~escape maildir)) - ""))) + (mu4e~call-mu `(add + :path ,path + :maildir ,maildir))) (defun mu4e~proc-sent (path maildir) "Add the message at PATH to the database. @@ -436,9 +428,9 @@ e.g. '/drafts'. if this works, we will receive (:info add :path :docid :fcc )." - (mu4e~proc-send-command "cmd:sent path:%s maildir:%s" - (mu4e~escape path) (mu4e~escape maildir))) - + (mu4e~call-mu `(sent + :path ,path + :maildir ,maildir))) (defun mu4e~proc-compose (type decrypt &optional 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 `mu4e-compose-func'." - (unless (member type '(forward reply edit resend new)) - (mu4e-error "Unsupported compose-type %S" type)) - (unless (eq (null docid) (eq type 'new)) - (mu4e-error "`new' implies docid not-nil, and vice-versa")) - (mu4e~proc-send-command - "cmd:compose type:%s docid:%d extract-encrypted:%s use-agent:true" - (symbol-name type) docid (if decrypt "true" "false"))) + (mu4e~call-mu `(compose + :type ,type + :decrypt ,decrypt + :docid ,docid))) (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~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) - "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, 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 )." - (let ((cmd - (concat "cmd:extract " - (cl-case action - (save - (format "action:save docid:%d index:%d path:%s extract-encrypted:%s use-agent:true" - docid partidx (mu4e~escape path) (if decrypt "true" "false"))) - (open (format "action:open docid:%d index:%d extract-encrypted:%s use-agent:true" - docid partidx (if decrypt "true" "false"))) - (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))) + (mu4e~call-mu `(extract + :action ,action + :docid ,docid + :index ,index + :decrypt ,decrypt + :path ,path + :what ,what + :param ,param))) (defun mu4e~proc-ping () "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) "Ask for contacts with PERSONAL AFTER TSTAMP. @@ -498,11 +478,10 @@ 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~proc-send-command - "cmd:contacts personal:%s after:%d tstamp:%s" - (if personal "true" "false") - (or after 0) - (or tstamp "0"))) + (mu4e~call-mu `(contacts + :personal ,personal + ;; :after ,(or after nil) + :tstamp ,(or tstamp nil)))) (defun mu4e~proc-view (docid-or-msgid &optional images decrypt) "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 if necessary. The result will be delivered to the function registered as `mu4e-view-func'." - (mu4e~proc-send-command - "cmd:view %s extract-images:%s extract-encrypted:%s use-agent:true" - (mu4e~docid-msgid-param docid-or-msgid) - (if images "true" "false") - (if decrypt "true" "false"))) + (mu4e~call-mu `(view + :docid ,(if (stringp docid-or-msgid) nil docid-or-msgid) + :msgid ,(if (stringp docid-or-msgid) docid-or-msgid nil) + :extract-images ,images + :extract-encrypt ,decrypt))) (defun mu4e~proc-view-path (path &optional images decrypt) "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 result will be delivered to the function registered as `mu4e-view-func'. Optionally DECRYPT." - (mu4e~proc-send-command - "cmd:view path:%s extract-images:%s extract-encrypted:%s use-agent:true" - (mu4e~escape path) - (if images "true" "false") - (if decrypt "true" "false"))) + (mu4e~call-mu `(view-path + :path ,path + :images ,images + :decrypt ,decrypt))) (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~proc-send-command "cmd:remove docid:%d" docid)) + (mu4e~call-mu `(remove :docid ,docid))) (provide 'mu4e-proc) ;;; mu4e-proc.el ends here