;;; mu4e-compose.el --- Compose and send messages -*- lexical-binding: t -*- ;; Copyright (C) 2011-2024 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema ;; This file is not part of GNU Emacs. ;; mu4e is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; mu4e is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with mu4e. If not, see . ;;; Commentary: ;; Implements mu4e-compose-mode, which is a `message-mode' derivative. This is a ;; *fairly* thin wrapper around the gnus functions for message composition, ;; integrated with mu4e. Still, quite a bit of code to make it work nicely in ;; the mu4e context. ;; Code (require 'message) (require 'nnheader) ;; for make-full-mail-header (require 'mu4e-obsolete) (require 'mu4e-server) (require 'mu4e-message) (require 'mu4e-context) (require 'mu4e-folders) ;;; User configuration for compose-mode (defgroup mu4e-compose nil "Customization for composing/sending messages." :group 'mu4e) (defcustom mu4e-sent-messages-behavior 'sent "Determines what mu4e does with sent messages. This is one of the symbols: * `sent' move the sent message to the Sent-folder (`mu4e-sent-folder') * `trash' move the sent message to the Trash-folder (`mu4e-trash-folder') * `delete' delete the sent message. Note, when using GMail/IMAP, you should set this to either `trash' or `delete', since GMail already takes care of keeping copies in the sent folder. Alternatively, `mu4e-sent-messages-behavior' can be a function which takes no arguments, and which should return one of the mentioned symbols, for example: (setq mu4e-sent-messages-behavior (lambda () (if (string= (message-sendmail-envelope-from) \"foo@example.com\") \\='delete \\='sent))) The various `message-' functions from `message-mode' are available for querying the message information." :type '(choice (const :tag "move message to mu4e-sent-folder" sent) (const :tag "move message to mu4e-trash-folder" trash) (const :tag "delete message" delete)) :group 'mu4e-compose) (defcustom mu4e-compose-switch nil "Where to display the new message? A symbol: - nil : default (new buffer) - window : compose in new window - frame or t : compose in new frame - display-buffer: use display-buffer / display-buffer-alist (for fine-tuning). For backward compatibility with `mu4e-compose-in-new-frame', t is treated as =\\'frame." :type 'symbol :group 'mu4e-compose) (defcustom mu4e-compose-context-policy 'ask "Policy for determining the context when composing a new message. If the value is `always-ask', ask the user unconditionally. In all other cases, if any context matches (using its match function), this context is used. Otherwise, if none of the contexts match, we have the following choices: - `pick-first': pick the first of the contexts available (ie. the default) - `ask': ask the user - `ask-if-none': ask if there is no context yet, otherwise leave it as it is - nil: return nil; leaves the current context as is. Also see `mu4e-context-policy'." :type '(choice (const :tag "Always ask what context to use" 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)) :safe 'symbolp :group 'mu4e-compose) (defcustom mu4e-compose-crypto-policy '(encrypt-encrypted-replies sign-encrypted-replies) "Policy to control when messages will be signed/encrypted. The value is a list which influence the way draft messages are created. Specifically, it might contain: - `sign-all-messages': Always add a signature. - `sign-new-messages': Add a signature to new message, ie. messages that aren't responses to another message. - `sign-forwarded-messages': Add a signature when forwarding a message - `sign-edited-messages': Add a signature to drafts - `sign-all-replies': Add a signature when responding to another message. - `sign-plain-replies': Add a signature when responding to non-encrypted messages. - `sign-encrypted-replies': Add a signature when responding to encrypted messages. It should be noted that certain symbols have priorities over one another. So `sign-all-messages' implies `sign-all-replies', which in turn implies `sign-plain-replies'. Adding both to the set, is not a contradiction, but a redundant configuration. All `sign-*' options have a `encrypt-*' analogue." :type '(set :greedy t (const :tag "Sign all messages" sign-all-messages) (const :tag "Encrypt all messages" encrypt-all-messages) (const :tag "Sign new messages" sign-new-messages) (const :tag "Encrypt new messages" encrypt-new-messages) y (const :tag "Sign forwarded messages" sign-forwarded-messages) (const :tag "Encrypt forwarded messages" encrypt-forwarded-messages) (const :tag "Sign edited messages" sign-edited-messages) (const :tag "Encrypt edited messages" edited-forwarded-messages) (const :tag "Sign all replies" sign-all-replies) (const :tag "Encrypt all replies" encrypt-all-replies) (const :tag "Sign replies to plain messages" sign-plain-replies) (const :tag "Encrypt replies to plain messages" encrypt-plain-replies) (const :tag "Sign replies to encrypted messages" sign-encrypted-replies) (const :tag "Encrypt replies to encrypted messages" encrypt-encrypted-replies)) :group 'mu4e-compose) (defcustom mu4e-compose-format-flowed nil "Whether to compose messages to be sent as format=flowed. \(Or with long lines if variable `use-hard-newlines' is set to nil). The variable `fill-flowed-encode-column' lets you customize the width beyond which format=flowed lines are wrapped." :type 'boolean :safe 'booleanp :group 'mu4e-compose) (defcustom mu4e-compose-pre-hook nil "Hook run just *before* message composition starts. If the compose-type is a symbol, either `reply' or `forward', the variable `mu4e-compose-parent-message' is the message replied to / being forwarded / edited, and `mu4e-compose-type' contains the type of message to be composed. Note that there is no draft message yet when this hook runs, it is meant for influencing the how mu4e constructs the draft message. If you want to do something with the draft messages after it has been constructed, `mu4e-compose-mode-hook' would be the place to do that." :type 'hook :group 'mu4e-compose) ;;; Runtime variables; useful for user-hooks etc. (defvar-local mu4e-compose-parent-message nil "The parent message plist. This is the message being replied to, forwarded or edited; used in `mu4e-compose-pre-hook'. For new (non-reply, forward etc.) messages, it is nil.") (defvar-local mu4e-compose-type nil "The compose-type for the current message.") ;;; Filenames (defun mu4e--message-basename() "Construct a randomized filename for a message with flags FLAGSTR. It looks something like