Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Message-mail fails to conform to semantics of compose-mail

4 views
Skip to first unread message

Larry Hunter

unread,
Mar 6, 1998, 3:00:00 AM3/6/98
to

I decided to give gnus a try as my main message composition system, setting
mail-user-agent to message-user-agent. I use RMAIL for reading mail.
However, I found quite a few places where message-mail fails to conform to
the semantics of compose-mail. There is also one area in which it seems to
me that the semantics of COMPOSE-MAIL is lacking an important piece.

These problems manifest themselves in various ways. For example, setting
MAIL-USER-AGENT to MESSAGE-USER-AGENT and executing COMPOSE-MAIL causes
MAIL-HEADER-FORMAT-FUNCTION to signal an error (was this tested at all
before release!?) Fixing that (see below) yields other indicative problems:
e.g., MAIL-YANK-ORIGINAL fails when using the MESSAGE-USER-AGENT to reply to
an RMAIL message. Also, the RMAIL message being replied to is not marked as
"Answered."

[Just as an aside, wouldn't it make more sense for the names to be
MAIL-USER-AGENT, MAIL-MESSAGE-AGENT, MAIL-SENDMAIL-AGENT, MAIL-MH-AGENT,
etc.? ]

These problems mostly arise because MESSAGE-MAIL does not conform to the
semantics of COMPOSE-MAIL as specified in its documentation. One problem
arises because the semantics of COMPOSE-MAIL doesn't allow for the passing
of information about whether a message is being composed in reply to some
other, and, if so, where the to-be-replied-to message is.

More specifically:

The compose-mail documentation states that the OTHER-HEADERS field is an
alist of (HEADER . VALUE) where both fields are strings. However,
MAIL-HEADER-FORMAT-FUNCTION (called by MESSAGE-MAIL) requires the headers to
be symbols. This is because gnus passes usenet message headers around as
symbols. This problem can be fixed without breaking any other gnus/message
functionality by allowing MAIL-HEADER-FORMAT-FUNCTION to take either strings
or symbols. However, it would make sense to rewrite gnus to represent
headers as strings uniformly. Here's the quick fix:

(setq mail-header-format-function
(lambda (header value)
"Function to format headers without a specified formatting function."
(let ((header-string (if (symbolp header) (symbol-name header)
(if (stringp header) header
(error "Unknown header type: %s" header)))))
(insert (capitalize header-string)
": "
(if (consp value) (car value) value)
"\n"))))


MESSAGE-MAIL ignores the CONTINUE argument to COMPOSE-MAIL. The CONTINUE
argument specifies whether to continue editing an article already composed.
This interacts with the SWITCH-TO-FUNCTION problem, below.

The SWITCH-FUNCTION argument, if non-nil, is a function to use to switch to
and display the buffer used for mail composition. This has the possible
values 'SWITCH-TO-BUFFER-OTHER-WINDOW and 'SWITCH-TO-BUFFER-OTHER-FRAME.
However, MESSAGE-MAIL hardcodes the function as MESSAGE-POP-TO-BUFFER, which
queries about erasing an existing message and calls POP-TO-BUFFER. By doing
this, gnus/message makes it impossible to compose mail in another frame,
rather than just another window, i.e. COMPOSE-MAIL-OTHER-FRAME doesn't do
the right thing with MAIL-USER-AGENT is MESSAGE-USER-AGENT.

The following function handles the CONTINUE and SWITCH-FUNCTION arguments
correctly, and retains most of the functionality of MESSAGE-POP-TO-BUFFER.

(defun message-switch-to-buffer (name continue switch-function)
"Switch to named buffer. If CONTINUE is non-nil, don't re-initialize the
buffer. If CONTINUE is nil and the buffer exists and is modified, query.
Make the switch using SWITCH-FUNCTION."
(set-buffer (funcall switch-function name))
(if (or continue
(and (buffer-modified-p)
(not (y-or-n-p "Message already being composed; erase? "))))
(message "Continue composing message.")
(progn
(erase-buffer)
(message-mode))))

