diff options
| author | Piyush <pshsrv@protonmail.com> | 2019-05-11 18:13:36 +0530 |
|---|---|---|
| committer | Piyush <pshsrv@protonmail.com> | 2019-05-11 18:13:36 +0530 |
| commit | c4a23bfb6687cb79014adebcc409a44f262cb77c (patch) | |
| tree | 2dd279fb89bf67e293a040b3ccc1375f3a3d5e71 /mu4e | |
| parent | c4e037c16fb4705d3be5c9bd228540e81379b198 (diff) | |
| parent | 7563b89c9cda397d43dd3d8d985a677bb3c8495f (diff) | |
Merge branch 'master' of github:djcb/mu
Diffstat (limited to 'mu4e')
| -rw-r--r-- | mu4e/mu4e-draft.el | 39 | ||||
| -rw-r--r-- | mu4e/mu4e-proc.el | 420 | ||||
| -rw-r--r-- | mu4e/mu4e-utils.el | 163 | ||||
| -rw-r--r-- | mu4e/mu4e-vars.el | 273 | ||||
| -rw-r--r-- | mu4e/mu4e.texi | 93 |
5 files changed, 474 insertions, 514 deletions
diff --git a/mu4e/mu4e-draft.el b/mu4e/mu4e-draft.el index f514b01..998a3ca 100644 --- a/mu4e/mu4e-draft.el +++ b/mu4e/mu4e-draft.el @@ -262,6 +262,45 @@ message. Return nil if there are no recipients for the particular field." (otherwise (mu4e-error "Unsupported field"))))) +;;; RFC2822 handling of phrases in mail-addresses +;;; The optional display-name contains a phrase, it sits before the angle-addr +;;; as specified in RFC2822 for email-addresses in header fields. +;;; contributed by jhelberg + +(defun mu4e~rfc822-phrase-type (ph) + "Return either atom, quoted-string, a corner-case or nil. This + checks for empty string first. Then quotes around the phrase + (returning 'rfc822-quoted-string). Then whether there is a quote + inside the phrase (returning 'rfc822-containing-quote). + The reverse of the RFC atext definition is then tested. + If it matches, nil is returned, if not, it is an 'rfc822-atom, which + is returned." + (cond + ((= (length ph) 0) 'rfc822-empty) + ((= (aref ph 0) ?\") + (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) + 'rfc822-quoted-string + 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one + ((string-match-p "[\"]" ph) 'rfc822-containing-quote) + ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) + (t 'rfc822-atom))) + +(defun mu4e~rfc822-quoteit (ph) + "Quote RFC822 phrase only if necessary. + Atoms and quoted strings don't need quotes. The rest do. In + case a phrase contains a quote, it will be escaped." + (let ((type (mu4e~rfc822-phrase-type ph))) + (cond + ((eq type 'rfc822-atom) ph) + ((eq type 'rfc822-quoted-string) ph) + ((eq type 'rfc822-containing-quote) + (format "\"%s\"" + (replace-regexp-in-string "\"" "\\\\\"" ph))) + (t (format "\"%s\"" ph))))) + + + + (defun mu4e~draft-from-construct () "Construct a value for the From:-field of the reply to MSG, diff --git a/mu4e/mu4e-proc.el b/mu4e/mu4e-proc.el index 9190ab2..7ddbe80 100644 --- a/mu4e/mu4e-proc.el +++ b/mu4e/mu4e-proc.el @@ -1,6 +1,6 @@ ;; mu4e-proc.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2017 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> @@ -37,13 +37,11 @@ ;; dealing with the length cookie that precedes expressions (defconst mu4e~cookie-pre "\376" - "Each expression we get from the backend (mu server) starts with -a length cookie: - <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") + "Each expression starts with a length cookie: +<`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") (defconst mu4e~cookie-post "\377" - "Each expression we get from the backend (mu server) starts with -a length cookie: - <`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") + "Each expression starts with a length cookie: +<`mu4e~cookie-pre'><length-in-hex><`mu4e~cookie-post'>.") (defconst mu4e~cookie-matcher-rx (concat mu4e~cookie-pre "\\([[:xdigit:]]+\\)" mu4e~cookie-post) "Regular expression matching the length cookie. @@ -56,12 +54,11 @@ Match 1 will be the length (in hex).") (defvar mu4e~proc-process nil "The mu-server process.") - (defun mu4e~proc-running-p () "Whether the mu process is running." (when (and mu4e~proc-process - (memq (process-status mu4e~proc-process) - '(run open listen connect stop))) + (memq (process-status mu4e~proc-process) + '(run open listen connect stop))) t)) (defsubst mu4e~proc-eat-sexp-from-buf () @@ -76,36 +73,37 @@ removed." ;; 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)) + (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))))))) + (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) - "A process-filter for the 'mu server' output. -It accumulates the strings into valid sexps by checking of the -';;eox' end-of-sexp marker, and then evaluating them. + "Filter string STR from PROC. +This process the 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 - => this will be passed to `mu4e-error-func'. + => passed to `mu4e-error-func'. 2a. a message sexp looks something like: \( @@ -156,92 +154,93 @@ The server output is as follows: (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) - (funcall mu4e-pong-func - (plist-get sexp :props))) - - ;; received a contacts message - ;; note: we use 'member', to match (:contacts nil) - ((plist-member sexp :contacts) - (funcall mu4e-contacts-func - (plist-get sexp :contacts))) - - ;; something got moved/flags changed - ((plist-get sexp :update) + (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) + (funcall mu4e-pong-func + (plist-get sexp :props))) + + ;; 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))) - - ;; do something with a temporary file - ((plist-get sexp :temp) - (funcall mu4e-temp-func - (plist-get sexp :temp) ;; name of the temp file - (plist-get sexp :what) ;; what to do with it - ;; (pipe|emacs|open-with...) - (plist-get sexp :docid) ;; docid of the message - (plist-get sexp :param)));; parameter for the action - - ;; 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)))))) + (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))) + + ;; do something with a temporary file + ((plist-get sexp :temp) + (funcall mu4e-temp-func + (plist-get sexp :temp) ;; name of the temp file + (plist-get sexp :what) ;; what to do with it + ;; (pipe|emacs|open-with...) + (plist-get sexp :docid) ;; docid of the message + (plist-get sexp :param)));; parameter for the action + + ;; 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 for transport -- put it in quotes, and escape -existing quotation. In particular, backslashes and -double-quotes." + "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-send-command (frm &rest args) - "Send as command to the mu server process. + "Send string from FRM and ARGS to the mu server process. Start the process if needed." (unless (mu4e~proc-running-p) (mu4e~proc-start)) @@ -254,13 +253,13 @@ Start the process if needed." (unless (file-executable-p mu4e-mu-binary) (mu4e-error (format "`mu4e-mu-binary' (%S) not found" mu4e-mu-binary))) (let* ((process-connection-type nil) ;; use a pipe - (args '("server")) - (args (append args (when mu4e-mu-home - (list (concat "--muhome=" mu4e-mu-home)))))) + (args '("server")) + (args (append args (when mu4e-mu-home + (list (concat "--muhome=" mu4e-mu-home)))))) (setq mu4e~proc-buf "") (setq mu4e~proc-process (apply 'start-process - mu4e~proc-name mu4e~proc-name - mu4e-mu-binary args)) + 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")) @@ -272,14 +271,14 @@ Start the process if needed." (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)))) + (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")) - ;; try sending SIGINT (C-c) to process, so it can exit gracefully + ;; the mu server signal handler will make it quit after 'quit' + (mu4e~proc-send-command "cmd:quit")) + ;; try sending SIGINT (C-c) to process, so it can exit gracefully (ignore-errors - (signal-process proc 'SIGINT)))) + (signal-process proc 'SIGINT)))) (setq mu4e~proc-process nil mu4e~proc-buf nil)) @@ -288,29 +287,29 @@ Start the process if needed." ;;(defconst mu4e-xapian-empty 19 "Error code: xapian is empty/non-existent") (defun mu4e~proc-sentinel (proc msg) - "Function that will be called when the mu-server process terminates." + "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))))) + (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 11) - (error "Database is locked by another process")) - ((eq code 15) - (error "Database needs upgrade; try `mu index --rebuild'")) - ((eq code 19) - (error "Database empty; try indexing some messages")) - (t (error "mu server process ended with exit code %d" code)))) + (cond + ((eq code 0) + (message nil)) ;; don't do anything + ((eq code 11) + (error "Database is locked by another process")) + ((eq code 15) + (error "Database needs upgrade; try `mu index --rebuild'")) + ((eq code 19) + (error "Database empty; try indexing some messages")) + (t (error "Mu server process ended with exit code %d" code)))) (t - (error "Something bad happened to the mu server process"))))) + (error "Something bad happened to the mu server process"))))) (defun mu4e~docid-msgid-param (docid-or-msgid) "Construct a backend parameter based on DOCID-OR-MSGID." @@ -319,15 +318,15 @@ Start the process if needed." (format "docid:%d" docid-or-msgid))) (defun mu4e~proc-find (query threads sortfield sortdir maxnum skip-dups - include-related) - "Start a database query for QUERY. + include-related) + "Run QUERY with THREADS SORTFIELD SORTDIR MAXNUM SKIP-DUPS INCLUDE-RELATED. If THREADS is non-nil, show results in threaded fasion, 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, +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'). @@ -351,9 +350,9 @@ or an error." (if include-related "true" "false"))) (defun mu4e~proc-move (docid-or-msgid &optional maildir flags no-view) - "Move message identified by DOCID-OR-MSGID to optional MAILDIR -and optionally setting FLAGS. If MAILDIR is nil, message will be -moved within the same maildir. + "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 @@ -386,19 +385,19 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my (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-maildir "/" maildir "/"))) + (file-exists-p (concat mu4e-maildir "/" maildir "/"))) (mu4e-error "Target dir does not exist")) (let* ((idparam (mu4e~docid-msgid-param docid-or-msgid)) - (flagstr - (when flags - (concat " flags:" - (if (stringp flags) flags (mu4e-flags-to-string flags))))) - (path - (when maildir - (format " maildir:%s" (mu4e~escape maildir)))) - (rename - (if (and maildir mu4e-change-filenames-when-moving) - "true" "false"))) + (flagstr + (when flags + (concat " flags:" + (if (stringp flags) flags (mu4e-flags-to-string flags))))) + (path + (when maildir + (format " maildir:%s" (mu4e~escape maildir)))) + (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 "") @@ -407,15 +406,20 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my (format "noview:%s" (if no-view "true" "false"))))) (defun mu4e~proc-index (path my-addresses cleanup lazy-check) - "Update the message database for filesystem PATH, which should -point to some maildir directory structure. MY-ADDRESSES is a list -of 'my' email addresses (see `mu4e-user-mail-address-list')." + "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 ",")))) + (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" - path addrs (if cleanup "true" : "false") (if lazy-check "true")) + "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) @@ -441,12 +445,12 @@ e.g. '/drafts'. (defun mu4e~proc-compose (type decrypt &optional docid) - "Start composing a message of certain TYPE (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'. + "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 will be delivered to the function registered as +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)) @@ -461,54 +465,55 @@ The result will be delivered to the function registered as (mu4e~proc-send-command "cmd:mkdir path:%s" (mu4e~escape path))) (defun mu4e~proc-extract (action docid partidx decrypt - &optional path what param) - "Extract an attachment with index PARTIDX from message with DOCID -and perform ACTION on it (as symbol, either `save', `open', `temp') which -mean: - * save: save the part to PARAM1 (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 + &optional path what param) + "Perform ACTION on part with DOCID PARTIDX 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 <path> :what <what> :param <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)))) - )) + (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))) - (defun mu4e~proc-ping () "Sends a ping to the mu server, expecting a (:pong ...) in response." (mu4e~proc-send-command "cmd:ping")) -(defun mu4e~proc-contacts (personal after) - "Sends the contacts command to the mu server. -A (:contacts (<list>)) 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)." +(defun mu4e~proc-contacts (personal after tstamp) + "Ask for contacts with PERSONAL AFTER TSTAMP. +S-expression (:contacts (<list>) :tstamp \"<tstamp>\") is expected in +response. If PERSONAL is non-nil, only get personal contacts, if +AFTER is non-nil, get only contacts seen AFTER (the time_t +value)." (mu4e~proc-send-command - "cmd:contacts personal:%s after:%d" + "cmd:contacts personal:%s after:%d tstamp:%s" (if personal "true" "false") - (or after 0))) + (or after 0) + (or tstamp "0"))) (defun mu4e~proc-view (docid-or-msgid &optional images decrypt) - "Get one particular message based on its DOCID-OR-MSGID. + "Get a message DOCID-OR-MSGID. 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'." +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) @@ -516,11 +521,11 @@ The result will be delivered to the function registered as (if decrypt "true" "false"))) (defun mu4e~proc-view-path (path &optional images decrypt) - "View message at PATH (keyword argument). + "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'." +`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) @@ -528,12 +533,11 @@ result will be delivered to the function registered as (if decrypt "true" "false"))) (defun mu4e~proc-remove (docid) - "Remove message identified by docid. -The results are reporter through either (:update ... ) or (:error) -sexp, which are handled my `mu4e-error-func', respectively." + "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)) - (provide 'mu4e-proc) - -;; End of mu4e-proc.el +;;; mu4e-proc.el ends here diff --git a/mu4e/mu4e-utils.el b/mu4e/mu4e-utils.el index 72fe2c7..ae43a33 100644 --- a/mu4e/mu4e-utils.el +++ b/mu4e/mu4e-utils.el @@ -1,7 +1,6 @@ ;;; mu4e-utils.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2017 Dirk-Jan C. Binnema -;; Copyright (C) 2013 Tibor Simko +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> @@ -666,7 +665,8 @@ process." "Indexing completed; processed %d, updated %d, cleaned-up %d" (plist-get info :processed) (plist-get info :updated) (plist-get info :cleaned-up)) - (unless (zerop (plist-get info :updated)) + (unless (and (not (string= mu4e~contacts-tstamp "0")) + (zerop (plist-get info :updated))) (mu4e~request-contacts-maybe) (run-hooks 'mu4e-index-updated-hook))))) ((plist-get info :message) @@ -680,121 +680,39 @@ process." (t (error "Error %d: %s" errcode errmsg)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; RFC2822 handling of phrases in mail-addresses -;;; The optional display-name contains a phrase, it sits before the angle-addr -;;; as specified in RFC2822 for email-addresses in header fields. -;;; contributed by jhelberg - -(defun mu4e~rfc822-phrase-type (ph) - "Return either atom, quoted-string, a corner-case or nil. This - checks for empty string first. Then quotes around the phrase - (returning 'rfc822-quoted-string). Then whether there is a quote - inside the phrase (returning 'rfc822-containing-quote). - The reverse of the RFC atext definition is then tested. - If it matches, nil is returned, if not, it is an 'rfc822-atom, which - is returned." - (cond - ((= (length ph) 0) 'rfc822-empty) - ((= (aref ph 0) ?\") - (if (string-match "\"\\([^\"\\\n]\\|\\\\.\\|\\\\\n\\)*\"" ph) - 'rfc822-quoted-string - 'rfc822-containing-quote)) ; starts with quote, but doesn't end with one - ((string-match-p "[\"]" ph) 'rfc822-containing-quote) - ((string-match-p "[\000-\037()\*<>@,;:\\\.]+" ph) nil) - (t 'rfc822-atom))) - -(defun mu4e~rfc822-quoteit (ph) - "Quote RFC822 phrase only if necessary. - Atoms and quoted strings don't need quotes. The rest do. In - case a phrase contains a quote, it will be escaped." - (let ((type (mu4e~rfc822-phrase-type ph))) - (cond - ((eq type 'rfc822-atom) ph) - ((eq type 'rfc822-quoted-string) ph) - ((eq type 'rfc822-containing-quote) - (format "\"%s\"" - (replace-regexp-in-string "\"" "\\\\\"" ph))) - (t (format "\"%s\"" ph))))) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defsubst mu4e~process-contact (contact) - "Process CONTACT, and either return nil when it should not be included, -or (rfc822-string . CONTACT) otherwise." - (when mu4e-contact-rewrite-function - (setq contact (funcall mu4e-contact-rewrite-function contact))) - (when contact - (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail)) - (ignore-rx (or mu4e-compose-complete-ignore-address-regexp "$^"))) - (when (and mail (not (string-match ignore-rx mail))) - (cons - (if name (format "%s <%s>" (mu4e~rfc822-quoteit name) mail) mail) - contact))))) - - -(defun mu4e~sort-contacts (contacts) - "Destructively sort contacts (only for cycling) in order of - 'mostly likely contact'.t See the code for the detail" - (let* ((now (+ (float-time) 3600)) ;; allow for clock diffs - (recent (- (float-time) (* 15 24 3600)))) - (cl-sort contacts - (lambda (c1 c2) - (let* ( (c1 (cdr c1)) (c2 (cdr c2)) - (personal1 (plist-get c1 :personal)) - (personal2 (plist-get c2 :personal)) - ;; note: freq, tstamp can only be missing if the rewrite - ;; function removed them. If the rewrite function changed the - ;; contact somehow, we guess it's important. - (freq1 (or (plist-get c1 :freq) 500)) - (freq2 (or (plist-get c2 :freq) 500)) - (tstamp1 (or (plist-get c1 :tstamp) now)) - (tstamp2 (or (plist-get c2 :tstamp) now))) - ;; only one is personal? if so, that one comes first - (if (not (equal personal1 personal2)) - (if personal1 t nil) - ;; only one is recent? that one comes first - (if (not (equal (> tstamp1 recent) (> tstamp2 recent))) - (> tstamp1 tstamp2) - ;; otherwise, use the frequency - (> freq1 freq2)))))))) - -(defun mu4e~sort-contacts-for-completion (contacts) - "Takes CONTACTS, which is a list of RFC-822 addresses, and sort them based -on the ranking in `mu4e~contacts.'" - (cl-sort contacts - (lambda (c1 c2) - (let ((rank1 (gethash c1 mu4e~contacts)) - (rank2 (gethash c2 mu4e~contacts))) - (< rank1 rank2))))) - -;; start and stopping -(defun mu4e~fill-contacts (contact-data) - "We receive a list of contacts, which each contact of the form - (:me NAME :mail EMAIL :tstamp TIMESTAMP :freq FREQUENCY) and -fill the hash `mu4e~contacts' with it, with each contact mapped -to an integer for their ranking. +(defvar mu4e~contacts-tstamp "0" + "Timestamp for the most recent contacts update." ) + +(defun mu4e~update-contacts (contacts &optional tstamp) + "Rceive a sorted list of CONTACTS. +Each of the contacts has the form + (FULL_EMAIL_ADDRESS . RANK) and fill the hash +`mu4e~contacts' with it, with each contact mapped to an integer +for their ranking. This is used by the completion function in mu4e-compose." - (let ((contacts) (rank 0)) - (dolist (contact contact-data) - (let ((contact-maybe (mu4e~process-contact contact))) - ;; note, this gives cells (rfc822-address . contact) - (when contact-maybe (push contact-maybe contacts)))) - (setq contacts (mu4e~sort-contacts contacts)) - ;; now, we have our nicely sorted list, map them to a list - ;; of increasing integers. We use that map in the composer - ;; to sort them there. It would have been so much easier if emacs - ;; allowed us to use the sorted-list as-is, but no such luck. - (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil - :size (length contacts))) + ;; We have our nicely sorted list, map them to a list + ;; of increasing integers. We use that map in the composer + ;; to sort them there. It would have been so much easier if emacs + ;; allowed us to use the sorted-list as-is, but no such luck. + (let ((n 0)) + (unless mu4e~contacts + (setq mu4e~contacts (make-hash-table :test 'equal :weakness nil + :size (length contacts)))) (dolist (contact contacts) - (puthash (car contact) rank mu4e~contacts) - (incf rank)) - (mu4e-index-message "Contacts received: %d" - (hash-table-count mu4e~contacts)))) + (incf n) + (let ((address + (if (functionp mu4e-contact-process-function) + (funcall mu4e-contact-process-function (car contact)) + (car contact)))) + (when address + (puthash address (cdr contact) mu4e~contacts)))) + + (setq mu4e~contacts-tstamp (or tstamp "0")) + (mu4e-index-message "Contacts updated: %d; total %d" + n (hash-table-count mu4e~contacts)))) (defun mu4e~check-requirements () "Check for the settings required for running mu4e." @@ -855,21 +773,21 @@ Checks whether the server process is live." the list of contacts we use for autocompletion; otherwise, do nothing." (when mu4e-compose-complete-addresses - (setq mu4e-contacts-func 'mu4e~fill-contacts) + (setq mu4e-contacts-func 'mu4e~update-contacts) (mu4e~proc-contacts mu4e-compose-complete-only-personal (when mu4e-compose-complete-only-after - (float-time - (apply 'encode-time - (mu4e-parse-time-string mu4e-compose-complete-only-after))))))) + (float-time + (apply 'encode-time + (mu4e-parse-time-string mu4e-compose-complete-only-after)))) + mu4e~contacts-tstamp))) (defun mu4e~start (&optional func) "If `mu4e-contexts' have been defined, but we don't have a context yet, switch to the matching one, or none matches, the -first. -If mu4e is already running, execute function FUNC (if non-nil). -Otherwise, check various requirements, then start mu4e. When -successful, call FUNC (if non-nil) afterwards." +first. If mu4e is already running, execute function FUNC (if +non-nil). Otherwise, check various requireme`'nts, then start mu4e. +When successful, call FUNC (if non-nil) afterwards." ;; if we're already running, simply go to the main view (if (mu4e-running-p) ;; already running? (when func (funcall func)) ;; yes! run func if defined @@ -905,7 +823,8 @@ successful, call FUNC (if non-nil) afterwards." "Clear any cached resources." (setq mu4e-maildir-list nil - mu4e~contacts nil)) + mu4e~contacts nil + mu4e~contacts-tstamp "0")) (defun mu4e~stop () "Stop the mu4e session." diff --git a/mu4e/mu4e-vars.el b/mu4e/mu4e-vars.el index 5b4dee1..8704ae8 100644 --- a/mu4e/mu4e-vars.el +++ b/mu4e/mu4e-vars.el @@ -1,6 +1,6 @@ ;;; mu4e-vars.el -- part of mu4e, the mu mail user agent ;; -;; Copyright (C) 2011-2018 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2019 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> @@ -37,7 +37,7 @@ "Location of the mu homedir, or nil for the default." :group 'mu4e :type '(choice (const :tag "Default location" nil) - (directory :tag "Specify location")) + (directory :tag "Specify location")) :safe 'stringp) (defcustom mu4e-mu-binary (executable-find "mu") @@ -111,7 +111,7 @@ faster." :type 'boolean :group 'mu4e :safe 'booleanp) If nil, don't update automatically. Note, changes in `mu4e-update-interval' only take effect after restarting mu4e." :type '(choice (const :tag "No automatic update" nil) - (integer :tag "Seconds")) + (integer :tag "Seconds")) :group 'mu4e :safe 'integerp) @@ -199,27 +199,27 @@ where QUERY is a string with a mu query, DESCRIPTION is a short description of the query (this will show up in the UI), and KEY is a shortcut key for the query." :type '(repeat (list (string :tag "Query") - (string :tag "Description") - character)) + (string :tag "Description") + character)) :group 'mu4e) (defvar mu4e-bookmarks `( ,(make-mu4e-bookmark - :name "Unread messages" - :query "flag:unread AND NOT flag:trashed" - :key ?u) + :name "Unread messages" + :query "flag:unread AND NOT flag:trashed" + :key ?u) ,(make-mu4e-bookmark - :name "Today's messages" - :query "date:today..now" - :key ?t) + :name "Today's messages" + :query "date:today..now" + :key ?t) ,(make-mu4e-bookmark - :name "Last 7 days" - :query "date:7d..now" - :key ?w) + :name "Last 7 days" + :query "date:7d..now" + :key ?w) ,(make-mu4e-bookmark - :name "Messages with images" - :query "mime:image/*" - :key ?p)) + :name "Messages with images" + :query "mime:image/*" + :key ?p)) "A list of pre-defined queries. Each query is represented by a mu4e-bookmark structure with parameters @t{:name} with the name of the bookmark, @t{:query} @@ -236,15 +236,15 @@ A symbol which is either: * `horizontal': split horizontally (headers on top) * `vertical': split vertically (headers on the left). * `single-window': view and headers in one window (mu4e will try not to - touch your window layout), main view in minibuffer + touch your window layout), main view in minibuffer * anything else: don't split (show either headers or messages, - not both) + not both) Also see `mu4e-headers-visible-lines' and `mu4e-headers-visible-columns'." :type '(choice (const :tag "Split horizontally" horizontal) - (const :tag "Split vertically" vertical) - (const :tag "Single window" single-window) - (const :tag "Don't split" nil)) + (const :tag "Split vertically" vertical) + (const :tag "Single window" single-window) + (const :tag "Don't split" nil)) :group 'mu4e-headers) (defcustom mu4e-view-max-specpdl-size 4096 @@ -301,12 +301,12 @@ contexts match, we have the following choices: Also see `mu4e-compose-context-policy'." :type '(choice - (const :tag "Always ask what context to use, even if one matches" - always-ask) - (const :tag "Ask if none of the contexts match" ask) - (const :tag "Ask when there's no context yet" ask-if-none) - (const :tag "Pick the first context if none match" pick-first) - (const :tag "Don't change the context when none match" nil)) + (const :tag "Always ask what context to use, even if one matches" + always-ask) + (const :tag "Ask if none of the contexts match" ask) + (const :tag "Ask when there's no context yet" ask-if-none) + (const :tag "Pick the first context if none match" pick-first) + (const :tag "Don't change the context when none match" nil)) :group 'mu4e) ;; crypto @@ -326,8 +326,8 @@ The setting is a symbol: * `ask': ask before decrypting anything * nil: don't try to decrypt anything." :type '(choice (const :tag "Try to decrypt automatically" t) - (const :tag "Ask before decrypting anything" ask) - (const :tag "Don't try to decrypt anything" nil)) + (const :tag "Ask before decrypting anything" ask) + (const :tag "Don't try to decrypt anything" nil)) :group 'mu4e-crypto) ;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs @@ -353,7 +353,7 @@ addresses)." :type 'boolean :group 'mu4e-compose) -(defcustom mu4e-compose-complete-only-after "2010-01-01" +(defcustom mu4e-compose-complete-only-after "2014-01-01" "Consider only contacts last seen after this date. Date must be a string, in a format parseable by `org-parse-time-string'. This excludes really old contacts. @@ -370,42 +370,39 @@ Set to nil to not have any time-based restriction." It is used as the identity function for converting contacts to their canonical counterpart; useful as an example." (let ((name (plist-get contact :name)) - (mail (plist-get contact :mail))) + (mail (plist-get contact :mail))) (list :name name :mail mail))) -(defcustom mu4e-contact-rewrite-function nil - "Function for rewriting or removing contacts. +(make-obsolete-variable 'mu4e-contacts-rewrite-function + "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") +(make-obsolete-variable 'mu4e-compose-complete-ignore-address-regexp + "mu4e-contact-process-function (see docstring)" "mu4e 1.3.2") -If the function receives the contact as a list of the form - (:name NAME :mail EMAIL ... other properties ... ) - (other properties may be there as well) +(defcustom mu4e-contact-process-function nil + "Function for processing contact information for use in auto-completion. -The function should return either: - - nil: remove this contact, or -- the rewritten cell, or -- the existing cell as-is +The function receives the contact as a string, e.g + \"Foo Bar <foo.bar@example.com>\" + \"cuux@example.com\" -For rewriting, it is recommended to use `plist-put' to set the -changed parameters, so the other properties stay in place. Those -are needed for sorting the contacts." +The function should return either: +- nil: do not use this contact for completion +- the (possibly rewritten) address, which must be +an RFC-2822-compatible e-mail address." :type 'function :group 'mu4e-compose) -(defcustom mu4e-compose-complete-ignore-address-regexp "no-?reply" - "Ignore any e-mail addresses for completion if they match this regexp." - :type 'string - :group 'mu4e-compose) - -(defcustom mu4e-compose-reply-ignore-address message-dont-reply-to-names +(defcustom mu4e-compose-reply-ignore-address +message-dont-reply-to-names "Addresses to prune when doing wide replies. -This can be a regexp matching the address, a list of regexps -or a predicate function. A value of nil keeps all the addresses." +This can be a regexp matching the address, a list of regexps or a +predicate function. A value of nil keeps all the addresses." :type '(choice - (const nil) - function - string - (repeat string)) + (const nil) + function + string + (repeat string)) :group 'mu4e-compose) (defcustom mu4e-compose-reply-to-address nil @@ -448,8 +445,8 @@ parameter refers to the original message being replied to / being forwarded / re-edited and is nil otherwise. `mu4e-drafts-folder' is only evaluated once." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-refile-folder "/archive" @@ -459,8 +456,8 @@ function that takes a message (a msg plist, see `mu4e-message-field'), and returns a folder. Note that the message parameter refers to the message-at-point." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-sent-folder "/sent" @@ -471,8 +468,8 @@ function that takes a message (a msg plist, see message parameter refers to the original message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-trash-folder "/trash" @@ -487,8 +484,8 @@ message-at-point. When using it when composing a message (see message being replied to / being forwarded / re-edited, and is nil otherwise." :type '(choice - (string :tag "Folder name") - (function :tag "Function return folder name")) + (string :tag "Folder name") + (function :tag "Function return folder name")) :group 'mu4e-folders) (defcustom mu4e-maildir-shortcuts nil @@ -714,113 +711,113 @@ mu4e-compose-mode." (defconst mu4e-header-info '( (:attachments . ( :name "Attachments" - :shortname "Atts" - :help "Message attachments" - :require-full t - :sortable nil)) + :shortname "Atts" + :help "Message attachments" + :require-full t + :sortable nil)) (:bcc . ( :name "Bcc" - :shortname "Bcc" - :help "Blind Carbon-Copy recipients for the message" - :sortable t)) + :shortname "Bcc" + :help "Blind Carbon-Copy recipients for the message" + :sortable t)) (:cc . ( :name "Cc" - :shortname "Cc" - :help "Carbon-Copy recipients for the message" - :sortable t)) + :shortname "Cc" + :help "Carbon-Copy recipients for the message" + :sortable t)) (:date . ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written" - :sortable t)) + :shortname "Date" + :help "Date/time when the message was written" + :sortable t)) (:human-date . ( :name "Date" - :shortname "Date" - :help "Date/time when the message was written." - :sortable :date)) + :shortname "Date" + :help "Date/time when the message was written." + :sortable :date)) (:flags . ( :name "Flags" - :shortname "Flgs" - :help "Flags for the message" - :sortable nil)) + :shortname "Flgs" + :help "Flags for the message" + :sortable nil)) (:from . ( :name "From" - :shortname "From" - :help "The sender of the message" - :sortable t)) + :shortname "From" + :help "The sender of the message" + :sortable t)) (:from-or-to . ( :name "From/To" - :shortname "From/To" - :help "Sender of the message if it's not me; otherwise the recipient" - :sortable nil)) + :shortname "From/To" + :help "Sender of the message if it's not me; otherwise the recipient" + :sortable nil)) (:maildir . ( :name "Maildir" - :shortname "Maildir" - :help "Maildir for this message" - :sortable t)) + :shortname "Maildir" + :help "Maildir for this message" + :sortable t)) (:list . ( :name "List-Id" - :shortname "List" - :help "Mailing list id for this message" - :sortable t)) + :shortname "List" + :help "Mailing list id for this message" + :sortable t)) (:mailing-list . ( :name "List" - :shortname "List" - :help "Mailing list friendly name for this message" - :sortable :list)) + :shortname "List" + :help "Mailing list friendly name for this message" + :sortable :list)) (:message-id . ( :name "Message-Id" - :shortname "MsgID" - :help "Message-Id for this message" - :sortable nil)) + :shortname "MsgID" + :help "Message-Id for this message" + :sortable nil)) (:path . ( :name "Path" - :shortname "Path" - :help "Full filesystem path to the message" - :sortable t)) + :shortname "Path" + :help "Full filesystem path to the message" + :sortable t)) (:signature . ( :name "Signature" - :shortname "Sgn" - :help "Check for the cryptographic signature" - :require-full t - :sortable nil)) + :shortname "Sgn" + :help "Check for the cryptographic signature" + :require-full t + :sortable nil)) (:decryption . ( :name "Decryption" - :shortname "Dec" - :help "Check the cryptographic decryption status" - :require-full t - :sortable nil)) + :shortname "Dec" + :help "Check the cryptographic decryption status" + :require-full t + :sortable nil)) (:size . ( :name "Size" - :shortname "Size" - :help "Size of the message" - :sortable t)) + :shortname "Size" + :help "Size of the message" + :sortable t)) (:subject . ( :name "Subject" - :shortname "Subject" - :help "Subject of the message" - :sortable t)) + :shortname "Subject" + :help "Subject of the message" + :sortable t)) (:tags . ( :name "Tags" - :shortname "Tags" - :help "Tags for the message" - :sortable nil)) + :shortname "Tags" + :help "Tags for the message" + :sortable nil)) (:thread-subject . ( :name "Subject" - :shortname "Subject" - :help "Subject of the thread" - :sortable :subject)) + :shortname "Subject" + :help "Subject of the thread" + :sortable :subject)) (:to . ( :name "To" - :shortname "To" - :help "Recipient of the message" - :sortable t)) + :shortname "To" + :help "Recipient of the message" + :sortable t)) (:user-agent . ( :name "User-Agent" - :shortname "UA" - :help "Program used for writing this message" - :require-full t - :sortable t))) + :shortname "UA" + :help "Program used for writing this message" + :require-full t + :sortable t))) "An alist of all possible header fields and information about them. This is used in the user-interface (the column headers in the header list, and the fields the message view). @@ -845,13 +842,13 @@ Note, `:sortable' is not supported for custom header fields.") (defvar mu4e-header-info-custom '( (:recipnum . ( :name "Number of recipients" - :shortname "Recip#" - :help "Number of recipients for this message" - :function - (lambda (msg) - (format "%d" - (+ (length (mu4e-message-field msg :to)) - (length (mu4e-message-field msg :cc)))))))) + :shortname "Recip#" + :help "Number of recipients for this message" + :function + (lambda (msg) + (format "%d" + (+ (length (mu4e-message-field msg :to)) + (length (mu4e-message-field msg :cc)))))))) "A list of custom (user-defined) headers. The format is similar to `mu4e-header-info', but adds a :function property, which diff --git a/mu4e/mu4e.texi b/mu4e/mu4e.texi index f70fd1a..d9a9f6e 100644 --- a/mu4e/mu4e.texi +++ b/mu4e/mu4e.texi @@ -1120,16 +1120,16 @@ An example message view: Date: Mon 19 Jan 2004 09:39:42 AM EET Maildir: /inbox Attachments(2): [1]DSCN4961.JPG(1.3M), [2]DSCN4962.JPG(1.4M) - + Hi Julia, - + Some pics from our trip to Cerin Amroth. Enjoy! - + All the best, Randy. - + On Sun 21 Dec 2003 09:06:34 PM EET, Julia wrote: - + [....] @end verbatim @end cartouche @@ -1571,9 +1571,9 @@ functionality is available, as well some @t{mu4e}-specifics. Its major mode is To: Wally the Walrus <wally@example.com> Subject: Re: Eau-qui d'eau qui? --text follows this line-- - + On Mon 16 Jan 2012 10:18:47 AM EET, Wally the Walrus wrote: - + > Hi Rupert, > > Dude - how are things? @@ -2574,8 +2574,8 @@ when starting; see the discussion in the previous section. :leave-func (lambda () (mu4e-message "Leaving Private context")) ;; we match based on the contact-fields of the message :match-func (lambda (msg) - (when msg - (mu4e-message-contact-field-matches msg + (when msg + (mu4e-message-contact-field-matches msg :to "aliced@@home.example.com"))) :vars '( ( user-mail-address . "aliced@@home.example.com" ) ( user-full-name . "Alice Derleth" ) @@ -2615,12 +2615,12 @@ when starting; see the discussion in the previous section. ;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should ;; guess or ask the correct context, e.g. - ;; start with the first (default) context; + ;; start with the first (default) context; ;; default is to ask-if-none (ask when there's no context yet, and none match) ;; (setq mu4e-context-policy 'pick-first) ;; compose with the current context is no context matches; - ;; default is to ask + ;; default is to ask ;; (setq mu4e-compose-context-policy nil) @end lisp @@ -2629,25 +2629,25 @@ A couple of notes about this example: @item You can manually switch the context use @code{M-x mu4e-context-switch}, by default bound to @kbd{;} in headers, view and main mode. The current context appears in the mode-line. -@item Normally, @code{M-x mu4e-context-switch} does not call the enter or +@item Normally, @code{M-x mu4e-context-switch} does not call the enter or leave functions if the 'new' context is the same as the old one. However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to invoke those function even in that case. -@item The function @code{mu4e-context-current} returns the current-context; +@item The function @code{mu4e-context-current} returns the current-context; the current context is also visible in the mode-line when in headers, view or main mode. -@item You can set any kind of variable; including settings for mail servers etc. +@item You can set any kind of variable; including settings for mail servers etc. However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home} are not changeable after they have been set without quitting @t{mu4e} first. -@item @code{leave-func} (if defined) for the context we are leaving, is invoked +@item @code{leave-func} (if defined) for the context we are leaving, is invoked before the @code{enter-func} (if defined) of the context we are entering. @item @code{enter-func} (if defined) is invoked before setting the variables. @item @code{match-func} (if defined) is invoked just before @code{mu4e-compose-pre-hook}. @item See the variables @code{mu4e-context-policy} and -@code{mu4e-compose-context-policy} to tweak what @t{mu4e} should do when +@code{mu4e-compose-context-policy} to tweak what @t{mu4e} should do when no context matches (or if you always want to be asked). -@item Finally, be careful to get the quotations right --- backticks, single quotes +@item Finally, be careful to get the quotations right --- backticks, single quotes and commas and note the '.' between variable name and its value. @end itemize @@ -3128,41 +3128,42 @@ As a fairly useless example, suppose we insist on reading @t{mu4e} as @node Contact functions @section Contact functions -It can sometimes be useful to rewrite the contact information that -@t{mu4e} provides, for example to convert them to some standardized -format, or to fix spelling errors. And sometimes, you may want to remove -certain contacts altogether. +It can sometimes be useful to discard or rewrite the contact +information that @t{mu4e} provides, for example to fix spelling +errors, or omit unwanted contacts. -For this, @t{mu4e} provides @code{mu4e-contact-rewrite-function}, which -passes each contact to a user-provided function, which is expected to -return either the possibly rewritten contact or @code{nil} to remove the -contact from the list --- note that the latter can also be achieved using -@code{mu4e-compose-complete-ignore-address-regexp}. +To handle this, @t{mu4e} provides +@code{mu4e-contact-process-function}, which, if defined, is applied to +each contact. If the result is @t{nil}, the contact is discarded, +otherwise the (modified or not) contact information is used. -Each of the contacts are property-lists (`plists'), with properties -@code{:name} (which may be @code{nil}), and @code{:mail}, and a number -of other properties which you should return unchanged. +Each contact is a full e-mail address as you would see in a +contact-field of an e-mail message, e.g., +@verbatim +"Foo Bar" <foo.bar@example.com> +@end verbatim +or +@verbatim +cuux@example.com +@end verbatim -Let's look at an example: +An example @code{mu4e-contact-process-function} might look like: @lisp -(defun my-rewrite-function (contact) - (let ((name (or (plist-get contact :name) "")) - (mail (plist-get contact :mail))) - (cond - ;; jonh smiht --> John Smith - ((string= "jonh smiht" name) - (plist-put contact :name "John C. Smith") - contact) - ;; remove evilspammer from the contacts list - ((string= "evilspammer@@example.com" mail) nil) - ;; others stay as the are - (t contact)))) - -(setq mu4e-contact-rewrite-function 'my-rewrite-function) +(defun my-contact-processor (contact) + (cond + ;; remove unwanted + ((string-match-p "evilspammer@@example.com" contact) nil) + ((string-match-p "noreply" contact) nil) + ;; + ;; jonh smiht --> John Smith + ((string-match "jonh smiht" contact) + (replace-regexp-in-string "jonh smiht" "John Smith" contact)) + (t contact))) + +(setq mu4e-contact-process-function 'my-contact-processor) @end lisp -This function is called for each of your contacts. @node Utility functions @section Utility functions @@ -3329,7 +3330,7 @@ such as @file{~/.emacs.d/init.el}) the following @emph{after} the (setq mu4e-compose-complete-addresses nil) (setq bbdb-mua-pop-up t) (setq bbdb-mua-pop-up-window-size 5) - (setq mu4e-view-show-addresses t) + (setq mu4e-view-show-addresses t) @end lisp @noindent |