The YANK-ACTION argument is a list of the of form (FUNCTION . ARGS). The
user-agent should apply function to args to insert the raw text of the
original message, then run MAIL-CITATION-HOOK. This is incompatible with
the MESSAGE-SETUP arguments, which wants a reply-buffer, and handles the
insertion itself. Since there are other callers to MESSAGE-SETUP, this
change will require fixing all the callers. Either that, or generating a
special message-setup-for-mail, which takes the correct arguments and
executes the YANK-ACTION. (Left as an exercise for the implementer :-)

There doesn't appear to me to be a well-defined way to determine if a call
to COMPOSE-MAIL is a reply to something, and if so, from what buffer. For
that reason, there appears to be no way that MESSAGE-MAIL can figure out the
right REPLY-BUFFER argument for MESSAGE-SETUP. That makes implementing
MAIL-YANK-ORIGINAL problematic (in this case, there's no way to know how to
set MESSAGE-REPLY-BUFFER). It seems to me that this information needs to be
passed as part of COMPOSE-MESSAGE. I would suggest an eigth optional
argument, REPLYING-TO, which is a buffer containing the message being
replied to, or null if there is none. Additionally, the default buffer for
the YANK-ACTION could be value of REPLYING-TO.

The SEND-ACTIONS argument is defined to be a list of actions, each of which
is in the form (FUNCTION . ARGS) and the user-agent must APPLY the function
to the args. However, MESSAGE-DO-ACTIONS uses EVAL to execute the action,
which leads to incompatibilities with argument evaluation. Since
MESSAGE-SETUP sets MESSAGE-SEND-ACTIONS to SEND-ACTIONS (which would
overwrite any previous actions -- is this intentional?) and message/gnus
later generates other actions which are added to this variable, they all
have to have the same evaluation semantics. To fix this correctly, all of
the actions generated by gnus/message should be changed to conform to the
COMPOSE-MAIL semantics, and MESSAGE-DO-ACTIONS should be changed to reflect
that. However, there are many other callers of MESSAGE-DO-ACTIONS which
will break given this change. One fairly gross way to work around this
might be to transform the SEND-ACTIONS passed by COMPOSE-MAIL to be EVALable:

(defun make-evalable (fn-apply-cons)
(let ((form (list (car fn-apply-cons)))
(unquoted-args (cdr fn-apply-cons)))
(while unquoted-args
(let ((arg (pop unquoted-args)))
(setq form (append form (if (or (stringp arg) (numberp arg))
(list arg)
(list (list 'quote arg)))))))
form))

Then massage the send-actions:

(defun message-mail (&optional to subject
other-headers continue switch-function
yank-action send-actions)
"Start editing a mail message to be sent."
(interactive)
(let ((message-this-is-mail t)
evalable-send-actions)
(while send-actions
(setq evalable-send-actions
(append evalable-send-actions
(list (make-evalable (pop send-actions))))))
(set-buffer (message-switch-to-buffer (message-buffer-name "mail" to)
continue switch-function))
(message-setup-for-mail
(nconc `((To . ,(or to "")) (Subject . ,(or subject ""))) other-headers)
yank-actions
evalable-send-actions)))

By the way, the (WHEN OTHER-HEADERS OTHER-HEADERS) line in the original is
redundant: just OTHER-HEADERS is the same thing.

Hope this is helpful, and I look forward to getting this working for 20.3!

Larry Hunter
hun...@nlm.nih.gov


Gnus v5.5; nntp 5.0
GNU Emacs 20.2.3 (mips-sgi-irix6.2)
of Thu Feb 26 1998 on work
200 lhc.nlm.nih.gov InterNetNews NNRP server INN 1.5.1 17-Dec-1996 ready (posting ok).

------------------ Environment follows ------------------

(setq gnus-default-nntp-server "lhc")
(setq gnus-novice-user nil)
(setq gnus-interactive-catchup nil)
(setq gnus-interactive-exit nil)
(setq gnus-load-hook
'(tm-setup/load-gnus))
(setq gnus-fetch-old-headers
'some)
(setq gnus-summary-make-false-root
'dummy)
(setq gnus-show-mime t)
(setq gnus-summary-mode-hook
'(mc-install-read-mode))
(setq gnus-summary-exit-hook
'(gnus-undo-boundary))
(setq gnus-parse-headers-hook
'(gnus-set-summary-default-charset gnus-decode-rfc1522))
(setq gnus-show-mime-method
'gnus-article-preview-mime-message)
(setq gnus-decode-encoded-word-method
'gnus-article-decode-encoded-word)
(setq gnus-subscribe-newsgroup-method
'gnus-subscribe-hierarchically)
(setq gnus-startup-hook
'(tm-gnus/bbdb-setup))
(setq gnus-asynchronous nil)
(setq gnus-use-header-prefetch t)
;; (makeunbound 'gnus-topic-mode)
;; (makeunbound 'gnus-topic-mode-hook)
;; (makeunbound 'gnus-topic-line-format)
;; (makeunbound 'gnus-topic-indent-level)
;; (makeunbound 'gnus-topic-display-empty-topics)
(setq message-directory "~/mail/")
(setq message-user-organization "National Library of Medicine")
(setq message-autosave-directory "~/mail")
(setq message-forward-start-separator "--[[message/rfc822]]\n")
(setq message-forward-end-separator "")
(setq message-included-forward-headers "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Mime-Version:\\|^Content-Type:\\|^Content-Transfer-Encoding:")
(setq message-use-followup-to
'use)
(setq message-sendmail-f-is-evil t)
(setq message-post-method
'(lambda
(arg)
(gnus-post-method arg "")))
(setq message-generate-headers-first t)
(setq message-setup-hook
'(message-maybe-setup-default-charset mime/editor-mode))
(setq message-header-hook
'(mime/encode-message-header))
(setq message-header-setup-hook
'(gnus-inews-insert-archive-gcc gnus-inews-insert-gcc))
(setq message-yank-prefix nil)
(setq message-signature nil)
(setq message-send-actions
'((gnus-bug-kill-buffer)
(when
(buffer-name
(get-buffer "*Group*"))
(save-excursion
(set-buffer
(get-buffer "*Group*"))
nil))
(when
(buffer-name
(get-buffer "*Summary comp.lang.lisp*"))
(save-excursion
(set-buffer
(get-buffer "*Summary comp.lang.lisp*"))
(gnus-summary-mark-article-as-replied 30304)))
(when
(buffer-name
(get-buffer "*Summary comp.lang.lisp*"))
(save-excursion
(set-buffer
(get-buffer "*Summary comp.lang.lisp*"))
(gnus-summary-mark-article-as-replied 30304)))
(when
(buffer-name
(get-buffer "*Summary comp.lang.lisp*"))
(save-excursion
(set-buffer
(get-buffer "*Summary comp.lang.lisp*"))
(gnus-summary-mark-article-as-replied 30304)))
(when
(buffer-name
(get-buffer "*Summary comp.lang.lisp*"))
(save-excursion
(set-buffer
(get-buffer "*Summary comp.lang.lisp*"))
(gnus-summary-mark-article-as-replied 30305)))
(when
(buffer-name
(get-buffer "*Summary comp.lang.lisp*"))
(save-excursion
(set-buffer
(get-buffer "*Summary comp.lang.lisp*"))
(gnus-summary-mark-article-as-replied 30305)))
(when
(buffer-name
(get-buffer "*mail to bug-gn...@prep.ai.mit.edu*"))
(save-excursion
(set-buffer
(get-buffer "*mail to bug-gn...@prep.ai.mit.edu*"))
nil))))
(setq message-exit-actions
'((set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)))
(setq message-kill-actions
'((set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)))
(setq message-postpone-actions
'((set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)
(set-window-configuration #<window-configuration>)))
(setq message-default-mail-headers "Fcc:~/mail/out-temp\n")
(setq message-default-news-headers "Fcc: ~/mail/usenet-postings.inbox\n")

0 new messages