-;;; message.el --- composing mail and news messages\r
-;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.\r
-\r
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>\r
-;; MORIOKA Tomohiko <morioka@jaist.ac.jp>\r
-;; Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>\r
-;; Keiichi Suzuki <kei-suzu@mail.wbs.ne.jp>\r
-;; Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>\r
-;; Katsumi Yamaoka <yamaoka@jpl.org>\r
-;; Keywords: mail, news, MIME\r
-\r
-;; This file is part of GNU Emacs.\r
-\r
-;; GNU Emacs is free software; you can redistribute it and/or modify\r
-;; it under the terms of the GNU General Public License as published by\r
-;; the Free Software Foundation; either version 2, or (at your option)\r
-;; any later version.\r
-\r
-;; GNU Emacs is distributed in the hope that it will be useful,\r
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-;; GNU General Public License for more details.\r
-\r
-;; You should have received a copy of the GNU General Public License\r
-;; along with GNU Emacs; see the file COPYING. If not, write to the\r
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
-;; Boston, MA 02111-1307, USA.\r
-\r
-;;; Commentary:\r
-\r
-;; This mode provides mail-sending facilities from within Emacs. It\r
-;; consists mainly of large chunks of code from the sendmail.el,\r
-;; gnus-msg.el and rnewspost.el files.\r
-\r
-;;; Code:\r
-\r
-(eval-when-compile\r
- (require 'cl)\r
- (require 'smtp)\r
- )\r
-\r
-(require 'mailheader)\r
-(require 'nnheader)\r
-(require 'easymenu)\r
-(require 'custom)\r
-(if (string-match "XEmacs\\|Lucid" emacs-version)\r
- (require 'mail-abbrevs)\r
- (require 'mailabbrev))\r
-(require 'mime-edit)\r
-\r
-;; Avoid byte-compile warnings.\r
-(eval-when-compile\r
- (require 'mail-parse)\r
- (require 'mm-bodies)\r
- (require 'mm-encode)\r
- (require 'mml)\r
- )\r
-\r
-(defgroup message '((user-mail-address custom-variable)\r
- (user-full-name custom-variable))\r
- "Mail and news message composing."\r
- :link '(custom-manual "(message)Top")\r
- :group 'mail\r
- :group 'news)\r
-\r
-(put 'user-mail-address 'custom-type 'string)\r
-(put 'user-full-name 'custom-type 'string)\r
-\r
-(defgroup message-various nil\r
- "Various Message Variables"\r
- :link '(custom-manual "(message)Various Message Variables")\r
- :group 'message)\r
-\r
-(defgroup message-buffers nil\r
- "Message Buffers"\r
- :link '(custom-manual "(message)Message Buffers")\r
- :group 'message)\r
-\r
-(defgroup message-sending nil\r
- "Message Sending"\r
- :link '(custom-manual "(message)Sending Variables")\r
- :group 'message)\r
-\r
-(defgroup message-interface nil\r
- "Message Interface"\r
- :link '(custom-manual "(message)Interface")\r
- :group 'message)\r
-\r
-(defgroup message-forwarding nil\r
- "Message Forwarding"\r
- :link '(custom-manual "(message)Forwarding")\r
- :group 'message-interface)\r
-\r
-(defgroup message-insertion nil\r
- "Message Insertion"\r
- :link '(custom-manual "(message)Insertion")\r
- :group 'message)\r
-\r
-(defgroup message-headers nil\r
- "Message Headers"\r
- :link '(custom-manual "(message)Message Headers")\r
- :group 'message)\r
-\r
-(defgroup message-news nil\r
- "Composing News Messages"\r
- :group 'message)\r
-\r
-(defgroup message-mail nil\r
- "Composing Mail Messages"\r
- :group 'message)\r
-\r
-(defgroup message-faces nil\r
- "Faces used for message composing."\r
- :group 'message\r
- :group 'faces)\r
-\r
-(defgroup message-frames nil\r
- "Message frames"\r
- :group 'message)\r
-\r
-(defcustom message-directory "~/Mail/"\r
- "*Directory from which all other mail file variables are derived."\r
- :group 'message-various\r
- :type 'directory)\r
-\r
-(defcustom message-max-buffers 10\r
- "*How many buffers to keep before starting to kill them off."\r
- :group 'message-buffers\r
- :type 'integer)\r
-\r
-(defcustom message-send-rename-function nil\r
- "Function called to rename the buffer after sending it."\r
- :group 'message-buffers\r
- :type 'function)\r
-\r
-(defcustom message-fcc-handler-function 'message-output\r
- "*A function called to save outgoing articles.\r
-This function will be called with the name of the file to store the\r
-article in. The default function is `message-output' which saves in Unix\r
-mailbox format."\r
- :type '(radio (function-item message-output)\r
- (function :tag "Other"))\r
- :group 'message-sending)\r
-\r
-(defcustom message-encode-function 'message-maybe-encode\r
- "*A function called to encode messages."\r
- :group 'message-sending\r
- :type 'function)\r
-\r
-(defcustom message-8bit-encoding-list '(8bit binary)\r
- "*8bit encoding type in Content-Transfer-Encoding field."\r
- :group 'message-sending\r
- :type '(repeat (symbol :tag "Type")))\r
-\r
-(defcustom message-courtesy-message\r
- "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"\r
- "*This is inserted at the start of a mailed copy of a posted message.\r
-If the string contains the format spec \"%s\", the Newsgroups\r
-the article has been posted to will be inserted there.\r
-If this variable is nil, no such courtesy message will be added."\r
- :group 'message-sending\r
- :type 'string)\r
-\r
-(defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"\r
- "*Regexp that matches headers to be removed in resent bounced mail."\r
- :group 'message-interface\r
- :type 'regexp)\r
-\r
-(defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit\r
- "Function to setup a re-sending bounced message."\r
- :group 'message-sending\r
- :type 'function)\r
-\r
-;;;###autoload\r
-(defcustom message-from-style 'default\r
- "*Specifies how \"From\" headers look.\r
-\r
-If `nil', they contain just the return address like:\r
- king@grassland.com\r
-If `parens', they look like:\r
- king@grassland.com (Elvis Parsley)\r
-If `angles', they look like:\r
- Elvis Parsley <king@grassland.com>\r
-\r
-Otherwise, most addresses look like `angles', but they look like\r
-`parens' if `angles' would need quoting and `parens' would not."\r
- :type '(choice (const :tag "simple" nil)\r
- (const parens)\r
- (const angles)\r
- (const default))\r
- :group 'message-headers)\r
-\r
-(defcustom message-syntax-checks nil\r
- ; Guess this one shouldn't be easy to customize...\r
- "*Controls what syntax checks should not be performed on outgoing posts.\r
-To disable checking of long signatures, for instance, add\r
- `(signature . disabled)' to this list.\r
-\r
-Don't touch this variable unless you really know what you're doing.\r
-\r
-Checks include subject-cmsg multiple-headers sendsys message-id from\r
-long-lines control-chars size new-text redirected-followup signature\r
-approved sender empty empty-headers message-id from subject\r
-shorten-followup-to existing-newsgroups buffer-file-name unchanged."\r
- :group 'message-news)\r
-\r
-(defcustom message-required-news-headers\r
- '(From Newsgroups Subject Date Message-ID\r
- (optional . Organization) Lines\r
- (optional . User-Agent))\r
- "*Headers to be generated or prompted for when posting an article.\r
-RFC977 and RFC1036 require From, Date, Newsgroups, Subject,\r
-Message-ID. Organization, Lines, In-Reply-To, Expires, and\r
-User-Agent are optional. If don't you want message to insert some\r
-header, remove it from this list."\r
- :group 'message-news\r
- :group 'message-headers\r
- :type '(repeat sexp))\r
-\r
-(defcustom message-required-mail-headers\r
- '(From Subject Date (optional . In-Reply-To) Message-ID Lines\r
- (optional . User-Agent))\r
- "*Headers to be generated or prompted for when mailing a message.\r
-RFC822 required that From, Date, To, Subject and Message-ID be\r
-included. Organization, Lines and User-Agent are optional."\r
- :group 'message-mail\r
- :group 'message-headers\r
- :type '(repeat sexp))\r
-\r
-(defcustom message-deletable-headers '(Message-ID Date Lines)\r
- "Headers to be deleted if they already exist and were generated by message previously."\r
- :group 'message-headers\r
- :type 'sexp)\r
-\r
-(defcustom message-ignored-news-headers\r
- "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:"\r
- "*Regexp of headers to be removed unconditionally before posting."\r
- :group 'message-news\r
- :group 'message-headers\r
- :type 'regexp)\r
-\r
-(defcustom message-ignored-mail-headers "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:"\r
- "*Regexp of headers to be removed unconditionally before mailing."\r
- :group 'message-mail\r
- :group 'message-headers\r
- :type 'regexp)\r
-\r
-(defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:"\r
- "*Header lines matching this regexp will be deleted before posting.\r
-It's best to delete old Path and Date headers before posting to avoid\r
-any confusion."\r
- :group 'message-interface\r
- :type 'regexp)\r
-\r
-(defcustom message-supersede-setup-function\r
- 'message-supersede-setup-for-mime-edit\r
- "Function to setup a supersede message."\r
- :group 'message-sending\r
- :type 'function)\r
-\r
-(defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"\r
- "*Regexp matching \"Re: \" in the subject line."\r
- :group 'message-various\r
- :type 'regexp)\r
-\r
-;;; Some sender agents encode the whole subject including leading "Re: ".\r
-;;; And if followup agent does not decode it for some reason (e.g. unknown\r
-;;; charset) and just add a new "Re: " in front of the encoded-word, the\r
-;;; result will contain multiple "Re: "'s.\r
-(defcustom message-subject-encoded-re-regexp\r
- (concat\r
- "^[ \t]*"\r
- (regexp-quote "=?")\r
- "[-!#$%&'*+0-9A-Z^_`a-z{|}~]+" ; charset\r
- (regexp-quote "?")\r
- "\\("\r
- "[Bb]" (regexp-quote "?") ; B encoding\r
- "\\(\\(CQk\\|CSA\\|IAk\\|ICA\\)[Jg]\\)*" ; \([ \t][ \t][ \t]\)*\r
- "\\("\r
- "[Uc][km]U6" ; [Rr][Ee]:\r
- "\\|"\r
- "\\(C[VX]\\|I[FH]\\)J[Fl]O[g-v]" ; [ \t][Rr][Ee]:\r
- "\\|"\r
- "\\(CQl\\|CSB\\|IAl\\|ICB\\)[Sy][RZ]T[o-r]" ; [ \t][ \t][Rr][Ee]:\r
- "\\)"\r
- "\\|"\r
- "[Qb]" (regexp-quote "?") ; Q encoding\r
- "\\(_\\|=09\\|=20\\)*"\r
- "\\([Rr]\\|=[57]2\\)\\([Ee]\\|=[46]5\\)\\(:\\|=3[Aa]\\)"\r
- "\\)"\r
- )\r
- "*Regexp matching \"Re: \" in the subject line.\r
-Unlike `message-subject-re-regexp', this regexp matches \"Re: \" within\r
-an encoded-word."\r
- :group 'message-various\r
- :type 'regexp)\r
-\r
-(defcustom message-use-subject-re t\r
- "*If t, remove any (buggy) \"Re: \"'s from the subject of the precursor\r
-and add a new \"Re: \". If it is nil, use the subject \"as-is\". If it\r
-is the symbol `guess', try to detect \"Re: \" within an encoded-word."\r
- :group 'message-various\r
- :type '(choice (const :tag "off" nil)\r
- (const :tag "on" t)\r
- (const guess)))\r
-\r
-;;;###autoload\r
-(defcustom message-signature-separator "^-- *$"\r
- "Regexp matching the signature separator."\r
- :type 'regexp\r
- :group 'message-various)\r
-\r
-(defcustom message-elide-elipsis "\n[...]\n\n"\r
- "*The string which is inserted for elided text."\r
- :type 'string\r
- :group 'message-various)\r
-\r
-(defcustom message-interactive nil\r
- "Non-nil means when sending a message wait for and display errors.\r
-nil means let mailer mail back a message to report errors."\r
- :group 'message-sending\r
- :group 'message-mail\r
- :type 'boolean)\r
-\r
-(defcustom message-generate-new-buffers 'unique\r
- "*Non-nil means that a new message buffer will be created whenever `message-setup' is called.\r
-If this is a function, call that function with three parameters: The type,\r
-the to address and the group name. (Any of these may be nil.) The function\r
-should return the new buffer name."\r
- :group 'message-buffers\r
- :type '(choice (const :tag "off" nil)\r
- (const :tag "unique" unique)\r
- (const :tag "unsent" unsent)\r
- (function fun)))\r
-\r
-(defcustom message-kill-buffer-on-exit nil\r
- "*Non-nil means that the message buffer will be killed after sending a message."\r
- :group 'message-buffers\r
- :type 'boolean)\r
-\r
-(defcustom message-kill-buffer-query-function 'yes-or-no-p\r
- "*A function called to query the user whether to kill buffer anyway or not.\r
-If it is t, the buffer will be killed peremptorily."\r
- :type '(radio (function-item yes-or-no-p)\r
- (function-item y-or-n-p)\r
- (function-item nnheader-Y-or-n-p)\r
- (function :tag "Other" t))\r
- :group 'message-buffers)\r
-\r
-(defvar gnus-local-organization)\r
-(defcustom message-user-organization\r
- (or (and (boundp 'gnus-local-organization)\r
- (stringp gnus-local-organization)\r
- gnus-local-organization)\r
- (getenv "ORGANIZATION")\r
- t)\r
- "*String to be used as an Organization header.\r
-If t, use `message-user-organization-file'."\r
- :group 'message-headers\r
- :type '(choice string\r
- (const :tag "consult file" t)))\r
-\r
-;;;###autoload\r
-(defcustom message-user-organization-file "/usr/lib/news/organization"\r
- "*Local news organization file."\r
- :type 'file\r
- :group 'message-headers)\r
-\r
-(defcustom message-forward-start-separator\r
- (concat (mime-make-tag "message" "rfc822") "\n")\r
- "*Delimiter inserted before forwarded messages."\r
- :group 'message-forwarding\r
- :type 'string)\r
-\r
-(defcustom message-forward-end-separator\r
- (concat (mime-make-tag "text" "plain") "\n")\r
- "*Delimiter inserted after forwarded messages."\r
- :group 'message-forwarding\r
- :type 'string)\r
-\r
-(defcustom message-signature-before-forwarded-message t\r
- "*If non-nil, put the signature before any included forwarded message."\r
- :group 'message-forwarding\r
- :type 'boolean)\r
-\r
-(defcustom message-included-forward-headers\r
- "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Mail-Copies-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"\r
- "*Regexp matching headers to be included in forwarded messages."\r
- :group 'message-forwarding\r
- :type 'regexp)\r
-\r
-(defcustom message-make-forward-subject-function\r
- 'message-forward-subject-author-subject\r
- "*A list of functions that are called to generate a subject header for forwarded messages.\r
-The subject generated by the previous function is passed into each\r
-successive function.\r
-\r
-The provided functions are:\r
-\r
-* message-forward-subject-author-subject (Source of article (author or\r
- newsgroup)), in brackets followed by the subject\r
-* message-forward-subject-fwd (Subject of article with 'Fwd:' prepended\r
- to it."\r
- :group 'message-forwarding\r
- :type '(radio (function-item message-forward-subject-author-subject)\r
- (function-item message-forward-subject-fwd)))\r
-\r
-(defcustom message-wash-forwarded-subjects nil\r
- "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."\r
- :group 'message-forwarding\r
- :type 'boolean)\r
-\r
-(defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"\r
- "*All headers that match this regexp will be deleted when resending a message."\r
- :group 'message-interface\r
- :type 'regexp)\r
-\r
-(defcustom message-ignored-cited-headers "."\r
- "*Delete these headers from the messages you yank."\r
- :group 'message-insertion\r
- :type 'regexp)\r
-\r
-(defcustom message-cancel-message "I am canceling my own article."\r
- "Message to be inserted in the cancel message."\r
- :group 'message-interface\r
- :type 'string)\r
-\r
-;; Useful to set in site-init.el\r
-;;;###autoload\r
-(defcustom message-send-mail-function 'message-send-mail-with-sendmail\r
- "Function to call to send the current buffer as mail.\r
-The headers should be delimited by a line whose contents match the\r
-variable `mail-header-separator'.\r
-\r
-Legal values include `message-send-mail-with-sendmail' (the default),\r
-`message-send-mail-with-mh', `message-send-mail-with-qmail' and\r
-`message-send-mail-with-smtp'."\r
- :type '(radio (function-item message-send-mail-with-sendmail)\r
- (function-item message-send-mail-with-mh)\r
- (function-item message-send-mail-with-qmail)\r
- (function-item message-send-mail-with-smtp)\r
- (function :tag "Other"))\r
- :group 'message-sending\r
- :group 'message-mail)\r
-\r
-;; 1997-09-29 by MORIOKA Tomohiko\r
-(defcustom message-send-news-function 'message-send-news-with-gnus\r
- "Function to call to send the current buffer as news.\r
-The headers should be delimited by a line whose contents match the\r
-variable `mail-header-separator'."\r
- :group 'message-sending\r
- :group 'message-news\r
- :type 'function)\r
-\r
-(defcustom message-reply-to-function nil\r
- "Function that should return a list of headers.\r
-This function should pick out addresses from the To, Cc, and From headers\r
-and respond with new To and Cc headers."\r
- :group 'message-interface\r
- :type 'function)\r
-\r
-(defcustom message-wide-reply-to-function nil\r
- "Function that should return a list of headers.\r
-This function should pick out addresses from the To, Cc, and From headers\r
-and respond with new To and Cc headers."\r
- :group 'message-interface\r
- :type 'function)\r
-\r
-(defcustom message-followup-to-function nil\r
- "Function that should return a list of headers.\r
-This function should pick out addresses from the To, Cc, and From headers\r
-and respond with new To and Cc headers."\r
- :group 'message-interface\r
- :type 'function)\r
-\r
-(defcustom message-use-followup-to 'ask\r
- "*Specifies what to do with Followup-To header.\r
-If nil, always ignore the header. If it is t, use its value, but\r
-query before using the \"poster\" value. If it is the symbol `ask',\r
-always query the user whether to use the value. If it is the symbol\r
-`use', always use the value."\r
- :group 'message-interface\r
- :type '(choice (const :tag "ignore" nil)\r
- (const :tag "maybe" t)\r
- (const :tag "always" use)\r
- (const :tag "ask" ask)))\r
-\r
-(defcustom message-use-mail-copies-to 'ask\r
- "*Specifies what to do with Mail-Copies-To header.\r
-If nil, always ignore the header. If it is t, use its value, but\r
-query before using the value other than \"always\" or \"never\".\r
-If it is the symbol `ask', always query the user whether to use\r
-the value. If it is the symbol `use', always use the value."\r
- :group 'message-interface\r
- :type '(choice (const :tag "ignore" nil)\r
- (const :tag "maybe" t)\r
- (const :tag "always" use)\r
- (const :tag "ask" ask)))\r
-\r
-(defcustom message-use-mail-followup-to 'ask\r
- "*Specifies what to do with Mail-Followup-To header.\r
-If nil, always ignore the header. If it is the symbol `ask', always\r
-query the user whether to use the value. If it is t or the symbol\r
-`use', always use the value."\r
- :group 'message-interface\r
- :type '(choice (const :tag "ignore" nil)\r
- (const :tag "maybe" t)\r
- (const :tag "always" use)\r
- (const :tag "ask" ask)))\r
-\r
-;;; XXX: 'ask and 'use are not implemented yet.\r
-(defcustom message-use-mail-reply-to 'ask\r
- "*Specifies what to do with Mail-Reply-To/Reply-To header.\r
-If nil, always ignore the header. If it is t or the symbol `use', use\r
-its value. If it is the symbol `ask', always query the user whether to\r
-use the value. Note that if \"Reply-To\" is marked as \"broken\", its value\r
-is never used."\r
- :group 'message-interface\r
- :type '(choice (const :tag "ignore" nil)\r
- (const :tag "maybe" t)\r
- (const :tag "always" use)\r
- (const :tag "ask" ask)))\r
-\r
-;; stuff relating to broken sendmail in MMDF\r
-(defcustom message-sendmail-f-is-evil nil\r
- "*Non-nil means that \"-f username\" should not be added to the sendmail\r
-command line, because it is even more evil than leaving it out."\r
- :group 'message-sending\r
- :type 'boolean)\r
-\r
-;; qmail-related stuff\r
-(defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"\r
- "Location of the qmail-inject program."\r
- :group 'message-sending\r
- :type 'file)\r
-\r
-(defcustom message-qmail-inject-args nil\r
- "Arguments passed to qmail-inject programs.\r
-This should be a list of strings, one string for each argument.\r
-\r
-For e.g., if you wish to set the envelope sender address so that bounces\r
-go to the right place or to deal with listserv's usage of that address, you\r
-might set this variable to '(\"-f\" \"you@some.where\")."\r
- :group 'message-sending\r
- :type '(repeat string))\r
-\r
-(defvar gnus-post-method)\r
-(defvar gnus-select-method)\r
-(defcustom message-post-method\r
- (cond ((and (boundp 'gnus-post-method)\r
- (listp gnus-post-method)\r
- gnus-post-method)\r
- gnus-post-method)\r
- ((boundp 'gnus-select-method)\r
- gnus-select-method)\r
- (t '(nnspool "")))\r
- "*Method used to post news.\r
-Note that when posting from inside Gnus, for instance, this\r
-variable isn't used."\r
- :group 'message-news\r
- :group 'message-sending\r
- ;; This should be the `gnus-select-method' widget, but that might\r
- ;; create a dependence to `gnus.el'.\r
- :type 'sexp)\r
-\r
-(defcustom message-generate-headers-first nil\r
- "*If non-nil, generate all possible headers before composing."\r
- :group 'message-headers\r
- :type 'boolean)\r
-\r
-(defcustom message-setup-hook '(turn-on-mime-edit)\r
- "Normal hook, run each time a new outgoing message is initialized.\r
-The function `message-setup' runs this hook."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-cancel-hook nil\r
- "Hook run when cancelling articles."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-signature-setup-hook nil\r
- "Normal hook, run each time a new outgoing message is initialized.\r
-It is run after the headers have been inserted and before\r
-the signature is inserted."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-bounce-setup-hook nil\r
- "Normal hook, run each time a re-sending bounced message is initialized.\r
-The function `message-bounce' runs this hook."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-supersede-setup-hook nil\r
- "Normal hook, run each time a supersede message is initialized.\r
-The function `message-supersede' runs this hook."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-mode-hook nil\r
- "Hook run in message mode buffers."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-header-hook '(eword-encode-header)\r
- "Hook run in a message mode buffer narrowed to the headers."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-header-setup-hook nil\r
- "Hook called narrowed to the headers when setting up a message buffer."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-;;;###autoload\r
-(defcustom message-citation-line-function 'message-insert-citation-line\r
- "*Function called to insert the \"Whomever writes:\" line."\r
- :type 'function\r
- :group 'message-insertion)\r
-\r
-;;;###autoload\r
-(defcustom message-yank-prefix "> "\r
- "*Prefix inserted on the lines of yanked messages."\r
- :type 'string\r
- :group 'message-insertion)\r
-\r
-(defcustom message-yank-add-new-references t\r
- "*Non-nil means new IDs will be added to \"References\" field when an\r
-article is yanked by the command `message-yank-original' interactively."\r
- :type 'boolean\r
- :group 'message-insertion)\r
-\r
-(defcustom message-indentation-spaces 3\r
- "*Number of spaces to insert at the beginning of each cited line.\r
-Used by `message-yank-original' via `message-yank-cite'."\r
- :group 'message-insertion\r
- :type 'integer)\r
-\r
-;;;###autoload\r
-(defcustom message-cite-function 'message-cite-original\r
- "*Function for citing an original message.\r
-Predefined functions include `message-cite-original' and\r
-`message-cite-original-without-signature'.\r
-Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."\r
- :type '(radio (function-item message-cite-original)\r
- (function-item sc-cite-original)\r
- (function :tag "Other"))\r
- :group 'message-insertion)\r
-\r
-;;;###autoload\r
-(defcustom message-indent-citation-function 'message-indent-citation\r
- "*Function for modifying a citation just inserted in the mail buffer.\r
-This can also be a list of functions. Each function can find the\r
-citation between (point) and (mark t). And each function should leave\r
-point and mark around the citation text as modified."\r
- :type 'function\r
- :group 'message-insertion)\r
-\r
-(defvar message-abbrevs-loaded nil)\r
-\r
-;;;###autoload\r
-(defcustom message-signature t\r
- "*String to be inserted at the end of the message buffer.\r
-If t, the `message-signature-file' file will be inserted instead.\r
-If a function, the result from the function will be used instead.\r
-If a form, the result from the form will be used instead."\r
- :type 'sexp\r
- :group 'message-insertion)\r
-\r
-;;;###autoload\r
-(defcustom message-signature-file "~/.signature"\r
- "*File containing the text inserted at end of message buffer."\r
- :type 'file\r
- :group 'message-insertion)\r
-\r
-(defcustom message-distribution-function nil\r
- "*Function called to return a Distribution header."\r
- :group 'message-news\r
- :group 'message-headers\r
- :type 'function)\r
-\r
-(defcustom message-expires 14\r
- "Number of days before your article expires."\r
- :group 'message-news\r
- :group 'message-headers\r
- :link '(custom-manual "(message)News Headers")\r
- :type 'integer)\r
-\r
-(defcustom message-user-path nil\r
- "If nil, use the NNTP server name in the Path header.\r
-If stringp, use this; if non-nil, use no host name (user name only)."\r
- :group 'message-news\r
- :group 'message-headers\r
- :link '(custom-manual "(message)News Headers")\r
- :type '(choice (const :tag "nntp" nil)\r
- (string :tag "name")\r
- (sexp :tag "none" :format "%t" t)))\r
-\r
-(defvar message-reply-buffer nil)\r
-(defvar message-reply-headers nil)\r
-(defvar message-sent-message-via nil)\r
-(defvar message-checksum nil)\r
-(defvar message-send-actions nil\r
- "A list of actions to be performed upon successful sending of a message.")\r
-(defvar message-exit-actions nil\r
- "A list of actions to be performed upon exiting after sending a message.")\r
-(defvar message-kill-actions nil\r
- "A list of actions to be performed before killing a message buffer.")\r
-(defvar message-postpone-actions nil\r
- "A list of actions to be performed after postponing a message.")\r
-(defvar message-original-frame nil)\r
-(defvar message-parameter-alist nil)\r
-(defvar message-startup-parameter-alist nil)\r
-\r
-(define-widget 'message-header-lines 'text\r
- "All header lines must be LFD terminated."\r
- :format "%t:%n%v"\r
- :valid-regexp "^\\'"\r
- :error "All header lines must be newline terminated")\r
-\r
-(defcustom message-default-headers ""\r
- "*A string containing header lines to be inserted in outgoing messages.\r
-It is inserted before you edit the message, so you can edit or delete\r
-these lines."\r
- :group 'message-headers\r
- :type 'message-header-lines)\r
-\r
-(defcustom message-default-mail-headers ""\r
- "*A string of header lines to be inserted in outgoing mails."\r
- :group 'message-headers\r
- :group 'message-mail\r
- :type 'message-header-lines)\r
-\r
-(defcustom message-default-news-headers ""\r
- "*A string of header lines to be inserted in outgoing news\r
-articles."\r
- :group 'message-headers\r
- :group 'message-news\r
- :type 'message-header-lines)\r
-\r
-;; Note: could use /usr/ucb/mail instead of sendmail;\r
-;; options -t, and -v if not interactive.\r
-(defcustom message-mailer-swallows-blank-line\r
- (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"\r
- system-configuration)\r
- (file-readable-p "/etc/sendmail.cf")\r
- (let ((buffer (get-buffer-create " *temp*")))\r
- (unwind-protect\r
- (save-excursion\r
- (set-buffer buffer)\r
- (insert-file-contents "/etc/sendmail.cf")\r
- (goto-char (point-min))\r
- (let ((case-fold-search nil))\r
- (re-search-forward "^OR\\>" nil t)))\r
- (kill-buffer buffer))))\r
- ;; According to RFC822, "The field-name must be composed of printable\r
- ;; ASCII characters (i. e., characters that have decimal values between\r
- ;; 33 and 126, except colon)", i. e., any chars except ctl chars,\r
- ;; space, or colon.\r
- '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))\r
- "*Set this non-nil if the system's mailer runs the header and body together.\r
-\(This problem exists on Sunos 4 when sendmail is run in remote mode.)\r
-The value should be an expression to test whether the problem will\r
-actually occur."\r
- :group 'message-sending\r
- :type 'sexp)\r
-\r
-;;; XXX: This symbol is overloaded! See below.\r
-(defvar message-user-agent nil\r
- "String of the form of PRODUCT/VERSION. Used for User-Agent header field.")\r
-\r
-;; Ignore errors in case this is used in Emacs 19.\r
-;; Don't use ignore-errors because this is copied into loaddefs.el.\r
-;;;###autoload\r
-(condition-case nil\r
- (define-mail-user-agent 'message-user-agent\r
- 'message-mail 'message-send-and-exit\r
- 'message-kill-buffer 'message-send-hook)\r
- (error nil))\r
-\r
-(defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)\r
- "If non-nil, delete the deletable headers before feeding to mh.")\r
-\r
-(defvar message-send-method-alist\r
- '((news message-news-p message-send-via-news)\r
- (mail message-mail-p message-send-via-mail))\r
- "Alist of ways to send outgoing messages.\r
-Each element has the form\r
-\r
- \(TYPE PREDICATE FUNCTION)\r
-\r
-where TYPE is a symbol that names the method; PREDICATE is a function\r
-called without any parameters to determine whether the message is\r
-a message of type TYPE; and FUNCTION is a function to be called if\r
-PREDICATE returns non-nil. FUNCTION is called with one parameter --\r
-the prefix.")\r
-\r
-(defvar message-mail-alias-type 'abbrev\r
- "*What alias expansion type to use in Message buffers.\r
-The default is `abbrev', which uses mailabbrev. nil switches\r
-mail aliases off.")\r
-\r
-(defcustom message-auto-save-directory\r
- (nnheader-concat message-directory "drafts/")\r
- "*Directory where Message auto-saves buffers if Gnus isn't running.\r
-If nil, Message won't auto-save."\r
- :group 'message-buffers\r
- :type 'directory)\r
-\r
-(defcustom message-buffer-naming-style 'unique\r
- "*The way new message buffers are named.\r
-Valid valued are `unique' and `unsent'."\r
- :group 'message-buffers\r
- :type '(choice (const :tag "unique" unique)\r
- (const :tag "unsent" unsent)))\r
-\r
-(defcustom message-default-charset nil\r
- "Default charset used in non-MULE XEmacsen.")\r
-\r
-;;; Internal variables.\r
-;;; Well, not really internal.\r
-\r
-(defvar message-mode-syntax-table\r
- (let ((table (copy-syntax-table text-mode-syntax-table)))\r
- (modify-syntax-entry ?% ". " table)\r
- (modify-syntax-entry ?> ". " table)\r
- (modify-syntax-entry ?< ". " table)\r
- table)\r
- "Syntax table used while in Message mode.")\r
-\r
-(defvar message-mode-abbrev-table text-mode-abbrev-table\r
- "Abbrev table used in Message mode buffers.\r
-Defaults to `text-mode-abbrev-table'.")\r
-(defgroup message-headers nil\r
- "Message headers."\r
- :link '(custom-manual "(message)Variables")\r
- :group 'message)\r
-\r
-(defface message-header-to-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "green2" :bold t))\r
- (((class color)\r
- (background light))\r
- (:foreground "MidnightBlue" :bold t))\r
- (t\r
- (:bold t :italic t)))\r
- "Face used for displaying From headers."\r
- :group 'message-faces)\r
-\r
-(defface message-header-cc-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "green4" :bold t))\r
- (((class color)\r
- (background light))\r
- (:foreground "MidnightBlue"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying Cc headers."\r
- :group 'message-faces)\r
-\r
-(defface message-header-subject-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "green3"))\r
- (((class color)\r
- (background light))\r
- (:foreground "navy blue" :bold t))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying subject headers."\r
- :group 'message-faces)\r
-\r
-(defface message-header-newsgroups-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "yellow" :bold t :italic t))\r
- (((class color)\r
- (background light))\r
- (:foreground "blue4" :bold t :italic t))\r
- (t\r
- (:bold t :italic t)))\r
- "Face used for displaying newsgroups headers."\r
- :group 'message-faces)\r
-\r
-(defface message-header-other-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "#b00000"))\r
- (((class color)\r
- (background light))\r
- (:foreground "steel blue"))\r
- (t\r
- (:bold t :italic t)))\r
- "Face used for displaying newsgroups headers."\r
- :group 'message-faces)\r
-\r
-(defface message-header-name-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "DarkGreen"))\r
- (((class color)\r
- (background light))\r
- (:foreground "cornflower blue"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying header names."\r
- :group 'message-faces)\r
-\r
-(defface message-header-xheader-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "blue"))\r
- (((class color)\r
- (background light))\r
- (:foreground "blue"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying X-Header headers."\r
- :group 'message-faces)\r
-\r
-(defface message-separator-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "blue3"))\r
- (((class color)\r
- (background light))\r
- (:foreground "brown"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying the separator."\r
- :group 'message-faces)\r
-\r
-(defface message-cited-text-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "red"))\r
- (((class color)\r
- (background light))\r
- (:foreground "red"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying cited text names."\r
- :group 'message-faces)\r
-\r
-(defface message-mml-face\r
- '((((class color)\r
- (background dark))\r
- (:foreground "ForestGreen"))\r
- (((class color)\r
- (background light))\r
- (:foreground "ForestGreen"))\r
- (t\r
- (:bold t)))\r
- "Face used for displaying MML."\r
- :group 'message-faces)\r
-\r
-(defvar message-font-lock-keywords\r
- (let* ((cite-prefix "A-Za-z")\r
- (cite-suffix (concat cite-prefix "0-9_.@-"))\r
- (content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))\r
- `((,(concat "^\\([Tt]o:\\)" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-to-face nil t))\r
- (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"\r
- "[Mm]ail-[Cc]opies-[Tt]o:\\|"\r
- "[Mm]ail-[Rr]eply-[Tt]o:\\|"\r
- "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-cc-face nil t))\r
- (,(concat "^\\([Ss]ubject:\\)" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-subject-face nil t))\r
- (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-newsgroups-face nil t))\r
- (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-other-face nil t))\r
- (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)\r
- (1 'message-header-name-face)\r
- (2 'message-header-name-face))\r
- ,@(if (and mail-header-separator\r
- (not (equal mail-header-separator "")))\r
- `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")\r
- 1 'message-separator-face))\r
- nil)\r
- (,(concat "^[ \t]*"\r
- "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"\r
- "[:>|}].*")\r
- (0 'message-cited-text-face))\r
- ("<#/?\\(multipart\\|part\\|external\\).*>"\r
- (0 'message-mml-face))))\r
- "Additional expressions to highlight in Message mode.")\r
-\r
-;; XEmacs does it like this. For Emacs, we have to set the\r
-;; `font-lock-defaults' buffer-local variable.\r
-(put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))\r
-\r
-(defvar message-face-alist\r
- '((bold . bold-region)\r
- (underline . underline-region)\r
- (default . (lambda (b e)\r
- (unbold-region b e)\r
- (ununderline-region b e))))\r
- "Alist of mail and news faces for facemenu.\r
-The cdr of ech entry is a function for applying the face to a region.")\r
-\r
-(defcustom message-send-hook nil\r
- "Hook run before sending messages."\r
- :group 'message-various\r
- :options '(ispell-message)\r
- :type 'hook)\r
-\r
-(defcustom message-send-mail-hook nil\r
- "Hook run before sending mail messages."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-send-news-hook nil\r
- "Hook run before sending news messages."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-sent-hook nil\r
- "Hook run after sending messages."\r
- :group 'message-various\r
- :type 'hook)\r
-\r
-(defcustom message-use-multi-frames nil\r
- "Make new frame when sending messages."\r
- :group 'message-frames\r
- :type 'boolean)\r
-\r
-(defcustom message-delete-frame-on-exit nil\r
- "Delete frame after sending messages."\r
- :group 'message-frames\r
- :type '(choice (const :tag "off" nil)\r
- (const :tag "always" t)\r
- (const :tag "ask" ask)))\r
-\r
-(defvar message-draft-coding-system\r
- (cond\r
- ((not (fboundp 'find-coding-system)) nil)\r
- ((find-coding-system 'emacs-mule) 'emacs-mule)\r
- ((find-coding-system 'escape-quoted) 'escape-quoted)\r
- ((find-coding-system 'no-conversion) 'no-conversion)\r
- (t nil))\r
- "Coding system to compose mail.")\r
-\r
-;;; Internal variables.\r
-\r
-(defvar message-buffer-list nil)\r
-(defvar message-this-is-news nil)\r
-(defvar message-this-is-mail nil)\r
-(defvar message-draft-article nil)\r
-(defvar message-mime-part nil)\r
-(defvar message-posting-charset nil)\r
-\r
-;; Byte-compiler warning\r
-(defvar gnus-active-hashtb)\r
-(defvar gnus-read-active-file)\r
-\r
-;;; Regexp matching the delimiter of messages in UNIX mail format\r
-;;; (UNIX From lines), minus the initial ^. It should be a copy\r
-;;; of rmail.el's rmail-unix-mail-delimiter.\r
-(defvar message-unix-mail-delimiter\r
- (let ((time-zone-regexp\r
- (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"\r
- "\\|[-+]?[0-9][0-9][0-9][0-9]"\r
- "\\|"\r
- "\\) *")))\r
- (concat\r
- "From "\r
-\r
- ;; Many things can happen to an RFC 822 mailbox before it is put into\r
- ;; a `From' line. The leading phrase can be stripped, e.g.\r
- ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'. The <> can be stripped, e.g.\r
- ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'. Everything starting with a CRLF\r
- ;; can be removed, e.g.\r
- ;; From: joe@y.z (Joe K\r
- ;; User)\r
- ;; can yield `From joe@y.z (Joe K Fri Mar 22 08:11:15 1996', and\r
- ;; From: Joe User\r
- ;; <joe@y.z>\r
- ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.\r
- ;; The mailbox can be removed or be replaced by white space, e.g.\r
- ;; From: "Joe User"{space}{tab}\r
- ;; <joe@y.z>\r
- ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',\r
- ;; where {space} and {tab} represent the Ascii space and tab characters.\r
- ;; We want to match the results of any of these manglings.\r
- ;; The following regexp rejects names whose first characters are\r
- ;; obviously bogus, but after that anything goes.\r
- "\\([^\0-\b\n-\r\^?].*\\)? "\r
-\r
- ;; The time the message was sent.\r
- "\\([^\0-\r \^?]+\\) +" ; day of the week\r
- "\\([^\0-\r \^?]+\\) +" ; month\r
- "\\([0-3]?[0-9]\\) +" ; day of month\r
- "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day\r
-\r
- ;; Perhaps a time zone, specified by an abbreviation, or by a\r
- ;; numeric offset.\r
- time-zone-regexp\r
-\r
- ;; The year.\r
- " \\([0-9][0-9]+\\) *"\r
-\r
- ;; On some systems the time zone can appear after the year, too.\r
- time-zone-regexp\r
-\r
- ;; Old uucp cruft.\r
- "\\(remote from .*\\)?"\r
-\r
- "\n"))\r
- "Regexp matching the delimiter of messages in UNIX mail format.")\r
-\r
-(defvar message-unsent-separator\r
- (concat "^ *---+ +Unsent message follows +---+ *$\\|"\r
- "^ *---+ +Returned message +---+ *$\\|"\r
- "^Start of returned message$\\|"\r
- "^ *---+ +Original message +---+ *$\\|"\r
- "^ *--+ +begin message +--+ *$\\|"\r
- "^ *---+ +Original message follows +---+ *$\\|"\r
- "^|? *---+ +Message text follows: +---+ *|?$")\r
- "A regexp that matches the separator before the text of a failed message.")\r
-\r
-(defvar message-header-format-alist\r
- `((Newsgroups)\r
- (To . message-fill-address)\r
- (Cc . message-fill-address)\r
- (Subject)\r
- (In-Reply-To)\r
- (Fcc)\r
- (Bcc)\r
- (Date)\r
- (Organization)\r
- (Distribution)\r
- (Lines)\r
- (Expires)\r
- (Message-ID)\r
- (References . message-shorten-references)\r
- (User-Agent))\r
- "Alist used for formatting headers.")\r
-\r
-(eval-and-compile\r
- (autoload 'message-setup-toolbar "messagexmas")\r
- (autoload 'mh-new-draft-name "mh-comp")\r
- (autoload 'mh-send-letter "mh-comp")\r
- (autoload 'gnus-point-at-eol "gnus-util")\r
- (autoload 'gnus-point-at-bol "gnus-util")\r
- (autoload 'gnus-output-to-mail "gnus-util")\r
- (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")\r
- (autoload 'nndraft-request-associate-buffer "nndraft")\r
- (autoload 'nndraft-request-expire-articles "nndraft")\r
- (autoload 'gnus-open-server "gnus-int")\r
- (autoload 'gnus-request-post "gnus-int")\r
- (autoload 'gnus-copy-article-buffer "gnus-msg")\r
- (autoload 'gnus-alive-p "gnus-util")\r
- (autoload 'rmail-output "rmail"))\r
-\r
-\f\r
-\r
-;;;\r
-;;; Utility functions.\r
-;;;\r
-(defun message-eval-parameter (parameter)\r
- (condition-case ()\r
- (if (symbolp parameter)\r
- (if (functionp parameter)\r
- (funcall parameter)\r
- (eval parameter))\r
- parameter)\r
- (error nil)))\r
-\r
-(defsubst message-get-parameter (key &optional alist)\r
- (unless alist\r
- (setq alist message-parameter-alist))\r
- (cdr (assq key alist)))\r
-\r
-(defmacro message-get-parameter-with-eval (key &optional alist)\r
- `(message-eval-parameter (message-get-parameter ,key ,alist)))\r
-\r
-(defmacro message-y-or-n-p (question show &rest text)\r
- "Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"\r
- `(message-talkative-question 'y-or-n-p ,question ,show ,@text))\r
-\r
-;; Delete the current line (and the next N lines.);\r
-(defmacro message-delete-line (&optional n)\r
- `(delete-region (progn (beginning-of-line) (point))\r
- (progn (forward-line ,(or n 1)) (point))))\r
-\r
-(defun message-tokenize-header (header &optional separator)\r
- "Split HEADER into a list of header elements.\r
-\",\" is used as the separator."\r
- (if (not header)\r
- nil\r
- (let ((regexp (format "[%s]+" (or separator ",")))\r
- (beg 1)\r
- (first t)\r
- quoted elems paren)\r
- (save-excursion\r
- (message-set-work-buffer)\r
- (insert header)\r
- (goto-char (point-min))\r
- (while (not (eobp))\r
- (if first\r
- (setq first nil)\r
- (forward-char 1))\r
- (cond ((and (> (point) beg)\r
- (or (eobp)\r
- (and (looking-at regexp)\r
- (not quoted)\r
- (not paren))))\r
- (push (buffer-substring beg (point)) elems)\r
- (setq beg (match-end 0)))\r
- ((eq (char-after) ?\")\r
- (setq quoted (not quoted)))\r
- ((and (eq (char-after) ?\()\r
- (not quoted))\r
- (setq paren t))\r
- ((and (eq (char-after) ?\))\r
- (not quoted))\r
- (setq paren nil))))\r
- (nreverse elems)))))\r
-\r
-(defun message-mail-file-mbox-p (file)\r
- "Say whether FILE looks like a Unix mbox file."\r
- (when (and (file-exists-p file)\r
- (file-readable-p file)\r
- (file-regular-p file))\r
- (with-temp-buffer\r
- (nnheader-insert-file-contents file)\r
- (goto-char (point-min))\r
- (looking-at message-unix-mail-delimiter))))\r
-\r
-(defun message-fetch-field (header &optional not-all)\r
- "The same as `mail-fetch-field', only remove all newlines."\r
- (let* ((inhibit-point-motion-hooks t)\r
- (value (mail-fetch-field header nil (not not-all))))\r
- (when value\r
- (while (string-match "\n[\t ]+" value)\r
- (setq value (replace-match " " t t value)))\r
- ;; We remove all text props.delete-region\r
- (format "%s" value))))\r
-\r
-(defun message-narrow-to-field ()\r
- "Narrow the buffer to the header on the current line."\r
- (beginning-of-line)\r
- (narrow-to-region\r
- (point)\r
- (progn\r
- (forward-line 1)\r
- (if (re-search-forward "^[^ \n\t]" nil t)\r
- (progn\r
- (beginning-of-line)\r
- (point))\r
- (point-max))))\r
- (goto-char (point-min)))\r
-\r
-(defun message-add-header (&rest headers)\r
- "Add the HEADERS to the message header, skipping those already present."\r
- (while headers\r
- (let (hclean)\r
- (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))\r
- (error "Invalid header `%s'" (car headers)))\r
- (setq hclean (match-string 1 (car headers)))\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)\r
- (insert (car headers) ?\n))))\r
- (setq headers (cdr headers))))\r
-\r
-(defun message-fetch-reply-field (header)\r
- "Fetch FIELD from the message we're replying to."\r
- (let ((buffer (message-eval-parameter message-reply-buffer)))\r
- (when (and buffer\r
- (buffer-name buffer))\r
- (save-excursion\r
- (set-buffer buffer)\r
- (message-fetch-field header)))))\r
-\r
-(defun message-set-work-buffer ()\r
- (if (get-buffer " *message work*")\r
- (progn\r
- (set-buffer " *message work*")\r
- (erase-buffer))\r
- (set-buffer (get-buffer-create " *message work*"))\r
- (kill-all-local-variables)))\r
-\r
-(defun message-functionp (form)\r
- "Return non-nil if FORM is funcallable."\r
- (or (and (symbolp form) (fboundp form))\r
- (and (listp form) (eq (car form) 'lambda))\r
- (byte-code-function-p form)))\r
-\r
-(defun message-strip-subject-re (subject)\r
- "Remove \"Re:\" from subject lines."\r
- (if (string-match message-subject-re-regexp subject)\r
- (substring subject (match-end 0))\r
- subject))\r
-\r
-(defun message-remove-header (header &optional is-regexp first reverse)\r
- "Remove HEADER in the narrowed buffer.\r
-If REGEXP, HEADER is a regular expression.\r
-If FIRST, only remove the first instance of the header.\r
-Return the number of headers removed."\r
- (goto-char (point-min))\r
- (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))\r
- (number 0)\r
- (case-fold-search t)\r
- last)\r
- (while (and (not (eobp))\r
- (not last))\r
- (if (if reverse\r
- (not (looking-at regexp))\r
- (looking-at regexp))\r
- (progn\r
- (incf number)\r
- (when first\r
- (setq last t))\r
- (delete-region\r
- (point)\r
- ;; There might be a continuation header, so we have to search\r
- ;; until we find a new non-continuation line.\r
- (progn\r
- (forward-line 1)\r
- (if (re-search-forward "^[^ \t]" nil t)\r
- (goto-char (match-beginning 0))\r
- (point-max)))))\r
- (forward-line 1)\r
- (if (re-search-forward "^[^ \t]" nil t)\r
- (goto-char (match-beginning 0))\r
- (goto-char (point-max)))))\r
- number))\r
-\r
-(defun message-remove-first-header (header)\r
- "Remove the first instance of HEADER if there is more than one."\r
- (let ((count 0)\r
- (regexp (concat "^" (regexp-quote header) ":")))\r
- (save-excursion\r
- (goto-char (point-min))\r
- (while (re-search-forward regexp nil t)\r
- (incf count)))\r
- (while (> count 1)\r
- (message-remove-header header nil t)\r
- (decf count))))\r
-\r
-(defun message-narrow-to-headers ()\r
- "Narrow the buffer to the head of the message."\r
- (widen)\r
- (narrow-to-region\r
- (goto-char (point-min))\r
- (if (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n") nil t)\r
- (match-beginning 0)\r
- (point-max)))\r
- (goto-char (point-min)))\r
-\r
-(defun message-narrow-to-head ()\r
- "Narrow the buffer to the head of the message.\r
-Point is left at the beginning of the narrowed-to region."\r
- (widen)\r
- (narrow-to-region\r
- (goto-char (point-min))\r
- (if (search-forward "\n\n" nil 1)\r
- (1- (point))\r
- (point-max)))\r
- (goto-char (point-min)))\r
-\r
-(defun message-narrow-to-headers-or-head ()\r
- "Narrow the buffer to the head of the message."\r
- (widen)\r
- (narrow-to-region\r
- (goto-char (point-min))\r
- (cond\r
- ((re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n") nil t)\r
- (match-beginning 0))\r
- ((search-forward "\n\n" nil t)\r
- (1- (point)))\r
- (t\r
- (point-max))))\r
- (goto-char (point-min)))\r
-\r
-(defun message-news-p ()\r
- "Say whether the current buffer contains a news message."\r
- (and (not message-this-is-mail)\r
- (or message-this-is-news\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (and (message-fetch-field "newsgroups")\r
- (not (message-fetch-field "posted-to"))))))))\r
-\r
-(defun message-mail-p ()\r
- "Say whether the current buffer contains a mail message."\r
- (and (not message-this-is-news)\r
- (or message-this-is-mail\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (or (message-fetch-field "to")\r
- (message-fetch-field "cc")\r
- (message-fetch-field "bcc")))))))\r
-\r
-(defun message-next-header ()\r
- "Go to the beginning of the next header."\r
- (beginning-of-line)\r
- (or (eobp) (forward-char 1))\r
- (not (if (re-search-forward "^[^ \t]" nil t)\r
- (beginning-of-line)\r
- (goto-char (point-max)))))\r
-\r
-(defun message-sort-headers-1 ()\r
- "Sort the buffer as headers using `message-rank' text props."\r
- (goto-char (point-min))\r
- (sort-subr\r
- nil 'message-next-header\r
- (lambda ()\r
- (message-next-header)\r
- (unless (bobp)\r
- (forward-char -1)))\r
- (lambda ()\r
- (or (get-text-property (point) 'message-rank)\r
- 10000))))\r
-\r
-(defun message-sort-headers ()\r
- "Sort the headers of the current message according to `message-header-format-alist'."\r
- (interactive)\r
- (save-excursion\r
- (save-restriction\r
- (let ((max (1+ (length message-header-format-alist)))\r
- rank)\r
- (message-narrow-to-headers)\r
- (while (re-search-forward "^[^ \n]+:" nil t)\r
- (put-text-property\r
- (match-beginning 0) (1+ (match-beginning 0))\r
- 'message-rank\r
- (if (setq rank (length (memq (assq (intern (buffer-substring\r
- (match-beginning 0)\r
- (1- (match-end 0))))\r
- message-header-format-alist)\r
- message-header-format-alist)))\r
- (- max rank)\r
- (1+ max)))))\r
- (message-sort-headers-1))))\r
-\r
-\f\r
-\r
-;;;\r
-;;; Message mode\r
-;;;\r
-\r
-;;; Set up keymap.\r
-\r
-(defvar message-mode-map nil)\r
-\r
-(unless message-mode-map\r
- (setq message-mode-map (make-keymap))\r
- (set-keymap-parent message-mode-map text-mode-map)\r
- (define-key message-mode-map "\C-c?" 'describe-mode)\r
-\r
- (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)\r
- (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)\r
- (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)\r
- (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)\r
- (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)\r
- ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)\r
- (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)\r
- (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)\r
- (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)\r
- (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)\r
- (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)\r
- (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)\r
- (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)\r
- (define-key message-mode-map "\C-c\C-b" 'message-goto-body)\r
- (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)\r
-\r
- (define-key message-mode-map "\C-c\C-t" 'message-insert-to)\r
- (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)\r
-\r
- (define-key message-mode-map "\C-c\C-y" 'message-yank-original)\r
- (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)\r
- (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)\r
- (define-key message-mode-map "\C-c\M-h" 'message-insert-headers)\r
- (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)\r
- (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)\r
- (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)\r
-\r
- (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)\r
- (define-key message-mode-map "\C-c\C-s" 'message-send)\r
- (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)\r
- (define-key message-mode-map "\C-c\C-d" 'message-dont-send)\r
-\r
- (define-key message-mode-map "\C-c\C-e" 'message-elide-region)\r
- (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)\r
- (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)\r
- (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)\r
-\r
- (define-key message-mode-map "\t" 'message-tab)\r
-\r
- (define-key message-mode-map "\C-x\C-s" 'message-save-drafts)\r
- (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))\r
-\r
-(easy-menu-define\r
- message-mode-menu message-mode-map "Message Menu."\r
- '("Message"\r
- ["Sort Headers" message-sort-headers t]\r
- ["Yank Original" message-yank-original t]\r
- ["Fill Yanked Message" message-fill-yanked-message t]\r
- ["Insert Signature" message-insert-signature t]\r
- ["Caesar (rot13) Message" message-caesar-buffer-body t]\r
- ["Caesar (rot13) Region" message-caesar-region (mark t)]\r
- ["Elide Region" message-elide-region (mark t)]\r
- ["Delete Outside Region" message-delete-not-region (mark t)]\r
- ["Kill To Signature" message-kill-to-signature t]\r
- ["Newline and Reformat" message-newline-and-reformat t]\r
- ["Rename buffer" message-rename-buffer t]\r
- ["Spellcheck" ispell-message t]\r
- "----"\r
- ["Send Message" message-send-and-exit t]\r
- ["Abort Message" message-dont-send t]\r
- ["Kill Message" message-kill-buffer t]))\r
-\r
-(easy-menu-define\r
- message-mode-field-menu message-mode-map ""\r
- '("Field"\r
- ["Fetch To" message-insert-to t]\r
- ["Fetch Newsgroups" message-insert-newsgroups t]\r
- "----"\r
- ["To" message-goto-to t]\r
- ["Subject" message-goto-subject t]\r
- ["Cc" message-goto-cc t]\r
- ["Reply-To" message-goto-reply-to t]\r
- ["Mail-Reply-To" message-goto-mail-reply-to t]\r
- ["Mail-Followup-To" message-goto-mail-followup-to t]\r
- ["Mail-Copies-To" message-goto-mail-copies-to t]\r
- ["Summary" message-goto-summary t]\r
- ["Keywords" message-goto-keywords t]\r
- ["Newsgroups" message-goto-newsgroups t]\r
- ["Followup-To" message-goto-followup-to t]\r
- ["Distribution" message-goto-distribution t]\r
- ["Body" message-goto-body t]\r
- ["Signature" message-goto-signature t]))\r
-\r
-(defvar facemenu-add-face-function)\r
-(defvar facemenu-remove-face-function)\r
-\r
-;;;###autoload\r
-(defun message-mode ()\r
- "Major mode for editing mail and news to be sent.\r
-Like Text Mode but with these additional commands:\r
-C-c C-s message-send (send the message) C-c C-c message-send-and-exit\r
-C-c C-f move to a header field (and create it if there isn't):\r
- C-c C-f C-t move to To C-c C-f C-s move to Subject\r
- C-c C-f C-c move to Cc C-c C-f C-b move to Bcc\r
- C-c C-f C-w move to Fcc C-c C-f C-r move to Reply-To\r
- C-c C-f C-u move to Summary C-c C-f C-n move to Newsgroups\r
- C-c C-f C-k move to Keywords C-c C-f C-d move to Distribution\r
- C-c C-f C-m move to Mail-Followup-To\r
- C-c C-f C-f move to Followup-To\r
-C-c C-t message-insert-to (add a To header to a news followup)\r
-C-c C-n message-insert-newsgroups (add a Newsgroup header to a news reply)\r
-C-c C-b message-goto-body (move to beginning of message text).\r
-C-c C-i message-goto-signature (move to the beginning of the signature).\r
-C-c C-w message-insert-signature (insert `message-signature-file' file).\r
-C-c C-y message-yank-original (insert current message, if any).\r
-C-c C-q message-fill-yanked-message (fill what was yanked).\r
-C-c C-e message-elide-region (elide the text between point and mark).\r
-C-c C-v message-delete-not-region (remove the text outside the region).\r
-C-c C-z message-kill-to-signature (kill the text up to the signature).\r
-C-c C-r message-caesar-buffer-body (rot13 the message body)."\r
- (interactive)\r
- (kill-all-local-variables)\r
- (set (make-local-variable 'message-reply-buffer) nil)\r
- (make-local-variable 'message-send-actions)\r
- (make-local-variable 'message-exit-actions)\r
- (make-local-variable 'message-kill-actions)\r
- (make-local-variable 'message-postpone-actions)\r
- (make-local-variable 'message-draft-article)\r
- (make-local-hook 'kill-buffer-hook)\r
- (set-syntax-table message-mode-syntax-table)\r
- (use-local-map message-mode-map)\r
- (setq local-abbrev-table message-mode-abbrev-table)\r
- (setq major-mode 'message-mode)\r
- (setq mode-name "Message")\r
- (setq buffer-offer-save t)\r
- (make-local-variable 'facemenu-add-face-function)\r
- (make-local-variable 'facemenu-remove-face-function)\r
- (setq facemenu-add-face-function\r
- (lambda (face end)\r
- (let ((face-fun (cdr (assq face message-face-alist))))\r
- (if face-fun\r
- (funcall face-fun (point) end)\r
- (error "Face %s not configured for %s mode" face mode-name)))\r
- "")\r
- facemenu-remove-face-function t)\r
- (make-local-variable 'paragraph-separate)\r
- (make-local-variable 'paragraph-start)\r
- ;; `-- ' precedes the signature. `-----' appears at the start of the\r
- ;; lines that delimit forwarded messages.\r
- ;; Lines containing just >= 3 dashes, perhaps after whitespace,\r
- ;; are also sometimes used and should be separators.\r
- (setq paragraph-start\r
- (concat (regexp-quote mail-header-separator)\r
- "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"\r
- "-- $\\|---+$\\|"\r
- page-delimiter\r
- ;;!!! Uhm... shurely this can't be right?\r
- "[> " (regexp-quote message-yank-prefix) "]+$"))\r
- (setq paragraph-separate paragraph-start)\r
- (make-local-variable 'message-reply-headers)\r
- (setq message-reply-headers nil)\r
- (make-local-variable 'message-user-agent)\r
- (make-local-variable 'message-post-method)\r
- (set (make-local-variable 'message-sent-message-via) nil)\r
- (set (make-local-variable 'message-checksum) nil)\r
- (make-local-variable 'message-parameter-alist)\r
- (setq message-parameter-alist\r
- (copy-sequence message-startup-parameter-alist))\r
- ;;(when (fboundp 'mail-hist-define-keys)\r
- ;; (mail-hist-define-keys))\r
- (when (string-match "XEmacs\\|Lucid" emacs-version)\r
- (message-setup-toolbar))\r
- (easy-menu-add message-mode-menu message-mode-map)\r
- (easy-menu-add message-mode-field-menu message-mode-map)\r
- ;; Allow mail alias things.\r
- (when (eq message-mail-alias-type 'abbrev)\r
- (if (fboundp 'mail-abbrevs-setup)\r
- (mail-abbrevs-setup)\r
- (mail-aliases-setup)))\r
- (message-set-auto-save-file-name)\r
- (unless (string-match "XEmacs" emacs-version)\r
- (set (make-local-variable 'font-lock-defaults)\r
- '(message-font-lock-keywords t)))\r
- (make-local-variable 'adaptive-fill-regexp)\r
- (setq adaptive-fill-regexp\r
- (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|" adaptive-fill-regexp))\r
- (unless (boundp 'adaptive-fill-first-line-regexp)\r
- (setq adaptive-fill-first-line-regexp nil))\r
- (make-local-variable 'adaptive-fill-first-line-regexp)\r
- (setq adaptive-fill-first-line-regexp\r
- (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|"\r
- adaptive-fill-first-line-regexp))\r
- (run-hooks 'text-mode-hook 'message-mode-hook))\r
-\r
-\f\r
-\r
-;;;\r
-;;; Message mode commands\r
-;;;\r
-\r
-;;; Movement commands\r
-\r
-(defun message-goto-to ()\r
- "Move point to the To header."\r
- (interactive)\r
- (message-position-on-field "To"))\r
-\r
-(defun message-goto-subject ()\r
- "Move point to the Subject header."\r
- (interactive)\r
- (message-position-on-field "Subject"))\r
-\r
-(defun message-goto-cc ()\r
- "Move point to the Cc header."\r
- (interactive)\r
- (message-position-on-field "Cc" "To"))\r
-\r
-(defun message-goto-bcc ()\r
- "Move point to the Bcc header."\r
- (interactive)\r
- (message-position-on-field "Bcc" "Cc" "To"))\r
-\r
-(defun message-goto-fcc ()\r
- "Move point to the Fcc header."\r
- (interactive)\r
- (message-position-on-field "Fcc" "To" "Newsgroups"))\r
-\r
-(defun message-goto-reply-to ()\r
- "Move point to the Reply-To header."\r
- (interactive)\r
- (message-position-on-field "Reply-To" "Subject"))\r
-\r
-(defun message-goto-mail-reply-to ()\r
- "Move point to the Mail-Reply-To header."\r
- (interactive)\r
- (message-position-on-field "Mail-Reply-To" "Subject"))\r
-\r
-(defun message-goto-mail-followup-to ()\r
- "Move point to the Mail-Followup-To header."\r
- (interactive)\r
- (message-position-on-field "Mail-Followup-To" "Subject"))\r
-\r
-(defun message-goto-mail-copies-to ()\r
- "Move point to the Mail-Copies-To header."\r
- (interactive)\r
- (message-position-on-field "Mail-Copies-To" "Subject"))\r
-\r
-(defun message-goto-newsgroups ()\r
- "Move point to the Newsgroups header."\r
- (interactive)\r
- (message-position-on-field "Newsgroups"))\r
-\r
-(defun message-goto-distribution ()\r
- "Move point to the Distribution header."\r
- (interactive)\r
- (message-position-on-field "Distribution"))\r
-\r
-(defun message-goto-followup-to ()\r
- "Move point to the Followup-To header."\r
- (interactive)\r
- (message-position-on-field "Followup-To" "Newsgroups"))\r
-\r
-(defun message-goto-keywords ()\r
- "Move point to the Keywords header."\r
- (interactive)\r
- (message-position-on-field "Keywords" "Subject"))\r
-\r
-(defun message-goto-summary ()\r
- "Move point to the Summary header."\r
- (interactive)\r
- (message-position-on-field "Summary" "Subject"))\r
-\r
-(defun message-goto-body ()\r
- "Move point to the beginning of the message body."\r
- (interactive)\r
- (if (looking-at "[ \t]*\n") (expand-abbrev))\r
- (goto-char (point-min))\r
- (search-forward (concat "\n" mail-header-separator "\n") nil t))\r
-\r
-(defun message-goto-eoh ()\r
- "Move point to the end of the headers."\r
- (interactive)\r
- (message-goto-body)\r
- (forward-line -2))\r
-\r
-(defun message-goto-signature ()\r
- "Move point to the beginning of the message signature.\r
-If there is no signature in the article, go to the end and\r
-return nil."\r
- (interactive)\r
- (goto-char (point-min))\r
- (if (re-search-forward message-signature-separator nil t)\r
- (forward-line 1)\r
- (goto-char (point-max))\r
- nil))\r
-\r
-\f\r
-\r
-(defun message-insert-to (&optional force)\r
- "Insert a To header that points to the author of the article being replied to.\r
-If the original author requested not to be sent mail, the function signals\r
-an error.\r
-With the prefix argument FORCE, insert the header anyway."\r
- (interactive "P")\r
- (let ((co (message-fetch-reply-field "mail-copies-to")))\r
- (when (and (null force)\r
- co\r
- (or (equal (downcase co) "never")\r
- (equal (downcase co) "nobody")))\r
- (error "The user has requested not to have copies sent via mail")))\r
- (when (and (message-position-on-field "To")\r
- (mail-fetch-field "to")\r
- (not (string-match "\\` *\\'" (mail-fetch-field "to"))))\r
- (insert ", "))\r
- (insert (or (message-fetch-reply-field "reply-to")\r
- (message-fetch-reply-field "from") "")))\r
-\r
-(defun message-insert-newsgroups ()\r
- "Insert the Newsgroups header from the article being replied to."\r
- (interactive)\r
- (when (and (message-position-on-field "Newsgroups")\r
- (mail-fetch-field "newsgroups")\r
- (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))\r
- (insert ","))\r
- (insert (or (message-fetch-reply-field "newsgroups") "")))\r
-\r
-\f\r
-\r
-;;; Various commands\r
-\r
-(defun message-delete-not-region (beg end)\r
- "Delete everything in the body of the current message that is outside of the region."\r
- (interactive "r")\r
- (save-excursion\r
- (goto-char end)\r
- (delete-region (point) (if (not (message-goto-signature))\r
- (point)\r
- (forward-line -2)\r
- (point)))\r
- (insert "\n")\r
- (goto-char beg)\r
- (delete-region beg (progn (message-goto-body)\r
- (forward-line 2)\r
- (point))))\r
- (when (message-goto-signature)\r
- (forward-line -2)))\r
-\r
-(defun message-kill-to-signature ()\r
- "Deletes all text up to the signature."\r
- (interactive)\r
- (let ((point (point)))\r
- (message-goto-signature)\r
- (unless (eobp)\r
- (forward-line -2))\r
- (kill-region point (point))\r
- (unless (bolp)\r
- (insert "\n"))))\r
-\r
-(defun message-newline-and-reformat ()\r
- "Insert four newlines, and then reformat if inside quoted text."\r
- (interactive)\r
- (let ((point (point))\r
- quoted)\r
- (save-excursion\r
- (beginning-of-line)\r
- (setq quoted (looking-at (regexp-quote message-yank-prefix))))\r
- (insert "\n\n\n\n")\r
- (when quoted\r
- (insert message-yank-prefix))\r
- (fill-paragraph nil)\r
- (goto-char point)\r
- (forward-line 2)))\r
-\r
-(defun message-insert-signature (&optional force)\r
- "Insert a signature. See documentation for the `message-signature' variable."\r
- (interactive (list 0))\r
- (let* ((signature\r
- (cond\r
- ((and (null message-signature)\r
- (eq force 0))\r
- (save-excursion\r
- (goto-char (point-max))\r
- (not (re-search-backward message-signature-separator nil t))))\r
- ((and (null message-signature)\r
- force)\r
- t)\r
- ((message-functionp message-signature)\r
- (funcall message-signature))\r
- ((listp message-signature)\r
- (eval message-signature))\r
- (t message-signature)))\r
- (signature\r
- (cond ((stringp signature)\r
- signature)\r
- ((and (eq t signature)\r
- message-signature-file\r
- (file-exists-p message-signature-file))\r
- signature))))\r
- (when signature\r
- (goto-char (point-max))\r
- ;; Insert the signature.\r
- (unless (bolp)\r
- (insert "\n"))\r
- (insert "\n-- \n")\r
- (if (eq signature t)\r
- (insert-file-contents message-signature-file)\r
- (insert signature))\r
- (goto-char (point-max))\r
- (or (bolp) (insert "\n")))))\r
-\r
-(defun message-elide-region (b e)\r
- "Elide the text between point and mark.\r
-An ellipsis (from `message-elide-elipsis') will be inserted where the\r
-text was killed."\r
- (interactive "r")\r
- (kill-region b e)\r
- (unless (bolp)\r
- (insert "\n"))\r
- (insert message-elide-elipsis))\r
-\r
-(defvar message-caesar-translation-table nil)\r
-\r
-(defun message-caesar-region (b e &optional n)\r
- "Caesar rotation of region by N, default 13, for decrypting netnews."\r
- (interactive\r
- (list\r
- (min (point) (or (mark t) (point)))\r
- (max (point) (or (mark t) (point)))\r
- (when current-prefix-arg\r
- (prefix-numeric-value current-prefix-arg))))\r
-\r
- (setq n (if (numberp n) (mod n 26) 13)) ;canonize N\r
- (unless (or (zerop n) ; no action needed for a rot of 0\r
- (= b e)) ; no region to rotate\r
- ;; We build the table, if necessary.\r
- (when (or (not message-caesar-translation-table)\r
- (/= (aref message-caesar-translation-table ?a) (+ ?a n)))\r
- (setq message-caesar-translation-table\r
- (message-make-caesar-translation-table n)))\r
- ;; Then we translate the region. Do it this way to retain\r
- ;; text properties.\r
- (while (< b e)\r
- (when (< (char-after b) 255)\r
- (subst-char-in-region\r
- b (1+ b) (char-after b)\r
- (aref message-caesar-translation-table (char-after b))))\r
- (incf b))))\r
-\r
-(defun message-make-caesar-translation-table (n)\r
- "Create a rot table with offset N."\r
- (let ((i -1)\r
- (table (make-string 256 0)))\r
- (while (< (incf i) 256)\r
- (aset table i i))\r
- (concat\r
- (substring table 0 ?A)\r
- (substring table (+ ?A n) (+ ?A n (- 26 n)))\r
- (substring table ?A (+ ?A n))\r
- (substring table (+ ?A 26) ?a)\r
- (substring table (+ ?a n) (+ ?a n (- 26 n)))\r
- (substring table ?a (+ ?a n))\r
- (substring table (+ ?a 26) 255))))\r
-\r
-(defun message-caesar-buffer-body (&optional rotnum)\r
- "Caesar rotates all letters in the current buffer by 13 places.\r
-Used to encode/decode possiblyun offensive messages (commonly in net.jokes).\r
-With prefix arg, specifies the number of places to rotate each letter forward.\r
-Mail and USENET news headers are not rotated."\r
- (interactive (if current-prefix-arg\r
- (list (prefix-numeric-value current-prefix-arg))\r
- (list nil)))\r
- (save-excursion\r
- (save-restriction\r
- (when (message-goto-body)\r
- (narrow-to-region (point) (point-max)))\r
- (message-caesar-region (point-min) (point-max) rotnum))))\r
-\r
-(defun message-pipe-buffer-body (program)\r
- "Pipe the message body in the current buffer through PROGRAM."\r
- (save-excursion\r
- (save-restriction\r
- (when (message-goto-body)\r
- (narrow-to-region (point) (point-max)))\r
- (let ((body (buffer-substring (point-min) (point-max))))\r
- (unless (equal 0 (call-process-region\r
- (point-min) (point-max) program t t))\r
- (insert body)\r
- (message "%s failed." program))))))\r
-\r
-(defun message-rename-buffer (&optional enter-string)\r
- "Rename the *message* buffer to \"*message* RECIPIENT\".\r
-If the function is run with a prefix, it will ask for a new buffer\r
-name, rather than giving an automatic name."\r
- (interactive "Pbuffer name: ")\r
- (save-excursion\r
- (save-restriction\r
- (goto-char (point-min))\r
- (narrow-to-region (point)\r
- (search-forward mail-header-separator nil 'end))\r
- (let* ((mail-to (or\r
- (if (message-news-p) (message-fetch-field "Newsgroups")\r
- (message-fetch-field "To"))\r
- ""))\r
- (mail-trimmed-to\r
- (if (string-match "," mail-to)\r
- (concat (substring mail-to 0 (match-beginning 0)) ", ...")\r
- mail-to))\r
- (name-default (concat "*message* " mail-trimmed-to))\r
- (name (if enter-string\r
- (read-string "New buffer name: " name-default)\r
- name-default)))\r
- (rename-buffer name t)))))\r
-\r
-(defun message-fill-yanked-message (&optional justifyp)\r
- "Fill the paragraphs of a message yanked into this one.\r
-Numeric argument means justify as well."\r
- (interactive "P")\r
- (save-excursion\r
- (goto-char (point-min))\r
- (search-forward (concat "\n" mail-header-separator "\n") nil t)\r
- (let ((fill-prefix message-yank-prefix))\r
- (fill-individual-paragraphs (point) (point-max) justifyp t))))\r
-\r
-(defun message-indent-citation ()\r
- "Modify text just inserted from a message to be cited.\r
-The inserted text should be the region.\r
-When this function returns, the region is again around the modified text.\r
-\r
-Normally, indent each nonblank line `message-indentation-spaces' spaces.\r
-However, if `message-yank-prefix' is non-nil, insert that prefix on each line."\r
- (let ((start (point)))\r
- ;; Remove unwanted headers.\r
- (when message-ignored-cited-headers\r
- (let (all-removed)\r
- (save-restriction\r
- (narrow-to-region\r
- (goto-char start)\r
- (if (search-forward "\n\n" nil t)\r
- (1- (point))\r
- (point)))\r
- (message-remove-header message-ignored-cited-headers t)\r
- (when (= (point-min) (point-max))\r
- (setq all-removed t))\r
- (goto-char (point-max)))\r
- (if all-removed\r
- (goto-char start)\r
- (forward-line 1))))\r
- ;; Delete blank lines at the start of the buffer.\r
- (while (and (point-min)\r
- (eolp)\r
- (not (eobp)))\r
- (message-delete-line))\r
- ;; Delete blank lines at the end of the buffer.\r
- (goto-char (point-max))\r
- (unless (eolp)\r
- (insert "\n"))\r
- (while (and (zerop (forward-line -1))\r
- (looking-at "$"))\r
- (message-delete-line))\r
- ;; Do the indentation.\r
- (if (null message-yank-prefix)\r
- (indent-rigidly start (mark t) message-indentation-spaces)\r
- (save-excursion\r
- (goto-char start)\r
- (while (< (point) (mark t))\r
- (insert message-yank-prefix)\r
- (forward-line 1))))\r
- (goto-char start)))\r
-\r
-(defvar gnus-article-copy)\r
-(defun message-yank-original (&optional arg)\r
- "Insert the message being replied to, if any.\r
-Puts point before the text and mark after.\r
-Normally indents each nonblank line ARG spaces (default 3). However,\r
-if `message-yank-prefix' is non-nil, insert that prefix on each line.\r
-\r
-This function uses `message-cite-function' to do the actual citing.\r
-\r
-Just \\[universal-argument] as argument means don't indent, insert no\r
-prefix, and don't delete any headers.\r
-\r
-In addition, if `message-yank-add-new-references' is non-nil and this\r
-command is called interactively, new IDs from the yanked article will\r
-be added to \"References\" field."\r
- (interactive "P")\r
- (let ((modified (buffer-modified-p))\r
- (buffer (message-eval-parameter message-reply-buffer))\r
- refs references)\r
- (when (and buffer\r
- message-cite-function)\r
- (delete-windows-on buffer t)\r
- (insert-buffer buffer) ; mark will be set at the end of article.\r
-\r
- ;; Add new IDs to References field.\r
- (when (and message-yank-add-new-references (interactive-p))\r
- (save-excursion\r
- (save-restriction\r
- (narrow-to-region (point) (mark t))\r
- (message-narrow-to-head)\r
- (setq refs (concat (or (message-fetch-field "References") "")\r
- " "\r
- (or (message-fetch-field "Message-ID") "")))\r
- (unless (string-match "^ +$" refs)\r
- (widen)\r
- (message-narrow-to-headers)\r
- (setq references (message-fetch-field "References"))\r
- (when references\r
- (setq references (split-string references)))\r
- (mapcar\r
- (lambda (ref)\r
- (or (zerop (length ref))\r
- (member ref references)\r
- (setq references (append references (list ref)))))\r
- (split-string refs))\r
- (when references\r
- (goto-char (point-min))\r
- (let ((case-fold-search t))\r
- (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)\r
- (replace-match "")\r
- (goto-char (point-max))))\r
- (mail-header-format\r
- (list (or (assq 'References message-header-format-alist)\r
- '(References . message-shorten-references)))\r
- (list (cons 'References\r
- (mapconcat 'identity references " "))))\r
- (backward-delete-char 1))))))\r
-\r
- (funcall message-cite-function)\r
- (message-exchange-point-and-mark)\r
- (unless (bolp)\r
- (insert ?\n))\r
- (unless modified\r
- (setq message-checksum (message-checksum))))))\r
-\r
-(defun message-cite-original-without-signature ()\r
- "Cite function in the standard Message manner."\r
- (let ((start (point))\r
- (end (mark t))\r
- (functions\r
- (when message-indent-citation-function\r
- (if (listp message-indent-citation-function)\r
- message-indent-citation-function\r
- (list message-indent-citation-function)))))\r
- (goto-char end)\r
- (when (re-search-backward message-signature-separator start t)\r
- ;; Also peel off any blank lines before the signature.\r
- (forward-line -1)\r
- (while (looking-at "^[ \t]*$")\r
- (forward-line -1))\r
- (forward-line 1)\r
- (delete-region (point) end))\r
- (goto-char start)\r
- (while functions\r
- (funcall (pop functions)))\r
- (when message-citation-line-function\r
- (unless (bolp)\r
- (insert "\n"))\r
- (funcall message-citation-line-function))))\r
-\r
-(defvar mail-citation-hook) ;Compiler directive\r
-(defun message-cite-original ()\r
- "Cite function in the standard Message manner."\r
- (if (and (boundp 'mail-citation-hook)\r
- mail-citation-hook)\r
- (run-hooks 'mail-citation-hook)\r
- (let ((start (point))\r
- (end (mark t))\r
- (functions\r
- (when message-indent-citation-function\r
- (if (listp message-indent-citation-function)\r
- message-indent-citation-function\r
- (list message-indent-citation-function)))))\r
- (goto-char start)\r
- (while functions\r
- (funcall (pop functions)))\r
- (when message-citation-line-function\r
- (unless (bolp)\r
- (insert "\n"))\r
- (funcall message-citation-line-function)))))\r
-\r
-(defun message-insert-citation-line ()\r
- "Function that inserts a simple citation line."\r
- (when message-reply-headers\r
- (insert (mail-header-from message-reply-headers) " writes:\n\n")))\r
-\r
-(defun message-position-on-field (header &rest afters)\r
- (let ((case-fold-search t))\r
- (save-restriction\r
- (narrow-to-region\r
- (goto-char (point-min))\r
- (progn\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (match-beginning 0)))\r
- (goto-char (point-min))\r
- (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)\r
- (progn\r
- (re-search-forward "^[^ \t]" nil 'move)\r
- (beginning-of-line)\r
- (skip-chars-backward "\n")\r
- t)\r
- (while (and afters\r
- (not (re-search-forward\r
- (concat "^" (regexp-quote (car afters)) ":")\r
- nil t)))\r
- (pop afters))\r
- (when afters\r
- (re-search-forward "^[^ \t]" nil 'move)\r
- (beginning-of-line))\r
- (insert header ": \n")\r
- (forward-char -1)\r
- nil))))\r
-\r
-(defun message-remove-signature ()\r
- "Remove the signature from the text between point and mark.\r
-The text will also be indented the normal way."\r
- (save-excursion\r
- (let ((start (point))\r
- mark)\r
- (if (not (re-search-forward message-signature-separator (mark t) t))\r
- ;; No signature here, so we just indent the cited text.\r
- (message-indent-citation)\r
- ;; Find the last non-empty line.\r
- (forward-line -1)\r
- (while (looking-at "[ \t]*$")\r
- (forward-line -1))\r
- (forward-line 1)\r
- (setq mark (set-marker (make-marker) (point)))\r
- (goto-char start)\r
- (message-indent-citation)\r
- ;; Enable undoing the deletion.\r
- (undo-boundary)\r
- (delete-region mark (mark t))\r
- (set-marker mark nil)))))\r
-\r
-\f\r
-\r
-;;;\r
-;;; Sending messages\r
-;;;\r
-\r
-;; Avoid byte-compile warning.\r
-(defvar message-encoding-buffer nil)\r
-(defvar message-edit-buffer nil)\r
-(defvar message-mime-mode nil)\r
-\r
-(defun message-send-and-exit (&optional arg)\r
- "Send message like `message-send', then, if no errors, exit from mail buffer."\r
- (interactive "P")\r
- (let ((buf (current-buffer))\r
- (actions message-exit-actions)\r
- (frame (selected-frame))\r
- (org-frame message-original-frame))\r
- (when (and (message-send arg)\r
- (buffer-name buf))\r
- (if message-kill-buffer-on-exit\r
- (kill-buffer buf)\r
- (bury-buffer buf)\r
- (when (eq buf (current-buffer))\r
- (message-bury buf)))\r
- (message-do-actions actions)\r
- (message-delete-frame frame org-frame)\r
- t)))\r
-\r
-(defun message-dont-send ()\r
- "Don't send the message you have been editing."\r
- (interactive)\r
- (message-save-drafts)\r
- (let ((actions message-postpone-actions)\r
- (frame (selected-frame))\r
- (org-frame message-original-frame))\r
- (message-bury (current-buffer))\r
- (message-do-actions actions)\r
- (message-delete-frame frame org-frame)))\r
-\r
-(defun message-kill-buffer ()\r
- "Kill the current buffer."\r
- (interactive)\r
- (when (or (not (buffer-modified-p))\r
- (eq t message-kill-buffer-query-function)\r
- (funcall message-kill-buffer-query-function\r
- "The buffer modified; kill anyway? "))\r
- (let ((actions message-kill-actions)\r
- (frame (selected-frame))\r
- (org-frame message-original-frame))\r
- (setq buffer-file-name nil)\r
- (kill-buffer (current-buffer))\r
- (message-do-actions actions)\r
- (message-delete-frame frame org-frame)))\r
- (message ""))\r
-\r
-(defun message-mimic-kill-buffer ()\r
- "Kill the current buffer with query."\r
- (interactive)\r
- (unless (eq 'message-mode major-mode)\r
- (error "%s must be invoked from a message buffer." this-command))\r
- (let ((command this-command)\r
- (bufname (read-buffer (format "Kill buffer: (default %s) "\r
- (buffer-name)))))\r
- (if (or (not bufname)\r
- (string-equal bufname "")\r
- (string-equal bufname (buffer-name)))\r
- (message-kill-buffer)\r
- (message "%s must be invoked only for the current buffer." command))))\r
-\r
-(defun message-delete-frame (frame org-frame)\r
- "Delete frame for editing message."\r
- (when (and (or (and (featurep 'xemacs)\r
- (not (eq 'tty (device-type))))\r
- window-system\r
- (>= emacs-major-version 20))\r
- (or (and (eq message-delete-frame-on-exit t)\r
- (select-frame frame)\r
- (or (eq frame org-frame)\r
- (prog1\r
- (y-or-n-p "Delete this frame?")\r
- (message ""))))\r
- (and (eq message-delete-frame-on-exit 'ask)\r
- (select-frame frame)\r
- (prog1\r
- (y-or-n-p "Delete this frame?")\r
- (message "")))))\r
- (delete-frame frame)))\r
-\r
-(defun message-bury (buffer)\r
- "Bury this mail buffer."\r
- (let ((newbuf (other-buffer buffer)))\r
- (bury-buffer buffer)\r
- (if (and (fboundp 'frame-parameters)\r
- (cdr (assq 'dedicated (frame-parameters)))\r
- (not (null (delq (selected-frame) (visible-frame-list)))))\r
- (delete-frame (selected-frame))\r
- (switch-to-buffer newbuf))))\r
-\r
-(defun message-send (&optional arg)\r
- "Send the message in the current buffer.\r
-If `message-interactive' is non-nil, wait for success indication\r
-or error messages, and inform user.\r
-Otherwise any failure is reported in a message back to\r
-the user from the mailer."\r
- (interactive "P")\r
- ;; Disabled test.\r
- (when (or (buffer-modified-p)\r
- (message-check-element 'unchanged)\r
- (y-or-n-p "No changes in the buffer; really send? "))\r
- ;; Make it possible to undo the coming changes.\r
- (undo-boundary)\r
- (let ((inhibit-read-only t))\r
- (put-text-property (point-min) (point-max) 'read-only nil))\r
- (run-hooks 'message-send-hook)\r
- (message "Sending...")\r
- (let ((message-encoding-buffer\r
- (message-generate-new-buffer-clone-locals " message encoding"))\r
- (message-edit-buffer (current-buffer))\r
- (message-mime-mode mime-edit-mode-flag)\r
- (alist message-send-method-alist)\r
- (success t)\r
- elem sent)\r
- (save-excursion\r
- (set-buffer message-encoding-buffer)\r
- (erase-buffer)\r
- (insert-buffer message-edit-buffer)\r
- (funcall message-encode-function)\r
- (message-fix-before-sending)\r
- (while (and success\r
- (setq elem (pop alist)))\r
- (when (and (or (not (funcall (cadr elem)))\r
- (and (or (not (memq (car elem)\r
- message-sent-message-via))\r
- (y-or-n-p\r
- (format\r
- "Already sent message via %s; resend? "\r
- (car elem))))\r
- (setq success (funcall (caddr elem) arg)))))\r
- (setq sent t))))\r
- (when (and success sent)\r
- (message-do-fcc)\r
- ;;(when (fboundp 'mail-hist-put-headers-into-history)\r
- ;; (mail-hist-put-headers-into-history))\r
- (save-excursion\r
- (run-hooks 'message-sent-hook))\r
- (message "Sending...done")\r
- ;; Mark the buffer as unmodified and delete autosave.\r
- (set-buffer-modified-p nil)\r
- (delete-auto-save-file-if-necessary t)\r
- (message-disassociate-draft)\r
- ;; Delete other mail buffers and stuff.\r
- (message-do-send-housekeeping)\r
- (message-do-actions message-send-actions)\r
- ;; Return success.\r
- t))))\r
-\r
-(defun message-send-via-mail (arg)\r
- "Send the current message via mail."\r
- (message-send-mail arg))\r
-\r
-(defun message-send-via-news (arg)\r
- "Send the current message via news."\r
- (message-send-news arg))\r
-\r
-(defmacro message-check (type &rest forms)\r
- "Eval FORMS if TYPE is to be checked."\r
- `(or (message-check-element ,type)\r
- (save-excursion\r
- ,@forms)))\r
-\r
-(put 'message-check 'lisp-indent-function 1)\r
-(put 'message-check 'edebug-form-spec '(form body))\r
-\r
-(defun message-fix-before-sending ()\r
- "Do various things to make the message nice before sending it."\r
- ;; Make sure there's a newline at the end of the message.\r
- (goto-char (point-max))\r
- (unless (bolp)\r
- (insert "\n"))\r
- ;; Delete all invisible text.\r
- (message-check 'invisible-text\r
- (when (text-property-any (point-min) (point-max) 'invisible t)\r
- (put-text-property (point-min) (point-max) 'invisible nil)\r
- (unless (yes-or-no-p "Invisible text found and made visible; continue posting? ")\r
- (error "Invisible text found and made visible")))))\r
-\r
-(defun message-add-action (action &rest types)\r
- "Add ACTION to be performed when doing an exit of type TYPES."\r
- (let (var)\r
- (while types\r
- (set (setq var (intern (format "message-%s-actions" (pop types))))\r
- (nconc (symbol-value var) (list action))))))\r
-\r
-(defun message-do-actions (actions)\r
- "Perform all actions in ACTIONS."\r
- ;; Now perform actions on successful sending.\r
- (while actions\r
- (ignore-errors\r
- (cond\r
- ;; A simple function.\r
- ((message-functionp (car actions))\r
- (funcall (car actions)))\r
- ;; Something to be evaled.\r
- (t\r
- (eval (car actions)))))\r
- (pop actions)))\r
-\r
-(defsubst message-maybe-split-and-send-mail ()\r
- "Split a message if necessary, and send it via mail.\r
-Returns nil if sending succeeded, returns any string if sending failed.\r
-This sub function is for exclusive use of `message-send-mail'."\r
- (let ((mime-edit-split-ignored-field-regexp\r
- mime-edit-split-ignored-field-regexp)\r
- (case-fold-search t)\r
- failure)\r
- (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)\r
- (setq mime-edit-split-ignored-field-regexp\r
- (concat (substring mime-edit-split-ignored-field-regexp\r
- 0 (match-beginning 0))\r
- "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"\r
- "_so_don't_rape_it!"\r
- (substring mime-edit-split-ignored-field-regexp\r
- (match-end 0)))))\r
- (setq failure\r
- (or\r
- (catch 'message-sending-mail-failure\r
- (mime-edit-maybe-split-and-send\r
- (function\r
- (lambda ()\r
- (interactive)\r
- (save-restriction\r
- (std11-narrow-to-header mail-header-separator)\r
- (goto-char (point-min))\r
- (when (re-search-forward "^Message-ID:" nil t)\r
- (delete-region (match-end 0) (std11-field-end))\r
- (insert " " (message-make-message-id))))\r
- (condition-case err\r
- (funcall message-send-mail-function)\r
- (error\r
- (throw 'message-sending-mail-failure err))))))\r
- nil)\r
- (condition-case err\r
- (progn\r
- (funcall message-send-mail-function)\r
- nil)\r
- (error err))))\r
- (when failure\r
- (if (eq 'error (car failure))\r
- (cadr failure)\r
- (prin1-to-string failure)))))\r
-\r
-(defun message-send-mail (&optional arg)\r
- (require 'mail-utils)\r
- (let ((tembuf (message-generate-new-buffer-clone-locals " message temp"))\r
- (case-fold-search nil)\r
- (news (message-news-p))\r
- failure)\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- ;; Insert some headers.\r
- (let ((message-deletable-headers\r
- (if news nil message-deletable-headers)))\r
- (message-generate-headers message-required-mail-headers))\r
- ;; Let the user do all of the above.\r
- (run-hooks 'message-header-hook))\r
- (if (not (message-check-mail-syntax))\r
- (progn\r
- (message "")\r
- nil)\r
- (unwind-protect\r
- (save-excursion\r
- (set-buffer tembuf)\r
- (erase-buffer)\r
- (insert-buffer message-encoding-buffer)\r
- ;; Remove some headers.\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- ;; Remove some headers.\r
- (message-remove-header message-ignored-mail-headers t))\r
- (goto-char (point-max))\r
- ;; require one newline at the end.\r
- (or (= (preceding-char) ?\n)\r
- (insert ?\n))\r
- (when (and news\r
- (or (message-fetch-field "cc")\r
- (message-fetch-field "to")))\r
- (message-insert-courtesy-copy))\r
- (setq failure (message-maybe-split-and-send-mail)))\r
- (kill-buffer tembuf))\r
- (set-buffer message-edit-buffer)\r
- (if failure\r
- (progn\r
- (message "Couldn't send message via mail: %s" failure)\r
- nil)\r
- (push 'mail message-sent-message-via)))))\r
-\r
-(defun message-send-mail-with-sendmail ()\r
- "Send off the prepared buffer with sendmail."\r
- (let ((errbuf (if message-interactive\r
- (generate-new-buffer " sendmail errors")\r
- 0))\r
- resend-to-addresses delimline)\r
- (let ((case-fold-search t))\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (setq resend-to-addresses (message-fetch-field "resent-to")))\r
- ;; Change header-delimiter to be what sendmail expects.\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n"))\r
- (replace-match "\n")\r
- (backward-char 1)\r
- (setq delimline (point-marker))\r
- (run-hooks 'message-send-mail-hook)\r
- ;; Insert an extra newline if we need it to work around\r
- ;; Sun's bug that swallows newlines.\r
- (goto-char (1+ delimline))\r
- (when (eval message-mailer-swallows-blank-line)\r
- (newline))\r
- (when message-interactive\r
- (save-excursion\r
- (set-buffer errbuf)\r
- (erase-buffer))))\r
- (let ((default-directory "/"))\r
- (as-binary-process\r
- (apply 'call-process-region\r
- (append (list (point-min) (point-max)\r
- (if (boundp 'sendmail-program)\r
- sendmail-program\r
- "/usr/lib/sendmail")\r
- nil errbuf nil "-oi")\r
- ;; Always specify who from,\r
- ;; since some systems have broken sendmails.\r
- ;; But some systems are more broken with -f, so\r
- ;; we'll let users override this.\r
- (if (null message-sendmail-f-is-evil)\r
- (list "-f" (user-login-name)))\r
- ;; These mean "report errors by mail"\r
- ;; and "deliver in background".\r
- (if (null message-interactive) '("-oem" "-odb"))\r
- ;; Get the addresses from the message\r
- ;; unless this is a resend.\r
- ;; We must not do that for a resend\r
- ;; because we would find the original addresses.\r
- ;; For a resend, include the specific addresses.\r
- (if resend-to-addresses\r
- (list resend-to-addresses)\r
- '("-t"))))))\r
- (when message-interactive\r
- (save-excursion\r
- (set-buffer errbuf)\r
- (goto-char (point-min))\r
- (while (re-search-forward "\n\n* *" nil t)\r
- (replace-match "; "))\r
- (if (not (zerop (buffer-size)))\r
- (error "Sending...failed to %s"\r
- (buffer-substring (point-min) (point-max)))))\r
- (when (bufferp errbuf)\r
- (kill-buffer errbuf)))))\r
-\r
-(defun message-send-mail-with-qmail ()\r
- "Pass the prepared message buffer to qmail-inject.\r
-Refer to the documentation for the variable `message-send-mail-function'\r
-to find out how to use this."\r
- ;; replace the header delimiter with a blank line\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n"))\r
- (replace-match "\n")\r
- (backward-char 1)\r
- (run-hooks 'message-send-mail-hook)\r
- ;; send the message\r
- (case\r
- (as-binary-process\r
- (apply\r
- 'call-process-region 1 (point-max) message-qmail-inject-program\r
- nil nil nil\r
- ;; qmail-inject's default behaviour is to look for addresses on the\r
- ;; command line; if there're none, it scans the headers.\r
- ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.\r
- ;;\r
- ;; in general, ALL of qmail-inject's defaults are perfect for simply\r
- ;; reading a formatted (i. e., at least a To: or Resent-To header)\r
- ;; message from stdin.\r
- ;;\r
- ;; qmail also has the advantage of not having been raped by\r
- ;; various vendors, so we don't have to allow for that, either --\r
- ;; compare this with message-send-mail-with-sendmail and weep\r
- ;; for sendmail's lost innocence.\r
- ;;\r
- ;; all this is way cool coz it lets us keep the arguments entirely\r
- ;; free for -inject-arguments -- a big win for the user and for us\r
- ;; since we don't have to play that double-guessing game and the user\r
- ;; gets full control (no gestapo'ish -f's, for instance). --sj\r
- message-qmail-inject-args))\r
- ;; qmail-inject doesn't say anything on it's stdout/stderr,\r
- ;; we have to look at the retval instead\r
- (0 nil)\r
- (1 (error "qmail-inject reported permanent failure"))\r
- (111 (error "qmail-inject reported transient failure"))\r
- ;; should never happen\r
- (t (error "qmail-inject reported unknown failure"))))\r
-\r
-(defun message-send-mail-with-mh ()\r
- "Send the prepared message buffer with mh."\r
- (let ((mh-previous-window-config nil)\r
- (name (mh-new-draft-name)))\r
- (setq buffer-file-name name)\r
- ;; MH wants to generate these headers itself.\r
- (when message-mh-deletable-headers\r
- (let ((headers message-mh-deletable-headers))\r
- (while headers\r
- (goto-char (point-min))\r
- (and (re-search-forward\r
- (concat "^" (symbol-name (car headers)) ": *") nil t)\r
- (message-delete-line))\r
- (pop headers))))\r
- (run-hooks 'message-send-mail-hook)\r
- ;; Pass it on to mh.\r
- (mh-send-letter)))\r
-\r
-(defun message-send-mail-with-smtp ()\r
- "Send off the prepared buffer with SMTP."\r
- (require 'smtp) ; XXX\r
- (let ((case-fold-search t)\r
- recipients)\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (setq recipients\r
- ;; XXX: Should be replaced by better one.\r
- (smtp-deduce-address-list (current-buffer)\r
- (point-min) (point-max)))\r
- ;; Remove BCC lines.\r
- (message-remove-header "bcc"))\r
- ;; replace the header delimiter with a blank line.\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n"))\r
- (replace-match "\n")\r
- (backward-char 1)\r
- (run-hooks 'message-send-mail-hook)\r
- (if recipients\r
- (let ((result (smtp-via-smtp user-mail-address\r
- recipients\r
- (current-buffer))))\r
- (unless (eq result t)\r
- (error "Sending failed; " result)))\r
- (error "Sending failed; no recipients"))))\r
-\r
-(defsubst message-maybe-split-and-send-news (method)\r
- "Split a message if necessary, and send it via news.\r
-Returns nil if sending succeeded, returns t if sending failed.\r
-This sub function is for exclusive use of `message-send-news'."\r
- (let ((mime-edit-split-ignored-field-regexp\r
- mime-edit-split-ignored-field-regexp)\r
- (case-fold-search t))\r
- (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)\r
- (setq mime-edit-split-ignored-field-regexp\r
- (concat (substring mime-edit-split-ignored-field-regexp\r
- 0 (match-beginning 0))\r
- "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"\r
- "_so_don't_rape_it!"\r
- (substring mime-edit-split-ignored-field-regexp\r
- (match-end 0)))))\r
- (or\r
- (catch 'message-sending-news-failure\r
- (mime-edit-maybe-split-and-send\r
- (function\r
- (lambda ()\r
- (interactive)\r
- (save-restriction\r
- (std11-narrow-to-header mail-header-separator)\r
- (goto-char (point-min))\r
- (when (re-search-forward "^Message-ID:" nil t)\r
- (delete-region (match-end 0) (std11-field-end))\r
- (insert " " (message-make-message-id))))\r
- (unless (funcall message-send-news-function method)\r
- (throw 'message-sending-news-failure t)))))\r
- nil)\r
- (not (funcall message-send-news-function method)))))\r
-\r
-(defun message-send-news (&optional arg)\r
- (let ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))\r
- (case-fold-search nil)\r
- (method (if (message-functionp message-post-method)\r
- (funcall message-post-method arg)\r
- message-post-method))\r
- (message-syntax-checks\r
- (if arg\r
- (cons '(existing-newsgroups . disabled)\r
- message-syntax-checks)\r
- message-syntax-checks))\r
- result)\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- ;; Insert some headers.\r
- (message-generate-headers message-required-news-headers)\r
- ;; Let the user do all of the above.\r
- (run-hooks 'message-header-hook))\r
- (message-cleanup-headers)\r
- (if (not (message-check-news-syntax))\r
- nil\r
- (unwind-protect\r
- (save-excursion\r
- (set-buffer tembuf)\r
- (buffer-disable-undo)\r
- (erase-buffer)\r
- (insert-buffer message-encoding-buffer)\r
- ;; Remove some headers.\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- ;; Remove some headers.\r
- (message-remove-header message-ignored-news-headers t))\r
- (goto-char (point-max))\r
- ;; require one newline at the end.\r
- (or (= (preceding-char) ?\n)\r
- (insert ?\n))\r
- (setq result (message-maybe-split-and-send-news method)))\r
- (kill-buffer tembuf))\r
- (set-buffer message-edit-buffer)\r
- (if result\r
- (progn\r
- (message "Couldn't send message via news: %s"\r
- (nnheader-get-report (car method)))\r
- nil)\r
- (push 'news message-sent-message-via)))))\r
-\r
-;; 1997-09-29 by MORIOKA Tomohiko\r
-(defun message-send-news-with-gnus (method)\r
- (let ((case-fold-search t))\r
- ;; Remove the delimiter.\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "\n"))\r
- (replace-match "\n")\r
- (backward-char 1)\r
- (run-hooks 'message-send-news-hook)\r
- ;;(require (car method))\r
- ;;(funcall (intern (format "%s-open-server" (car method)))\r
- ;;(cadr method) (cddr method))\r
- ;;(setq result\r
- ;; (funcall (intern (format "%s-request-post" (car method)))\r
- ;; (cadr method)))\r
- (gnus-open-server method)\r
- (gnus-request-post method)\r
- ))\r
-\r
-;;;\r
-;;; Header generation & syntax checking.\r
-;;;\r
-\r
-(defun message-check-element (type)\r
- "Returns non-nil if this type is not to be checked."\r
- (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)\r
- t\r
- (let ((able (assq type message-syntax-checks)))\r
- (and (consp able)\r
- (eq (cdr able) 'disabled)))))\r
-\r
-(defun message-check-news-syntax ()\r
- "Check the syntax of the message."\r
- (save-excursion\r
- (save-restriction\r
- (widen)\r
- (and\r
- ;; We narrow to the headers and check them first.\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (message-check-news-header-syntax)))\r
- ;; Check the body.\r
- (save-excursion\r
- (set-buffer message-edit-buffer)\r
- (message-check-news-body-syntax))))))\r
-\r
-(defun message-check-news-header-syntax ()\r
- (and\r
- ;; Check the Subject header.\r
- (message-check 'subject\r
- (let* ((case-fold-search t)\r
- (subject (message-fetch-field "subject")))\r
- (or\r
- (and subject\r
- (not (string-match "\\`[ \t]*\\'" subject)))\r
- (ignore\r
- (message\r
- "The subject field is empty or missing. Posting is denied.")))))\r
- ;; Check for commands in Subject.\r
- (message-check 'subject-cmsg\r
- (if (string-match "^cmsg " (message-fetch-field "subject"))\r
- (y-or-n-p\r
- "The control code \"cmsg\" is in the subject. Really post? ")\r
- t))\r
- ;; Check for multiple identical headers.\r
- (message-check 'multiple-headers\r
- (let (found)\r
- (while (and (not found)\r
- (re-search-forward "^[^ \t:]+: " nil t))\r
- (save-excursion\r
- (or (re-search-forward\r
- (concat "^"\r
- (regexp-quote\r
- (setq found\r
- (buffer-substring\r
- (match-beginning 0) (- (match-end 0) 2))))\r
- ":")\r
- nil t)\r
- (setq found nil))))\r
- (if found\r
- (y-or-n-p (format "Multiple %s headers. Really post? " found))\r
- t)))\r
- ;; Check for Version and Sendsys.\r
- (message-check 'sendsys\r
- (if (re-search-forward "^Sendsys:\\|^Version:" nil t)\r
- (y-or-n-p\r
- (format "The article contains a %s command. Really post? "\r
- (buffer-substring (match-beginning 0)\r
- (1- (match-end 0)))))\r
- t))\r
- ;; See whether we can shorten Followup-To.\r
- (message-check 'shorten-followup-to\r
- (let ((newsgroups (message-fetch-field "newsgroups"))\r
- (followup-to (message-fetch-field "followup-to"))\r
- to)\r
- (when (and newsgroups\r
- (string-match "," newsgroups)\r
- (not followup-to)\r
- (not\r
- (zerop\r
- (length\r
- (setq to (completing-read\r
- "Followups to: (default all groups) "\r
- (mapcar (lambda (g) (list g))\r
- (cons "poster"\r
- (message-tokenize-header\r
- newsgroups)))))))))\r
- (goto-char (point-min))\r
- (insert "Followup-To: " to "\n"))\r
- t))\r
- ;; Check "Shoot me".\r
- (message-check 'shoot\r
- (if (re-search-forward\r
- "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)\r
- (y-or-n-p "You appear to have a misconfigured system. Really post? ")\r
- t))\r
- ;; Check for Approved.\r
- (message-check 'approved\r
- (if (re-search-forward "^Approved:" nil t)\r
- (y-or-n-p "The article contains an Approved header. Really post? ")\r
- t))\r
- ;; Check the Message-ID header.\r
- (message-check 'message-id\r
- (let* ((case-fold-search t)\r
- (message-id (message-fetch-field "message-id" t)))\r
- (or (not message-id)\r
- ;; Is there an @ in the ID?\r
- (and (string-match "@" message-id)\r
- ;; Is there a dot in the ID?\r
- (string-match "@[^.]*\\." message-id)\r
- ;; Does the ID end with a dot?\r
- (not (string-match "\\.>" message-id)))\r
- (y-or-n-p\r
- (format "The Message-ID looks strange: \"%s\". Really post? "\r
- message-id)))))\r
- ;; Check the Newsgroups & Followup-To headers.\r
- (message-check 'existing-newsgroups\r
- (let* ((case-fold-search t)\r
- (newsgroups (message-fetch-field "newsgroups"))\r
- (followup-to (message-fetch-field "followup-to"))\r
- (groups (message-tokenize-header\r
- (if followup-to\r
- (concat newsgroups "," followup-to)\r
- newsgroups)))\r
- (hashtb (and (boundp 'gnus-active-hashtb)\r
- gnus-active-hashtb))\r
- errors)\r
- (if (or (not hashtb)\r
- (not (boundp 'gnus-read-active-file))\r
- (not gnus-read-active-file)\r
- (eq gnus-read-active-file 'some))\r
- t\r
- (while groups\r
- (when (and (not (boundp (intern (car groups) hashtb)))\r
- (not (equal (car groups) "poster")))\r
- (push (car groups) errors))\r
- (pop groups))\r
- (if (not errors)\r
- t\r
- (y-or-n-p\r
- (format\r
- "Really post to %s unknown group%s: %s "\r
- (if (= (length errors) 1) "this" "these")\r
- (if (= (length errors) 1) "" "s")\r
- (mapconcat 'identity errors ", ")))))))\r
- ;; Check the Newsgroups & Followup-To headers for syntax errors.\r
- (message-check 'valid-newsgroups\r
- (let ((case-fold-search t)\r
- (headers '("Newsgroups" "Followup-To"))\r
- header error)\r
- (while (and headers (not error))\r
- (when (setq header (mail-fetch-field (car headers)))\r
- (if (or\r
- (not\r
- (string-match\r
- "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"\r
- header))\r
- (memq\r
- nil (mapcar\r
- (lambda (g)\r
- (not (string-match "\\.\\'\\|\\.\\." g)))\r
- (message-tokenize-header header ","))))\r
- (setq error t)))\r
- (unless error\r
- (pop headers)))\r
- (if (not error)\r
- t\r
- (y-or-n-p\r
- (format "The %s header looks odd: \"%s\". Really post? "\r
- (car headers) header)))))\r
- (message-check 'repeated-newsgroups\r
- (let ((case-fold-search t)\r
- (headers '("Newsgroups" "Followup-To"))\r
- header error groups group)\r
- (while (and headers\r
- (not error))\r
- (when (setq header (mail-fetch-field (pop headers)))\r
- (setq groups (message-tokenize-header header ","))\r
- (while (setq group (pop groups))\r
- (when (member group groups)\r
- (setq error group\r
- groups nil)))))\r
- (if (not error)\r
- t\r
- (y-or-n-p\r
- (format "Group %s is repeated in headers. Really post? " error)))))\r
- ;; Check the From header.\r
- (message-check 'from\r
- (let* ((case-fold-search t)\r
- (from (message-fetch-field "from"))\r
- (ad (nth 1 (std11-extract-address-components from))))\r
- (cond\r
- ((not from)\r
- (message "There is no From line. Posting is denied.")\r
- nil)\r
- ((or (not (string-match "@[^\\.]*\\." ad)) ;larsi@ifi\r
- (string-match "\\.\\." ad) ;larsi@ifi..uio\r
- (string-match "@\\." ad) ;larsi@.ifi.uio\r
- (string-match "\\.$" ad) ;larsi@ifi.uio.\r
- (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio\r
- (string-match "(.*).*(.*)" from)) ;(lars) (lars)\r
- (message\r
- "Denied posting -- the From looks strange: \"%s\"." from)\r
- nil)\r
- (t t))))))\r
-\r
-(defun message-check-news-body-syntax ()\r
- (and\r
- ;; Check for long lines.\r
- (message-check 'long-lines\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (while (and\r
- (progn\r
- (end-of-line)\r
- (< (current-column) 80))\r
- (zerop (forward-line 1))))\r
- (or (bolp)\r
- (eobp)\r
- (y-or-n-p\r
- "You have lines longer than 79 characters. Really post? ")))\r
- ;; Check whether the article is empty.\r
- (message-check 'empty\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (forward-line 1)\r
- (let ((b (point)))\r
- (goto-char (point-max))\r
- (re-search-backward message-signature-separator nil t)\r
- (beginning-of-line)\r
- (or (re-search-backward "[^ \n\t]" b t)\r
- (y-or-n-p "Empty article. Really post? "))))\r
- ;; Check for control characters.\r
- (message-check 'control-chars\r
- (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)\r
- (y-or-n-p\r
- "The article contains control characters. Really post? ")\r
- t))\r
- ;; Check 8bit characters.\r
- (message-check '8bit\r
- (message-check-8bit))\r
- ;; Check excessive size.\r
- (message-check 'size\r
- (if (> (buffer-size) 60000)\r
- (y-or-n-p\r
- (format "The article is %d octets long. Really post? "\r
- (buffer-size)))\r
- t))\r
- ;; Check whether any new text has been added.\r
- (message-check 'new-text\r
- (or\r
- (not message-checksum)\r
- (not (eq (message-checksum) message-checksum))\r
- (y-or-n-p\r
- "It looks like no new text has been added. Really post? ")))\r
- ;; Check the length of the signature.\r
- (message-check 'signature\r
- (goto-char (point-max))\r
- (if (or (not (re-search-backward message-signature-separator nil t))\r
- (search-forward message-forward-end-separator nil t))\r
- t\r
- (if (> (count-lines (point) (point-max)) 5)\r
- (y-or-n-p\r
- (format\r
- "Your .sig is %d lines; it should be max 4. Really post? "\r
- (1- (count-lines (point) (point-max)))))\r
- t)))))\r
-\r
-(defun message-check-mail-syntax ()\r
- "Check the syntax of the message."\r
- (save-excursion\r
- (save-restriction\r
- (widen)\r
- (and\r
- ;; We narrow to the headers and check them first.\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (message-check-mail-header-syntax)))\r
- ;; Check the body.\r
- (save-excursion\r
- (set-buffer message-edit-buffer)\r
- (message-check-mail-body-syntax))))))\r
-\r
-(defun message-check-mail-header-syntax ()\r
- t)\r
-\r
-(defun message-check-mail-body-syntax ()\r
- (and\r
- ;; Check 8bit characters.\r
- (message-check '8bit\r
- (message-check-8bit)\r
- )))\r
-\r
-(defun message-check-8bit ()\r
- "Check the article contains 8bit characters."\r
- (save-excursion\r
- (set-buffer message-encoding-buffer)\r
- (message-narrow-to-headers)\r
- (let* ((case-fold-search t)\r
- (field-value (message-fetch-field "content-transfer-encoding")))\r
- (if (and field-value\r
- (member (downcase field-value) message-8bit-encoding-list))\r
- t\r
- (widen)\r
- (set-buffer (get-buffer-create " message syntax"))\r
- (erase-buffer)\r
- (goto-char (point-min))\r
- (set-buffer-multibyte nil)\r
- (insert-buffer message-encoding-buffer)\r
- (goto-char (point-min))\r
- (if (re-search-forward "[^\x00-\x7f]" nil t)\r
- (y-or-n-p\r
- "The article contains 8bit characters. Really post? ")\r
- t)))))\r
-\r
-(defun message-checksum ()\r
- "Return a \"checksum\" for the current buffer."\r
- (let ((sum 0))\r
- (save-excursion\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (while (not (eobp))\r
- (when (not (looking-at "[ \t\n]"))\r
- (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)\r
- (char-after))))\r
- (forward-char 1)))\r
- sum))\r
-\r
-(defun message-do-fcc ()\r
- "Process Fcc headers in the current buffer."\r
- (let ((case-fold-search t)\r
- (coding-system-for-write 'raw-text)\r
- list file)\r
- (save-excursion\r
- (set-buffer (get-buffer-create " *message temp*"))\r
- (erase-buffer)\r
- (insert-buffer-substring message-encoding-buffer)\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (while (setq file (message-fetch-field "fcc"))\r
- (push file list)\r
- (message-remove-header "fcc" nil t)))\r
- (goto-char (point-min))\r
- (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (replace-match "" t t)\r
- ;; Process FCC operations.\r
- (while list\r
- (setq file (pop list))\r
- (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)\r
- ;; Pipe the article to the program in question.\r
- (call-process-region (point-min) (point-max) shell-file-name\r
- nil nil nil shell-command-switch\r
- (match-string 1 file))\r
- ;; Save the article.\r
- (setq file (expand-file-name file))\r
- (unless (file-exists-p (file-name-directory file))\r
- (make-directory (file-name-directory file) t))\r
- (if (and message-fcc-handler-function\r
- (not (eq message-fcc-handler-function 'rmail-output)))\r
- (funcall message-fcc-handler-function file)\r
- (if (and (file-readable-p file) (mail-file-babyl-p file))\r
- (rmail-output file 1 nil t)\r
- (let ((mail-use-rfc822 t))\r
- (rmail-output file 1 t t))))))\r
- (kill-buffer (current-buffer)))))\r
-\r
-(defun message-output (filename)\r
- "Append this article to Unix/babyl mail file.."\r
- (if (and (file-readable-p filename)\r
- (mail-file-babyl-p filename))\r
- (rmail-output-to-rmail-file filename t)\r
- (gnus-output-to-mail filename t)))\r
-\r
-(defun message-cleanup-headers ()\r
- "Do various automatic cleanups of the headers."\r
- ;; Remove empty lines in the header.\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- ;; Remove blank lines.\r
- (while (re-search-forward "^[ \t]*\n" nil t)\r
- (replace-match "" t t))\r
-\r
- ;; Correct Newsgroups and Followup-To headers: Change sequence of\r
- ;; spaces to comma and eliminate spaces around commas. Eliminate\r
- ;; embedded line breaks.\r
- (goto-char (point-min))\r
- (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)\r
- (save-restriction\r
- (narrow-to-region\r
- (point)\r
- (if (re-search-forward "^[^ \t]" nil t)\r
- (match-beginning 0)\r
- (forward-line 1)\r
- (point)))\r
- (goto-char (point-min))\r
- (while (re-search-forward "\n[ \t]+" nil t)\r
- (replace-match " " t t)) ;No line breaks (too confusing)\r
- (goto-char (point-min))\r
- (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)\r
- (replace-match "," t t))\r
- (goto-char (point-min))\r
- ;; Remove trailing commas.\r
- (when (re-search-forward ",+$" nil t)\r
- (replace-match "" t t))))))\r
-\r
-(defun message-make-date (&optional now)\r
- "Make a valid data header.\r
-If NOW, use that time instead."\r
- (let* ((now (or now (current-time)))\r
- (zone (nth 8 (decode-time now)))\r
- (sign "+"))\r
- (when (< zone 0)\r
- (setq sign "-")\r
- (setq zone (- zone)))\r
- (concat\r
- (format-time-string "%d" now)\r
- ;; The month name of the %b spec is locale-specific. Pfff.\r
- (format " %s "\r
- (capitalize (car (rassoc (nth 4 (decode-time now))\r
- parse-time-months))))\r
- (format-time-string "%Y %H:%M:%S " now)\r
- ;; We do all of this because XEmacs doesn't have the %z spec.\r
- (format "%s%02d%02d" sign (/ zone 3600) (% zone 3600)))))\r
-\r
-(defun message-make-followup-subject (subject)\r
- "Make a followup Subject."\r
- (cond\r
- ((and (eq message-use-subject-re 'guess)\r
- (string-match message-subject-encoded-re-regexp subject))\r
- subject)\r
- (message-use-subject-re\r
- (concat "Re: " (message-strip-subject-re subject)))\r
- (t subject)))\r
-\r
-(defun message-make-message-id ()\r
- "Make a unique Message-ID."\r
- (concat "<" (message-unique-id)\r
- (let ((psubject (save-excursion (message-fetch-field "subject")))\r
- (psupersedes\r
- (save-excursion (message-fetch-field "supersedes"))))\r
- (if (or\r
- (and message-reply-headers\r
- (mail-header-references message-reply-headers)\r
- (mail-header-subject message-reply-headers)\r
- psubject\r
- (mail-header-subject message-reply-headers)\r
- (not (string=\r
- (message-strip-subject-re\r
- (mail-header-subject message-reply-headers))\r
- (message-strip-subject-re psubject))))\r
- (and psupersedes\r
- (string-match "_-_@" psupersedes)))\r
- "_-_" ""))\r
- "@" (message-make-fqdn) ">"))\r
-\r
-(defvar message-unique-id-char nil)\r
-\r
-;; If you ever change this function, make sure the new version\r
-;; cannot generate IDs that the old version could.\r
-;; You might for example insert a "." somewhere (not next to another dot\r
-;; or string boundary), or modify the "fsf" string.\r
-(defun message-unique-id ()\r
- ;; Don't use microseconds from (current-time), they may be unsupported.\r
- ;; Instead we use this randomly inited counter.\r
- (setq message-unique-id-char\r
- (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))\r
- ;; (current-time) returns 16-bit ints,\r
- ;; and 2^16*25 just fits into 4 digits i base 36.\r
- (* 25 25)))\r
- (let ((tm (current-time)))\r
- (concat\r
- (if (memq system-type '(ms-dos emx vax-vms))\r
- (let ((user (downcase (user-login-name))))\r
- (while (string-match "[^a-z0-9_]" user)\r
- (aset user (match-beginning 0) ?_))\r
- user)\r
- (message-number-base36 (user-uid) -1))\r
- (message-number-base36 (+ (car tm)\r
- (lsh (% message-unique-id-char 25) 16)) 4)\r
- (message-number-base36 (+ (nth 1 tm)\r
- (lsh (/ message-unique-id-char 25) 16)) 4)\r
- ;; Append the newsreader name, because while the generated\r
- ;; ID is unique to this newsreader, other newsreaders might\r
- ;; otherwise generate the same ID via another algorithm.\r
- ".fsf")))\r
-\r
-(defun message-number-base36 (num len)\r
- (if (if (< len 0)\r
- (<= num 0)\r
- (= len 0))\r
- ""\r
- (concat (message-number-base36 (/ num 36) (1- len))\r
- (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"\r
- (% num 36))))))\r
-\r
-(defun message-make-organization ()\r
- "Make an Organization header."\r
- (let* ((organization\r
- (when message-user-organization\r
- (if (message-functionp message-user-organization)\r
- (funcall message-user-organization)\r
- message-user-organization))))\r
- (save-excursion\r
- (message-set-work-buffer)\r
- (cond ((stringp organization)\r
- (insert organization))\r
- ((and (eq t organization)\r
- message-user-organization-file\r
- (file-exists-p message-user-organization-file))\r
- (insert-file-contents message-user-organization-file)))\r
- (goto-char (point-min))\r
- (while (re-search-forward "[\t\n]+" nil t)\r
- (replace-match "" t t))\r
- (unless (zerop (buffer-size))\r
- (buffer-string)))))\r
-\r
-(defun message-make-lines ()\r
- "Count the number of lines and return numeric string."\r
- (save-excursion\r
- (save-restriction\r
- (widen)\r
- (goto-char (point-min))\r
- (re-search-forward\r
- (concat "^" (regexp-quote mail-header-separator) "$"))\r
- (forward-line 1)\r
- (int-to-string (count-lines (point) (point-max))))))\r
-\r
-(defun message-make-in-reply-to ()\r
- "Return the In-Reply-To header for this message."\r
- (when message-reply-headers\r
- (let ((mid (mail-header-message-id message-reply-headers))\r
- (from (mail-header-from message-reply-headers))\r
- (date (mail-header-date message-reply-headers)))\r
- (when mid\r
- (concat mid\r
- (when from\r
- (let ((pair (std11-extract-address-components from)))\r
- (concat "\n ("\r
- (or (car pair) (cadr pair))\r
- "'s message of \""\r
- (if (or (not date) (string= date ""))\r
- "(unknown date)" date)\r
- "\")"))))))))\r
-\r
-(defun message-make-distribution ()\r
- "Make a Distribution header."\r
- (let ((orig-distribution (message-fetch-reply-field "distribution")))\r
- (cond ((message-functionp message-distribution-function)\r
- (funcall message-distribution-function))\r
- (t orig-distribution))))\r
-\r
-(defun message-make-expires ()\r
- "Return an Expires header based on `message-expires'."\r
- (let ((current (current-time))\r
- (future (* 1.0 message-expires 60 60 24)))\r
- ;; Add the future to current.\r
- (setcar current (+ (car current) (round (/ future (expt 2 16)))))\r
- (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))\r
- (message-make-date current)))\r
-\r
-(defun message-make-path ()\r
- "Return uucp path."\r
- (let ((login-name (user-login-name)))\r
- (cond ((null message-user-path)\r
- (concat (system-name) "!" login-name))\r
- ((stringp message-user-path)\r
- ;; Support GENERICPATH. Suggested by vixie@decwrl.dec.com.\r
- (concat message-user-path "!" login-name))\r
- (t login-name))))\r
-\r
-(defun message-make-from ()\r
- "Make a From header."\r
- (let* ((style message-from-style)\r
- (login (message-make-address))\r
- (fullname\r
- (or (and (boundp 'user-full-name)\r
- user-full-name)\r
- (user-full-name))))\r
- (when (string= fullname "&")\r
- (setq fullname (user-login-name)))\r
- (save-excursion\r
- (message-set-work-buffer)\r
- (cond\r
- ((or (null style)\r
- (equal fullname ""))\r
- (insert login))\r
- ((or (eq style 'angles)\r
- (and (not (eq style 'parens))\r
- ;; Use angles if no quoting is needed, or if parens would\r
- ;; need quoting too.\r
- (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))\r
- (let ((tmp (concat fullname nil)))\r
- (while (string-match "([^()]*)" tmp)\r
- (aset tmp (match-beginning 0) ?-)\r
- (aset tmp (1- (match-end 0)) ?-))\r
- (string-match "[\\()]" tmp)))))\r
- (insert fullname)\r
- (goto-char (point-min))\r
- ;; Look for a character that cannot appear unquoted\r
- ;; according to RFC 822.\r
- (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)\r
- ;; Quote fullname, escaping specials.\r
- (goto-char (point-min))\r
- (insert "\"")\r
- (while (re-search-forward "[\"\\]" nil 1)\r
- (replace-match "\\\\\\&" t))\r
- (insert "\""))\r
- (insert " <" login ">"))\r
- (t ; 'parens or default\r
- (insert login " (")\r
- (let ((fullname-start (point)))\r
- (insert fullname)\r
- (goto-char fullname-start)\r
- ;; RFC 822 says \ and nonmatching parentheses\r
- ;; must be escaped in comments.\r
- ;; Escape every instance of ()\ ...\r
- (while (re-search-forward "[()\\]" nil 1)\r
- (replace-match "\\\\\\&" t))\r
- ;; ... then undo escaping of matching parentheses,\r
- ;; including matching nested parentheses.\r
- (goto-char fullname-start)\r
- (while (re-search-forward\r
- "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"\r
- nil 1)\r
- (replace-match "\\1(\\3)" t)\r
- (goto-char fullname-start)))\r
- (insert ")")))\r
- (buffer-string))))\r
-\r
-(defun message-make-sender ()\r
- "Return the \"real\" user address.\r
-This function tries to ignore all user modifications, and\r
-give as trustworthy answer as possible."\r
- (concat (user-login-name) "@" (system-name)))\r
-\r
-(defun message-make-address ()\r
- "Make the address of the user."\r
- (or (message-user-mail-address)\r
- (concat (user-login-name) "@" (message-make-domain))))\r
-\r
-(defun message-user-mail-address ()\r
- "Return the pertinent part of `user-mail-address'."\r
- (when user-mail-address\r
- (if (string-match " " user-mail-address)\r
- (nth 1 (std11-extract-address-components user-mail-address))\r
- user-mail-address)))\r
-\r
-(defun message-make-fqdn ()\r
- "Return user's fully qualified domain name."\r
- (let ((system-name (system-name))\r
- (user-mail (message-user-mail-address)))\r
- (cond\r
- ((string-match "[^.]\\.[^.]" system-name)\r
- ;; `system-name' returned the right result.\r
- system-name)\r
- ;; Try `mail-host-address'.\r
- ((and (boundp 'mail-host-address)\r
- (stringp mail-host-address)\r
- (string-match "\\." mail-host-address))\r
- mail-host-address)\r
- ;; We try `user-mail-address' as a backup.\r
- ((and user-mail\r
- (string-match "\\." user-mail)\r
- (string-match "@\\(.*\\)\\'" user-mail))\r
- (match-string 1 user-mail))\r
- ;; Default to this bogus thing.\r
- (t\r
- (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))\r
-\r
-(defun message-make-host-name ()\r
- "Return the name of the host."\r
- (let ((fqdn (message-make-fqdn)))\r
- (string-match "^[^.]+\\." fqdn)\r
- (substring fqdn 0 (1- (match-end 0)))))\r
-\r
-(defun message-make-domain ()\r
- "Return the domain name."\r
- (or mail-host-address\r
- (message-make-fqdn)))\r
-\r
-;; Dummy to avoid byte-compile warning.\r
-(defvar mule-version)\r
-(defvar emacs-beta-version)\r
-(defvar xemacs-codename)\r
-(defvar gnus-inviolable-extended-version)\r
-\r
-(defun message-make-user-agent ()\r
- "Return user-agent info if the value `message-user-agent' is non-nil. If the\r
-\"User-Agent\" field has already exist, it's value will be added in the return\r
-string."\r
- (when message-user-agent\r
- (save-excursion\r
- (goto-char (point-min))\r
- (let ((case-fold-search t)\r
- user-agent start p end)\r
- (if (re-search-forward "^User-Agent:[\t ]*" nil t)\r
- (progn\r
- (setq start (match-beginning 0)\r
- p (match-end 0)\r
- end (std11-field-end)\r
- user-agent (buffer-substring-no-properties p end))\r
- (delete-region start (1+ end))\r
- (concat message-user-agent " " user-agent))\r
- message-user-agent)))))\r
-\r
-(defun message-generate-headers (headers)\r
- "Prepare article HEADERS.\r
-Headers already prepared in the buffer are not modified."\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (let* ((Date (message-make-date))\r
- (Message-ID (message-make-message-id))\r
- (Organization (message-make-organization))\r
- (From (message-make-from))\r
- (Path (message-make-path))\r
- (Subject nil)\r
- (Newsgroups nil)\r
- (In-Reply-To (message-make-in-reply-to))\r
- (To nil)\r
- (Distribution (message-make-distribution))\r
- (Lines (message-make-lines))\r
- (User-Agent (message-make-user-agent))\r
- (Expires (message-make-expires))\r
- (case-fold-search t)\r
- header value elem)\r
- ;; First we remove any old generated headers.\r
- (let ((headers message-deletable-headers))\r
- (unless (buffer-modified-p)\r
- (setq headers (delq 'Message-ID (copy-sequence headers))))\r
- (while headers\r
- (goto-char (point-min))\r
- (and (re-search-forward\r
- (concat "^" (symbol-name (car headers)) ": *") nil t)\r
- (get-text-property (1+ (match-beginning 0)) 'message-deletable)\r
- (message-delete-line))\r
- (pop headers)))\r
- ;; Go through all the required headers and see if they are in the\r
- ;; articles already. If they are not, or are empty, they are\r
- ;; inserted automatically - except for Subject, Newsgroups and\r
- ;; Distribution.\r
- (while headers\r
- (goto-char (point-min))\r
- (setq elem (pop headers))\r
- (if (consp elem)\r
- (if (eq (car elem) 'optional)\r
- (setq header (cdr elem))\r
- (setq header (car elem)))\r
- (setq header elem))\r
- (when (or (not (re-search-forward\r
- (concat "^"\r
- (regexp-quote\r
- (downcase\r
- (if (stringp header)\r
- header\r
- (symbol-name header))))\r
- ":")\r
- nil t))\r
- (progn\r
- ;; The header was found. We insert a space after the\r
- ;; colon, if there is none.\r
- (if (/= (char-after) ? ) (insert " ") (forward-char 1))\r
- ;; Find out whether the header is empty...\r
- (looking-at "[ \t]*$")))\r
- ;; So we find out what value we should insert.\r
- (setq value\r
- (cond\r
- ((and (consp elem) (eq (car elem) 'optional))\r
- ;; This is an optional header. If the cdr of this\r
- ;; is something that is nil, then we do not insert\r
- ;; this header.\r
- (setq header (cdr elem))\r
- (or (and (fboundp (cdr elem)) (funcall (cdr elem)))\r
- (and (boundp (cdr elem)) (symbol-value (cdr elem)))))\r
- ((consp elem)\r
- ;; The element is a cons. Either the cdr is a\r
- ;; string to be inserted verbatim, or it is a\r
- ;; function, and we insert the value returned from\r
- ;; this function.\r
- (or (and (stringp (cdr elem)) (cdr elem))\r
- (and (fboundp (cdr elem)) (funcall (cdr elem)))))\r
- ((and (boundp header) (symbol-value header))\r
- ;; The element is a symbol. We insert the value\r
- ;; of this symbol, if any.\r
- (symbol-value header))\r
- (t\r
- ;; We couldn't generate a value for this header,\r
- ;; so we just ask the user.\r
- (read-from-minibuffer\r
- (format "Empty header for %s; enter value: " header)))))\r
- ;; Finally insert the header.\r
- (when (and value\r
- (not (equal value "")))\r
- (save-excursion\r
- (if (bolp)\r
- (progn\r
- ;; This header didn't exist, so we insert it.\r
- (goto-char (point-max))\r
- (insert (if (stringp header) header (symbol-name header))\r
- ": " value "\n")\r
- (forward-line -1))\r
- ;; The value of this header was empty, so we clear\r
- ;; totally and insert the new value.\r
- (delete-region (point) (gnus-point-at-eol))\r
- (insert value))\r
- ;; Add the deletable property to the headers that require it.\r
- (and (memq header message-deletable-headers)\r
- (progn (beginning-of-line) (looking-at "[^:]+: "))\r
- (add-text-properties\r
- (point) (match-end 0)\r
- '(message-deletable t face italic) (current-buffer)))))))\r
- ;; Insert new Sender if the From is strange.\r
- (let ((from (message-fetch-field "from"))\r
- (sender (message-fetch-field "sender"))\r
- (secure-sender (message-make-sender)))\r
- (when (and from\r
- (not (message-check-element 'sender))\r
- (not (string=\r
- (downcase\r
- (cadr (std11-extract-address-components from)))\r
- (downcase secure-sender)))\r
- (or (null sender)\r
- (not\r
- (string=\r
- (downcase\r
- (cadr (std11-extract-address-components sender)))\r
- (downcase secure-sender)))))\r
- (goto-char (point-min))\r
- ;; Rename any old Sender headers to Original-Sender.\r
- (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)\r
- (beginning-of-line)\r
- (insert "Original-")\r
- (beginning-of-line))\r
- (when (or (message-news-p)\r
- (string-match "@.+\\.." secure-sender))\r
- (insert "Sender: " secure-sender "\n")))))))\r
-\r
-(defun message-insert-courtesy-copy ()\r
- "Insert a courtesy message in mail copies of combined messages."\r
- (let (newsgroups)\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (when (setq newsgroups (message-fetch-field "newsgroups"))\r
- (goto-char (point-max))\r
- (insert "Posted-To: " newsgroups "\n")))\r
- (forward-line 1)\r
- (when message-courtesy-message\r
- (cond\r
- ((string-match "%s" message-courtesy-message)\r
- (insert (format message-courtesy-message newsgroups)))\r
- (t\r
- (insert message-courtesy-message)))))))\r
-\r
-;;;\r
-;;; Setting up a message buffer\r
-;;;\r
-\r
-(defun message-fill-address (header value)\r
- (save-restriction\r
- (narrow-to-region (point) (point))\r
- (insert (capitalize (symbol-name header))\r
- ": "\r
- (if (consp value) (car value) value)\r
- "\n")\r
- (narrow-to-region (point-min) (1- (point-max)))\r
- (let (quoted last)\r
- (goto-char (point-min))\r
- (while (not (eobp))\r
- (skip-chars-forward "^,\"" (point-max))\r
- (if (or (eq (char-after) ?,)\r
- (eobp))\r
- (when (not quoted)\r
- (if (and (> (current-column) 78)\r
- last)\r
- (save-excursion\r
- (goto-char last)\r
- (looking-at "[ \t]*")\r
- (replace-match "\n " t t)))\r
- (setq last (1+ (point))))\r
- (setq quoted (not quoted)))\r
- (unless (eobp)\r
- (forward-char 1))))\r
- (goto-char (point-max))\r
- (widen)\r
- (forward-line 1)))\r
-\r
-(defun message-fill-references (header value)\r
- (insert (capitalize (symbol-name header))\r
- ": "\r
- (std11-fill-msg-id-list-string\r
- (if (consp value) (car value) value))\r
- "\n"))\r
-\r
-(defun message-fill-header (header value)\r
- (let ((begin (point))\r
- (fill-column 78)\r
- (fill-prefix " "))\r
- (insert (capitalize (symbol-name header))\r
- ": "\r
- (if (consp value) (car value) value)\r
- "\n")\r
- (save-restriction\r
- (narrow-to-region begin (point))\r
- (fill-region-as-paragraph begin (point))\r
- ;; Tapdance around looong Message-IDs.\r
- (forward-line -1)\r
- (when (looking-at "[ \t]*$")\r
- (message-delete-line))\r
- (goto-char begin)\r
- (re-search-forward ":" nil t)\r
- (when (looking-at "\n[ \t]+")\r
- (replace-match " " t t))\r
- (goto-char (point-max)))))\r
-\r
-(defun message-shorten-references (header references)\r
- "Limit REFERENCES to be shorter than 988 characters."\r
- (let ((max 988)\r
- (cut 4)\r
- refs)\r
- (with-temp-buffer\r
- (insert references)\r
- (goto-char (point-min))\r
- (while (re-search-forward "<[^>]+>" nil t)\r
- (push (match-string 0) refs))\r
- (setq refs (nreverse refs))\r
- (while (> (length (mapconcat 'identity refs " ")) max)\r
- (when (< (length refs) (1+ cut))\r
- (decf cut))\r
- (setcdr (nthcdr cut refs) (cddr (nthcdr cut refs)))))\r
- (insert (capitalize (symbol-name header)) ": "\r
- (mapconcat 'identity refs " ") "\n")))\r
-\r
-(defun message-position-point ()\r
- "Move point to where the user probably wants to find it."\r
- (message-narrow-to-headers)\r
- (cond\r
- ((re-search-forward "^[^:]+:[ \t]*$" nil t)\r
- (search-backward ":" )\r
- (widen)\r
- (forward-char 1)\r
- (if (eq (char-after) ? )\r
- (forward-char 1)\r
- (insert " ")))\r
- (t\r
- (goto-char (point-max))\r
- (widen)\r
- (forward-line 1)\r
- (unless (looking-at "$")\r
- (forward-line 2)))\r
- (sit-for 0)))\r
-\r
-(defun message-buffer-name (type &optional to group)\r
- "Return a new (unique) buffer name based on TYPE and TO."\r
- (cond\r
- ;; Generate a new buffer name The Message Way.\r
- ((eq message-generate-new-buffers 'unique)\r
- (generate-new-buffer-name\r
- (concat "*" type\r
- (if to\r
- (concat " to "\r
- (or (car (std11-extract-address-components to))\r
- to) "")\r
- "")\r
- (if (and group (not (string= group ""))) (concat " on " group) "")\r
- "*")))\r
- ;; Check whether `message-generate-new-buffers' is a function,\r
- ;; and if so, call it.\r
- ((message-functionp message-generate-new-buffers)\r
- (funcall message-generate-new-buffers type to group))\r
- ((eq message-generate-new-buffers 'unsent)\r
- (generate-new-buffer-name\r
- (concat "*unsent " type\r
- (if to\r
- (concat " to "\r
- (or (car (mail-extract-address-components to))\r
- to) "")\r
- "")\r
- (if (and group (not (string= group ""))) (concat " on " group) "")\r
- "*")))\r
- ;; Use standard name.\r
- (t\r
- (format "*%s message*" type))))\r
-\r
-(defun message-pop-to-buffer (name)\r
- "Pop to buffer NAME, and warn if it already exists and is modified."\r
- (let ((pop-up-frames pop-up-frames)\r
- (special-display-buffer-names special-display-buffer-names)\r
- (special-display-regexps special-display-regexps)\r
- (same-window-buffer-names same-window-buffer-names)\r
- (same-window-regexps same-window-regexps)\r
- (buffer (get-buffer name))\r
- (cur (current-buffer)))\r
- (if (or (and (featurep 'xemacs)\r
- (not (eq 'tty (device-type))))\r
- window-system\r
- (>= emacs-major-version 20))\r
- (when message-use-multi-frames\r
- (setq pop-up-frames t\r
- special-display-buffer-names nil\r
- special-display-regexps nil\r
- same-window-buffer-names nil\r
- same-window-regexps nil))\r
- (setq pop-up-frames nil))\r
- (if (and buffer\r
- (buffer-name buffer))\r
- (progn\r
- (set-buffer (pop-to-buffer buffer))\r
- (when (and (buffer-modified-p)\r
- (not (y-or-n-p\r
- "Message already being composed; erase? ")))\r
- (error "Message being composed")))\r
- (set-buffer (pop-to-buffer name)))\r
- (erase-buffer)\r
- (message-mode)\r
- (when pop-up-frames\r
- (make-local-variable 'message-original-frame)\r
- (setq message-original-frame (selected-frame)))))\r
-\r
-(defun message-do-send-housekeeping ()\r
- "Kill old message buffers."\r
- ;; We might have sent this buffer already. Delete it from the\r
- ;; list of buffers.\r
- (setq message-buffer-list (delq (current-buffer) message-buffer-list))\r
- (while (and message-max-buffers\r
- message-buffer-list\r
- (>= (length message-buffer-list) message-max-buffers))\r
- ;; Kill the oldest buffer -- unless it has been changed.\r
- (let ((buffer (pop message-buffer-list)))\r
- (when (and (buffer-name buffer)\r
- (not (buffer-modified-p buffer)))\r
- (kill-buffer buffer))))\r
- ;; Rename the buffer.\r
- (if message-send-rename-function\r
- (funcall message-send-rename-function)\r
- (when (string-match "\\`\\*\\(unsent \\)?" (buffer-name))\r
- (rename-buffer\r
- (concat "*sent " (substring (buffer-name) (match-end 0))) t)))\r
- ;; Push the current buffer onto the list.\r
- (when message-max-buffers\r
- (setq message-buffer-list\r
- (nconc message-buffer-list (list (current-buffer))))))\r
-\r
-(defvar mc-modes-alist)\r
-(defun message-setup (headers &optional replybuffer actions)\r
- (when (and (boundp 'mc-modes-alist)\r
- (not (assq 'message-mode mc-modes-alist)))\r
- (push '(message-mode (encrypt . mc-encrypt-message)\r
- (sign . mc-sign-message))\r
- mc-modes-alist))\r
- (when actions\r
- (setq message-send-actions actions))\r
- (setq message-reply-buffer\r
- (or (message-get-parameter 'reply-buffer)\r
- replybuffer))\r
- (goto-char (point-min))\r
- ;; Insert all the headers.\r
- (mail-header-format\r
- (let ((h headers)\r
- (alist message-header-format-alist))\r
- (while h\r
- (unless (assq (caar h) message-header-format-alist)\r
- (push (list (caar h)) alist))\r
- (pop h))\r
- alist)\r
- headers)\r
- (delete-region (point) (progn (forward-line -1) (point)))\r
- (when message-default-headers\r
- (insert message-default-headers)\r
- (or (bolp) (insert ?\n)))\r
- (put-text-property\r
- (point)\r
- (progn\r
- (insert mail-header-separator "\n")\r
- (1- (point)))\r
- 'read-only nil)\r
- (forward-line -1)\r
- (when (message-news-p)\r
- (when message-default-news-headers\r
- (insert message-default-news-headers)\r
- (or (bolp) (insert ?\n)))\r
- (when message-generate-headers-first\r
- (message-generate-headers\r
- (delq 'Lines\r
- (delq 'Subject\r
- (copy-sequence message-required-news-headers))))))\r
- (when (message-mail-p)\r
- (when message-default-mail-headers\r
- (insert message-default-mail-headers)\r
- (or (bolp) (insert ?\n)))\r
- (when message-generate-headers-first\r
- (message-generate-headers\r
- (delq 'Lines\r
- (delq 'Subject\r
- (copy-sequence message-required-mail-headers))))))\r
- (run-hooks 'message-signature-setup-hook)\r
- (message-insert-signature)\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (run-hooks 'message-header-setup-hook))\r
- (set-buffer-modified-p nil)\r
- (setq buffer-undo-list nil)\r
- (run-hooks 'message-setup-hook)\r
- (message-position-point)\r
- (undo-boundary))\r
-\r
-(defun message-set-auto-save-file-name ()\r
- "Associate the message buffer with a file in the drafts directory."\r
- (when message-auto-save-directory\r
- (if (gnus-alive-p)\r
- (setq message-draft-article\r
- (nndraft-request-associate-buffer "drafts"))\r
- (setq buffer-file-name (expand-file-name "*message*"\r
- message-auto-save-directory))\r
- (setq buffer-auto-save-file-name (make-auto-save-file-name)))\r
- (clear-visited-file-modtime)\r
- (setq buffer-file-coding-system message-draft-coding-system)))\r
-\r
-(defun message-disassociate-draft ()\r
- "Disassociate the message buffer from the drafts directory."\r
- (when message-draft-article\r
- (nndraft-request-expire-articles\r
- (list message-draft-article) "drafts" nil t)))\r
-\r
-(defun message-insert-headers ()\r
- "Generate the headers for the article."\r
- (interactive)\r
- (save-excursion\r
- (save-restriction\r
- (message-narrow-to-headers)\r
- (when (message-news-p)\r
- (message-generate-headers\r
- (delq 'Lines\r
- (delq 'Subject\r
- (copy-sequence message-required-news-headers)))))\r
- (when (message-mail-p)\r
- (message-generate-headers\r
- (delq 'Lines\r
- (delq 'Subject\r
- (copy-sequence message-required-mail-headers))))))))\r
-\r
-\f\r
-\r
-;;;\r
-;;; Commands for interfacing with message\r
-;;;\r
-\r
-;;;###autoload\r
-(defun message-mail (&optional to subject\r
- other-headers continue switch-function\r
- yank-action send-actions)\r
- "Start editing a mail message to be sent.\r
-OTHER-HEADERS is an alist of header/value pairs."\r
- (interactive)\r
- (let ((message-this-is-mail t))\r
- (message-pop-to-buffer (message-buffer-name "mail" to))\r
- (message-setup\r
- (nconc\r
- `((To . ,(or to "")) (Subject . ,(or subject "")))\r
- (when other-headers other-headers)))))\r
-\r
-;;;###autoload\r
-(defun message-news (&optional newsgroups subject)\r
- "Start editing a news article to be sent."\r
- (interactive)\r
- (let ((message-this-is-news t))\r
- (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))\r
- (message-setup `((Newsgroups . ,(or newsgroups ""))\r
- (Subject . ,(or subject ""))))))\r
-\r
-;;;###autoload\r
-(defun message-reply (&optional to-address wide)\r
- "Start editing a reply to the article in the current buffer."\r
- (interactive)\r
- (let ((cur (current-buffer))\r
- from subject date to cc\r
- references message-id follow-to\r
- (inhibit-point-motion-hooks t)\r
- mct never-mct mft mrt gnus-warning)\r
- (save-restriction\r
- (message-narrow-to-head)\r
- ;; Allow customizations to have their say.\r
- (if (not wide)\r
- ;; This is a regular reply.\r
- (if (message-functionp message-reply-to-function)\r
- (setq follow-to (funcall message-reply-to-function)))\r
- ;; This is a followup.\r
- (if (message-functionp message-wide-reply-to-function)\r
- (save-excursion\r
- (setq follow-to\r
- (funcall message-wide-reply-to-function)))))\r
- ;; Find all relevant headers we need.\r
- (setq from (message-fetch-field "from")\r
- date (message-fetch-field "date" t)\r
- subject (or (message-fetch-field "subject") "none")\r
- references (message-fetch-field "references")\r
- message-id (message-fetch-field "message-id" t)\r
- to (message-fetch-field "to")\r
- cc (message-fetch-field "cc")\r
- mct (when (and wide message-use-mail-copies-to)\r
- (message-fetch-field "mail-copies-to"))\r
- mft (when (and wide message-use-mail-followup-to)\r
- (message-fetch-field "mail-followup-to"))\r
- mrt (when message-use-mail-reply-to\r
- (or (message-fetch-field "mail-reply-to")\r
- (message-fetch-field "reply-to")))\r
- gnus-warning (message-fetch-field "gnus-warning"))\r
- (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))\r
- (setq message-id (match-string 0 gnus-warning)))\r
- ;; Remove any (buggy) Re:'s that are present and make a\r
- ;; proper one.\r
- (setq subject (message-make-followup-subject subject))\r
- (widen))\r
-\r
- ;; Handle special values of Mail-Copies-To.\r
- (when mct\r
- (cond\r
- ((and (or (equal (downcase mct) "never")\r
- (equal (downcase mct) "nobody"))\r
- (or (not (eq message-use-mail-copies-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: never? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: never'\r
-directs you not to send your response to the author.")))\r
- (setq never-mct t)\r
- (setq mct nil))\r
- ((and (or (equal (downcase mct) "always")\r
- (equal (downcase mct) "poster"))\r
- (or (not (eq message-use-mail-copies-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: always? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: always'\r
-sends a copy of your response to the author.")))\r
- (setq mct (or mrt from)))\r
- ((and (eq message-use-mail-copies-to 'ask)\r
- (not\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: " mct " ? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: " mct "'\r
-sends a copy of your response to " (if (string-match "," mct)\r
- "the specified addresses"\r
- "that address") ".")))\r
- (setq mct nil))\r
- ))\r
-\r
- (unless follow-to\r
- (cond\r
- (to-address (setq follow-to (list (cons 'To to-address))))\r
- ((not wide) (setq follow-to (list (cons 'To (or mrt from)))))\r
- ;; Handle Mail-Followup-To.\r
- ((and mft\r
- (or (not (eq message-use-mail-followup-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Followup-To: " mft "? ") t "\\r
-You should normally obey the Mail-Followup-To: header.\r
-\r
- `Mail-Followup-To: " mft "'\r
-directs your response to " (if (string-match "," mft)\r
- "the specified addresses"\r
- "that address only") ".\r
-\r
-A typical situation where Mail-Followup-To is used is when the author thinks\r
-that further discussion should take place only in "\r
- (if (string-match "," mft)\r
- "the specified mailing lists"\r
- "that mailing list") ".")))\r
- (setq follow-to (list (cons 'To mft)))\r
- (when mct\r
- (push (cons 'Cc mct) follow-to)))\r
- (t\r
- (let (ccalist)\r
- (save-excursion\r
- (message-set-work-buffer)\r
- (unless never-mct\r
- (insert (or mrt from "")))\r
- (insert (if to (concat (if (bolp) "" ", ") to "") ""))\r
- (insert (if mct (concat (if (bolp) "" ", ") mct) ""))\r
- (insert (if cc (concat (if (bolp) "" ", ") cc) ""))\r
- (goto-char (point-min))\r
- (while (re-search-forward "[ \t]+" nil t)\r
- (replace-match " " t t))\r
- ;; Remove addresses that match `rmail-dont-reply-to-names'.\r
- (insert (prog1 (rmail-dont-reply-to (buffer-string))\r
- (erase-buffer)))\r
- (goto-char (point-min))\r
- ;; Perhaps Mail-Copies-To: never removed the only address?\r
- (when (eobp)\r
- (insert (or mrt from "")))\r
- (setq ccalist\r
- (mapcar\r
- (lambda (addr)\r
- (cons (mail-strip-quoted-names addr) addr))\r
- (message-tokenize-header (buffer-string))))\r
- (let ((s ccalist))\r
- (while s\r
- (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))\r
- (setq follow-to (list (cons 'To (cdr (pop ccalist)))))\r
- (when ccalist\r
- (let ((ccs (cons 'Cc (mapconcat\r
- (lambda (addr) (cdr addr)) ccalist ", "))))\r
- (when (string-match "^ +" (cdr ccs))\r
- (setcdr ccs (substring (cdr ccs) (match-end 0))))\r
- (push ccs follow-to)))))))\r
-\r
- (message-pop-to-buffer (message-buffer-name\r
- (if wide "wide reply" "reply") from\r
- (if wide to-address nil)))\r
-\r
- (setq message-reply-headers\r
- (make-full-mail-header-from-decoded-header\r
- 0 subject from date message-id references 0 0 ""))\r
-\r
- (message-setup\r
- `((Subject . ,subject)\r
- ,@follow-to\r
- ,@(if (or references message-id)\r
- `((References . ,(concat (or references "") (and references " ")\r
- (or message-id ""))))))\r
- cur)))\r
-\r
-;;;###autoload\r
-(defun message-wide-reply (&optional to-address)\r
- "Make a \"wide\" reply to the message in the current buffer."\r
- (interactive)\r
- (message-reply to-address t))\r
-\r
-;;;###autoload\r
-(defun message-followup (&optional to-newsgroups)\r
- "Follow up to the message in the current buffer.\r
-If TO-NEWSGROUPS, use that as the new Newsgroups line."\r
- (interactive)\r
- (let ((cur (current-buffer))\r
- from subject date mct\r
- references message-id follow-to\r
- (inhibit-point-motion-hooks t)\r
- (message-this-is-news t)\r
- followup-to distribution newsgroups gnus-warning posted-to mft mrt)\r
- (save-restriction\r
- (message-narrow-to-head)\r
- (when (message-functionp message-followup-to-function)\r
- (setq follow-to\r
- (funcall message-followup-to-function)))\r
- (setq from (message-fetch-field "from")\r
- date (message-fetch-field "date" t)\r
- subject (or (message-fetch-field "subject") "none")\r
- references (message-fetch-field "references")\r
- message-id (message-fetch-field "message-id" t)\r
- followup-to (when message-use-followup-to\r
- (message-fetch-field "followup-to"))\r
- distribution (message-fetch-field "distribution")\r
- newsgroups (message-fetch-field "newsgroups")\r
- posted-to (message-fetch-field "posted-to")\r
- mct (when message-use-mail-copies-to\r
- (message-fetch-field "mail-copies-to"))\r
- mft (when message-use-mail-followup-to\r
- (message-fetch-field "mail-followup-to"))\r
- mrt (when message-use-mail-reply-to\r
- (or (message-fetch-field "mail-reply-to")\r
- (message-fetch-field "reply-to")))\r
- gnus-warning (message-fetch-field "gnus-warning"))\r
- (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))\r
- (setq message-id (match-string 0 gnus-warning)))\r
- ;; Remove bogus distribution.\r
- (when (and (stringp distribution)\r
- (let ((case-fold-search t))\r
- (string-match "world" distribution)))\r
- (setq distribution nil))\r
- ;; Remove any (buggy) Re:'s that are present and make a\r
- ;; proper one.\r
- (setq subject (message-make-followup-subject subject))\r
- (widen))\r
-\r
- ;; Handle special values of Mail-Copies-To.\r
- (when mct\r
- (cond\r
- ((and (or (equal (downcase mct) "never")\r
- (equal (downcase mct) "nobody"))\r
- (or (not (eq message-use-mail-copies-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: never? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: never'\r
-directs you not to send your response to the author.")))\r
- (setq mct nil))\r
- ((and (or (equal (downcase mct) "always")\r
- (equal (downcase mct) "poster"))\r
- (or (not (eq message-use-mail-copies-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: always? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: always'\r
-sends a copy of your response to the author.")))\r
- (setq mct (or mrt from)))\r
- ((and (eq message-use-mail-copies-to 'ask)\r
- (not\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Copies-To: " mct " ? ") t "\\r
-You should normally obey the Mail-Copies-To: header.\r
-\r
- `Mail-Copies-To: " mct "'\r
-sends a copy of your response to " (if (string-match "," mct)\r
- "the specified addresses"\r
- "that address") ".")))\r
- (setq mct nil))\r
- ))\r
-\r
- (unless follow-to\r
- (cond\r
- (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))\r
- ;; Handle Followup-To.\r
- (followup-to\r
- (cond\r
- ((equal (downcase followup-to) "poster")\r
- (if (or (eq message-use-followup-to 'use)\r
- (message-y-or-n-p "Obey Followup-To: poster? " t "\\r
-You should normally obey the Followup-To: header.\r
-\r
- `Followup-To: poster'\r
-sends your response via e-mail instead of news.\r
-\r
-A typical situation where `Followup-To: poster' is used is when the author\r
-does not read the newsgroup, so he wouldn't see any replies sent to it."))\r
- (setq message-this-is-news nil\r
- distribution nil\r
- follow-to (list (cons 'To (or mrt from ""))))\r
- (setq follow-to (list (cons 'Newsgroups newsgroups)))))\r
- (t\r
- (if (or (equal followup-to newsgroups)\r
- (not (eq message-use-followup-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Followup-To: " followup-to "? ") t "\\r
-You should normally obey the Followup-To: header.\r
-\r
- `Followup-To: " followup-to "'\r
-directs your response to " (if (string-match "," followup-to)\r
- "the specified newsgroups"\r
- "that newsgroup only") ".\r
-\r
-If a message is posted to several newsgroups, Followup-To is often\r
-used to direct the following discussion to one newsgroup only,\r
-because discussions that are spread over several newsgroup tend to\r
-be fragmented and very difficult to follow.\r
-\r
-Also, some source/announcement newsgroups are not indented for discussion;\r
-responses here are directed to other newsgroups."))\r
- (setq follow-to (list (cons 'Newsgroups followup-to)))\r
- (setq follow-to (list (cons 'Newsgroups newsgroups)))))))\r
- ;; Handle Mail-Followup-To, followup via e-mail.\r
- ((and mft\r
- (or (not (eq message-use-mail-followup-to 'ask))\r
- (message-y-or-n-p\r
- (concat "Obey Mail-Followup-To: " mft "? ") t "\\r
-You should normally obey the Mail-Followup-To: header.\r
-\r
- `Mail-Followup-To: " mft "'\r
-directs your response to " (if (string-match "," mft)\r
- "the specified addresses"\r
- "that address only") " instead of news.\r
-\r
-A typical situation where Mail-Followup-To is used is when the author thinks\r
-that further discussion should take place only in "\r
- (if (string-match "," mft)\r
- "the specified mailing lists"\r
- "that mailing list") ".")))\r
- (setq message-this-is-news nil\r
- distribution nil\r
- follow-to (list (cons 'To mft))))\r
- (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))\r
- (t\r
- (setq follow-to (list (cons 'Newsgroups newsgroups))))))\r
-\r
- (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))\r
-\r
- (setq message-reply-headers\r
- (make-full-mail-header-from-decoded-header\r
- 0 subject from date message-id references 0 0 ""))\r
-\r
- (message-setup\r
- `((Subject . ,subject)\r
- ,@follow-to\r
- ,@(and mct (list (cons 'Cc mct)))\r
- ,@(and distribution (list (cons 'Distribution distribution)))\r
- ,@(if (or references message-id)\r
- `((References . ,(concat (or references "") (and references " ")\r
- (or message-id ""))))))\r
- cur)))\r
-\r
-;;;###autoload\r
-(defun message-cancel-news ()\r
- "Cancel an article you posted."\r
- (interactive)\r
- (unless (message-news-p)\r
- (error "This is not a news article; canceling is impossible"))\r
- (when (yes-or-no-p "Do you really want to cancel this article? ")\r
- (let (from newsgroups message-id distribution buf sender)\r
- (save-excursion\r
- ;; Get header info. from original article.\r
- (save-restriction\r
- (message-narrow-to-head)\r
- (setq from (message-fetch-field "from")\r
- sender (message-fetch-field "sender")\r
- newsgroups (message-fetch-field "newsgroups")\r
- message-id (message-fetch-field "message-id" t)\r
- distribution (message-fetch-field "distribution")))\r
- ;; Make sure that this article was written by the user.\r
- (unless (or (and sender\r
- (string-equal\r
- (downcase sender)\r
- (downcase (message-make-sender))))\r
- (string-equal\r
- (downcase (cadr (std11-extract-address-components\r
- from)))\r
- (downcase (cadr (std11-extract-address-components\r
- (message-make-from))))))\r
- (error "This article is not yours"))\r
- ;; Make control message.\r
- (setq buf (set-buffer (get-buffer-create " *message cancel*")))\r
- (erase-buffer)\r
- (insert "Newsgroups: " newsgroups "\n"\r
- "From: " (message-make-from) "\n"\r
- "Subject: cmsg cancel " message-id "\n"\r
- "Control: cancel " message-id "\n"\r
- (if distribution\r
- (concat "Distribution: " distribution "\n")\r
- "")\r
- mail-header-separator "\n"\r
- message-cancel-message)\r
- (run-hooks 'message-cancel-hook)\r
- (message "Canceling your article...")\r
- (if (let ((message-syntax-checks\r
- 'dont-check-for-anything-just-trust-me)\r
- (message-encoding-buffer (current-buffer))\r
- (message-edit-buffer (current-buffer)))\r
- (message-send-news))\r
- (message "Canceling your article...done"))\r
- (kill-buffer buf)))))\r
-\r
-(defun message-supersede-setup-for-mime-edit ()\r
- (set (make-local-variable 'message-setup-hook) nil)\r
- (mime-edit-again))\r
-\r
-;;;###autoload\r
-(defun message-supersede ()\r
- "Start composing a message to supersede the current message.\r
-This is done simply by taking the old article and adding a Supersedes\r
-header line with the old Message-ID."\r
- (interactive)\r
- (let ((cur (current-buffer))\r
- (sender (message-fetch-field "sender"))\r
- (from (message-fetch-field "from")))\r
- ;; Check whether the user owns the article that is to be superseded.\r
- (unless (or (and sender\r
- (string-equal\r
- (downcase sender)\r
- (downcase (message-make-sender))))\r
- (string-equal\r
- (downcase (cadr (std11-extract-address-components from)))\r
- (downcase (cadr (std11-extract-address-components\r
- (message-make-from))))))\r
- (error "This article is not yours"))\r
- ;; Get a normal message buffer.\r
- (message-pop-to-buffer (message-buffer-name "supersede"))\r
- (insert-buffer-substring cur)\r
- (message-narrow-to-head)\r
- ;; Remove unwanted headers.\r
- (when message-ignored-supersedes-headers\r
- (message-remove-header message-ignored-supersedes-headers t))\r
- (goto-char (point-min))\r
- (if (not (re-search-forward "^Message-ID: " nil t))\r
- (error "No Message-ID in this article")\r
- (replace-match "Supersedes: " t t))\r
- (goto-char (point-max))\r
- (insert mail-header-separator)\r
- (widen)\r
- (when message-supersede-setup-function\r
- (funcall message-supersede-setup-function))\r
- (run-hooks 'message-supersede-setup-hook)\r
- (goto-char (point-min))\r
- (search-forward (concat "\n" mail-header-separator "\n") nil t)))\r
-\r
-;;;###autoload\r
-(defun message-recover ()\r
- "Reread contents of current buffer from its last auto-save file."\r
- (interactive)\r
- (let ((file-name (make-auto-save-file-name)))\r
- (cond ((save-window-excursion\r
- (if (not (eq system-type 'vax-vms))\r
- (with-output-to-temp-buffer "*Directory*"\r
- (buffer-disable-undo standard-output)\r
- (let ((default-directory "/"))\r
- (call-process\r
- "ls" nil standard-output nil "-l" file-name))))\r
- (yes-or-no-p (format "Recover auto save file %s? " file-name)))\r
- (let ((buffer-read-only nil))\r
- (erase-buffer)\r
- (insert-file-contents file-name nil)))\r
- (t (error "message-recover cancelled")))))\r
-\r
-;;; Washing Subject:\r
-\r
-(defun message-wash-subject (subject)\r
- "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."\r
- (with-temp-buffer\r
- (insert-string subject)\r
- (goto-char (point-min))\r
- ;; strip Re/Fwd stuff off the beginning\r
- (while (re-search-forward\r
- "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)\r
- (replace-match ""))\r
-\r
- ;; and gnus-style forwards [foo@bar.com] subject\r
- (goto-char (point-min))\r
- (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)\r
- (replace-match ""))\r
-\r
- ;; and off the end\r
- (goto-char (point-max))\r
- (while (re-search-backward "([Ff][Ww][Dd])" nil t)\r
- (replace-match ""))\r
-\r
- ;; and finally, any whitespace that was left-over\r
- (goto-char (point-min))\r
- (while (re-search-forward "^[ \t]+" nil t)\r
- (replace-match ""))\r
- (goto-char (point-max))\r
- (while (re-search-backward "[ \t]+$" nil t)\r
- (replace-match ""))\r
-\r
- (buffer-string)))\r
-\r
-;;; Forwarding messages.\r
-\r
-(defun message-forward-subject-author-subject (subject)\r
- "Generate a subject for a forwarded message.\r
-The form is: [Source] Subject, where if the original message was mail,\r
-Source is the sender, and if the original message was news, Source is\r
-the list of newsgroups is was posted to."\r
- (concat "["\r
- (or (message-fetch-field\r
- (if (message-news-p) "newsgroups" "from"))\r
- "(nowhere)")\r
- "] " subject))\r
-\r
-(defun message-forward-subject-fwd (subject)\r
- "Generate a subject for a forwarded message.\r
-The form is: Fwd: Subject, where Subject is the original subject of\r
-the message."\r
- (concat "Fwd: " subject))\r
-\r
-(defun message-make-forward-subject ()\r
- "Return a Subject header suitable for the message in the current buffer."\r
- (save-excursion\r
- (save-restriction\r
- (current-buffer)\r
- (message-narrow-to-head)\r
- (let ((funcs message-make-forward-subject-function)\r
- (subject (if message-wash-forwarded-subjects\r
- (message-wash-subject\r
- (or (nnheader-decode-subject\r
- (message-fetch-field "Subject"))\r
- ""))\r
- (or (nnheader-decode-subject\r
- (message-fetch-field "Subject"))\r
- ""))))\r
- ;; Make sure funcs is a list.\r
- (and funcs\r
- (not (listp funcs))\r
- (setq funcs (list funcs)))\r
- ;; Apply funcs in order, passing subject generated by previous\r
- ;; func to the next one.\r
- (while funcs\r
- (when (message-functionp (car funcs))\r
- (setq subject (funcall (car funcs) subject)))\r
- (setq funcs (cdr funcs)))\r
- subject))))\r
-\r
-;;;###autoload\r
-(defun message-forward (&optional news)\r
- "Forward the current message via mail.\r
-Optional NEWS will use news to forward instead of mail."\r
- (interactive "P")\r
- (let ((cur (current-buffer))\r
- (subject (message-make-forward-subject))\r
- art-beg)\r
- (if news (message-news nil subject) (message-mail nil subject))\r
- ;; Put point where we want it before inserting the forwarded\r
- ;; message.\r
- (if message-signature-before-forwarded-message\r
- (goto-char (point-max))\r
- (message-goto-body))\r
- ;; Make sure we're at the start of the line.\r
- (unless (eolp)\r
- (insert "\n"))\r
- ;; Narrow to the area we are to insert.\r
- (narrow-to-region (point) (point))\r
- ;; Insert the separators and the forwarded buffer.\r
- (insert message-forward-start-separator)\r
- (setq art-beg (point))\r
- (insert-buffer-substring cur)\r
- (goto-char (point-max))\r
- (insert message-forward-end-separator)\r
- (set-text-properties (point-min) (point-max) nil)\r
- ;; Remove all unwanted headers.\r
- (goto-char art-beg)\r
- (narrow-to-region (point) (if (search-forward "\n\n" nil t)\r
- (1- (point))\r
- (point)))\r
- (goto-char (point-min))\r
- (message-remove-header message-included-forward-headers t nil t)\r
- (widen)\r
- (message-position-point)))\r
-\r
-;;;###autoload\r
-(defun message-resend (address)\r
- "Resend the current article to ADDRESS."\r
- (interactive "sResend message to: ")\r
- (message "Resending message to %s..." address)\r
- (save-excursion\r
- (let ((cur (current-buffer))\r
- beg)\r
- ;; We first set up a normal mail buffer.\r
- (set-buffer (get-buffer-create " *message resend*"))\r
- (erase-buffer)\r
- ;; avoid to turn-on-mime-edit\r
- (let (message-setup-hook)\r
- (message-setup `((To . ,address)))\r
- )\r
- ;; Insert our usual headers.\r
- (message-generate-headers '(From Date To))\r
- (message-narrow-to-headers)\r
- ;; Rename them all to "Resent-*".\r
- (while (re-search-forward "^[A-Za-z]" nil t)\r
- (forward-char -1)\r
- (insert "Resent-"))\r
- (widen)\r
- (forward-line)\r
- (delete-region (point) (point-max))\r
- (setq beg (point))\r
- ;; Insert the message to be resent.\r
- (insert-buffer-substring cur)\r
- (goto-char (point-min))\r
- (search-forward "\n\n")\r
- (forward-char -1)\r
- (save-restriction\r
- (narrow-to-region beg (point))\r
- (message-remove-header message-ignored-resent-headers t)\r
- (goto-char (point-max)))\r
- (insert mail-header-separator)\r
- ;; Rename all old ("Also-")Resent headers.\r
- (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)\r
- (beginning-of-line)\r
- (insert "Also-"))\r
- ;; Quote any "From " lines at the beginning.\r
- (goto-char beg)\r
- (when (looking-at "From ")\r
- (replace-match "X-From-Line: "))\r
- ;; Send it.\r
- (let ((message-encoding-buffer (current-buffer))\r
- (message-edit-buffer (current-buffer)))\r
- (let (message-required-mail-headers)\r
- (message-send-mail)))\r
- (kill-buffer (current-buffer)))\r
- (message "Resending message to %s...done" address)))\r
-\r
-(defun message-bounce-setup-for-mime-edit ()\r
- (set (make-local-variable 'message-setup-hook) nil)\r
- (mime-edit-again))\r
-\r
-;;;###autoload\r
-(defun message-bounce ()\r
- "Re-mail the current message.\r
-This only makes sense if the current message is a bounce message than\r
-contains some mail you have written which has been bounced back to\r
-you."\r
- (interactive)\r
- (let ((cur (current-buffer))\r
- boundary)\r
- (message-pop-to-buffer (message-buffer-name "bounce"))\r
- (insert-buffer-substring cur)\r
- (undo-boundary)\r
- (message-narrow-to-head)\r
- (if (and (message-fetch-field "MIME-Version")\r
- (setq boundary (message-fetch-field "Content-Type")))\r
- (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)\r
- (setq boundary (concat (match-string 1 boundary) " *\n"\r
- "Content-Type: message/rfc822"))\r
- (setq boundary nil)))\r
- (widen)\r
- (goto-char (point-min))\r
- (search-forward "\n\n" nil t)\r
- (or (and boundary\r
- (re-search-forward boundary nil t)\r
- (forward-line 2))\r
- (and (re-search-forward message-unsent-separator nil t)\r
- (forward-line 1))\r
- (re-search-forward "^Return-Path:.*\n" nil t))\r
- ;; We remove everything before the bounced mail.\r
- (delete-region\r
- (point-min)\r
- (if (re-search-forward "^[^ \n\t]+:" nil t)\r
- (match-beginning 0)\r
- (point)))\r
- (save-restriction\r
- (message-narrow-to-head)\r
- (message-remove-header message-ignored-bounced-headers t)\r
- (goto-char (point-max))\r
- (insert mail-header-separator))\r
- (when message-bounce-setup-function\r
- (funcall message-bounce-setup-function))\r
- (run-hooks 'message-bounce-setup-hook)\r
- (message-position-point)))\r
-\r
-;;;\r
-;;; Interactive entry points for new message buffers.\r
-;;;\r
-\r
-;;;###autoload\r
-(defun message-mail-other-window (&optional to subject)\r
- "Like `message-mail' command, but display mail buffer in another window."\r
- (interactive)\r
- (let ((pop-up-windows t)\r
- (special-display-buffer-names nil)\r
- (special-display-regexps nil)\r
- (same-window-buffer-names nil)\r
- (same-window-regexps nil))\r
- (message-pop-to-buffer (message-buffer-name "mail" to)))\r
- (let ((message-this-is-mail t))\r
- (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))\r
-\r
-;;;###autoload\r
-(defun message-mail-other-frame (&optional to subject)\r
- "Like `message-mail' command, but display mail buffer in another frame."\r
- (interactive)\r
- (let ((pop-up-frames t)\r
- (special-display-buffer-names nil)\r
- (special-display-regexps nil)\r
- (same-window-buffer-names nil)\r
- (same-window-regexps nil))\r
- (message-pop-to-buffer (message-buffer-name "mail" to)))\r
- (let ((message-this-is-mail t))\r
- (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))\r
-\r
-;;;###autoload\r
-(defun message-news-other-window (&optional newsgroups subject)\r
- "Start editing a news article to be sent."\r
- (interactive)\r
- (let ((pop-up-windows t)\r
- (special-display-buffer-names nil)\r
- (special-display-regexps nil)\r
- (same-window-buffer-names nil)\r
- (same-window-regexps nil))\r
- (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))\r
- (let ((message-this-is-news t))\r
- (message-setup `((Newsgroups . ,(or newsgroups ""))\r
- (Subject . ,(or subject ""))))))\r
-\r
-;;;###autoload\r
-(defun message-news-other-frame (&optional newsgroups subject)\r
- "Start editing a news article to be sent."\r
- (interactive)\r
- (let ((pop-up-frames t)\r
- (special-display-buffer-names nil)\r
- (special-display-regexps nil)\r
- (same-window-buffer-names nil)\r
- (same-window-regexps nil))\r
- (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))\r
- (let ((message-this-is-news t))\r
- (message-setup `((Newsgroups . ,(or newsgroups ""))\r
- (Subject . ,(or subject ""))))))\r
-\r
-;;; underline.el\r
-\r
-;; This code should be moved to underline.el (from which it is stolen).\r
-\r
-;;;###autoload\r
-(defun bold-region (start end)\r
- "Bold all nonblank characters in the region.\r
-Works by overstriking characters.\r
-Called from program, takes two arguments START and END\r
-which specify the range to operate on."\r
- (interactive "r")\r
- (save-excursion\r
- (let ((end1 (make-marker)))\r
- (move-marker end1 (max start end))\r
- (goto-char (min start end))\r
- (while (< (point) end1)\r
- (or (looking-at "[_\^@- ]")\r
- (insert (char-after) "\b"))\r
- (forward-char 1)))))\r
-\r
-;;;###autoload\r
-(defun unbold-region (start end)\r
- "Remove all boldness (overstruck characters) in the region.\r
-Called from program, takes two arguments START and END\r
-which specify the range to operate on."\r
- (interactive "r")\r
- (save-excursion\r
- (let ((end1 (make-marker)))\r
- (move-marker end1 (max start end))\r
- (goto-char (min start end))\r
- (while (re-search-forward "\b" end1 t)\r
- (if (eq (char-after) (char-after (- (point) 2)))\r
- (delete-char -2))))))\r
-\r
-(defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)\r
-\r
-;; Support for toolbar\r
-(when (string-match "XEmacs\\|Lucid" emacs-version)\r
- (require 'messagexmas))\r
-\r
-;;; Group name completion.\r
-\r
-(defvar message-newgroups-header-regexp\r
- "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"\r
- "Regexp that match headers that lists groups.")\r
-\r
-(defun message-tab ()\r
- "Expand group names in Newsgroups and Followup-To headers.\r
-Do a `tab-to-tab-stop' if not in those headers."\r
- (interactive)\r
- (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))\r
- (mail-abbrev-in-expansion-header-p))\r
- (message-expand-group)\r
- (tab-to-tab-stop)))\r
-\r
-(defvar gnus-active-hashtb)\r
-(defun message-expand-group ()\r
- "Expand the group name under point."\r
- (let* ((b (save-excursion\r
- (save-restriction\r
- (narrow-to-region\r
- (save-excursion\r
- (beginning-of-line)\r
- (skip-chars-forward "^:")\r
- (1+ (point)))\r
- (point))\r
- (skip-chars-backward "^, \t\n") (point))))\r
- (completion-ignore-case t)\r
- (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")\r
- (point))))\r
- (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))\r
- (completions (all-completions string hashtb))\r
- comp)\r
- (delete-region b (point))\r
- (cond\r
- ((= (length completions) 1)\r
- (if (string= (car completions) string)\r
- (progn\r
- (insert string)\r
- (message "Only matching group"))\r
- (insert (car completions))))\r
- ((and (setq comp (try-completion string hashtb))\r
- (not (string= comp string)))\r
- (insert comp))\r
- (t\r
- (insert string)\r
- (if (not comp)\r
- (message "No matching groups")\r
- (save-selected-window\r
- (pop-to-buffer "*Completions*")\r
- (buffer-disable-undo)\r
- (let ((buffer-read-only nil))\r
- (erase-buffer)\r
- (let ((standard-output (current-buffer)))\r
- (display-completion-list (sort completions 'string<)))\r
- (goto-char (point-min))\r
- (delete-region (point) (progn (forward-line 3) (point))))))))))\r
-\r
-;;; Help stuff.\r
-\r
-(defun message-talkative-question (ask question show &rest text)\r
- "Call FUNCTION with argument QUESTION; optionally display TEXT... args.\r
-If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.\r
-The following arguments may contain lists of values."\r
- (if (and show\r
- (setq text (message-flatten-list text)))\r
- (save-window-excursion\r
- (save-excursion\r
- (with-output-to-temp-buffer " *MESSAGE information message*"\r
- (set-buffer " *MESSAGE information message*")\r
- (mapcar 'princ text)\r
- (goto-char (point-min))))\r
- (funcall ask question))\r
- (funcall ask question)))\r
-\r
-(defun message-flatten-list (list)\r
- "Return a new, flat list that contains all elements of LIST.\r
-\r
-\(message-flatten-list '(1 (2 3 (4 5 (6))) 7))\r
-=> (1 2 3 4 5 6 7)"\r
- (cond ((consp list)\r
- (apply 'append (mapcar 'message-flatten-list list)))\r
- (list\r
- (list list))))\r
-\r
-(defun message-generate-new-buffer-clone-locals (name &optional varstr)\r
- "Create and return a buffer with a name based on NAME using generate-new-buffer.\r
-Then clone the local variables and values from the old buffer to the\r
-new one, cloning only the locals having a substring matching the\r
-regexp varstr."\r
- (let ((oldbuf (current-buffer)))\r
- (save-excursion\r
- (set-buffer (generate-new-buffer name))\r
- (message-clone-locals oldbuf)\r
- (current-buffer))))\r
-\r
-(defun message-clone-locals (buffer)\r
- "Clone the local variables from BUFFER to the current buffer."\r
- (let ((locals (save-excursion\r
- (set-buffer buffer)\r
- (buffer-local-variables)))\r
- (regexp\r
- "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))\r
- (mapcar\r
- (lambda (local)\r
- (when (and (consp local)\r
- (car local)\r
- (string-match regexp (symbol-name (car local))))\r
- (ignore-errors\r
- (set (make-local-variable (car local))\r
- (cdr local)))))\r
- locals)))\r
-\r
-\r
-;;; @ for MIME Edit mode\r
-;;;\r
-\r
-(defun message-maybe-encode ()\r
- (when message-mime-mode\r
- (run-hooks 'mime-edit-translate-hook)\r
- (if (catch 'mime-edit-error\r
- (save-excursion\r
- (mime-edit-translate-body)\r
- ))\r
- (error "Translation error!")\r
- )\r
- (end-of-invisible)\r
- (run-hooks 'mime-edit-exit-hook)\r
- ))\r
-\r
-(defun message-mime-insert-article (&optional full-headers)\r
- (interactive "P")\r
- (let ((message-cite-function 'mime-edit-inserted-message-filter)\r
- (message-reply-buffer\r
- (message-get-parameter-with-eval 'original-buffer))\r
- (start (point)))\r
- (message-yank-original nil)\r
- (save-excursion\r
- (narrow-to-region (goto-char start)\r
- (if (search-forward "\n\n" nil t)\r
- (1- (point))\r
- (point-max)))\r
- (goto-char (point-min))\r
- (let ((message-included-forward-headers\r
- (if full-headers "" message-included-forward-headers)))\r
- (message-remove-header message-included-forward-headers t nil t))\r
- (widen))))\r
-\r
-(set-alist 'mime-edit-message-inserter-alist\r
- 'message-mode (function message-mime-insert-article))\r
-\r
-;;; Miscellaneous functions\r
-\r
-;; stolen (and renamed) from nnheader.el\r
-(defun message-replace-chars-in-string (string from to)\r
- "Replace characters in STRING from FROM to TO."\r
- (let ((string (substring string 0)) ;Copy string.\r
- (len (length string))\r
- (idx 0))\r
- ;; Replace all occurrences of FROM with TO.\r
- (while (< idx len)\r
- (when (= (aref string idx) from)\r
- (aset string idx to))\r
- (setq idx (1+ idx)))\r
- string))\r
-\r
-;;;\r
-;;; MIME functions\r
-;;;\r
-\r
-(defun message-mime-query-file (prompt)\r
- (let ((file (read-file-name prompt nil nil t)))\r
- ;; Prevent some common errors. This is inspired by similar code in\r
- ;; VM.\r
- (when (file-directory-p file)\r
- (error "%s is a directory, cannot attach" file))\r
- (unless (file-exists-p file)\r
- (error "No such file: %s" file))\r
- (unless (file-readable-p file)\r
- (error "Permission denied: %s" file))\r
- file))\r
-\r
-(defun message-mime-query-type (file)\r
- (let* ((default (or (mm-default-file-encoding file)\r
- ;; Perhaps here we should check what the file\r
- ;; looks like, and offer text/plain if it looks\r
- ;; like text/plain.\r
- "application/octet-stream"))\r
- (string (completing-read\r
- (format "Content type (default %s): " default)\r
- (delete-duplicates\r
- (mapcar (lambda (m) (list (cdr m))) mailcap-mime-extensions)\r
- :test 'equal))))\r
- (if (not (equal string ""))\r
- string\r
- default)))\r
-\r
-(defun message-mime-query-description ()\r
- (let ((description (read-string "One line description: ")))\r
- (when (string-match "\\`[ \t]*\\'" description)\r
- (setq description nil))\r
- description))\r
-\r
-(defun message-mime-attach-file (file &optional type description)\r
- "Attach a file to the outgoing MIME message.\r
-The file is not inserted or encoded until you send the message with\r
-`\\[message-send-and-exit]' or `\\[message-send]'.\r
-\r
-FILE is the name of the file to attach. TYPE is its content-type, a\r
-string of the form \"type/subtype\". DESCRIPTION is a one-line\r
-description of the attachment."\r
- (interactive\r
- (let* ((file (message-mime-query-file "Attach file: "))\r
- (type (message-mime-query-type file))\r
- (description (message-mime-query-description)))\r
- (list file type description)))\r
- (insert (format\r
- "<#part type=%s filename=%s%s disposition=attachment><#/part>\n"\r
- type (prin1-to-string file)\r
- (if description\r
- (format " description=%s" (prin1-to-string description))\r
- ""))))\r
-\r
-(defun message-mime-attach-external (file &optional type description)\r
- "Attach an external file into the buffer.\r
-FILE is an ange-ftp/efs specification of the part location.\r
-TYPE is the MIME type to use."\r
- (interactive\r
- (let* ((file (message-mime-query-file "Attach external file: "))\r
- (type (message-mime-query-type file))\r
- (description (message-mime-query-description)))\r
- (list file type description)))\r
- (insert (format\r
- "<#external type=%s name=%s disposition=attachment><#/external>\n"\r
- type (prin1-to-string file))))\r
-\r
-(defun message-encode-message-body ()\r
- (let ((mail-parse-charset message-default-charset)\r
- (case-fold-search t)\r
- lines multipart-p content-type-p)\r
- (message-goto-body)\r
- (save-restriction\r
- (narrow-to-region (point) (point-max))\r
- (let ((new (mml-generate-mime)))\r
- (when new\r
- (delete-region (point-min) (point-max))\r
- (insert new)\r
- (goto-char (point-min))\r
- (if (eq (aref new 0) ?\n)\r
- (delete-char 1)\r
- (search-forward "\n\n")\r
- (setq lines (buffer-substring (point-min) (1- (point))))\r
- (delete-region (point-min) (point))))))\r
- (save-restriction\r
- (message-narrow-to-headers-or-head)\r
- (message-remove-header "Mime-Version")\r
- (goto-char (point-max))\r
- (insert "Mime-Version: 1.0\n")\r
- (when lines\r
- (insert lines))\r
- (setq multipart-p\r
- (re-search-backward "^Content-Type: multipart/" nil t))\r
- (goto-char (point-max))\r
- (setq content-type-p\r
- (re-search-backward "^Content-Type:" nil t)))\r
- (save-restriction\r
- (message-narrow-to-headers-or-head)\r
- (message-remove-first-header "Content-Type")\r
- (message-remove-first-header "Content-Transfer-Encoding"))\r
- (when multipart-p\r
- (save-restriction\r
- (message-narrow-to-headers-or-head)\r
- (message-remove-first-header "Content-Type")\r
- (message-remove-first-header "Content-Transfer-Encoding"))\r
- (message-goto-body)\r
- (insert "This is a MIME multipart message. If you are reading\n")\r
- (insert "this, you shouldn't.\n"))\r
- ;; We always make sure that the message has a Content-Type header.\r
- ;; This is because some broken MTAs and MUAs get awfully confused\r
- ;; when confronted with a message with a MIME-Version header and\r
- ;; without a Content-Type header. For instance, Solaris'\r
- ;; /usr/bin/mail.\r
- (unless content-type-p\r
- (goto-char (point-min))\r
- (re-search-forward "^MIME-Version:")\r
- (forward-line 1)\r
- (insert "Content-Type: text/plain; charset=us-ascii\n"))))\r
-\r
-(defvar message-save-buffer " *encoding")\r
-(defun message-save-drafts ()\r
- (interactive)\r
- (if (not (get-buffer message-save-buffer))\r
- (get-buffer-create message-save-buffer))\r
- (let ((filename buffer-file-name)\r
- (buffer (current-buffer)))\r
- (set-buffer message-save-buffer)\r
- (erase-buffer)\r
- (insert-buffer buffer)\r
- (mime-edit-translate-buffer)\r
- (write-region (point-min) (point-max) filename)\r
- (set-buffer buffer)\r
- (set-buffer-modified-p nil)))\r
-\r
-(provide 'message)\r
-\r
-(run-hooks 'message-load-hook)\r
-\r
-;;; message.el ends here\r
+;;; message.el --- composing mail and news messages
+;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
+;; Shuhei KOBAYASHI <shuhei-k@jaist.ac.jp>
+;; Keiichi Suzuki <kei-suzu@mail.wbs.ne.jp>
+;; Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
+;; Katsumi Yamaoka <yamaoka@jpl.org>
+;; Keywords: mail, news, MIME
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This mode provides mail-sending facilities from within Emacs. It
+;; consists mainly of large chunks of code from the sendmail.el,
+;; gnus-msg.el and rnewspost.el files.
+
+;;; Code:
+
+(eval-when-compile
+ (require 'cl)
+ (require 'smtp)
+ )
+
+(require 'mailheader)
+(require 'nnheader)
+(require 'easymenu)
+(require 'custom)
+(if (string-match "XEmacs\\|Lucid" emacs-version)
+ (require 'mail-abbrevs)
+ (require 'mailabbrev))
+(require 'mime-edit)
+
+;; Avoid byte-compile warnings.
+(eval-when-compile
+ (require 'mail-parse)
+ (require 'mm-bodies)
+ (require 'mm-encode)
+ (require 'mml)
+ )
+
+(defgroup message '((user-mail-address custom-variable)
+ (user-full-name custom-variable))
+ "Mail and news message composing."
+ :link '(custom-manual "(message)Top")
+ :group 'mail
+ :group 'news)
+
+(put 'user-mail-address 'custom-type 'string)
+(put 'user-full-name 'custom-type 'string)
+
+(defgroup message-various nil
+ "Various Message Variables"
+ :link '(custom-manual "(message)Various Message Variables")
+ :group 'message)
+
+(defgroup message-buffers nil
+ "Message Buffers"
+ :link '(custom-manual "(message)Message Buffers")
+ :group 'message)
+
+(defgroup message-sending nil
+ "Message Sending"
+ :link '(custom-manual "(message)Sending Variables")
+ :group 'message)
+
+(defgroup message-interface nil
+ "Message Interface"
+ :link '(custom-manual "(message)Interface")
+ :group 'message)
+
+(defgroup message-forwarding nil
+ "Message Forwarding"
+ :link '(custom-manual "(message)Forwarding")
+ :group 'message-interface)
+
+(defgroup message-insertion nil
+ "Message Insertion"
+ :link '(custom-manual "(message)Insertion")
+ :group 'message)
+
+(defgroup message-headers nil
+ "Message Headers"
+ :link '(custom-manual "(message)Message Headers")
+ :group 'message)
+
+(defgroup message-news nil
+ "Composing News Messages"
+ :group 'message)
+
+(defgroup message-mail nil
+ "Composing Mail Messages"
+ :group 'message)
+
+(defgroup message-faces nil
+ "Faces used for message composing."
+ :group 'message
+ :group 'faces)
+
+(defgroup message-frames nil
+ "Message frames"
+ :group 'message)
+
+(defcustom message-directory "~/Mail/"
+ "*Directory from which all other mail file variables are derived."
+ :group 'message-various
+ :type 'directory)
+
+(defcustom message-max-buffers 10
+ "*How many buffers to keep before starting to kill them off."
+ :group 'message-buffers
+ :type 'integer)
+
+(defcustom message-send-rename-function nil
+ "Function called to rename the buffer after sending it."
+ :group 'message-buffers
+ :type 'function)
+
+(defcustom message-fcc-handler-function 'message-output
+ "*A function called to save outgoing articles.
+This function will be called with the name of the file to store the
+article in. The default function is `message-output' which saves in Unix
+mailbox format."
+ :type '(radio (function-item message-output)
+ (function :tag "Other"))
+ :group 'message-sending)
+
+(defcustom message-encode-function 'message-maybe-encode
+ "*A function called to encode messages."
+ :group 'message-sending
+ :type 'function)
+
+(defcustom message-8bit-encoding-list '(8bit binary)
+ "*8bit encoding type in Content-Transfer-Encoding field."
+ :group 'message-sending
+ :type '(repeat (symbol :tag "Type")))
+
+(defcustom message-courtesy-message
+ "The following message is a courtesy copy of an article\nthat has been posted to %s as well.\n\n"
+ "*This is inserted at the start of a mailed copy of a posted message.
+If the string contains the format spec \"%s\", the Newsgroups
+the article has been posted to will be inserted there.
+If this variable is nil, no such courtesy message will be added."
+ :group 'message-sending
+ :type 'string)
+
+(defcustom message-ignored-bounced-headers "^\\(Received\\|Return-Path\\):"
+ "*Regexp that matches headers to be removed in resent bounced mail."
+ :group 'message-interface
+ :type 'regexp)
+
+(defcustom message-bounce-setup-function 'message-bounce-setup-for-mime-edit
+ "Function to setup a re-sending bounced message."
+ :group 'message-sending
+ :type 'function)
+
+;;;###autoload
+(defcustom message-from-style 'default
+ "*Specifies how \"From\" headers look.
+
+If `nil', they contain just the return address like:
+ king@grassland.com
+If `parens', they look like:
+ king@grassland.com (Elvis Parsley)
+If `angles', they look like:
+ Elvis Parsley <king@grassland.com>
+
+Otherwise, most addresses look like `angles', but they look like
+`parens' if `angles' would need quoting and `parens' would not."
+ :type '(choice (const :tag "simple" nil)
+ (const parens)
+ (const angles)
+ (const default))
+ :group 'message-headers)
+
+(defcustom message-syntax-checks nil
+ ; Guess this one shouldn't be easy to customize...
+ "*Controls what syntax checks should not be performed on outgoing posts.
+To disable checking of long signatures, for instance, add
+ `(signature . disabled)' to this list.
+
+Don't touch this variable unless you really know what you're doing.
+
+Checks include subject-cmsg multiple-headers sendsys message-id from
+long-lines control-chars size new-text redirected-followup signature
+approved sender empty empty-headers message-id from subject
+shorten-followup-to existing-newsgroups buffer-file-name unchanged."
+ :group 'message-news)
+
+(defcustom message-required-news-headers
+ '(From Newsgroups Subject Date Message-ID
+ (optional . Organization) Lines
+ (optional . User-Agent))
+ "*Headers to be generated or prompted for when posting an article.
+RFC977 and RFC1036 require From, Date, Newsgroups, Subject,
+Message-ID. Organization, Lines, In-Reply-To, Expires, and
+User-Agent are optional. If don't you want message to insert some
+header, remove it from this list."
+ :group 'message-news
+ :group 'message-headers
+ :type '(repeat sexp))
+
+(defcustom message-required-mail-headers
+ '(From Subject Date (optional . In-Reply-To) Message-ID Lines
+ (optional . User-Agent))
+ "*Headers to be generated or prompted for when mailing a message.
+RFC822 required that From, Date, To, Subject and Message-ID be
+included. Organization, Lines and User-Agent are optional."
+ :group 'message-mail
+ :group 'message-headers
+ :type '(repeat sexp))
+
+(defcustom message-deletable-headers '(Message-ID Date Lines)
+ "Headers to be deleted if they already exist and were generated by message previously."
+ :group 'message-headers
+ :type 'sexp)
+
+(defcustom message-ignored-news-headers
+ "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:"
+ "*Regexp of headers to be removed unconditionally before posting."
+ :group 'message-news
+ :group 'message-headers
+ :type 'regexp)
+
+(defcustom message-ignored-mail-headers "^[GF]cc:\\|^Resent-Fcc:\\|^Xref:"
+ "*Regexp of headers to be removed unconditionally before mailing."
+ :group 'message-mail
+ :group 'message-headers
+ :type 'regexp)
+
+(defcustom message-ignored-supersedes-headers "^Path:\\|^Date\\|^NNTP-Posting-Host:\\|^Xref:\\|^Lines:\\|^Received:\\|^X-From-Line:\\|^X-Trace:\\|^X-Complaints-To:\\|Return-Path:\\|^Supersedes:"
+ "*Header lines matching this regexp will be deleted before posting.
+It's best to delete old Path and Date headers before posting to avoid
+any confusion."
+ :group 'message-interface
+ :type 'regexp)
+
+(defcustom message-supersede-setup-function
+ 'message-supersede-setup-for-mime-edit
+ "Function to setup a supersede message."
+ :group 'message-sending
+ :type 'function)
+
+(defcustom message-subject-re-regexp "^[ \t]*\\([Rr][Ee]:[ \t]*\\)*[ \t]*"
+ "*Regexp matching \"Re: \" in the subject line."
+ :group 'message-various
+ :type 'regexp)
+
+;;; Some sender agents encode the whole subject including leading "Re: ".
+;;; And if followup agent does not decode it for some reason (e.g. unknown
+;;; charset) and just add a new "Re: " in front of the encoded-word, the
+;;; result will contain multiple "Re: "'s.
+(defcustom message-subject-encoded-re-regexp
+ (concat
+ "^[ \t]*"
+ (regexp-quote "=?")
+ "[-!#$%&'*+0-9A-Z^_`a-z{|}~]+" ; charset
+ (regexp-quote "?")
+ "\\("
+ "[Bb]" (regexp-quote "?") ; B encoding
+ "\\(\\(CQk\\|CSA\\|IAk\\|ICA\\)[Jg]\\)*" ; \([ \t][ \t][ \t]\)*
+ "\\("
+ "[Uc][km]U6" ; [Rr][Ee]:
+ "\\|"
+ "\\(C[VX]\\|I[FH]\\)J[Fl]O[g-v]" ; [ \t][Rr][Ee]:
+ "\\|"
+ "\\(CQl\\|CSB\\|IAl\\|ICB\\)[Sy][RZ]T[o-r]" ; [ \t][ \t][Rr][Ee]:
+ "\\)"
+ "\\|"
+ "[Qb]" (regexp-quote "?") ; Q encoding
+ "\\(_\\|=09\\|=20\\)*"
+ "\\([Rr]\\|=[57]2\\)\\([Ee]\\|=[46]5\\)\\(:\\|=3[Aa]\\)"
+ "\\)"
+ )
+ "*Regexp matching \"Re: \" in the subject line.
+Unlike `message-subject-re-regexp', this regexp matches \"Re: \" within
+an encoded-word."
+ :group 'message-various
+ :type 'regexp)
+
+(defcustom message-use-subject-re t
+ "*If t, remove any (buggy) \"Re: \"'s from the subject of the precursor
+and add a new \"Re: \". If it is nil, use the subject \"as-is\". If it
+is the symbol `guess', try to detect \"Re: \" within an encoded-word."
+ :group 'message-various
+ :type '(choice (const :tag "off" nil)
+ (const :tag "on" t)
+ (const guess)))
+
+;;;###autoload
+(defcustom message-signature-separator "^-- *$"
+ "Regexp matching the signature separator."
+ :type 'regexp
+ :group 'message-various)
+
+(defcustom message-elide-elipsis "\n[...]\n\n"
+ "*The string which is inserted for elided text."
+ :type 'string
+ :group 'message-various)
+
+(defcustom message-interactive nil
+ "Non-nil means when sending a message wait for and display errors.
+nil means let mailer mail back a message to report errors."
+ :group 'message-sending
+ :group 'message-mail
+ :type 'boolean)
+
+(defcustom message-generate-new-buffers 'unique
+ "*Non-nil means that a new message buffer will be created whenever `message-setup' is called.
+If this is a function, call that function with three parameters: The type,
+the to address and the group name. (Any of these may be nil.) The function
+should return the new buffer name."
+ :group 'message-buffers
+ :type '(choice (const :tag "off" nil)
+ (const :tag "unique" unique)
+ (const :tag "unsent" unsent)
+ (function fun)))
+
+(defcustom message-kill-buffer-on-exit nil
+ "*Non-nil means that the message buffer will be killed after sending a message."
+ :group 'message-buffers
+ :type 'boolean)
+
+(defcustom message-kill-buffer-query-function 'yes-or-no-p
+ "*A function called to query the user whether to kill buffer anyway or not.
+If it is t, the buffer will be killed peremptorily."
+ :type '(radio (function-item yes-or-no-p)
+ (function-item y-or-n-p)
+ (function-item nnheader-Y-or-n-p)
+ (function :tag "Other" t))
+ :group 'message-buffers)
+
+(defvar gnus-local-organization)
+(defcustom message-user-organization
+ (or (and (boundp 'gnus-local-organization)
+ (stringp gnus-local-organization)
+ gnus-local-organization)
+ (getenv "ORGANIZATION")
+ t)
+ "*String to be used as an Organization header.
+If t, use `message-user-organization-file'."
+ :group 'message-headers
+ :type '(choice string
+ (const :tag "consult file" t)))
+
+;;;###autoload
+(defcustom message-user-organization-file "/usr/lib/news/organization"
+ "*Local news organization file."
+ :type 'file
+ :group 'message-headers)
+
+(defcustom message-forward-start-separator
+ (concat (mime-make-tag "message" "rfc822") "\n")
+ "*Delimiter inserted before forwarded messages."
+ :group 'message-forwarding
+ :type 'string)
+
+(defcustom message-forward-end-separator
+ (concat (mime-make-tag "text" "plain") "\n")
+ "*Delimiter inserted after forwarded messages."
+ :group 'message-forwarding
+ :type 'string)
+
+(defcustom message-signature-before-forwarded-message t
+ "*If non-nil, put the signature before any included forwarded message."
+ :group 'message-forwarding
+ :type 'boolean)
+
+(defcustom message-included-forward-headers
+ "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^\\(Mail-\\)?Followup-To:\\|^\\(Mail-\\)?Reply-To:\\|^Mail-Copies-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^Cc:\\|^Posted-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-\\|^Message-ID:\\|^References:\\|^Content-\\|^MIME-Version:"
+ "*Regexp matching headers to be included in forwarded messages."
+ :group 'message-forwarding
+ :type 'regexp)
+
+(defcustom message-make-forward-subject-function
+ 'message-forward-subject-author-subject
+ "*A list of functions that are called to generate a subject header for forwarded messages.
+The subject generated by the previous function is passed into each
+successive function.
+
+The provided functions are:
+
+* message-forward-subject-author-subject (Source of article (author or
+ newsgroup)), in brackets followed by the subject
+* message-forward-subject-fwd (Subject of article with 'Fwd:' prepended
+ to it."
+ :group 'message-forwarding
+ :type '(radio (function-item message-forward-subject-author-subject)
+ (function-item message-forward-subject-fwd)))
+
+(defcustom message-wash-forwarded-subjects nil
+ "*If non-nil, try to remove as much old cruft as possible from the subject of messages before generating the new subject of a forward."
+ :group 'message-forwarding
+ :type 'boolean)
+
+(defcustom message-ignored-resent-headers "^Return-receipt\\|^X-Gnus\\|^Gnus-Warning:"
+ "*All headers that match this regexp will be deleted when resending a message."
+ :group 'message-interface
+ :type 'regexp)
+
+(defcustom message-ignored-cited-headers "."
+ "*Delete these headers from the messages you yank."
+ :group 'message-insertion
+ :type 'regexp)
+
+(defcustom message-cancel-message "I am canceling my own article."
+ "Message to be inserted in the cancel message."
+ :group 'message-interface
+ :type 'string)
+
+;; Useful to set in site-init.el
+;;;###autoload
+(defcustom message-send-mail-function 'message-send-mail-with-sendmail
+ "Function to call to send the current buffer as mail.
+The headers should be delimited by a line whose contents match the
+variable `mail-header-separator'.
+
+Legal values include `message-send-mail-with-sendmail' (the default),
+`message-send-mail-with-mh', `message-send-mail-with-qmail' and
+`message-send-mail-with-smtp'."
+ :type '(radio (function-item message-send-mail-with-sendmail)
+ (function-item message-send-mail-with-mh)
+ (function-item message-send-mail-with-qmail)
+ (function-item message-send-mail-with-smtp)
+ (function :tag "Other"))
+ :group 'message-sending
+ :group 'message-mail)
+
+;; 1997-09-29 by MORIOKA Tomohiko
+(defcustom message-send-news-function 'message-send-news-with-gnus
+ "Function to call to send the current buffer as news.
+The headers should be delimited by a line whose contents match the
+variable `mail-header-separator'."
+ :group 'message-sending
+ :group 'message-news
+ :type 'function)
+
+(defcustom message-reply-to-function nil
+ "Function that should return a list of headers.
+This function should pick out addresses from the To, Cc, and From headers
+and respond with new To and Cc headers."
+ :group 'message-interface
+ :type 'function)
+
+(defcustom message-wide-reply-to-function nil
+ "Function that should return a list of headers.
+This function should pick out addresses from the To, Cc, and From headers
+and respond with new To and Cc headers."
+ :group 'message-interface
+ :type 'function)
+
+(defcustom message-followup-to-function nil
+ "Function that should return a list of headers.
+This function should pick out addresses from the To, Cc, and From headers
+and respond with new To and Cc headers."
+ :group 'message-interface
+ :type 'function)
+
+(defcustom message-use-followup-to 'ask
+ "*Specifies what to do with Followup-To header.
+If nil, always ignore the header. If it is t, use its value, but
+query before using the \"poster\" value. If it is the symbol `ask',
+always query the user whether to use the value. If it is the symbol
+`use', always use the value."
+ :group 'message-interface
+ :type '(choice (const :tag "ignore" nil)
+ (const :tag "maybe" t)
+ (const :tag "always" use)
+ (const :tag "ask" ask)))
+
+(defcustom message-use-mail-copies-to 'ask
+ "*Specifies what to do with Mail-Copies-To header.
+If nil, always ignore the header. If it is t, use its value, but
+query before using the value other than \"always\" or \"never\".
+If it is the symbol `ask', always query the user whether to use
+the value. If it is the symbol `use', always use the value."
+ :group 'message-interface
+ :type '(choice (const :tag "ignore" nil)
+ (const :tag "maybe" t)
+ (const :tag "always" use)
+ (const :tag "ask" ask)))
+
+(defcustom message-use-mail-followup-to 'ask
+ "*Specifies what to do with Mail-Followup-To header.
+If nil, always ignore the header. If it is the symbol `ask', always
+query the user whether to use the value. If it is t or the symbol
+`use', always use the value."
+ :group 'message-interface
+ :type '(choice (const :tag "ignore" nil)
+ (const :tag "maybe" t)
+ (const :tag "always" use)
+ (const :tag "ask" ask)))
+
+;;; XXX: 'ask and 'use are not implemented yet.
+(defcustom message-use-mail-reply-to 'ask
+ "*Specifies what to do with Mail-Reply-To/Reply-To header.
+If nil, always ignore the header. If it is t or the symbol `use', use
+its value. If it is the symbol `ask', always query the user whether to
+use the value. Note that if \"Reply-To\" is marked as \"broken\", its value
+is never used."
+ :group 'message-interface
+ :type '(choice (const :tag "ignore" nil)
+ (const :tag "maybe" t)
+ (const :tag "always" use)
+ (const :tag "ask" ask)))
+
+;; stuff relating to broken sendmail in MMDF
+(defcustom message-sendmail-f-is-evil nil
+ "*Non-nil means that \"-f username\" should not be added to the sendmail
+command line, because it is even more evil than leaving it out."
+ :group 'message-sending
+ :type 'boolean)
+
+;; qmail-related stuff
+(defcustom message-qmail-inject-program "/var/qmail/bin/qmail-inject"
+ "Location of the qmail-inject program."
+ :group 'message-sending
+ :type 'file)
+
+(defcustom message-qmail-inject-args nil
+ "Arguments passed to qmail-inject programs.
+This should be a list of strings, one string for each argument.
+
+For e.g., if you wish to set the envelope sender address so that bounces
+go to the right place or to deal with listserv's usage of that address, you
+might set this variable to '(\"-f\" \"you@some.where\")."
+ :group 'message-sending
+ :type '(repeat string))
+
+(defvar gnus-post-method)
+(defvar gnus-select-method)
+(defcustom message-post-method
+ (cond ((and (boundp 'gnus-post-method)
+ (listp gnus-post-method)
+ gnus-post-method)
+ gnus-post-method)
+ ((boundp 'gnus-select-method)
+ gnus-select-method)
+ (t '(nnspool "")))
+ "*Method used to post news.
+Note that when posting from inside Gnus, for instance, this
+variable isn't used."
+ :group 'message-news
+ :group 'message-sending
+ ;; This should be the `gnus-select-method' widget, but that might
+ ;; create a dependence to `gnus.el'.
+ :type 'sexp)
+
+(defcustom message-generate-headers-first nil
+ "*If non-nil, generate all possible headers before composing."
+ :group 'message-headers
+ :type 'boolean)
+
+(defcustom message-setup-hook '(turn-on-mime-edit)
+ "Normal hook, run each time a new outgoing message is initialized.
+The function `message-setup' runs this hook."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-cancel-hook nil
+ "Hook run when cancelling articles."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-signature-setup-hook nil
+ "Normal hook, run each time a new outgoing message is initialized.
+It is run after the headers have been inserted and before
+the signature is inserted."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-bounce-setup-hook nil
+ "Normal hook, run each time a re-sending bounced message is initialized.
+The function `message-bounce' runs this hook."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-supersede-setup-hook nil
+ "Normal hook, run each time a supersede message is initialized.
+The function `message-supersede' runs this hook."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-mode-hook nil
+ "Hook run in message mode buffers."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-header-hook '(eword-encode-header)
+ "Hook run in a message mode buffer narrowed to the headers."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-header-setup-hook nil
+ "Hook called narrowed to the headers when setting up a message buffer."
+ :group 'message-various
+ :type 'hook)
+
+;;;###autoload
+(defcustom message-citation-line-function 'message-insert-citation-line
+ "*Function called to insert the \"Whomever writes:\" line."
+ :type 'function
+ :group 'message-insertion)
+
+;;;###autoload
+(defcustom message-yank-prefix "> "
+ "*Prefix inserted on the lines of yanked messages."
+ :type 'string
+ :group 'message-insertion)
+
+(defcustom message-yank-add-new-references t
+ "*Non-nil means new IDs will be added to \"References\" field when an
+article is yanked by the command `message-yank-original' interactively."
+ :type 'boolean
+ :group 'message-insertion)
+
+(defcustom message-indentation-spaces 3
+ "*Number of spaces to insert at the beginning of each cited line.
+Used by `message-yank-original' via `message-yank-cite'."
+ :group 'message-insertion
+ :type 'integer)
+
+;;;###autoload
+(defcustom message-cite-function 'message-cite-original
+ "*Function for citing an original message.
+Predefined functions include `message-cite-original' and
+`message-cite-original-without-signature'.
+Note that `message-cite-original' uses `mail-citation-hook' if that is non-nil."
+ :type '(radio (function-item message-cite-original)
+ (function-item sc-cite-original)
+ (function :tag "Other"))
+ :group 'message-insertion)
+
+;;;###autoload
+(defcustom message-indent-citation-function 'message-indent-citation
+ "*Function for modifying a citation just inserted in the mail buffer.
+This can also be a list of functions. Each function can find the
+citation between (point) and (mark t). And each function should leave
+point and mark around the citation text as modified."
+ :type 'function
+ :group 'message-insertion)
+
+(defvar message-abbrevs-loaded nil)
+
+;;;###autoload
+(defcustom message-signature t
+ "*String to be inserted at the end of the message buffer.
+If t, the `message-signature-file' file will be inserted instead.
+If a function, the result from the function will be used instead.
+If a form, the result from the form will be used instead."
+ :type 'sexp
+ :group 'message-insertion)
+
+;;;###autoload
+(defcustom message-signature-file "~/.signature"
+ "*File containing the text inserted at end of message buffer."
+ :type 'file
+ :group 'message-insertion)
+
+(defcustom message-distribution-function nil
+ "*Function called to return a Distribution header."
+ :group 'message-news
+ :group 'message-headers
+ :type 'function)
+
+(defcustom message-expires 14
+ "Number of days before your article expires."
+ :group 'message-news
+ :group 'message-headers
+ :link '(custom-manual "(message)News Headers")
+ :type 'integer)
+
+(defcustom message-user-path nil
+ "If nil, use the NNTP server name in the Path header.
+If stringp, use this; if non-nil, use no host name (user name only)."
+ :group 'message-news
+ :group 'message-headers
+ :link '(custom-manual "(message)News Headers")
+ :type '(choice (const :tag "nntp" nil)
+ (string :tag "name")
+ (sexp :tag "none" :format "%t" t)))
+
+(defvar message-reply-buffer nil)
+(defvar message-reply-headers nil)
+(defvar message-sent-message-via nil)
+(defvar message-checksum nil)
+(defvar message-send-actions nil
+ "A list of actions to be performed upon successful sending of a message.")
+(defvar message-exit-actions nil
+ "A list of actions to be performed upon exiting after sending a message.")
+(defvar message-kill-actions nil
+ "A list of actions to be performed before killing a message buffer.")
+(defvar message-postpone-actions nil
+ "A list of actions to be performed after postponing a message.")
+(defvar message-original-frame nil)
+(defvar message-parameter-alist nil)
+(defvar message-startup-parameter-alist nil)
+
+(define-widget 'message-header-lines 'text
+ "All header lines must be LFD terminated."
+ :format "%t:%n%v"
+ :valid-regexp "^\\'"
+ :error "All header lines must be newline terminated")
+
+(defcustom message-default-headers ""
+ "*A string containing header lines to be inserted in outgoing messages.
+It is inserted before you edit the message, so you can edit or delete
+these lines."
+ :group 'message-headers
+ :type 'message-header-lines)
+
+(defcustom message-default-mail-headers ""
+ "*A string of header lines to be inserted in outgoing mails."
+ :group 'message-headers
+ :group 'message-mail
+ :type 'message-header-lines)
+
+(defcustom message-default-news-headers ""
+ "*A string of header lines to be inserted in outgoing news
+articles."
+ :group 'message-headers
+ :group 'message-news
+ :type 'message-header-lines)
+
+;; Note: could use /usr/ucb/mail instead of sendmail;
+;; options -t, and -v if not interactive.
+(defcustom message-mailer-swallows-blank-line
+ (if (and (string-match "sparc-sun-sunos\\(\\'\\|[^5]\\)"
+ system-configuration)
+ (file-readable-p "/etc/sendmail.cf")
+ (let ((buffer (get-buffer-create " *temp*")))
+ (unwind-protect
+ (save-excursion
+ (set-buffer buffer)
+ (insert-file-contents "/etc/sendmail.cf")
+ (goto-char (point-min))
+ (let ((case-fold-search nil))
+ (re-search-forward "^OR\\>" nil t)))
+ (kill-buffer buffer))))
+ ;; According to RFC822, "The field-name must be composed of printable
+ ;; ASCII characters (i. e., characters that have decimal values between
+ ;; 33 and 126, except colon)", i. e., any chars except ctl chars,
+ ;; space, or colon.
+ '(looking-at "[ \t]\\|[][!\"#$%&'()*+,-./0-9;<=>?@A-Z\\\\^_`a-z{|}~]+:"))
+ "*Set this non-nil if the system's mailer runs the header and body together.
+\(This problem exists on Sunos 4 when sendmail is run in remote mode.)
+The value should be an expression to test whether the problem will
+actually occur."
+ :group 'message-sending
+ :type 'sexp)
+
+;;; XXX: This symbol is overloaded! See below.
+(defvar message-user-agent nil
+ "String of the form of PRODUCT/VERSION. Used for User-Agent header field.")
+
+;; Ignore errors in case this is used in Emacs 19.
+;; Don't use ignore-errors because this is copied into loaddefs.el.
+;;;###autoload
+(condition-case nil
+ (define-mail-user-agent 'message-user-agent
+ 'message-mail 'message-send-and-exit
+ 'message-kill-buffer 'message-send-hook)
+ (error nil))
+
+(defvar message-mh-deletable-headers '(Message-ID Date Lines Sender)
+ "If non-nil, delete the deletable headers before feeding to mh.")
+
+(defvar message-send-method-alist
+ '((news message-news-p message-send-via-news)
+ (mail message-mail-p message-send-via-mail))
+ "Alist of ways to send outgoing messages.
+Each element has the form
+
+ \(TYPE PREDICATE FUNCTION)
+
+where TYPE is a symbol that names the method; PREDICATE is a function
+called without any parameters to determine whether the message is
+a message of type TYPE; and FUNCTION is a function to be called if
+PREDICATE returns non-nil. FUNCTION is called with one parameter --
+the prefix.")
+
+(defvar message-mail-alias-type 'abbrev
+ "*What alias expansion type to use in Message buffers.
+The default is `abbrev', which uses mailabbrev. nil switches
+mail aliases off.")
+
+(defcustom message-auto-save-directory
+ (nnheader-concat message-directory "drafts/")
+ "*Directory where Message auto-saves buffers if Gnus isn't running.
+If nil, Message won't auto-save."
+ :group 'message-buffers
+ :type 'directory)
+
+(defcustom message-buffer-naming-style 'unique
+ "*The way new message buffers are named.
+Valid valued are `unique' and `unsent'."
+ :group 'message-buffers
+ :type '(choice (const :tag "unique" unique)
+ (const :tag "unsent" unsent)))
+
+(defcustom message-default-charset nil
+ "Default charset used in non-MULE XEmacsen.")
+
+;;; Internal variables.
+;;; Well, not really internal.
+
+(defvar message-mode-syntax-table
+ (let ((table (copy-syntax-table text-mode-syntax-table)))
+ (modify-syntax-entry ?% ". " table)
+ (modify-syntax-entry ?> ". " table)
+ (modify-syntax-entry ?< ". " table)
+ table)
+ "Syntax table used while in Message mode.")
+
+(defvar message-mode-abbrev-table text-mode-abbrev-table
+ "Abbrev table used in Message mode buffers.
+Defaults to `text-mode-abbrev-table'.")
+(defgroup message-headers nil
+ "Message headers."
+ :link '(custom-manual "(message)Variables")
+ :group 'message)
+
+(defface message-header-to-face
+ '((((class color)
+ (background dark))
+ (:foreground "green2" :bold t))
+ (((class color)
+ (background light))
+ (:foreground "MidnightBlue" :bold t))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying From headers."
+ :group 'message-faces)
+
+(defface message-header-cc-face
+ '((((class color)
+ (background dark))
+ (:foreground "green4" :bold t))
+ (((class color)
+ (background light))
+ (:foreground "MidnightBlue"))
+ (t
+ (:bold t)))
+ "Face used for displaying Cc headers."
+ :group 'message-faces)
+
+(defface message-header-subject-face
+ '((((class color)
+ (background dark))
+ (:foreground "green3"))
+ (((class color)
+ (background light))
+ (:foreground "navy blue" :bold t))
+ (t
+ (:bold t)))
+ "Face used for displaying subject headers."
+ :group 'message-faces)
+
+(defface message-header-newsgroups-face
+ '((((class color)
+ (background dark))
+ (:foreground "yellow" :bold t :italic t))
+ (((class color)
+ (background light))
+ (:foreground "blue4" :bold t :italic t))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying newsgroups headers."
+ :group 'message-faces)
+
+(defface message-header-other-face
+ '((((class color)
+ (background dark))
+ (:foreground "#b00000"))
+ (((class color)
+ (background light))
+ (:foreground "steel blue"))
+ (t
+ (:bold t :italic t)))
+ "Face used for displaying newsgroups headers."
+ :group 'message-faces)
+
+(defface message-header-name-face
+ '((((class color)
+ (background dark))
+ (:foreground "DarkGreen"))
+ (((class color)
+ (background light))
+ (:foreground "cornflower blue"))
+ (t
+ (:bold t)))
+ "Face used for displaying header names."
+ :group 'message-faces)
+
+(defface message-header-xheader-face
+ '((((class color)
+ (background dark))
+ (:foreground "blue"))
+ (((class color)
+ (background light))
+ (:foreground "blue"))
+ (t
+ (:bold t)))
+ "Face used for displaying X-Header headers."
+ :group 'message-faces)
+
+(defface message-separator-face
+ '((((class color)
+ (background dark))
+ (:foreground "blue3"))
+ (((class color)
+ (background light))
+ (:foreground "brown"))
+ (t
+ (:bold t)))
+ "Face used for displaying the separator."
+ :group 'message-faces)
+
+(defface message-cited-text-face
+ '((((class color)
+ (background dark))
+ (:foreground "red"))
+ (((class color)
+ (background light))
+ (:foreground "red"))
+ (t
+ (:bold t)))
+ "Face used for displaying cited text names."
+ :group 'message-faces)
+
+(defface message-mml-face
+ '((((class color)
+ (background dark))
+ (:foreground "ForestGreen"))
+ (((class color)
+ (background light))
+ (:foreground "ForestGreen"))
+ (t
+ (:bold t)))
+ "Face used for displaying MML."
+ :group 'message-faces)
+
+(defvar message-font-lock-keywords
+ (let* ((cite-prefix "A-Za-z")
+ (cite-suffix (concat cite-prefix "0-9_.@-"))
+ (content "[ \t]*\\(.+\\(\n[ \t].*\\)*\\)"))
+ `((,(concat "^\\([Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-to-face nil t))
+ (,(concat "^\\([GBF]?[Cc][Cc]:\\|[Rr]eply-[Tt]o:\\|"
+ "[Mm]ail-[Cc]opies-[Tt]o:\\|"
+ "[Mm]ail-[Rr]eply-[Tt]o:\\|"
+ "[Mm]ail-[Ff]ollowup-[Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-cc-face nil t))
+ (,(concat "^\\([Ss]ubject:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-subject-face nil t))
+ (,(concat "^\\([Nn]ewsgroups:\\|Followup-[Tt]o:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-newsgroups-face nil t))
+ (,(concat "^\\([A-Z][^: \n\t]+:\\)" content)
+ (1 'message-header-name-face)
+ (2 'message-header-other-face nil t))
+ (,(concat "^\\(X-[A-Za-z0-9-]+\\|In-Reply-To\\):" content)
+ (1 'message-header-name-face)
+ (2 'message-header-name-face))
+ ,@(if (and mail-header-separator
+ (not (equal mail-header-separator "")))
+ `((,(concat "^\\(" (regexp-quote mail-header-separator) "\\)$")
+ 1 'message-separator-face))
+ nil)
+ (,(concat "^[ \t]*"
+ "\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
+ "[:>|}].*")
+ (0 'message-cited-text-face))
+ ("<#/?\\(multipart\\|part\\|external\\).*>"
+ (0 'message-mml-face))))
+ "Additional expressions to highlight in Message mode.")
+
+;; XEmacs does it like this. For Emacs, we have to set the
+;; `font-lock-defaults' buffer-local variable.
+(put 'message-mode 'font-lock-defaults '(message-font-lock-keywords t))
+
+(defvar message-face-alist
+ '((bold . bold-region)
+ (underline . underline-region)
+ (default . (lambda (b e)
+ (unbold-region b e)
+ (ununderline-region b e))))
+ "Alist of mail and news faces for facemenu.
+The cdr of ech entry is a function for applying the face to a region.")
+
+(defcustom message-send-hook nil
+ "Hook run before sending messages."
+ :group 'message-various
+ :options '(ispell-message)
+ :type 'hook)
+
+(defcustom message-send-mail-hook nil
+ "Hook run before sending mail messages."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-send-news-hook nil
+ "Hook run before sending news messages."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-sent-hook nil
+ "Hook run after sending messages."
+ :group 'message-various
+ :type 'hook)
+
+(defcustom message-use-multi-frames nil
+ "Make new frame when sending messages."
+ :group 'message-frames
+ :type 'boolean)
+
+(defcustom message-delete-frame-on-exit nil
+ "Delete frame after sending messages."
+ :group 'message-frames
+ :type '(choice (const :tag "off" nil)
+ (const :tag "always" t)
+ (const :tag "ask" ask)))
+
+(defvar message-draft-coding-system
+ (cond
+ ((not (fboundp 'find-coding-system)) nil)
+ ((find-coding-system 'emacs-mule) 'emacs-mule)
+ ((find-coding-system 'escape-quoted) 'escape-quoted)
+ ((find-coding-system 'no-conversion) 'no-conversion)
+ (t nil))
+ "Coding system to compose mail.")
+
+;;; Internal variables.
+
+(defvar message-buffer-list nil)
+(defvar message-this-is-news nil)
+(defvar message-this-is-mail nil)
+(defvar message-draft-article nil)
+(defvar message-mime-part nil)
+(defvar message-posting-charset nil)
+
+;; Byte-compiler warning
+(defvar gnus-active-hashtb)
+(defvar gnus-read-active-file)
+
+;;; Regexp matching the delimiter of messages in UNIX mail format
+;;; (UNIX From lines), minus the initial ^. It should be a copy
+;;; of rmail.el's rmail-unix-mail-delimiter.
+(defvar message-unix-mail-delimiter
+ (let ((time-zone-regexp
+ (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
+ "\\|[-+]?[0-9][0-9][0-9][0-9]"
+ "\\|"
+ "\\) *")))
+ (concat
+ "From "
+
+ ;; Many things can happen to an RFC 822 mailbox before it is put into
+ ;; a `From' line. The leading phrase can be stripped, e.g.
+ ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'. The <> can be stripped, e.g.
+ ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'. Everything starting with a CRLF
+ ;; can be removed, e.g.
+ ;; From: joe@y.z (Joe K
+ ;; User)
+ ;; can yield `From joe@y.z (Joe K Fri Mar 22 08:11:15 1996', and
+ ;; From: Joe User
+ ;; <joe@y.z>
+ ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
+ ;; The mailbox can be removed or be replaced by white space, e.g.
+ ;; From: "Joe User"{space}{tab}
+ ;; <joe@y.z>
+ ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
+ ;; where {space} and {tab} represent the Ascii space and tab characters.
+ ;; We want to match the results of any of these manglings.
+ ;; The following regexp rejects names whose first characters are
+ ;; obviously bogus, but after that anything goes.
+ "\\([^\0-\b\n-\r\^?].*\\)? "
+
+ ;; The time the message was sent.
+ "\\([^\0-\r \^?]+\\) +" ; day of the week
+ "\\([^\0-\r \^?]+\\) +" ; month
+ "\\([0-3]?[0-9]\\) +" ; day of month
+ "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *" ; time of day
+
+ ;; Perhaps a time zone, specified by an abbreviation, or by a
+ ;; numeric offset.
+ time-zone-regexp
+
+ ;; The year.
+ " \\([0-9][0-9]+\\) *"
+
+ ;; On some systems the time zone can appear after the year, too.
+ time-zone-regexp
+
+ ;; Old uucp cruft.
+ "\\(remote from .*\\)?"
+
+ "\n"))
+ "Regexp matching the delimiter of messages in UNIX mail format.")
+
+(defvar message-unsent-separator
+ (concat "^ *---+ +Unsent message follows +---+ *$\\|"
+ "^ *---+ +Returned message +---+ *$\\|"
+ "^Start of returned message$\\|"
+ "^ *---+ +Original message +---+ *$\\|"
+ "^ *--+ +begin message +--+ *$\\|"
+ "^ *---+ +Original message follows +---+ *$\\|"
+ "^|? *---+ +Message text follows: +---+ *|?$")
+ "A regexp that matches the separator before the text of a failed message.")
+
+(defvar message-header-format-alist
+ `((Newsgroups)
+ (To . message-fill-address)
+ (Cc . message-fill-address)
+ (Subject)
+ (In-Reply-To)
+ (Fcc)
+ (Bcc)
+ (Date)
+ (Organization)
+ (Distribution)
+ (Lines)
+ (Expires)
+ (Message-ID)
+ (References . message-shorten-references)
+ (User-Agent))
+ "Alist used for formatting headers.")
+
+(eval-and-compile
+ (autoload 'message-setup-toolbar "messagexmas")
+ (autoload 'mh-new-draft-name "mh-comp")
+ (autoload 'mh-send-letter "mh-comp")
+ (autoload 'gnus-point-at-eol "gnus-util")
+ (autoload 'gnus-point-at-bol "gnus-util")
+ (autoload 'gnus-output-to-mail "gnus-util")
+ (autoload 'mail-abbrev-in-expansion-header-p "mailabbrev")
+ (autoload 'nndraft-request-associate-buffer "nndraft")
+ (autoload 'nndraft-request-expire-articles "nndraft")
+ (autoload 'gnus-open-server "gnus-int")
+ (autoload 'gnus-request-post "gnus-int")
+ (autoload 'gnus-copy-article-buffer "gnus-msg")
+ (autoload 'gnus-alive-p "gnus-util")
+ (autoload 'rmail-output "rmail"))
+
+\f
+
+;;;
+;;; Utility functions.
+;;;
+(defun message-eval-parameter (parameter)
+ (condition-case ()
+ (if (symbolp parameter)
+ (if (functionp parameter)
+ (funcall parameter)
+ (eval parameter))
+ parameter)
+ (error nil)))
+
+(defsubst message-get-parameter (key &optional alist)
+ (unless alist
+ (setq alist message-parameter-alist))
+ (cdr (assq key alist)))
+
+(defmacro message-get-parameter-with-eval (key &optional alist)
+ `(message-eval-parameter (message-get-parameter ,key ,alist)))
+
+(defmacro message-y-or-n-p (question show &rest text)
+ "Ask QUESTION, displaying the rest of the arguments in a temp. buffer if SHOW"
+ `(message-talkative-question 'y-or-n-p ,question ,show ,@text))
+
+;; Delete the current line (and the next N lines.);
+(defmacro message-delete-line (&optional n)
+ `(delete-region (progn (beginning-of-line) (point))
+ (progn (forward-line ,(or n 1)) (point))))
+
+(defun message-tokenize-header (header &optional separator)
+ "Split HEADER into a list of header elements.
+\",\" is used as the separator."
+ (if (not header)
+ nil
+ (let ((regexp (format "[%s]+" (or separator ",")))
+ (beg 1)
+ (first t)
+ quoted elems paren)
+ (save-excursion
+ (message-set-work-buffer)
+ (insert header)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (if first
+ (setq first nil)
+ (forward-char 1))
+ (cond ((and (> (point) beg)
+ (or (eobp)
+ (and (looking-at regexp)
+ (not quoted)
+ (not paren))))
+ (push (buffer-substring beg (point)) elems)
+ (setq beg (match-end 0)))
+ ((eq (char-after) ?\")
+ (setq quoted (not quoted)))
+ ((and (eq (char-after) ?\()
+ (not quoted))
+ (setq paren t))
+ ((and (eq (char-after) ?\))
+ (not quoted))
+ (setq paren nil))))
+ (nreverse elems)))))
+
+(defun message-mail-file-mbox-p (file)
+ "Say whether FILE looks like a Unix mbox file."
+ (when (and (file-exists-p file)
+ (file-readable-p file)
+ (file-regular-p file))
+ (with-temp-buffer
+ (nnheader-insert-file-contents file)
+ (goto-char (point-min))
+ (looking-at message-unix-mail-delimiter))))
+
+(defun message-fetch-field (header &optional not-all)
+ "The same as `mail-fetch-field', only remove all newlines."
+ (let* ((inhibit-point-motion-hooks t)
+ (value (mail-fetch-field header nil (not not-all))))
+ (when value
+ (while (string-match "\n[\t ]+" value)
+ (setq value (replace-match " " t t value)))
+ ;; We remove all text props.delete-region
+ (format "%s" value))))
+
+(defun message-narrow-to-field ()
+ "Narrow the buffer to the header on the current line."
+ (beginning-of-line)
+ (narrow-to-region
+ (point)
+ (progn
+ (forward-line 1)
+ (if (re-search-forward "^[^ \n\t]" nil t)
+ (progn
+ (beginning-of-line)
+ (point))
+ (point-max))))
+ (goto-char (point-min)))
+
+(defun message-add-header (&rest headers)
+ "Add the HEADERS to the message header, skipping those already present."
+ (while headers
+ (let (hclean)
+ (unless (string-match "^\\([^:]+\\):[ \t]*[^ \t]" (car headers))
+ (error "Invalid header `%s'" (car headers)))
+ (setq hclean (match-string 1 (car headers)))
+ (save-restriction
+ (message-narrow-to-headers)
+ (unless (re-search-forward (concat "^" (regexp-quote hclean) ":") nil t)
+ (insert (car headers) ?\n))))
+ (setq headers (cdr headers))))
+
+(defun message-fetch-reply-field (header)
+ "Fetch FIELD from the message we're replying to."
+ (let ((buffer (message-eval-parameter message-reply-buffer)))
+ (when (and buffer
+ (buffer-name buffer))
+ (save-excursion
+ (set-buffer buffer)
+ (message-fetch-field header)))))
+
+(defun message-set-work-buffer ()
+ (if (get-buffer " *message work*")
+ (progn
+ (set-buffer " *message work*")
+ (erase-buffer))
+ (set-buffer (get-buffer-create " *message work*"))
+ (kill-all-local-variables)))
+
+(defun message-functionp (form)
+ "Return non-nil if FORM is funcallable."
+ (or (and (symbolp form) (fboundp form))
+ (and (listp form) (eq (car form) 'lambda))
+ (byte-code-function-p form)))
+
+(defun message-strip-subject-re (subject)
+ "Remove \"Re:\" from subject lines."
+ (if (string-match message-subject-re-regexp subject)
+ (substring subject (match-end 0))
+ subject))
+
+(defun message-remove-header (header &optional is-regexp first reverse)
+ "Remove HEADER in the narrowed buffer.
+If REGEXP, HEADER is a regular expression.
+If FIRST, only remove the first instance of the header.
+Return the number of headers removed."
+ (goto-char (point-min))
+ (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":")))
+ (number 0)
+ (case-fold-search t)
+ last)
+ (while (and (not (eobp))
+ (not last))
+ (if (if reverse
+ (not (looking-at regexp))
+ (looking-at regexp))
+ (progn
+ (incf number)
+ (when first
+ (setq last t))
+ (delete-region
+ (point)
+ ;; There might be a continuation header, so we have to search
+ ;; until we find a new non-continuation line.
+ (progn
+ (forward-line 1)
+ (if (re-search-forward "^[^ \t]" nil t)
+ (goto-char (match-beginning 0))
+ (point-max)))))
+ (forward-line 1)
+ (if (re-search-forward "^[^ \t]" nil t)
+ (goto-char (match-beginning 0))
+ (goto-char (point-max)))))
+ number))
+
+(defun message-remove-first-header (header)
+ "Remove the first instance of HEADER if there is more than one."
+ (let ((count 0)
+ (regexp (concat "^" (regexp-quote header) ":")))
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward regexp nil t)
+ (incf count)))
+ (while (> count 1)
+ (message-remove-header header nil t)
+ (decf count))))
+
+(defun message-narrow-to-headers ()
+ "Narrow the buffer to the head of the message."
+ (widen)
+ (narrow-to-region
+ (goto-char (point-min))
+ (if (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
+ (match-beginning 0)
+ (point-max)))
+ (goto-char (point-min)))
+
+(defun message-narrow-to-head ()
+ "Narrow the buffer to the head of the message.
+Point is left at the beginning of the narrowed-to region."
+ (widen)
+ (narrow-to-region
+ (goto-char (point-min))
+ (if (search-forward "\n\n" nil 1)
+ (1- (point))
+ (point-max)))
+ (goto-char (point-min)))
+
+(defun message-narrow-to-headers-or-head ()
+ "Narrow the buffer to the head of the message."
+ (widen)
+ (narrow-to-region
+ (goto-char (point-min))
+ (cond
+ ((re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n") nil t)
+ (match-beginning 0))
+ ((search-forward "\n\n" nil t)
+ (1- (point)))
+ (t
+ (point-max))))
+ (goto-char (point-min)))
+
+(defun message-news-p ()
+ "Say whether the current buffer contains a news message."
+ (and (not message-this-is-mail)
+ (or message-this-is-news
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (and (message-fetch-field "newsgroups")
+ (not (message-fetch-field "posted-to"))))))))
+
+(defun message-mail-p ()
+ "Say whether the current buffer contains a mail message."
+ (and (not message-this-is-news)
+ (or message-this-is-mail
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (or (message-fetch-field "to")
+ (message-fetch-field "cc")
+ (message-fetch-field "bcc")))))))
+
+(defun message-next-header ()
+ "Go to the beginning of the next header."
+ (beginning-of-line)
+ (or (eobp) (forward-char 1))
+ (not (if (re-search-forward "^[^ \t]" nil t)
+ (beginning-of-line)
+ (goto-char (point-max)))))
+
+(defun message-sort-headers-1 ()
+ "Sort the buffer as headers using `message-rank' text props."
+ (goto-char (point-min))
+ (sort-subr
+ nil 'message-next-header
+ (lambda ()
+ (message-next-header)
+ (unless (bobp)
+ (forward-char -1)))
+ (lambda ()
+ (or (get-text-property (point) 'message-rank)
+ 10000))))
+
+(defun message-sort-headers ()
+ "Sort the headers of the current message according to `message-header-format-alist'."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (let ((max (1+ (length message-header-format-alist)))
+ rank)
+ (message-narrow-to-headers)
+ (while (re-search-forward "^[^ \n]+:" nil t)
+ (put-text-property
+ (match-beginning 0) (1+ (match-beginning 0))
+ 'message-rank
+ (if (setq rank (length (memq (assq (intern (buffer-substring
+ (match-beginning 0)
+ (1- (match-end 0))))
+ message-header-format-alist)
+ message-header-format-alist)))
+ (- max rank)
+ (1+ max)))))
+ (message-sort-headers-1))))
+
+\f
+
+;;;
+;;; Message mode
+;;;
+
+;;; Set up keymap.
+
+(defvar message-mode-map nil)
+
+(unless message-mode-map
+ (setq message-mode-map (make-keymap))
+ (set-keymap-parent message-mode-map text-mode-map)
+ (define-key message-mode-map "\C-c?" 'describe-mode)
+
+ (define-key message-mode-map "\C-c\C-f\C-t" 'message-goto-to)
+ (define-key message-mode-map "\C-c\C-f\C-b" 'message-goto-bcc)
+ (define-key message-mode-map "\C-c\C-f\C-w" 'message-goto-fcc)
+ (define-key message-mode-map "\C-c\C-f\C-c" 'message-goto-cc)
+ (define-key message-mode-map "\C-c\C-f\C-s" 'message-goto-subject)
+ ;; (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-reply-to)
+ (define-key message-mode-map "\C-c\C-f\C-r" 'message-goto-mail-reply-to)
+ (define-key message-mode-map "\C-c\C-f\C-m" 'message-goto-mail-followup-to)
+ (define-key message-mode-map "\C-c\C-f\C-n" 'message-goto-newsgroups)
+ (define-key message-mode-map "\C-c\C-f\C-d" 'message-goto-distribution)
+ (define-key message-mode-map "\C-c\C-f\C-f" 'message-goto-followup-to)
+ (define-key message-mode-map "\C-c\C-f\C-k" 'message-goto-keywords)
+ (define-key message-mode-map "\C-c\C-f\C-u" 'message-goto-summary)
+ (define-key message-mode-map "\C-c\C-b" 'message-goto-body)
+ (define-key message-mode-map "\C-c\C-i" 'message-goto-signature)
+
+ (define-key message-mode-map "\C-c\C-t" 'message-insert-to)
+ (define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
+
+ (define-key message-mode-map "\C-c\C-y" 'message-yank-original)
+ (define-key message-mode-map "\C-c\C-q" 'message-fill-yanked-message)
+ (define-key message-mode-map "\C-c\C-w" 'message-insert-signature)
+ (define-key message-mode-map "\C-c\M-h" 'message-insert-headers)
+ (define-key message-mode-map "\C-c\C-r" 'message-caesar-buffer-body)
+ (define-key message-mode-map "\C-c\C-o" 'message-sort-headers)
+ (define-key message-mode-map "\C-c\M-r" 'message-rename-buffer)
+
+ (define-key message-mode-map "\C-c\C-c" 'message-send-and-exit)
+ (define-key message-mode-map "\C-c\C-s" 'message-send)
+ (define-key message-mode-map "\C-c\C-k" 'message-kill-buffer)
+ (define-key message-mode-map "\C-c\C-d" 'message-dont-send)
+
+ (define-key message-mode-map "\C-c\C-e" 'message-elide-region)
+ (define-key message-mode-map "\C-c\C-v" 'message-delete-not-region)
+ (define-key message-mode-map "\C-c\C-z" 'message-kill-to-signature)
+ (define-key message-mode-map "\M-\r" 'message-newline-and-reformat)
+
+ (define-key message-mode-map "\t" 'message-tab)
+
+ (define-key message-mode-map "\C-x\C-s" 'message-save-drafts)
+ (define-key message-mode-map "\C-xk" 'message-mimic-kill-buffer))
+
+(easy-menu-define
+ message-mode-menu message-mode-map "Message Menu."
+ '("Message"
+ ["Sort Headers" message-sort-headers t]
+ ["Yank Original" message-yank-original t]
+ ["Fill Yanked Message" message-fill-yanked-message t]
+ ["Insert Signature" message-insert-signature t]
+ ["Caesar (rot13) Message" message-caesar-buffer-body t]
+ ["Caesar (rot13) Region" message-caesar-region (mark t)]
+ ["Elide Region" message-elide-region (mark t)]
+ ["Delete Outside Region" message-delete-not-region (mark t)]
+ ["Kill To Signature" message-kill-to-signature t]
+ ["Newline and Reformat" message-newline-and-reformat t]
+ ["Rename buffer" message-rename-buffer t]
+ ["Spellcheck" ispell-message t]
+ "----"
+ ["Send Message" message-send-and-exit t]
+ ["Abort Message" message-dont-send t]
+ ["Kill Message" message-kill-buffer t]))
+
+(easy-menu-define
+ message-mode-field-menu message-mode-map ""
+ '("Field"
+ ["Fetch To" message-insert-to t]
+ ["Fetch Newsgroups" message-insert-newsgroups t]
+ "----"
+ ["To" message-goto-to t]
+ ["Subject" message-goto-subject t]
+ ["Cc" message-goto-cc t]
+ ["Reply-To" message-goto-reply-to t]
+ ["Mail-Reply-To" message-goto-mail-reply-to t]
+ ["Mail-Followup-To" message-goto-mail-followup-to t]
+ ["Mail-Copies-To" message-goto-mail-copies-to t]
+ ["Summary" message-goto-summary t]
+ ["Keywords" message-goto-keywords t]
+ ["Newsgroups" message-goto-newsgroups t]
+ ["Followup-To" message-goto-followup-to t]
+ ["Distribution" message-goto-distribution t]
+ ["Body" message-goto-body t]
+ ["Signature" message-goto-signature t]))
+
+(defvar facemenu-add-face-function)
+(defvar facemenu-remove-face-function)
+
+;;;###autoload
+(defun message-mode ()
+ "Major mode for editing mail and news to be sent.
+Like Text Mode but with these additional commands:
+C-c C-s message-send (send the message) C-c C-c message-send-and-exit
+C-c C-f move to a header field (and create it if there isn't):
+ C-c C-f C-t move to To C-c C-f C-s move to Subject
+ C-c C-f C-c move to Cc C-c C-f C-b move to Bcc
+ C-c C-f C-w move to Fcc C-c C-f C-r move to Reply-To
+ C-c C-f C-u move to Summary C-c C-f C-n move to Newsgroups
+ C-c C-f C-k move to Keywords C-c C-f C-d move to Distribution
+ C-c C-f C-m move to Mail-Followup-To
+ C-c C-f C-f move to Followup-To
+C-c C-t message-insert-to (add a To header to a news followup)
+C-c C-n message-insert-newsgroups (add a Newsgroup header to a news reply)
+C-c C-b message-goto-body (move to beginning of message text).
+C-c C-i message-goto-signature (move to the beginning of the signature).
+C-c C-w message-insert-signature (insert `message-signature-file' file).
+C-c C-y message-yank-original (insert current message, if any).
+C-c C-q message-fill-yanked-message (fill what was yanked).
+C-c C-e message-elide-region (elide the text between point and mark).
+C-c C-v message-delete-not-region (remove the text outside the region).
+C-c C-z message-kill-to-signature (kill the text up to the signature).
+C-c C-r message-caesar-buffer-body (rot13 the message body)."
+ (interactive)
+ (kill-all-local-variables)
+ (set (make-local-variable 'message-reply-buffer) nil)
+ (make-local-variable 'message-send-actions)
+ (make-local-variable 'message-exit-actions)
+ (make-local-variable 'message-kill-actions)
+ (make-local-variable 'message-postpone-actions)
+ (make-local-variable 'message-draft-article)
+ (make-local-hook 'kill-buffer-hook)
+ (set-syntax-table message-mode-syntax-table)
+ (use-local-map message-mode-map)
+ (setq local-abbrev-table message-mode-abbrev-table)
+ (setq major-mode 'message-mode)
+ (setq mode-name "Message")
+ (setq buffer-offer-save t)
+ (make-local-variable 'facemenu-add-face-function)
+ (make-local-variable 'facemenu-remove-face-function)
+ (setq facemenu-add-face-function
+ (lambda (face end)
+ (let ((face-fun (cdr (assq face message-face-alist))))
+ (if face-fun
+ (funcall face-fun (point) end)
+ (error "Face %s not configured for %s mode" face mode-name)))
+ "")
+ facemenu-remove-face-function t)
+ (make-local-variable 'paragraph-separate)
+ (make-local-variable 'paragraph-start)
+ ;; `-- ' precedes the signature. `-----' appears at the start of the
+ ;; lines that delimit forwarded messages.
+ ;; Lines containing just >= 3 dashes, perhaps after whitespace,
+ ;; are also sometimes used and should be separators.
+ (setq paragraph-start
+ (concat (regexp-quote mail-header-separator)
+ "$\\|[ \t]*[a-z0-9A-Z]*>+[ \t]*$\\|[ \t]*$\\|"
+ "-- $\\|---+$\\|"
+ page-delimiter
+ ;;!!! Uhm... shurely this can't be right?
+ "[> " (regexp-quote message-yank-prefix) "]+$"))
+ (setq paragraph-separate paragraph-start)
+ (make-local-variable 'message-reply-headers)
+ (setq message-reply-headers nil)
+ (make-local-variable 'message-user-agent)
+ (make-local-variable 'message-post-method)
+ (set (make-local-variable 'message-sent-message-via) nil)
+ (set (make-local-variable 'message-checksum) nil)
+ (make-local-variable 'message-parameter-alist)
+ (setq message-parameter-alist
+ (copy-sequence message-startup-parameter-alist))
+ ;;(when (fboundp 'mail-hist-define-keys)
+ ;; (mail-hist-define-keys))
+ (when (string-match "XEmacs\\|Lucid" emacs-version)
+ (message-setup-toolbar))
+ (easy-menu-add message-mode-menu message-mode-map)
+ (easy-menu-add message-mode-field-menu message-mode-map)
+ ;; Allow mail alias things.
+ (when (eq message-mail-alias-type 'abbrev)
+ (if (fboundp 'mail-abbrevs-setup)
+ (mail-abbrevs-setup)
+ (mail-aliases-setup)))
+ (message-set-auto-save-file-name)
+ (unless (string-match "XEmacs" emacs-version)
+ (set (make-local-variable 'font-lock-defaults)
+ '(message-font-lock-keywords t)))
+ (make-local-variable 'adaptive-fill-regexp)
+ (setq adaptive-fill-regexp
+ (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|" adaptive-fill-regexp))
+ (unless (boundp 'adaptive-fill-first-line-regexp)
+ (setq adaptive-fill-first-line-regexp nil))
+ (make-local-variable 'adaptive-fill-first-line-regexp)
+ (setq adaptive-fill-first-line-regexp
+ (concat "[ \t]*[-a-z0-9A-Z]*>+[ \t]*\\|"
+ adaptive-fill-first-line-regexp))
+ (run-hooks 'text-mode-hook 'message-mode-hook))
+
+\f
+
+;;;
+;;; Message mode commands
+;;;
+
+;;; Movement commands
+
+(defun message-goto-to ()
+ "Move point to the To header."
+ (interactive)
+ (message-position-on-field "To"))
+
+(defun message-goto-subject ()
+ "Move point to the Subject header."
+ (interactive)
+ (message-position-on-field "Subject"))
+
+(defun message-goto-cc ()
+ "Move point to the Cc header."
+ (interactive)
+ (message-position-on-field "Cc" "To"))
+
+(defun message-goto-bcc ()
+ "Move point to the Bcc header."
+ (interactive)
+ (message-position-on-field "Bcc" "Cc" "To"))
+
+(defun message-goto-fcc ()
+ "Move point to the Fcc header."
+ (interactive)
+ (message-position-on-field "Fcc" "To" "Newsgroups"))
+
+(defun message-goto-reply-to ()
+ "Move point to the Reply-To header."
+ (interactive)
+ (message-position-on-field "Reply-To" "Subject"))
+
+(defun message-goto-mail-reply-to ()
+ "Move point to the Mail-Reply-To header."
+ (interactive)
+ (message-position-on-field "Mail-Reply-To" "Subject"))
+
+(defun message-goto-mail-followup-to ()
+ "Move point to the Mail-Followup-To header."
+ (interactive)
+ (message-position-on-field "Mail-Followup-To" "Subject"))
+
+(defun message-goto-mail-copies-to ()
+ "Move point to the Mail-Copies-To header."
+ (interactive)
+ (message-position-on-field "Mail-Copies-To" "Subject"))
+
+(defun message-goto-newsgroups ()
+ "Move point to the Newsgroups header."
+ (interactive)
+ (message-position-on-field "Newsgroups"))
+
+(defun message-goto-distribution ()
+ "Move point to the Distribution header."
+ (interactive)
+ (message-position-on-field "Distribution"))
+
+(defun message-goto-followup-to ()
+ "Move point to the Followup-To header."
+ (interactive)
+ (message-position-on-field "Followup-To" "Newsgroups"))
+
+(defun message-goto-keywords ()
+ "Move point to the Keywords header."
+ (interactive)
+ (message-position-on-field "Keywords" "Subject"))
+
+(defun message-goto-summary ()
+ "Move point to the Summary header."
+ (interactive)
+ (message-position-on-field "Summary" "Subject"))
+
+(defun message-goto-body ()
+ "Move point to the beginning of the message body."
+ (interactive)
+ (if (looking-at "[ \t]*\n") (expand-abbrev))
+ (goto-char (point-min))
+ (search-forward (concat "\n" mail-header-separator "\n") nil t))
+
+(defun message-goto-eoh ()
+ "Move point to the end of the headers."
+ (interactive)
+ (message-goto-body)
+ (forward-line -2))
+
+(defun message-goto-signature ()
+ "Move point to the beginning of the message signature.
+If there is no signature in the article, go to the end and
+return nil."
+ (interactive)
+ (goto-char (point-min))
+ (if (re-search-forward message-signature-separator nil t)
+ (forward-line 1)
+ (goto-char (point-max))
+ nil))
+
+\f
+
+(defun message-insert-to (&optional force)
+ "Insert a To header that points to the author of the article being replied to.
+If the original author requested not to be sent mail, the function signals
+an error.
+With the prefix argument FORCE, insert the header anyway."
+ (interactive "P")
+ (let ((co (message-fetch-reply-field "mail-copies-to")))
+ (when (and (null force)
+ co
+ (or (equal (downcase co) "never")
+ (equal (downcase co) "nobody")))
+ (error "The user has requested not to have copies sent via mail")))
+ (when (and (message-position-on-field "To")
+ (mail-fetch-field "to")
+ (not (string-match "\\` *\\'" (mail-fetch-field "to"))))
+ (insert ", "))
+ (insert (or (message-fetch-reply-field "reply-to")
+ (message-fetch-reply-field "from") "")))
+
+(defun message-insert-newsgroups ()
+ "Insert the Newsgroups header from the article being replied to."
+ (interactive)
+ (when (and (message-position-on-field "Newsgroups")
+ (mail-fetch-field "newsgroups")
+ (not (string-match "\\` *\\'" (mail-fetch-field "newsgroups"))))
+ (insert ","))
+ (insert (or (message-fetch-reply-field "newsgroups") "")))
+
+\f
+
+;;; Various commands
+
+(defun message-delete-not-region (beg end)
+ "Delete everything in the body of the current message that is outside of the region."
+ (interactive "r")
+ (save-excursion
+ (goto-char end)
+ (delete-region (point) (if (not (message-goto-signature))
+ (point)
+ (forward-line -2)
+ (point)))
+ (insert "\n")
+ (goto-char beg)
+ (delete-region beg (progn (message-goto-body)
+ (forward-line 2)
+ (point))))
+ (when (message-goto-signature)
+ (forward-line -2)))
+
+(defun message-kill-to-signature ()
+ "Deletes all text up to the signature."
+ (interactive)
+ (let ((point (point)))
+ (message-goto-signature)
+ (unless (eobp)
+ (forward-line -2))
+ (kill-region point (point))
+ (unless (bolp)
+ (insert "\n"))))
+
+(defun message-newline-and-reformat ()
+ "Insert four newlines, and then reformat if inside quoted text."
+ (interactive)
+ (let ((point (point))
+ quoted)
+ (save-excursion
+ (beginning-of-line)
+ (setq quoted (looking-at (regexp-quote message-yank-prefix))))
+ (insert "\n\n\n\n")
+ (when quoted
+ (insert message-yank-prefix))
+ (fill-paragraph nil)
+ (goto-char point)
+ (forward-line 2)))
+
+(defun message-insert-signature (&optional force)
+ "Insert a signature. See documentation for the `message-signature' variable."
+ (interactive (list 0))
+ (let* ((signature
+ (cond
+ ((and (null message-signature)
+ (eq force 0))
+ (save-excursion
+ (goto-char (point-max))
+ (not (re-search-backward message-signature-separator nil t))))
+ ((and (null message-signature)
+ force)
+ t)
+ ((message-functionp message-signature)
+ (funcall message-signature))
+ ((listp message-signature)
+ (eval message-signature))
+ (t message-signature)))
+ (signature
+ (cond ((stringp signature)
+ signature)
+ ((and (eq t signature)
+ message-signature-file
+ (file-exists-p message-signature-file))
+ signature))))
+ (when signature
+ (goto-char (point-max))
+ ;; Insert the signature.
+ (unless (bolp)
+ (insert "\n"))
+ (insert "\n-- \n")
+ (if (eq signature t)
+ (insert-file-contents message-signature-file)
+ (insert signature))
+ (goto-char (point-max))
+ (or (bolp) (insert "\n")))))
+
+(defun message-elide-region (b e)
+ "Elide the text between point and mark.
+An ellipsis (from `message-elide-elipsis') will be inserted where the
+text was killed."
+ (interactive "r")
+ (kill-region b e)
+ (unless (bolp)
+ (insert "\n"))
+ (insert message-elide-elipsis))
+
+(defvar message-caesar-translation-table nil)
+
+(defun message-caesar-region (b e &optional n)
+ "Caesar rotation of region by N, default 13, for decrypting netnews."
+ (interactive
+ (list
+ (min (point) (or (mark t) (point)))
+ (max (point) (or (mark t) (point)))
+ (when current-prefix-arg
+ (prefix-numeric-value current-prefix-arg))))
+
+ (setq n (if (numberp n) (mod n 26) 13)) ;canonize N
+ (unless (or (zerop n) ; no action needed for a rot of 0
+ (= b e)) ; no region to rotate
+ ;; We build the table, if necessary.
+ (when (or (not message-caesar-translation-table)
+ (/= (aref message-caesar-translation-table ?a) (+ ?a n)))
+ (setq message-caesar-translation-table
+ (message-make-caesar-translation-table n)))
+ ;; Then we translate the region. Do it this way to retain
+ ;; text properties.
+ (while (< b e)
+ (when (< (char-after b) 255)
+ (subst-char-in-region
+ b (1+ b) (char-after b)
+ (aref message-caesar-translation-table (char-after b))))
+ (incf b))))
+
+(defun message-make-caesar-translation-table (n)
+ "Create a rot table with offset N."
+ (let ((i -1)
+ (table (make-string 256 0)))
+ (while (< (incf i) 256)
+ (aset table i i))
+ (concat
+ (substring table 0 ?A)
+ (substring table (+ ?A n) (+ ?A n (- 26 n)))
+ (substring table ?A (+ ?A n))
+ (substring table (+ ?A 26) ?a)
+ (substring table (+ ?a n) (+ ?a n (- 26 n)))
+ (substring table ?a (+ ?a n))
+ (substring table (+ ?a 26) 255))))
+
+(defun message-caesar-buffer-body (&optional rotnum)
+ "Caesar rotates all letters in the current buffer by 13 places.
+Used to encode/decode possiblyun offensive messages (commonly in net.jokes).
+With prefix arg, specifies the number of places to rotate each letter forward.
+Mail and USENET news headers are not rotated."
+ (interactive (if current-prefix-arg
+ (list (prefix-numeric-value current-prefix-arg))
+ (list nil)))
+ (save-excursion
+ (save-restriction
+ (when (message-goto-body)
+ (narrow-to-region (point) (point-max)))
+ (message-caesar-region (point-min) (point-max) rotnum))))
+
+(defun message-pipe-buffer-body (program)
+ "Pipe the message body in the current buffer through PROGRAM."
+ (save-excursion
+ (save-restriction
+ (when (message-goto-body)
+ (narrow-to-region (point) (point-max)))
+ (let ((body (buffer-substring (point-min) (point-max))))
+ (unless (equal 0 (call-process-region
+ (point-min) (point-max) program t t))
+ (insert body)
+ (message "%s failed." program))))))
+
+(defun message-rename-buffer (&optional enter-string)
+ "Rename the *message* buffer to \"*message* RECIPIENT\".
+If the function is run with a prefix, it will ask for a new buffer
+name, rather than giving an automatic name."
+ (interactive "Pbuffer name: ")
+ (save-excursion
+ (save-restriction
+ (goto-char (point-min))
+ (narrow-to-region (point)
+ (search-forward mail-header-separator nil 'end))
+ (let* ((mail-to (or
+ (if (message-news-p) (message-fetch-field "Newsgroups")
+ (message-fetch-field "To"))
+ ""))
+ (mail-trimmed-to
+ (if (string-match "," mail-to)
+ (concat (substring mail-to 0 (match-beginning 0)) ", ...")
+ mail-to))
+ (name-default (concat "*message* " mail-trimmed-to))
+ (name (if enter-string
+ (read-string "New buffer name: " name-default)
+ name-default)))
+ (rename-buffer name t)))))
+
+(defun message-fill-yanked-message (&optional justifyp)
+ "Fill the paragraphs of a message yanked into this one.
+Numeric argument means justify as well."
+ (interactive "P")
+ (save-excursion
+ (goto-char (point-min))
+ (search-forward (concat "\n" mail-header-separator "\n") nil t)
+ (let ((fill-prefix message-yank-prefix))
+ (fill-individual-paragraphs (point) (point-max) justifyp t))))
+
+(defun message-indent-citation ()
+ "Modify text just inserted from a message to be cited.
+The inserted text should be the region.
+When this function returns, the region is again around the modified text.
+
+Normally, indent each nonblank line `message-indentation-spaces' spaces.
+However, if `message-yank-prefix' is non-nil, insert that prefix on each line."
+ (let ((start (point)))
+ ;; Remove unwanted headers.
+ (when message-ignored-cited-headers
+ (let (all-removed)
+ (save-restriction
+ (narrow-to-region
+ (goto-char start)
+ (if (search-forward "\n\n" nil t)
+ (1- (point))
+ (point)))
+ (message-remove-header message-ignored-cited-headers t)
+ (when (= (point-min) (point-max))
+ (setq all-removed t))
+ (goto-char (point-max)))
+ (if all-removed
+ (goto-char start)
+ (forward-line 1))))
+ ;; Delete blank lines at the start of the buffer.
+ (while (and (point-min)
+ (eolp)
+ (not (eobp)))
+ (message-delete-line))
+ ;; Delete blank lines at the end of the buffer.
+ (goto-char (point-max))
+ (unless (eolp)
+ (insert "\n"))
+ (while (and (zerop (forward-line -1))
+ (looking-at "$"))
+ (message-delete-line))
+ ;; Do the indentation.
+ (if (null message-yank-prefix)
+ (indent-rigidly start (mark t) message-indentation-spaces)
+ (save-excursion
+ (goto-char start)
+ (while (< (point) (mark t))
+ (insert message-yank-prefix)
+ (forward-line 1))))
+ (goto-char start)))
+
+(defvar gnus-article-copy)
+(defun message-yank-original (&optional arg)
+ "Insert the message being replied to, if any.
+Puts point before the text and mark after.
+Normally indents each nonblank line ARG spaces (default 3). However,
+if `message-yank-prefix' is non-nil, insert that prefix on each line.
+
+This function uses `message-cite-function' to do the actual citing.
+
+Just \\[universal-argument] as argument means don't indent, insert no
+prefix, and don't delete any headers.
+
+In addition, if `message-yank-add-new-references' is non-nil and this
+command is called interactively, new IDs from the yanked article will
+be added to \"References\" field."
+ (interactive "P")
+ (let ((modified (buffer-modified-p))
+ (buffer (message-eval-parameter message-reply-buffer))
+ refs references)
+ (when (and buffer
+ message-cite-function)
+ (delete-windows-on buffer t)
+ (insert-buffer buffer) ; mark will be set at the end of article.
+
+ ;; Add new IDs to References field.
+ (when (and message-yank-add-new-references (interactive-p))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region (point) (mark t))
+ (message-narrow-to-head)
+ (setq refs (concat (or (message-fetch-field "References") "")
+ " "
+ (or (message-fetch-field "Message-ID") "")))
+ (unless (string-match "^ +$" refs)
+ (widen)
+ (message-narrow-to-headers)
+ (setq references (message-fetch-field "References"))
+ (when references
+ (setq references (split-string references)))
+ (mapcar
+ (lambda (ref)
+ (or (zerop (length ref))
+ (member ref references)
+ (setq references (append references (list ref)))))
+ (split-string refs))
+ (when references
+ (goto-char (point-min))
+ (let ((case-fold-search t))
+ (if (re-search-forward "^References:\\([\t ]+.+\n\\)+" nil t)
+ (replace-match "")
+ (goto-char (point-max))))
+ (mail-header-format
+ (list (or (assq 'References message-header-format-alist)
+ '(References . message-shorten-references)))
+ (list (cons 'References
+ (mapconcat 'identity references " "))))
+ (backward-delete-char 1))))))
+
+ (funcall message-cite-function)
+ (message-exchange-point-and-mark)
+ (unless (bolp)
+ (insert ?\n))
+ (unless modified
+ (setq message-checksum (message-checksum))))))
+
+(defun message-cite-original-without-signature ()
+ "Cite function in the standard Message manner."
+ (let ((start (point))
+ (end (mark t))
+ (functions
+ (when message-indent-citation-function
+ (if (listp message-indent-citation-function)
+ message-indent-citation-function
+ (list message-indent-citation-function)))))
+ (goto-char end)
+ (when (re-search-backward message-signature-separator start t)
+ ;; Also peel off any blank lines before the signature.
+ (forward-line -1)
+ (while (looking-at "^[ \t]*$")
+ (forward-line -1))
+ (forward-line 1)
+ (delete-region (point) end))
+ (goto-char start)
+ (while functions
+ (funcall (pop functions)))
+ (when message-citation-line-function
+ (unless (bolp)
+ (insert "\n"))
+ (funcall message-citation-line-function))))
+
+(defvar mail-citation-hook) ;Compiler directive
+(defun message-cite-original ()
+ "Cite function in the standard Message manner."
+ (if (and (boundp 'mail-citation-hook)
+ mail-citation-hook)
+ (run-hooks 'mail-citation-hook)
+ (let ((start (point))
+ (end (mark t))
+ (functions
+ (when message-indent-citation-function
+ (if (listp message-indent-citation-function)
+ message-indent-citation-function
+ (list message-indent-citation-function)))))
+ (goto-char start)
+ (while functions
+ (funcall (pop functions)))
+ (when message-citation-line-function
+ (unless (bolp)
+ (insert "\n"))
+ (funcall message-citation-line-function)))))
+
+(defun message-insert-citation-line ()
+ "Function that inserts a simple citation line."
+ (when message-reply-headers
+ (insert (mail-header-from message-reply-headers) " writes:\n\n")))
+
+(defun message-position-on-field (header &rest afters)
+ (let ((case-fold-search t))
+ (save-restriction
+ (narrow-to-region
+ (goto-char (point-min))
+ (progn
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (match-beginning 0)))
+ (goto-char (point-min))
+ (if (re-search-forward (concat "^" (regexp-quote header) ":") nil t)
+ (progn
+ (re-search-forward "^[^ \t]" nil 'move)
+ (beginning-of-line)
+ (skip-chars-backward "\n")
+ t)
+ (while (and afters
+ (not (re-search-forward
+ (concat "^" (regexp-quote (car afters)) ":")
+ nil t)))
+ (pop afters))
+ (when afters
+ (re-search-forward "^[^ \t]" nil 'move)
+ (beginning-of-line))
+ (insert header ": \n")
+ (forward-char -1)
+ nil))))
+
+(defun message-remove-signature ()
+ "Remove the signature from the text between point and mark.
+The text will also be indented the normal way."
+ (save-excursion
+ (let ((start (point))
+ mark)
+ (if (not (re-search-forward message-signature-separator (mark t) t))
+ ;; No signature here, so we just indent the cited text.
+ (message-indent-citation)
+ ;; Find the last non-empty line.
+ (forward-line -1)
+ (while (looking-at "[ \t]*$")
+ (forward-line -1))
+ (forward-line 1)
+ (setq mark (set-marker (make-marker) (point)))
+ (goto-char start)
+ (message-indent-citation)
+ ;; Enable undoing the deletion.
+ (undo-boundary)
+ (delete-region mark (mark t))
+ (set-marker mark nil)))))
+
+\f
+
+;;;
+;;; Sending messages
+;;;
+
+;; Avoid byte-compile warning.
+(defvar message-encoding-buffer nil)
+(defvar message-edit-buffer nil)
+(defvar message-mime-mode nil)
+
+(defun message-send-and-exit (&optional arg)
+ "Send message like `message-send', then, if no errors, exit from mail buffer."
+ (interactive "P")
+ (let ((buf (current-buffer))
+ (actions message-exit-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
+ (when (and (message-send arg)
+ (buffer-name buf))
+ (if message-kill-buffer-on-exit
+ (kill-buffer buf)
+ (bury-buffer buf)
+ (when (eq buf (current-buffer))
+ (message-bury buf)))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame)
+ t)))
+
+(defun message-dont-send ()
+ "Don't send the message you have been editing."
+ (interactive)
+ (message-save-drafts)
+ (let ((actions message-postpone-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
+ (message-bury (current-buffer))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame)))
+
+(defun message-kill-buffer ()
+ "Kill the current buffer."
+ (interactive)
+ (when (or (not (buffer-modified-p))
+ (eq t message-kill-buffer-query-function)
+ (funcall message-kill-buffer-query-function
+ "The buffer modified; kill anyway? "))
+ (let ((actions message-kill-actions)
+ (frame (selected-frame))
+ (org-frame message-original-frame))
+ (setq buffer-file-name nil)
+ (kill-buffer (current-buffer))
+ (message-do-actions actions)
+ (message-delete-frame frame org-frame)))
+ (message ""))
+
+(defun message-mimic-kill-buffer ()
+ "Kill the current buffer with query."
+ (interactive)
+ (unless (eq 'message-mode major-mode)
+ (error "%s must be invoked from a message buffer." this-command))
+ (let ((command this-command)
+ (bufname (read-buffer (format "Kill buffer: (default %s) "
+ (buffer-name)))))
+ (if (or (not bufname)
+ (string-equal bufname "")
+ (string-equal bufname (buffer-name)))
+ (message-kill-buffer)
+ (message "%s must be invoked only for the current buffer." command))))
+
+(defun message-delete-frame (frame org-frame)
+ "Delete frame for editing message."
+ (when (and (or (and (featurep 'xemacs)
+ (not (eq 'tty (device-type))))
+ window-system
+ (>= emacs-major-version 20))
+ (or (and (eq message-delete-frame-on-exit t)
+ (select-frame frame)
+ (or (eq frame org-frame)
+ (prog1
+ (y-or-n-p "Delete this frame?")
+ (message ""))))
+ (and (eq message-delete-frame-on-exit 'ask)
+ (select-frame frame)
+ (prog1
+ (y-or-n-p "Delete this frame?")
+ (message "")))))
+ (delete-frame frame)))
+
+(defun message-bury (buffer)
+ "Bury this mail buffer."
+ (let ((newbuf (other-buffer buffer)))
+ (bury-buffer buffer)
+ (if (and (fboundp 'frame-parameters)
+ (cdr (assq 'dedicated (frame-parameters)))
+ (not (null (delq (selected-frame) (visible-frame-list)))))
+ (delete-frame (selected-frame))
+ (switch-to-buffer newbuf))))
+
+(defun message-send (&optional arg)
+ "Send the message in the current buffer.
+If `message-interactive' is non-nil, wait for success indication
+or error messages, and inform user.
+Otherwise any failure is reported in a message back to
+the user from the mailer."
+ (interactive "P")
+ ;; Disabled test.
+ (when (or (buffer-modified-p)
+ (message-check-element 'unchanged)
+ (y-or-n-p "No changes in the buffer; really send? "))
+ ;; Make it possible to undo the coming changes.
+ (undo-boundary)
+ (let ((inhibit-read-only t))
+ (put-text-property (point-min) (point-max) 'read-only nil))
+ (run-hooks 'message-send-hook)
+ (message "Sending...")
+ (let ((message-encoding-buffer
+ (message-generate-new-buffer-clone-locals " message encoding"))
+ (message-edit-buffer (current-buffer))
+ (message-mime-mode mime-edit-mode-flag)
+ (alist message-send-method-alist)
+ (success t)
+ elem sent)
+ (save-excursion
+ (set-buffer message-encoding-buffer)
+ (erase-buffer)
+ (insert-buffer message-edit-buffer)
+ (funcall message-encode-function)
+ (message-fix-before-sending)
+ (while (and success
+ (setq elem (pop alist)))
+ (when (and (or (not (funcall (cadr elem)))
+ (and (or (not (memq (car elem)
+ message-sent-message-via))
+ (y-or-n-p
+ (format
+ "Already sent message via %s; resend? "
+ (car elem))))
+ (setq success (funcall (caddr elem) arg)))))
+ (setq sent t))))
+ (when (and success sent)
+ (message-do-fcc)
+ ;;(when (fboundp 'mail-hist-put-headers-into-history)
+ ;; (mail-hist-put-headers-into-history))
+ (save-excursion
+ (run-hooks 'message-sent-hook))
+ (message "Sending...done")
+ ;; Mark the buffer as unmodified and delete autosave.
+ (set-buffer-modified-p nil)
+ (delete-auto-save-file-if-necessary t)
+ (message-disassociate-draft)
+ ;; Delete other mail buffers and stuff.
+ (message-do-send-housekeeping)
+ (message-do-actions message-send-actions)
+ ;; Return success.
+ t))))
+
+(defun message-send-via-mail (arg)
+ "Send the current message via mail."
+ (message-send-mail arg))
+
+(defun message-send-via-news (arg)
+ "Send the current message via news."
+ (message-send-news arg))
+
+(defmacro message-check (type &rest forms)
+ "Eval FORMS if TYPE is to be checked."
+ `(or (message-check-element ,type)
+ (save-excursion
+ ,@forms)))
+
+(put 'message-check 'lisp-indent-function 1)
+(put 'message-check 'edebug-form-spec '(form body))
+
+(defun message-fix-before-sending ()
+ "Do various things to make the message nice before sending it."
+ ;; Make sure there's a newline at the end of the message.
+ (goto-char (point-max))
+ (unless (bolp)
+ (insert "\n"))
+ ;; Delete all invisible text.
+ (message-check 'invisible-text
+ (when (text-property-any (point-min) (point-max) 'invisible t)
+ (put-text-property (point-min) (point-max) 'invisible nil)
+ (unless (yes-or-no-p "Invisible text found and made visible; continue posting? ")
+ (error "Invisible text found and made visible")))))
+
+(defun message-add-action (action &rest types)
+ "Add ACTION to be performed when doing an exit of type TYPES."
+ (let (var)
+ (while types
+ (set (setq var (intern (format "message-%s-actions" (pop types))))
+ (nconc (symbol-value var) (list action))))))
+
+(defun message-do-actions (actions)
+ "Perform all actions in ACTIONS."
+ ;; Now perform actions on successful sending.
+ (while actions
+ (ignore-errors
+ (cond
+ ;; A simple function.
+ ((message-functionp (car actions))
+ (funcall (car actions)))
+ ;; Something to be evaled.
+ (t
+ (eval (car actions)))))
+ (pop actions)))
+
+(defsubst message-maybe-split-and-send-mail ()
+ "Split a message if necessary, and send it via mail.
+Returns nil if sending succeeded, returns any string if sending failed.
+This sub function is for exclusive use of `message-send-mail'."
+ (let ((mime-edit-split-ignored-field-regexp
+ mime-edit-split-ignored-field-regexp)
+ (case-fold-search t)
+ failure)
+ (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
+ (setq mime-edit-split-ignored-field-regexp
+ (concat (substring mime-edit-split-ignored-field-regexp
+ 0 (match-beginning 0))
+ "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
+ "_so_don't_rape_it!"
+ (substring mime-edit-split-ignored-field-regexp
+ (match-end 0)))))
+ (setq failure
+ (or
+ (catch 'message-sending-mail-failure
+ (mime-edit-maybe-split-and-send
+ (function
+ (lambda ()
+ (interactive)
+ (save-restriction
+ (std11-narrow-to-header mail-header-separator)
+ (goto-char (point-min))
+ (when (re-search-forward "^Message-ID:" nil t)
+ (delete-region (match-end 0) (std11-field-end))
+ (insert " " (message-make-message-id))))
+ (condition-case err
+ (funcall message-send-mail-function)
+ (error
+ (throw 'message-sending-mail-failure err))))))
+ nil)
+ (condition-case err
+ (progn
+ (funcall message-send-mail-function)
+ nil)
+ (error err))))
+ (when failure
+ (if (eq 'error (car failure))
+ (cadr failure)
+ (prin1-to-string failure)))))
+
+(defun message-send-mail (&optional arg)
+ (require 'mail-utils)
+ (let ((tembuf (message-generate-new-buffer-clone-locals " message temp"))
+ (case-fold-search nil)
+ (news (message-news-p))
+ failure)
+ (save-restriction
+ (message-narrow-to-headers)
+ ;; Insert some headers.
+ (let ((message-deletable-headers
+ (if news nil message-deletable-headers)))
+ (message-generate-headers message-required-mail-headers))
+ ;; Let the user do all of the above.
+ (run-hooks 'message-header-hook))
+ (if (not (message-check-mail-syntax))
+ (progn
+ (message "")
+ nil)
+ (unwind-protect
+ (save-excursion
+ (set-buffer tembuf)
+ (erase-buffer)
+ (insert-buffer message-encoding-buffer)
+ ;; Remove some headers.
+ (save-restriction
+ (message-narrow-to-headers)
+ ;; Remove some headers.
+ (message-remove-header message-ignored-mail-headers t))
+ (goto-char (point-max))
+ ;; require one newline at the end.
+ (or (= (preceding-char) ?\n)
+ (insert ?\n))
+ (when (and news
+ (or (message-fetch-field "cc")
+ (message-fetch-field "to")))
+ (message-insert-courtesy-copy))
+ (setq failure (message-maybe-split-and-send-mail)))
+ (kill-buffer tembuf))
+ (set-buffer message-edit-buffer)
+ (if failure
+ (progn
+ (message "Couldn't send message via mail: %s" failure)
+ nil)
+ (push 'mail message-sent-message-via)))))
+
+(defun message-send-mail-with-sendmail ()
+ "Send off the prepared buffer with sendmail."
+ (let ((errbuf (if message-interactive
+ (generate-new-buffer " sendmail errors")
+ 0))
+ resend-to-addresses delimline)
+ (let ((case-fold-search t))
+ (save-restriction
+ (message-narrow-to-headers)
+ (setq resend-to-addresses (message-fetch-field "resent-to")))
+ ;; Change header-delimiter to be what sendmail expects.
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n"))
+ (replace-match "\n")
+ (backward-char 1)
+ (setq delimline (point-marker))
+ (run-hooks 'message-send-mail-hook)
+ ;; Insert an extra newline if we need it to work around
+ ;; Sun's bug that swallows newlines.
+ (goto-char (1+ delimline))
+ (when (eval message-mailer-swallows-blank-line)
+ (newline))
+ (when message-interactive
+ (save-excursion
+ (set-buffer errbuf)
+ (erase-buffer))))
+ (let ((default-directory "/"))
+ (as-binary-process
+ (apply 'call-process-region
+ (append (list (point-min) (point-max)
+ (if (boundp 'sendmail-program)
+ sendmail-program
+ "/usr/lib/sendmail")
+ nil errbuf nil "-oi")
+ ;; Always specify who from,
+ ;; since some systems have broken sendmails.
+ ;; But some systems are more broken with -f, so
+ ;; we'll let users override this.
+ (if (null message-sendmail-f-is-evil)
+ (list "-f" (user-login-name)))
+ ;; These mean "report errors by mail"
+ ;; and "deliver in background".
+ (if (null message-interactive) '("-oem" "-odb"))
+ ;; Get the addresses from the message
+ ;; unless this is a resend.
+ ;; We must not do that for a resend
+ ;; because we would find the original addresses.
+ ;; For a resend, include the specific addresses.
+ (if resend-to-addresses
+ (list resend-to-addresses)
+ '("-t"))))))
+ (when message-interactive
+ (save-excursion
+ (set-buffer errbuf)
+ (goto-char (point-min))
+ (while (re-search-forward "\n\n* *" nil t)
+ (replace-match "; "))
+ (if (not (zerop (buffer-size)))
+ (error "Sending...failed to %s"
+ (buffer-substring (point-min) (point-max)))))
+ (when (bufferp errbuf)
+ (kill-buffer errbuf)))))
+
+(defun message-send-mail-with-qmail ()
+ "Pass the prepared message buffer to qmail-inject.
+Refer to the documentation for the variable `message-send-mail-function'
+to find out how to use this."
+ ;; replace the header delimiter with a blank line
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n"))
+ (replace-match "\n")
+ (backward-char 1)
+ (run-hooks 'message-send-mail-hook)
+ ;; send the message
+ (case
+ (as-binary-process
+ (apply
+ 'call-process-region 1 (point-max) message-qmail-inject-program
+ nil nil nil
+ ;; qmail-inject's default behaviour is to look for addresses on the
+ ;; command line; if there're none, it scans the headers.
+ ;; yes, it does The Right Thing w.r.t. Resent-To and it's kin.
+ ;;
+ ;; in general, ALL of qmail-inject's defaults are perfect for simply
+ ;; reading a formatted (i. e., at least a To: or Resent-To header)
+ ;; message from stdin.
+ ;;
+ ;; qmail also has the advantage of not having been raped by
+ ;; various vendors, so we don't have to allow for that, either --
+ ;; compare this with message-send-mail-with-sendmail and weep
+ ;; for sendmail's lost innocence.
+ ;;
+ ;; all this is way cool coz it lets us keep the arguments entirely
+ ;; free for -inject-arguments -- a big win for the user and for us
+ ;; since we don't have to play that double-guessing game and the user
+ ;; gets full control (no gestapo'ish -f's, for instance). --sj
+ message-qmail-inject-args))
+ ;; qmail-inject doesn't say anything on it's stdout/stderr,
+ ;; we have to look at the retval instead
+ (0 nil)
+ (1 (error "qmail-inject reported permanent failure"))
+ (111 (error "qmail-inject reported transient failure"))
+ ;; should never happen
+ (t (error "qmail-inject reported unknown failure"))))
+
+(defun message-send-mail-with-mh ()
+ "Send the prepared message buffer with mh."
+ (let ((mh-previous-window-config nil)
+ (name (mh-new-draft-name)))
+ (setq buffer-file-name name)
+ ;; MH wants to generate these headers itself.
+ (when message-mh-deletable-headers
+ (let ((headers message-mh-deletable-headers))
+ (while headers
+ (goto-char (point-min))
+ (and (re-search-forward
+ (concat "^" (symbol-name (car headers)) ": *") nil t)
+ (message-delete-line))
+ (pop headers))))
+ (run-hooks 'message-send-mail-hook)
+ ;; Pass it on to mh.
+ (mh-send-letter)))
+
+(defun message-send-mail-with-smtp ()
+ "Send off the prepared buffer with SMTP."
+ (require 'smtp) ; XXX
+ (let ((case-fold-search t)
+ recipients)
+ (save-restriction
+ (message-narrow-to-headers)
+ (setq recipients
+ ;; XXX: Should be replaced by better one.
+ (smtp-deduce-address-list (current-buffer)
+ (point-min) (point-max)))
+ ;; Remove BCC lines.
+ (message-remove-header "bcc"))
+ ;; replace the header delimiter with a blank line.
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n"))
+ (replace-match "\n")
+ (backward-char 1)
+ (run-hooks 'message-send-mail-hook)
+ (if recipients
+ (let ((result (smtp-via-smtp user-mail-address
+ recipients
+ (current-buffer))))
+ (unless (eq result t)
+ (error "Sending failed; " result)))
+ (error "Sending failed; no recipients"))))
+
+(defsubst message-maybe-split-and-send-news (method)
+ "Split a message if necessary, and send it via news.
+Returns nil if sending succeeded, returns t if sending failed.
+This sub function is for exclusive use of `message-send-news'."
+ (let ((mime-edit-split-ignored-field-regexp
+ mime-edit-split-ignored-field-regexp)
+ (case-fold-search t))
+ (while (string-match "Message-ID" mime-edit-split-ignored-field-regexp)
+ (setq mime-edit-split-ignored-field-regexp
+ (concat (substring mime-edit-split-ignored-field-regexp
+ 0 (match-beginning 0))
+ "Hey_MIME-Edit,_there_is_an_inviolable_Message_ID"
+ "_so_don't_rape_it!"
+ (substring mime-edit-split-ignored-field-regexp
+ (match-end 0)))))
+ (or
+ (catch 'message-sending-news-failure
+ (mime-edit-maybe-split-and-send
+ (function
+ (lambda ()
+ (interactive)
+ (save-restriction
+ (std11-narrow-to-header mail-header-separator)
+ (goto-char (point-min))
+ (when (re-search-forward "^Message-ID:" nil t)
+ (delete-region (match-end 0) (std11-field-end))
+ (insert " " (message-make-message-id))))
+ (unless (funcall message-send-news-function method)
+ (throw 'message-sending-news-failure t)))))
+ nil)
+ (not (funcall message-send-news-function method)))))
+
+(defun message-send-news (&optional arg)
+ (let ((tembuf (message-generate-new-buffer-clone-locals " *message temp*"))
+ (case-fold-search nil)
+ (method (if (message-functionp message-post-method)
+ (funcall message-post-method arg)
+ message-post-method))
+ (message-syntax-checks
+ (if arg
+ (cons '(existing-newsgroups . disabled)
+ message-syntax-checks)
+ message-syntax-checks))
+ result)
+ (save-restriction
+ (message-narrow-to-headers)
+ ;; Insert some headers.
+ (message-generate-headers message-required-news-headers)
+ ;; Let the user do all of the above.
+ (run-hooks 'message-header-hook))
+ (message-cleanup-headers)
+ (if (not (message-check-news-syntax))
+ nil
+ (unwind-protect
+ (save-excursion
+ (set-buffer tembuf)
+ (buffer-disable-undo)
+ (erase-buffer)
+ (insert-buffer message-encoding-buffer)
+ ;; Remove some headers.
+ (save-restriction
+ (message-narrow-to-headers)
+ ;; Remove some headers.
+ (message-remove-header message-ignored-news-headers t))
+ (goto-char (point-max))
+ ;; require one newline at the end.
+ (or (= (preceding-char) ?\n)
+ (insert ?\n))
+ (setq result (message-maybe-split-and-send-news method)))
+ (kill-buffer tembuf))
+ (set-buffer message-edit-buffer)
+ (if result
+ (progn
+ (message "Couldn't send message via news: %s"
+ (nnheader-get-report (car method)))
+ nil)
+ (push 'news message-sent-message-via)))))
+
+;; 1997-09-29 by MORIOKA Tomohiko
+(defun message-send-news-with-gnus (method)
+ (let ((case-fold-search t))
+ ;; Remove the delimiter.
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "\n"))
+ (replace-match "\n")
+ (backward-char 1)
+ (run-hooks 'message-send-news-hook)
+ ;;(require (car method))
+ ;;(funcall (intern (format "%s-open-server" (car method)))
+ ;;(cadr method) (cddr method))
+ ;;(setq result
+ ;; (funcall (intern (format "%s-request-post" (car method)))
+ ;; (cadr method)))
+ (gnus-open-server method)
+ (gnus-request-post method)
+ ))
+
+;;;
+;;; Header generation & syntax checking.
+;;;
+
+(defun message-check-element (type)
+ "Returns non-nil if this type is not to be checked."
+ (if (eq message-syntax-checks 'dont-check-for-anything-just-trust-me)
+ t
+ (let ((able (assq type message-syntax-checks)))
+ (and (consp able)
+ (eq (cdr able) 'disabled)))))
+
+(defun message-check-news-syntax ()
+ "Check the syntax of the message."
+ (save-excursion
+ (save-restriction
+ (widen)
+ (and
+ ;; We narrow to the headers and check them first.
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (message-check-news-header-syntax)))
+ ;; Check the body.
+ (save-excursion
+ (set-buffer message-edit-buffer)
+ (message-check-news-body-syntax))))))
+
+(defun message-check-news-header-syntax ()
+ (and
+ ;; Check the Subject header.
+ (message-check 'subject
+ (let* ((case-fold-search t)
+ (subject (message-fetch-field "subject")))
+ (or
+ (and subject
+ (not (string-match "\\`[ \t]*\\'" subject)))
+ (ignore
+ (message
+ "The subject field is empty or missing. Posting is denied.")))))
+ ;; Check for commands in Subject.
+ (message-check 'subject-cmsg
+ (if (string-match "^cmsg " (message-fetch-field "subject"))
+ (y-or-n-p
+ "The control code \"cmsg\" is in the subject. Really post? ")
+ t))
+ ;; Check for multiple identical headers.
+ (message-check 'multiple-headers
+ (let (found)
+ (while (and (not found)
+ (re-search-forward "^[^ \t:]+: " nil t))
+ (save-excursion
+ (or (re-search-forward
+ (concat "^"
+ (regexp-quote
+ (setq found
+ (buffer-substring
+ (match-beginning 0) (- (match-end 0) 2))))
+ ":")
+ nil t)
+ (setq found nil))))
+ (if found
+ (y-or-n-p (format "Multiple %s headers. Really post? " found))
+ t)))
+ ;; Check for Version and Sendsys.
+ (message-check 'sendsys
+ (if (re-search-forward "^Sendsys:\\|^Version:" nil t)
+ (y-or-n-p
+ (format "The article contains a %s command. Really post? "
+ (buffer-substring (match-beginning 0)
+ (1- (match-end 0)))))
+ t))
+ ;; See whether we can shorten Followup-To.
+ (message-check 'shorten-followup-to
+ (let ((newsgroups (message-fetch-field "newsgroups"))
+ (followup-to (message-fetch-field "followup-to"))
+ to)
+ (when (and newsgroups
+ (string-match "," newsgroups)
+ (not followup-to)
+ (not
+ (zerop
+ (length
+ (setq to (completing-read
+ "Followups to: (default all groups) "
+ (mapcar (lambda (g) (list g))
+ (cons "poster"
+ (message-tokenize-header
+ newsgroups)))))))))
+ (goto-char (point-min))
+ (insert "Followup-To: " to "\n"))
+ t))
+ ;; Check "Shoot me".
+ (message-check 'shoot
+ (if (re-search-forward
+ "Message-ID.*.i-did-not-set--mail-host-address--so-shoot-me" nil t)
+ (y-or-n-p "You appear to have a misconfigured system. Really post? ")
+ t))
+ ;; Check for Approved.
+ (message-check 'approved
+ (if (re-search-forward "^Approved:" nil t)
+ (y-or-n-p "The article contains an Approved header. Really post? ")
+ t))
+ ;; Check the Message-ID header.
+ (message-check 'message-id
+ (let* ((case-fold-search t)
+ (message-id (message-fetch-field "message-id" t)))
+ (or (not message-id)
+ ;; Is there an @ in the ID?
+ (and (string-match "@" message-id)
+ ;; Is there a dot in the ID?
+ (string-match "@[^.]*\\." message-id)
+ ;; Does the ID end with a dot?
+ (not (string-match "\\.>" message-id)))
+ (y-or-n-p
+ (format "The Message-ID looks strange: \"%s\". Really post? "
+ message-id)))))
+ ;; Check the Newsgroups & Followup-To headers.
+ (message-check 'existing-newsgroups
+ (let* ((case-fold-search t)
+ (newsgroups (message-fetch-field "newsgroups"))
+ (followup-to (message-fetch-field "followup-to"))
+ (groups (message-tokenize-header
+ (if followup-to
+ (concat newsgroups "," followup-to)
+ newsgroups)))
+ (hashtb (and (boundp 'gnus-active-hashtb)
+ gnus-active-hashtb))
+ errors)
+ (if (or (not hashtb)
+ (not (boundp 'gnus-read-active-file))
+ (not gnus-read-active-file)
+ (eq gnus-read-active-file 'some))
+ t
+ (while groups
+ (when (and (not (boundp (intern (car groups) hashtb)))
+ (not (equal (car groups) "poster")))
+ (push (car groups) errors))
+ (pop groups))
+ (if (not errors)
+ t
+ (y-or-n-p
+ (format
+ "Really post to %s unknown group%s: %s "
+ (if (= (length errors) 1) "this" "these")
+ (if (= (length errors) 1) "" "s")
+ (mapconcat 'identity errors ", ")))))))
+ ;; Check the Newsgroups & Followup-To headers for syntax errors.
+ (message-check 'valid-newsgroups
+ (let ((case-fold-search t)
+ (headers '("Newsgroups" "Followup-To"))
+ header error)
+ (while (and headers (not error))
+ (when (setq header (mail-fetch-field (car headers)))
+ (if (or
+ (not
+ (string-match
+ "\\`\\([-+_&.a-zA-Z0-9]+\\)?\\(,[-+_&.a-zA-Z0-9]+\\)*\\'"
+ header))
+ (memq
+ nil (mapcar
+ (lambda (g)
+ (not (string-match "\\.\\'\\|\\.\\." g)))
+ (message-tokenize-header header ","))))
+ (setq error t)))
+ (unless error
+ (pop headers)))
+ (if (not error)
+ t
+ (y-or-n-p
+ (format "The %s header looks odd: \"%s\". Really post? "
+ (car headers) header)))))
+ (message-check 'repeated-newsgroups
+ (let ((case-fold-search t)
+ (headers '("Newsgroups" "Followup-To"))
+ header error groups group)
+ (while (and headers
+ (not error))
+ (when (setq header (mail-fetch-field (pop headers)))
+ (setq groups (message-tokenize-header header ","))
+ (while (setq group (pop groups))
+ (when (member group groups)
+ (setq error group
+ groups nil)))))
+ (if (not error)
+ t
+ (y-or-n-p
+ (format "Group %s is repeated in headers. Really post? " error)))))
+ ;; Check the From header.
+ (message-check 'from
+ (let* ((case-fold-search t)
+ (from (message-fetch-field "from"))
+ (ad (nth 1 (std11-extract-address-components from))))
+ (cond
+ ((not from)
+ (message "There is no From line. Posting is denied.")
+ nil)
+ ((or (not (string-match "@[^\\.]*\\." ad)) ;larsi@ifi
+ (string-match "\\.\\." ad) ;larsi@ifi..uio
+ (string-match "@\\." ad) ;larsi@.ifi.uio
+ (string-match "\\.$" ad) ;larsi@ifi.uio.
+ (not (string-match "^[^@]+@[^@]+$" ad)) ;larsi.ifi.uio
+ (string-match "(.*).*(.*)" from)) ;(lars) (lars)
+ (message
+ "Denied posting -- the From looks strange: \"%s\"." from)
+ nil)
+ (t t))))))
+
+(defun message-check-news-body-syntax ()
+ (and
+ ;; Check for long lines.
+ (message-check 'long-lines
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (while (and
+ (progn
+ (end-of-line)
+ (< (current-column) 80))
+ (zerop (forward-line 1))))
+ (or (bolp)
+ (eobp)
+ (y-or-n-p
+ "You have lines longer than 79 characters. Really post? ")))
+ ;; Check whether the article is empty.
+ (message-check 'empty
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (forward-line 1)
+ (let ((b (point)))
+ (goto-char (point-max))
+ (re-search-backward message-signature-separator nil t)
+ (beginning-of-line)
+ (or (re-search-backward "[^ \n\t]" b t)
+ (y-or-n-p "Empty article. Really post? "))))
+ ;; Check for control characters.
+ (message-check 'control-chars
+ (if (re-search-forward "[\000-\007\013\015-\032\034-\037\200-\237]" nil t)
+ (y-or-n-p
+ "The article contains control characters. Really post? ")
+ t))
+ ;; Check 8bit characters.
+ (message-check '8bit
+ (message-check-8bit))
+ ;; Check excessive size.
+ (message-check 'size
+ (if (> (buffer-size) 60000)
+ (y-or-n-p
+ (format "The article is %d octets long. Really post? "
+ (buffer-size)))
+ t))
+ ;; Check whether any new text has been added.
+ (message-check 'new-text
+ (or
+ (not message-checksum)
+ (not (eq (message-checksum) message-checksum))
+ (y-or-n-p
+ "It looks like no new text has been added. Really post? ")))
+ ;; Check the length of the signature.
+ (message-check 'signature
+ (goto-char (point-max))
+ (if (or (not (re-search-backward message-signature-separator nil t))
+ (search-forward message-forward-end-separator nil t))
+ t
+ (if (> (count-lines (point) (point-max)) 5)
+ (y-or-n-p
+ (format
+ "Your .sig is %d lines; it should be max 4. Really post? "
+ (1- (count-lines (point) (point-max)))))
+ t)))))
+
+(defun message-check-mail-syntax ()
+ "Check the syntax of the message."
+ (save-excursion
+ (save-restriction
+ (widen)
+ (and
+ ;; We narrow to the headers and check them first.
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (message-check-mail-header-syntax)))
+ ;; Check the body.
+ (save-excursion
+ (set-buffer message-edit-buffer)
+ (message-check-mail-body-syntax))))))
+
+(defun message-check-mail-header-syntax ()
+ t)
+
+(defun message-check-mail-body-syntax ()
+ (and
+ ;; Check 8bit characters.
+ (message-check '8bit
+ (message-check-8bit)
+ )))
+
+(defun message-check-8bit ()
+ "Check the article contains 8bit characters."
+ (save-excursion
+ (set-buffer message-encoding-buffer)
+ (message-narrow-to-headers)
+ (let* ((case-fold-search t)
+ (field-value (message-fetch-field "content-transfer-encoding")))
+ (if (and field-value
+ (member (downcase field-value) message-8bit-encoding-list))
+ t
+ (widen)
+ (set-buffer (get-buffer-create " message syntax"))
+ (erase-buffer)
+ (goto-char (point-min))
+ (set-buffer-multibyte nil)
+ (insert-buffer message-encoding-buffer)
+ (goto-char (point-min))
+ (if (re-search-forward "[^\x00-\x7f]" nil t)
+ (y-or-n-p
+ "The article contains 8bit characters. Really post? ")
+ t)))))
+
+(defun message-checksum ()
+ "Return a \"checksum\" for the current buffer."
+ (let ((sum 0))
+ (save-excursion
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (while (not (eobp))
+ (when (not (looking-at "[ \t\n]"))
+ (setq sum (logxor (ash sum 1) (if (natnump sum) 0 1)
+ (char-after))))
+ (forward-char 1)))
+ sum))
+
+(defun message-do-fcc ()
+ "Process Fcc headers in the current buffer."
+ (let ((case-fold-search t)
+ (coding-system-for-write 'raw-text)
+ list file)
+ (save-excursion
+ (set-buffer (get-buffer-create " *message temp*"))
+ (erase-buffer)
+ (insert-buffer-substring message-encoding-buffer)
+ (save-restriction
+ (message-narrow-to-headers)
+ (while (setq file (message-fetch-field "fcc"))
+ (push file list)
+ (message-remove-header "fcc" nil t)))
+ (goto-char (point-min))
+ (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
+ (replace-match "" t t)
+ ;; Process FCC operations.
+ (while list
+ (setq file (pop list))
+ (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file)
+ ;; Pipe the article to the program in question.
+ (call-process-region (point-min) (point-max) shell-file-name
+ nil nil nil shell-command-switch
+ (match-string 1 file))
+ ;; Save the article.
+ (setq file (expand-file-name file))
+ (unless (file-exists-p (file-name-directory file))
+ (make-directory (file-name-directory file) t))
+ (if (and message-fcc-handler-function
+ (not (eq message-fcc-handler-function 'rmail-output)))
+ (funcall message-fcc-handler-function file)
+ (if (and (file-readable-p file) (mail-file-babyl-p file))
+ (rmail-output file 1 nil t)
+ (let ((mail-use-rfc822 t))
+ (rmail-output file 1 t t))))))
+ (kill-buffer (current-buffer)))))
+
+(defun message-output (filename)
+ "Append this article to Unix/babyl mail file.."
+ (if (and (file-readable-p filename)
+ (mail-file-babyl-p filename))
+ (rmail-output-to-rmail-file filename t)
+ (gnus-output-to-mail filename t)))
+
+(defun message-cleanup-headers ()
+ "Do various automatic cleanups of the headers."
+ ;; Remove empty lines in the header.
+ (save-restriction
+ (message-narrow-to-headers)
+ ;; Remove blank lines.
+ (while (re-search-forward "^[ \t]*\n" nil t)
+ (replace-match "" t t))
+
+ ;; Correct Newsgroups and Followup-To headers: Change sequence of
+ ;; spaces to comma and eliminate spaces around commas. Eliminate
+ ;; embedded line breaks.
+ (goto-char (point-min))
+ (while (re-search-forward "^\\(Newsgroups\\|Followup-To\\): +" nil t)
+ (save-restriction
+ (narrow-to-region
+ (point)
+ (if (re-search-forward "^[^ \t]" nil t)
+ (match-beginning 0)
+ (forward-line 1)
+ (point)))
+ (goto-char (point-min))
+ (while (re-search-forward "\n[ \t]+" nil t)
+ (replace-match " " t t)) ;No line breaks (too confusing)
+ (goto-char (point-min))
+ (while (re-search-forward "[ \t\n]*,[ \t\n]*\\|[ \t]+" nil t)
+ (replace-match "," t t))
+ (goto-char (point-min))
+ ;; Remove trailing commas.
+ (when (re-search-forward ",+$" nil t)
+ (replace-match "" t t))))))
+
+(defun message-make-date (&optional now)
+ "Make a valid data header.
+If NOW, use that time instead."
+ (let* ((now (or now (current-time)))
+ (zone (nth 8 (decode-time now)))
+ (sign "+"))
+ (when (< zone 0)
+ (setq sign "-")
+ (setq zone (- zone)))
+ (concat
+ (format-time-string "%d" now)
+ ;; The month name of the %b spec is locale-specific. Pfff.
+ (format " %s "
+ (capitalize (car (rassoc (nth 4 (decode-time now))
+ parse-time-months))))
+ (format-time-string "%Y %H:%M:%S " now)
+ ;; We do all of this because XEmacs doesn't have the %z spec.
+ (format "%s%02d%02d" sign (/ zone 3600) (% zone 3600)))))
+
+(defun message-make-followup-subject (subject)
+ "Make a followup Subject."
+ (cond
+ ((and (eq message-use-subject-re 'guess)
+ (string-match message-subject-encoded-re-regexp subject))
+ subject)
+ (message-use-subject-re
+ (concat "Re: " (message-strip-subject-re subject)))
+ (t subject)))
+
+(defun message-make-message-id ()
+ "Make a unique Message-ID."
+ (concat "<" (message-unique-id)
+ (let ((psubject (save-excursion (message-fetch-field "subject")))
+ (psupersedes
+ (save-excursion (message-fetch-field "supersedes"))))
+ (if (or
+ (and message-reply-headers
+ (mail-header-references message-reply-headers)
+ (mail-header-subject message-reply-headers)
+ psubject
+ (mail-header-subject message-reply-headers)
+ (not (string=
+ (message-strip-subject-re
+ (mail-header-subject message-reply-headers))
+ (message-strip-subject-re psubject))))
+ (and psupersedes
+ (string-match "_-_@" psupersedes)))
+ "_-_" ""))
+ "@" (message-make-fqdn) ">"))
+
+(defvar message-unique-id-char nil)
+
+;; If you ever change this function, make sure the new version
+;; cannot generate IDs that the old version could.
+;; You might for example insert a "." somewhere (not next to another dot
+;; or string boundary), or modify the "fsf" string.
+(defun message-unique-id ()
+ ;; Don't use microseconds from (current-time), they may be unsupported.
+ ;; Instead we use this randomly inited counter.
+ (setq message-unique-id-char
+ (% (1+ (or message-unique-id-char (logand (random t) (1- (lsh 1 20)))))
+ ;; (current-time) returns 16-bit ints,
+ ;; and 2^16*25 just fits into 4 digits i base 36.
+ (* 25 25)))
+ (let ((tm (current-time)))
+ (concat
+ (if (memq system-type '(ms-dos emx vax-vms))
+ (let ((user (downcase (user-login-name))))
+ (while (string-match "[^a-z0-9_]" user)
+ (aset user (match-beginning 0) ?_))
+ user)
+ (message-number-base36 (user-uid) -1))
+ (message-number-base36 (+ (car tm)
+ (lsh (% message-unique-id-char 25) 16)) 4)
+ (message-number-base36 (+ (nth 1 tm)
+ (lsh (/ message-unique-id-char 25) 16)) 4)
+ ;; Append the newsreader name, because while the generated
+ ;; ID is unique to this newsreader, other newsreaders might
+ ;; otherwise generate the same ID via another algorithm.
+ ".fsf")))
+
+(defun message-number-base36 (num len)
+ (if (if (< len 0)
+ (<= num 0)
+ (= len 0))
+ ""
+ (concat (message-number-base36 (/ num 36) (1- len))
+ (char-to-string (aref "zyxwvutsrqponmlkjihgfedcba9876543210"
+ (% num 36))))))
+
+(defun message-make-organization ()
+ "Make an Organization header."
+ (let* ((organization
+ (when message-user-organization
+ (if (message-functionp message-user-organization)
+ (funcall message-user-organization)
+ message-user-organization))))
+ (save-excursion
+ (message-set-work-buffer)
+ (cond ((stringp organization)
+ (insert organization))
+ ((and (eq t organization)
+ message-user-organization-file
+ (file-exists-p message-user-organization-file))
+ (insert-file-contents message-user-organization-file)))
+ (goto-char (point-min))
+ (while (re-search-forward "[\t\n]+" nil t)
+ (replace-match "" t t))
+ (unless (zerop (buffer-size))
+ (buffer-string)))))
+
+(defun message-make-lines ()
+ "Count the number of lines and return numeric string."
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (re-search-forward
+ (concat "^" (regexp-quote mail-header-separator) "$"))
+ (forward-line 1)
+ (int-to-string (count-lines (point) (point-max))))))
+
+(defun message-make-in-reply-to ()
+ "Return the In-Reply-To header for this message."
+ (when message-reply-headers
+ (let ((mid (mail-header-message-id message-reply-headers))
+ (from (mail-header-from message-reply-headers))
+ (date (mail-header-date message-reply-headers)))
+ (when mid
+ (concat mid
+ (when from
+ (let ((pair (std11-extract-address-components from)))
+ (concat "\n ("
+ (or (car pair) (cadr pair))
+ "'s message of \""
+ (if (or (not date) (string= date ""))
+ "(unknown date)" date)
+ "\")"))))))))
+
+(defun message-make-distribution ()
+ "Make a Distribution header."
+ (let ((orig-distribution (message-fetch-reply-field "distribution")))
+ (cond ((message-functionp message-distribution-function)
+ (funcall message-distribution-function))
+ (t orig-distribution))))
+
+(defun message-make-expires ()
+ "Return an Expires header based on `message-expires'."
+ (let ((current (current-time))
+ (future (* 1.0 message-expires 60 60 24)))
+ ;; Add the future to current.
+ (setcar current (+ (car current) (round (/ future (expt 2 16)))))
+ (setcar (cdr current) (+ (nth 1 current) (% (round future) (expt 2 16))))
+ (message-make-date current)))
+
+(defun message-make-path ()
+ "Return uucp path."
+ (let ((login-name (user-login-name)))
+ (cond ((null message-user-path)
+ (concat (system-name) "!" login-name))
+ ((stringp message-user-path)
+ ;; Support GENERICPATH. Suggested by vixie@decwrl.dec.com.
+ (concat message-user-path "!" login-name))
+ (t login-name))))
+
+(defun message-make-from ()
+ "Make a From header."
+ (let* ((style message-from-style)
+ (login (message-make-address))
+ (fullname
+ (or (and (boundp 'user-full-name)
+ user-full-name)
+ (user-full-name))))
+ (when (string= fullname "&")
+ (setq fullname (user-login-name)))
+ (save-excursion
+ (message-set-work-buffer)
+ (cond
+ ((or (null style)
+ (equal fullname ""))
+ (insert login))
+ ((or (eq style 'angles)
+ (and (not (eq style 'parens))
+ ;; Use angles if no quoting is needed, or if parens would
+ ;; need quoting too.
+ (or (not (string-match "[^- !#-'*+/-9=?A-Z^-~]" fullname))
+ (let ((tmp (concat fullname nil)))
+ (while (string-match "([^()]*)" tmp)
+ (aset tmp (match-beginning 0) ?-)
+ (aset tmp (1- (match-end 0)) ?-))
+ (string-match "[\\()]" tmp)))))
+ (insert fullname)
+ (goto-char (point-min))
+ ;; Look for a character that cannot appear unquoted
+ ;; according to RFC 822.
+ (when (re-search-forward "[^- !#-'*+/-9=?A-Z^-~]" nil 1)
+ ;; Quote fullname, escaping specials.
+ (goto-char (point-min))
+ (insert "\"")
+ (while (re-search-forward "[\"\\]" nil 1)
+ (replace-match "\\\\\\&" t))
+ (insert "\""))
+ (insert " <" login ">"))
+ (t ; 'parens or default
+ (insert login " (")
+ (let ((fullname-start (point)))
+ (insert fullname)
+ (goto-char fullname-start)
+ ;; RFC 822 says \ and nonmatching parentheses
+ ;; must be escaped in comments.
+ ;; Escape every instance of ()\ ...
+ (while (re-search-forward "[()\\]" nil 1)
+ (replace-match "\\\\\\&" t))
+ ;; ... then undo escaping of matching parentheses,
+ ;; including matching nested parentheses.
+ (goto-char fullname-start)
+ (while (re-search-forward
+ "\\(\\=\\|[^\\]\\(\\\\\\\\\\)*\\)\\\\(\\(\\([^\\]\\|\\\\\\\\\\)*\\)\\\\)"
+ nil 1)
+ (replace-match "\\1(\\3)" t)
+ (goto-char fullname-start)))
+ (insert ")")))
+ (buffer-string))))
+
+(defun message-make-sender ()
+ "Return the \"real\" user address.
+This function tries to ignore all user modifications, and
+give as trustworthy answer as possible."
+ (concat (user-login-name) "@" (system-name)))
+
+(defun message-make-address ()
+ "Make the address of the user."
+ (or (message-user-mail-address)
+ (concat (user-login-name) "@" (message-make-domain))))
+
+(defun message-user-mail-address ()
+ "Return the pertinent part of `user-mail-address'."
+ (when user-mail-address
+ (if (string-match " " user-mail-address)
+ (nth 1 (std11-extract-address-components user-mail-address))
+ user-mail-address)))
+
+(defun message-make-fqdn ()
+ "Return user's fully qualified domain name."
+ (let ((system-name (system-name))
+ (user-mail (message-user-mail-address)))
+ (cond
+ ((string-match "[^.]\\.[^.]" system-name)
+ ;; `system-name' returned the right result.
+ system-name)
+ ;; Try `mail-host-address'.
+ ((and (boundp 'mail-host-address)
+ (stringp mail-host-address)
+ (string-match "\\." mail-host-address))
+ mail-host-address)
+ ;; We try `user-mail-address' as a backup.
+ ((and user-mail
+ (string-match "\\." user-mail)
+ (string-match "@\\(.*\\)\\'" user-mail))
+ (match-string 1 user-mail))
+ ;; Default to this bogus thing.
+ (t
+ (concat system-name ".i-did-not-set--mail-host-address--so-shoot-me")))))
+
+(defun message-make-host-name ()
+ "Return the name of the host."
+ (let ((fqdn (message-make-fqdn)))
+ (string-match "^[^.]+\\." fqdn)
+ (substring fqdn 0 (1- (match-end 0)))))
+
+(defun message-make-domain ()
+ "Return the domain name."
+ (or mail-host-address
+ (message-make-fqdn)))
+
+;; Dummy to avoid byte-compile warning.
+(defvar mule-version)
+(defvar emacs-beta-version)
+(defvar xemacs-codename)
+(defvar gnus-inviolable-extended-version)
+
+(defun message-make-user-agent ()
+ "Return user-agent info if the value `message-user-agent' is non-nil. If the
+\"User-Agent\" field has already exist, it's value will be added in the return
+string."
+ (when message-user-agent
+ (save-excursion
+ (goto-char (point-min))
+ (let ((case-fold-search t)
+ user-agent start p end)
+ (if (re-search-forward "^User-Agent:[\t ]*" nil t)
+ (progn
+ (setq start (match-beginning 0)
+ p (match-end 0)
+ end (std11-field-end)
+ user-agent (buffer-substring-no-properties p end))
+ (delete-region start (1+ end))
+ (concat message-user-agent " " user-agent))
+ message-user-agent)))))
+
+(defun message-generate-headers (headers)
+ "Prepare article HEADERS.
+Headers already prepared in the buffer are not modified."
+ (save-restriction
+ (message-narrow-to-headers)
+ (let* ((Date (message-make-date))
+ (Message-ID (message-make-message-id))
+ (Organization (message-make-organization))
+ (From (message-make-from))
+ (Path (message-make-path))
+ (Subject nil)
+ (Newsgroups nil)
+ (In-Reply-To (message-make-in-reply-to))
+ (To nil)
+ (Distribution (message-make-distribution))
+ (Lines (message-make-lines))
+ (User-Agent (message-make-user-agent))
+ (Expires (message-make-expires))
+ (case-fold-search t)
+ header value elem)
+ ;; First we remove any old generated headers.
+ (let ((headers message-deletable-headers))
+ (unless (buffer-modified-p)
+ (setq headers (delq 'Message-ID (copy-sequence headers))))
+ (while headers
+ (goto-char (point-min))
+ (and (re-search-forward
+ (concat "^" (symbol-name (car headers)) ": *") nil t)
+ (get-text-property (1+ (match-beginning 0)) 'message-deletable)
+ (message-delete-line))
+ (pop headers)))
+ ;; Go through all the required headers and see if they are in the
+ ;; articles already. If they are not, or are empty, they are
+ ;; inserted automatically - except for Subject, Newsgroups and
+ ;; Distribution.
+ (while headers
+ (goto-char (point-min))
+ (setq elem (pop headers))
+ (if (consp elem)
+ (if (eq (car elem) 'optional)
+ (setq header (cdr elem))
+ (setq header (car elem)))
+ (setq header elem))
+ (when (or (not (re-search-forward
+ (concat "^"
+ (regexp-quote
+ (downcase
+ (if (stringp header)
+ header
+ (symbol-name header))))
+ ":")
+ nil t))
+ (progn
+ ;; The header was found. We insert a space after the
+ ;; colon, if there is none.
+ (if (/= (char-after) ? ) (insert " ") (forward-char 1))
+ ;; Find out whether the header is empty...
+ (looking-at "[ \t]*$")))
+ ;; So we find out what value we should insert.
+ (setq value
+ (cond
+ ((and (consp elem) (eq (car elem) 'optional))
+ ;; This is an optional header. If the cdr of this
+ ;; is something that is nil, then we do not insert
+ ;; this header.
+ (setq header (cdr elem))
+ (or (and (fboundp (cdr elem)) (funcall (cdr elem)))
+ (and (boundp (cdr elem)) (symbol-value (cdr elem)))))
+ ((consp elem)
+ ;; The element is a cons. Either the cdr is a
+ ;; string to be inserted verbatim, or it is a
+ ;; function, and we insert the value returned from
+ ;; this function.
+ (or (and (stringp (cdr elem)) (cdr elem))
+ (and (fboundp (cdr elem)) (funcall (cdr elem)))))
+ ((and (boundp header) (symbol-value header))
+ ;; The element is a symbol. We insert the value
+ ;; of this symbol, if any.
+ (symbol-value header))
+ (t
+ ;; We couldn't generate a value for this header,
+ ;; so we just ask the user.
+ (read-from-minibuffer
+ (format "Empty header for %s; enter value: " header)))))
+ ;; Finally insert the header.
+ (when (and value
+ (not (equal value "")))
+ (save-excursion
+ (if (bolp)
+ (progn
+ ;; This header didn't exist, so we insert it.
+ (goto-char (point-max))
+ (insert (if (stringp header) header (symbol-name header))
+ ": " value "\n")
+ (forward-line -1))
+ ;; The value of this header was empty, so we clear
+ ;; totally and insert the new value.
+ (delete-region (point) (gnus-point-at-eol))
+ (insert value))
+ ;; Add the deletable property to the headers that require it.
+ (and (memq header message-deletable-headers)
+ (progn (beginning-of-line) (looking-at "[^:]+: "))
+ (add-text-properties
+ (point) (match-end 0)
+ '(message-deletable t face italic) (current-buffer)))))))
+ ;; Insert new Sender if the From is strange.
+ (let ((from (message-fetch-field "from"))
+ (sender (message-fetch-field "sender"))
+ (secure-sender (message-make-sender)))
+ (when (and from
+ (not (message-check-element 'sender))
+ (not (string=
+ (downcase
+ (cadr (std11-extract-address-components from)))
+ (downcase secure-sender)))
+ (or (null sender)
+ (not
+ (string=
+ (downcase
+ (cadr (std11-extract-address-components sender)))
+ (downcase secure-sender)))))
+ (goto-char (point-min))
+ ;; Rename any old Sender headers to Original-Sender.
+ (when (re-search-forward "^\\(Original-\\)*Sender:" nil t)
+ (beginning-of-line)
+ (insert "Original-")
+ (beginning-of-line))
+ (when (or (message-news-p)
+ (string-match "@.+\\.." secure-sender))
+ (insert "Sender: " secure-sender "\n")))))))
+
+(defun message-insert-courtesy-copy ()
+ "Insert a courtesy message in mail copies of combined messages."
+ (let (newsgroups)
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (when (setq newsgroups (message-fetch-field "newsgroups"))
+ (goto-char (point-max))
+ (insert "Posted-To: " newsgroups "\n")))
+ (forward-line 1)
+ (when message-courtesy-message
+ (cond
+ ((string-match "%s" message-courtesy-message)
+ (insert (format message-courtesy-message newsgroups)))
+ (t
+ (insert message-courtesy-message)))))))
+
+;;;
+;;; Setting up a message buffer
+;;;
+
+(defun message-fill-address (header value)
+ (save-restriction
+ (narrow-to-region (point) (point))
+ (insert (capitalize (symbol-name header))
+ ": "
+ (if (consp value) (car value) value)
+ "\n")
+ (narrow-to-region (point-min) (1- (point-max)))
+ (let (quoted last)
+ (goto-char (point-min))
+ (while (not (eobp))
+ (skip-chars-forward "^,\"" (point-max))
+ (if (or (eq (char-after) ?,)
+ (eobp))
+ (when (not quoted)
+ (if (and (> (current-column) 78)
+ last)
+ (save-excursion
+ (goto-char last)
+ (looking-at "[ \t]*")
+ (replace-match "\n " t t)))
+ (setq last (1+ (point))))
+ (setq quoted (not quoted)))
+ (unless (eobp)
+ (forward-char 1))))
+ (goto-char (point-max))
+ (widen)
+ (forward-line 1)))
+
+(defun message-fill-references (header value)
+ (insert (capitalize (symbol-name header))
+ ": "
+ (std11-fill-msg-id-list-string
+ (if (consp value) (car value) value))
+ "\n"))
+
+(defun message-fill-header (header value)
+ (let ((begin (point))
+ (fill-column 78)
+ (fill-prefix " "))
+ (insert (capitalize (symbol-name header))
+ ": "
+ (if (consp value) (car value) value)
+ "\n")
+ (save-restriction
+ (narrow-to-region begin (point))
+ (fill-region-as-paragraph begin (point))
+ ;; Tapdance around looong Message-IDs.
+ (forward-line -1)
+ (when (looking-at "[ \t]*$")
+ (message-delete-line))
+ (goto-char begin)
+ (re-search-forward ":" nil t)
+ (when (looking-at "\n[ \t]+")
+ (replace-match " " t t))
+ (goto-char (point-max)))))
+
+(defun message-shorten-references (header references)
+ "Limit REFERENCES to be shorter than 988 characters."
+ (let ((max 988)
+ (cut 4)
+ refs)
+ (with-temp-buffer
+ (insert references)
+ (goto-char (point-min))
+ (while (re-search-forward "<[^>]+>" nil t)
+ (push (match-string 0) refs))
+ (setq refs (nreverse refs))
+ (while (> (length (mapconcat 'identity refs " ")) max)
+ (when (< (length refs) (1+ cut))
+ (decf cut))
+ (setcdr (nthcdr cut refs) (cddr (nthcdr cut refs)))))
+ (insert (capitalize (symbol-name header)) ": "
+ (mapconcat 'identity refs " ") "\n")))
+
+(defun message-position-point ()
+ "Move point to where the user probably wants to find it."
+ (message-narrow-to-headers)
+ (cond
+ ((re-search-forward "^[^:]+:[ \t]*$" nil t)
+ (search-backward ":" )
+ (widen)
+ (forward-char 1)
+ (if (eq (char-after) ? )
+ (forward-char 1)
+ (insert " ")))
+ (t
+ (goto-char (point-max))
+ (widen)
+ (forward-line 1)
+ (unless (looking-at "$")
+ (forward-line 2)))
+ (sit-for 0)))
+
+(defun message-buffer-name (type &optional to group)
+ "Return a new (unique) buffer name based on TYPE and TO."
+ (cond
+ ;; Generate a new buffer name The Message Way.
+ ((eq message-generate-new-buffers 'unique)
+ (generate-new-buffer-name
+ (concat "*" type
+ (if to
+ (concat " to "
+ (or (car (std11-extract-address-components to))
+ to) "")
+ "")
+ (if (and group (not (string= group ""))) (concat " on " group) "")
+ "*")))
+ ;; Check whether `message-generate-new-buffers' is a function,
+ ;; and if so, call it.
+ ((message-functionp message-generate-new-buffers)
+ (funcall message-generate-new-buffers type to group))
+ ((eq message-generate-new-buffers 'unsent)
+ (generate-new-buffer-name
+ (concat "*unsent " type
+ (if to
+ (concat " to "
+ (or (car (mail-extract-address-components to))
+ to) "")
+ "")
+ (if (and group (not (string= group ""))) (concat " on " group) "")
+ "*")))
+ ;; Use standard name.
+ (t
+ (format "*%s message*" type))))
+
+(defun message-pop-to-buffer (name)
+ "Pop to buffer NAME, and warn if it already exists and is modified."
+ (let ((pop-up-frames pop-up-frames)
+ (special-display-buffer-names special-display-buffer-names)
+ (special-display-regexps special-display-regexps)
+ (same-window-buffer-names same-window-buffer-names)
+ (same-window-regexps same-window-regexps)
+ (buffer (get-buffer name))
+ (cur (current-buffer)))
+ (if (or (and (featurep 'xemacs)
+ (not (eq 'tty (device-type))))
+ window-system
+ (>= emacs-major-version 20))
+ (when message-use-multi-frames
+ (setq pop-up-frames t
+ special-display-buffer-names nil
+ special-display-regexps nil
+ same-window-buffer-names nil
+ same-window-regexps nil))
+ (setq pop-up-frames nil))
+ (if (and buffer
+ (buffer-name buffer))
+ (progn
+ (set-buffer (pop-to-buffer buffer))
+ (when (and (buffer-modified-p)
+ (not (y-or-n-p
+ "Message already being composed; erase? ")))
+ (error "Message being composed")))
+ (set-buffer (pop-to-buffer name)))
+ (erase-buffer)
+ (message-mode)
+ (when pop-up-frames
+ (make-local-variable 'message-original-frame)
+ (setq message-original-frame (selected-frame)))))
+
+(defun message-do-send-housekeeping ()
+ "Kill old message buffers."
+ ;; We might have sent this buffer already. Delete it from the
+ ;; list of buffers.
+ (setq message-buffer-list (delq (current-buffer) message-buffer-list))
+ (while (and message-max-buffers
+ message-buffer-list
+ (>= (length message-buffer-list) message-max-buffers))
+ ;; Kill the oldest buffer -- unless it has been changed.
+ (let ((buffer (pop message-buffer-list)))
+ (when (and (buffer-name buffer)
+ (not (buffer-modified-p buffer)))
+ (kill-buffer buffer))))
+ ;; Rename the buffer.
+ (if message-send-rename-function
+ (funcall message-send-rename-function)
+ (when (string-match "\\`\\*\\(unsent \\)?" (buffer-name))
+ (rename-buffer
+ (concat "*sent " (substring (buffer-name) (match-end 0))) t)))
+ ;; Push the current buffer onto the list.
+ (when message-max-buffers
+ (setq message-buffer-list
+ (nconc message-buffer-list (list (current-buffer))))))
+
+(defvar mc-modes-alist)
+(defun message-setup (headers &optional replybuffer actions)
+ (when (and (boundp 'mc-modes-alist)
+ (not (assq 'message-mode mc-modes-alist)))
+ (push '(message-mode (encrypt . mc-encrypt-message)
+ (sign . mc-sign-message))
+ mc-modes-alist))
+ (when actions
+ (setq message-send-actions actions))
+ (setq message-reply-buffer
+ (or (message-get-parameter 'reply-buffer)
+ replybuffer))
+ (goto-char (point-min))
+ ;; Insert all the headers.
+ (mail-header-format
+ (let ((h headers)
+ (alist message-header-format-alist))
+ (while h
+ (unless (assq (caar h) message-header-format-alist)
+ (push (list (caar h)) alist))
+ (pop h))
+ alist)
+ headers)
+ (delete-region (point) (progn (forward-line -1) (point)))
+ (when message-default-headers
+ (insert message-default-headers)
+ (or (bolp) (insert ?\n)))
+ (put-text-property
+ (point)
+ (progn
+ (insert mail-header-separator "\n")
+ (1- (point)))
+ 'read-only nil)
+ (forward-line -1)
+ (when (message-news-p)
+ (when message-default-news-headers
+ (insert message-default-news-headers)
+ (or (bolp) (insert ?\n)))
+ (when message-generate-headers-first
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-news-headers))))))
+ (when (message-mail-p)
+ (when message-default-mail-headers
+ (insert message-default-mail-headers)
+ (or (bolp) (insert ?\n)))
+ (when message-generate-headers-first
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-mail-headers))))))
+ (run-hooks 'message-signature-setup-hook)
+ (message-insert-signature)
+ (save-restriction
+ (message-narrow-to-headers)
+ (run-hooks 'message-header-setup-hook))
+ (set-buffer-modified-p nil)
+ (setq buffer-undo-list nil)
+ (run-hooks 'message-setup-hook)
+ (message-position-point)
+ (undo-boundary))
+
+(defun message-set-auto-save-file-name ()
+ "Associate the message buffer with a file in the drafts directory."
+ (when message-auto-save-directory
+ (if (gnus-alive-p)
+ (setq message-draft-article
+ (nndraft-request-associate-buffer "drafts"))
+ (setq buffer-file-name (expand-file-name "*message*"
+ message-auto-save-directory))
+ (setq buffer-auto-save-file-name (make-auto-save-file-name)))
+ (clear-visited-file-modtime)
+ (setq buffer-file-coding-system message-draft-coding-system)))
+
+(defun message-disassociate-draft ()
+ "Disassociate the message buffer from the drafts directory."
+ (when message-draft-article
+ (nndraft-request-expire-articles
+ (list message-draft-article) "drafts" nil t)))
+
+(defun message-insert-headers ()
+ "Generate the headers for the article."
+ (interactive)
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-headers)
+ (when (message-news-p)
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-news-headers)))))
+ (when (message-mail-p)
+ (message-generate-headers
+ (delq 'Lines
+ (delq 'Subject
+ (copy-sequence message-required-mail-headers))))))))
+
+\f
+
+;;;
+;;; Commands for interfacing with message
+;;;
+
+;;;###autoload
+(defun message-mail (&optional to subject
+ other-headers continue switch-function
+ yank-action send-actions)
+ "Start editing a mail message to be sent.
+OTHER-HEADERS is an alist of header/value pairs."
+ (interactive)
+ (let ((message-this-is-mail t))
+ (message-pop-to-buffer (message-buffer-name "mail" to))
+ (message-setup
+ (nconc
+ `((To . ,(or to "")) (Subject . ,(or subject "")))
+ (when other-headers other-headers)))))
+
+;;;###autoload
+(defun message-news (&optional newsgroups subject)
+ "Start editing a news article to be sent."
+ (interactive)
+ (let ((message-this-is-news t))
+ (message-pop-to-buffer (message-buffer-name "news" nil newsgroups))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (Subject . ,(or subject ""))))))
+
+;;;###autoload
+(defun message-reply (&optional to-address wide)
+ "Start editing a reply to the article in the current buffer."
+ (interactive)
+ (let ((cur (current-buffer))
+ from subject date to cc
+ references message-id follow-to
+ (inhibit-point-motion-hooks t)
+ mct never-mct mft mrt gnus-warning)
+ (save-restriction
+ (message-narrow-to-head)
+ ;; Allow customizations to have their say.
+ (if (not wide)
+ ;; This is a regular reply.
+ (if (message-functionp message-reply-to-function)
+ (setq follow-to (funcall message-reply-to-function)))
+ ;; This is a followup.
+ (if (message-functionp message-wide-reply-to-function)
+ (save-excursion
+ (setq follow-to
+ (funcall message-wide-reply-to-function)))))
+ ;; Find all relevant headers we need.
+ (setq from (message-fetch-field "from")
+ date (message-fetch-field "date" t)
+ subject (or (message-fetch-field "subject") "none")
+ references (message-fetch-field "references")
+ message-id (message-fetch-field "message-id" t)
+ to (message-fetch-field "to")
+ cc (message-fetch-field "cc")
+ mct (when (and wide message-use-mail-copies-to)
+ (message-fetch-field "mail-copies-to"))
+ mft (when (and wide message-use-mail-followup-to)
+ (message-fetch-field "mail-followup-to"))
+ mrt (when message-use-mail-reply-to
+ (or (message-fetch-field "mail-reply-to")
+ (message-fetch-field "reply-to")))
+ gnus-warning (message-fetch-field "gnus-warning"))
+ (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
+ (setq message-id (match-string 0 gnus-warning)))
+ ;; Remove any (buggy) Re:'s that are present and make a
+ ;; proper one.
+ (setq subject (message-make-followup-subject subject))
+ (widen))
+
+ ;; Handle special values of Mail-Copies-To.
+ (when mct
+ (cond
+ ((and (or (equal (downcase mct) "never")
+ (equal (downcase mct) "nobody"))
+ (or (not (eq message-use-mail-copies-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: never? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: never'
+directs you not to send your response to the author.")))
+ (setq never-mct t)
+ (setq mct nil))
+ ((and (or (equal (downcase mct) "always")
+ (equal (downcase mct) "poster"))
+ (or (not (eq message-use-mail-copies-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: always? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: always'
+sends a copy of your response to the author.")))
+ (setq mct (or mrt from)))
+ ((and (eq message-use-mail-copies-to 'ask)
+ (not
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: " mct " ? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: " mct "'
+sends a copy of your response to " (if (string-match "," mct)
+ "the specified addresses"
+ "that address") ".")))
+ (setq mct nil))
+ ))
+
+ (unless follow-to
+ (cond
+ (to-address (setq follow-to (list (cons 'To to-address))))
+ ((not wide) (setq follow-to (list (cons 'To (or mrt from)))))
+ ;; Handle Mail-Followup-To.
+ ((and mft
+ (or (not (eq message-use-mail-followup-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Followup-To: " mft "? ") t "\
+You should normally obey the Mail-Followup-To: header.
+
+ `Mail-Followup-To: " mft "'
+directs your response to " (if (string-match "," mft)
+ "the specified addresses"
+ "that address only") ".
+
+A typical situation where Mail-Followup-To is used is when the author thinks
+that further discussion should take place only in "
+ (if (string-match "," mft)
+ "the specified mailing lists"
+ "that mailing list") ".")))
+ (setq follow-to (list (cons 'To mft)))
+ (when mct
+ (push (cons 'Cc mct) follow-to)))
+ (t
+ (let (ccalist)
+ (save-excursion
+ (message-set-work-buffer)
+ (unless never-mct
+ (insert (or mrt from "")))
+ (insert (if to (concat (if (bolp) "" ", ") to "") ""))
+ (insert (if mct (concat (if (bolp) "" ", ") mct) ""))
+ (insert (if cc (concat (if (bolp) "" ", ") cc) ""))
+ (goto-char (point-min))
+ (while (re-search-forward "[ \t]+" nil t)
+ (replace-match " " t t))
+ ;; Remove addresses that match `rmail-dont-reply-to-names'.
+ (insert (prog1 (rmail-dont-reply-to (buffer-string))
+ (erase-buffer)))
+ (goto-char (point-min))
+ ;; Perhaps Mail-Copies-To: never removed the only address?
+ (when (eobp)
+ (insert (or mrt from "")))
+ (setq ccalist
+ (mapcar
+ (lambda (addr)
+ (cons (mail-strip-quoted-names addr) addr))
+ (message-tokenize-header (buffer-string))))
+ (let ((s ccalist))
+ (while s
+ (setq ccalist (delq (assoc (car (pop s)) s) ccalist)))))
+ (setq follow-to (list (cons 'To (cdr (pop ccalist)))))
+ (when ccalist
+ (let ((ccs (cons 'Cc (mapconcat
+ (lambda (addr) (cdr addr)) ccalist ", "))))
+ (when (string-match "^ +" (cdr ccs))
+ (setcdr ccs (substring (cdr ccs) (match-end 0))))
+ (push ccs follow-to)))))))
+
+ (message-pop-to-buffer (message-buffer-name
+ (if wide "wide reply" "reply") from
+ (if wide to-address nil)))
+
+ (setq message-reply-headers
+ (make-full-mail-header-from-decoded-header
+ 0 subject from date message-id references 0 0 ""))
+
+ (message-setup
+ `((Subject . ,subject)
+ ,@follow-to
+ ,@(if (or references message-id)
+ `((References . ,(concat (or references "") (and references " ")
+ (or message-id ""))))))
+ cur)))
+
+;;;###autoload
+(defun message-wide-reply (&optional to-address)
+ "Make a \"wide\" reply to the message in the current buffer."
+ (interactive)
+ (message-reply to-address t))
+
+;;;###autoload
+(defun message-followup (&optional to-newsgroups)
+ "Follow up to the message in the current buffer.
+If TO-NEWSGROUPS, use that as the new Newsgroups line."
+ (interactive)
+ (let ((cur (current-buffer))
+ from subject date mct
+ references message-id follow-to
+ (inhibit-point-motion-hooks t)
+ (message-this-is-news t)
+ followup-to distribution newsgroups gnus-warning posted-to mft mrt)
+ (save-restriction
+ (message-narrow-to-head)
+ (when (message-functionp message-followup-to-function)
+ (setq follow-to
+ (funcall message-followup-to-function)))
+ (setq from (message-fetch-field "from")
+ date (message-fetch-field "date" t)
+ subject (or (message-fetch-field "subject") "none")
+ references (message-fetch-field "references")
+ message-id (message-fetch-field "message-id" t)
+ followup-to (when message-use-followup-to
+ (message-fetch-field "followup-to"))
+ distribution (message-fetch-field "distribution")
+ newsgroups (message-fetch-field "newsgroups")
+ posted-to (message-fetch-field "posted-to")
+ mct (when message-use-mail-copies-to
+ (message-fetch-field "mail-copies-to"))
+ mft (when message-use-mail-followup-to
+ (message-fetch-field "mail-followup-to"))
+ mrt (when message-use-mail-reply-to
+ (or (message-fetch-field "mail-reply-to")
+ (message-fetch-field "reply-to")))
+ gnus-warning (message-fetch-field "gnus-warning"))
+ (when (and gnus-warning (string-match "<[^>]+>" gnus-warning))
+ (setq message-id (match-string 0 gnus-warning)))
+ ;; Remove bogus distribution.
+ (when (and (stringp distribution)
+ (let ((case-fold-search t))
+ (string-match "world" distribution)))
+ (setq distribution nil))
+ ;; Remove any (buggy) Re:'s that are present and make a
+ ;; proper one.
+ (setq subject (message-make-followup-subject subject))
+ (widen))
+
+ ;; Handle special values of Mail-Copies-To.
+ (when mct
+ (cond
+ ((and (or (equal (downcase mct) "never")
+ (equal (downcase mct) "nobody"))
+ (or (not (eq message-use-mail-copies-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: never? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: never'
+directs you not to send your response to the author.")))
+ (setq mct nil))
+ ((and (or (equal (downcase mct) "always")
+ (equal (downcase mct) "poster"))
+ (or (not (eq message-use-mail-copies-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: always? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: always'
+sends a copy of your response to the author.")))
+ (setq mct (or mrt from)))
+ ((and (eq message-use-mail-copies-to 'ask)
+ (not
+ (message-y-or-n-p
+ (concat "Obey Mail-Copies-To: " mct " ? ") t "\
+You should normally obey the Mail-Copies-To: header.
+
+ `Mail-Copies-To: " mct "'
+sends a copy of your response to " (if (string-match "," mct)
+ "the specified addresses"
+ "that address") ".")))
+ (setq mct nil))
+ ))
+
+ (unless follow-to
+ (cond
+ (to-newsgroups (setq follow-to (list (cons 'Newsgroups to-newsgroups))))
+ ;; Handle Followup-To.
+ (followup-to
+ (cond
+ ((equal (downcase followup-to) "poster")
+ (if (or (eq message-use-followup-to 'use)
+ (message-y-or-n-p "Obey Followup-To: poster? " t "\
+You should normally obey the Followup-To: header.
+
+ `Followup-To: poster'
+sends your response via e-mail instead of news.
+
+A typical situation where `Followup-To: poster' is used is when the author
+does not read the newsgroup, so he wouldn't see any replies sent to it."))
+ (setq message-this-is-news nil
+ distribution nil
+ follow-to (list (cons 'To (or mrt from ""))))
+ (setq follow-to (list (cons 'Newsgroups newsgroups)))))
+ (t
+ (if (or (equal followup-to newsgroups)
+ (not (eq message-use-followup-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Followup-To: " followup-to "? ") t "\
+You should normally obey the Followup-To: header.
+
+ `Followup-To: " followup-to "'
+directs your response to " (if (string-match "," followup-to)
+ "the specified newsgroups"
+ "that newsgroup only") ".
+
+If a message is posted to several newsgroups, Followup-To is often
+used to direct the following discussion to one newsgroup only,
+because discussions that are spread over several newsgroup tend to
+be fragmented and very difficult to follow.
+
+Also, some source/announcement newsgroups are not indented for discussion;
+responses here are directed to other newsgroups."))
+ (setq follow-to (list (cons 'Newsgroups followup-to)))
+ (setq follow-to (list (cons 'Newsgroups newsgroups)))))))
+ ;; Handle Mail-Followup-To, followup via e-mail.
+ ((and mft
+ (or (not (eq message-use-mail-followup-to 'ask))
+ (message-y-or-n-p
+ (concat "Obey Mail-Followup-To: " mft "? ") t "\
+You should normally obey the Mail-Followup-To: header.
+
+ `Mail-Followup-To: " mft "'
+directs your response to " (if (string-match "," mft)
+ "the specified addresses"
+ "that address only") " instead of news.
+
+A typical situation where Mail-Followup-To is used is when the author thinks
+that further discussion should take place only in "
+ (if (string-match "," mft)
+ "the specified mailing lists"
+ "that mailing list") ".")))
+ (setq message-this-is-news nil
+ distribution nil
+ follow-to (list (cons 'To mft))))
+ (posted-to (setq follow-to (list (cons 'Newsgroups posted-to))))
+ (t
+ (setq follow-to (list (cons 'Newsgroups newsgroups))))))
+
+ (message-pop-to-buffer (message-buffer-name "followup" from newsgroups))
+
+ (setq message-reply-headers
+ (make-full-mail-header-from-decoded-header
+ 0 subject from date message-id references 0 0 ""))
+
+ (message-setup
+ `((Subject . ,subject)
+ ,@follow-to
+ ,@(and mct (list (cons 'Cc mct)))
+ ,@(and distribution (list (cons 'Distribution distribution)))
+ ,@(if (or references message-id)
+ `((References . ,(concat (or references "") (and references " ")
+ (or message-id ""))))))
+ cur)))
+
+;;;###autoload
+(defun message-cancel-news ()
+ "Cancel an article you posted."
+ (interactive)
+ (unless (message-news-p)
+ (error "This is not a news article; canceling is impossible"))
+ (when (yes-or-no-p "Do you really want to cancel this article? ")
+ (let (from newsgroups message-id distribution buf sender)
+ (save-excursion
+ ;; Get header info. from original article.
+ (save-restriction
+ (message-narrow-to-head)
+ (setq from (message-fetch-field "from")
+ sender (message-fetch-field "sender")
+ newsgroups (message-fetch-field "newsgroups")
+ message-id (message-fetch-field "message-id" t)
+ distribution (message-fetch-field "distribution")))
+ ;; Make sure that this article was written by the user.
+ (unless (or (and sender
+ (string-equal
+ (downcase sender)
+ (downcase (message-make-sender))))
+ (string-equal
+ (downcase (cadr (std11-extract-address-components
+ from)))
+ (downcase (cadr (std11-extract-address-components
+ (message-make-from))))))
+ (error "This article is not yours"))
+ ;; Make control message.
+ (setq buf (set-buffer (get-buffer-create " *message cancel*")))
+ (erase-buffer)
+ (insert "Newsgroups: " newsgroups "\n"
+ "From: " (message-make-from) "\n"
+ "Subject: cmsg cancel " message-id "\n"
+ "Control: cancel " message-id "\n"
+ (if distribution
+ (concat "Distribution: " distribution "\n")
+ "")
+ mail-header-separator "\n"
+ message-cancel-message)
+ (run-hooks 'message-cancel-hook)
+ (message "Canceling your article...")
+ (if (let ((message-syntax-checks
+ 'dont-check-for-anything-just-trust-me)
+ (message-encoding-buffer (current-buffer))
+ (message-edit-buffer (current-buffer)))
+ (message-send-news))
+ (message "Canceling your article...done"))
+ (kill-buffer buf)))))
+
+(defun message-supersede-setup-for-mime-edit ()
+ (set (make-local-variable 'message-setup-hook) nil)
+ (mime-edit-again))
+
+;;;###autoload
+(defun message-supersede ()
+ "Start composing a message to supersede the current message.
+This is done simply by taking the old article and adding a Supersedes
+header line with the old Message-ID."
+ (interactive)
+ (let ((cur (current-buffer))
+ (sender (message-fetch-field "sender"))
+ (from (message-fetch-field "from")))
+ ;; Check whether the user owns the article that is to be superseded.
+ (unless (or (and sender
+ (string-equal
+ (downcase sender)
+ (downcase (message-make-sender))))
+ (string-equal
+ (downcase (cadr (std11-extract-address-components from)))
+ (downcase (cadr (std11-extract-address-components
+ (message-make-from))))))
+ (error "This article is not yours"))
+ ;; Get a normal message buffer.
+ (message-pop-to-buffer (message-buffer-name "supersede"))
+ (insert-buffer-substring cur)
+ (message-narrow-to-head)
+ ;; Remove unwanted headers.
+ (when message-ignored-supersedes-headers
+ (message-remove-header message-ignored-supersedes-headers t))
+ (goto-char (point-min))
+ (if (not (re-search-forward "^Message-ID: " nil t))
+ (error "No Message-ID in this article")
+ (replace-match "Supersedes: " t t))
+ (goto-char (point-max))
+ (insert mail-header-separator)
+ (widen)
+ (when message-supersede-setup-function
+ (funcall message-supersede-setup-function))
+ (run-hooks 'message-supersede-setup-hook)
+ (goto-char (point-min))
+ (search-forward (concat "\n" mail-header-separator "\n") nil t)))
+
+;;;###autoload
+(defun message-recover ()
+ "Reread contents of current buffer from its last auto-save file."
+ (interactive)
+ (let ((file-name (make-auto-save-file-name)))
+ (cond ((save-window-excursion
+ (if (not (eq system-type 'vax-vms))
+ (with-output-to-temp-buffer "*Directory*"
+ (buffer-disable-undo standard-output)
+ (let ((default-directory "/"))
+ (call-process
+ "ls" nil standard-output nil "-l" file-name))))
+ (yes-or-no-p (format "Recover auto save file %s? " file-name)))
+ (let ((buffer-read-only nil))
+ (erase-buffer)
+ (insert-file-contents file-name nil)))
+ (t (error "message-recover cancelled")))))
+
+;;; Washing Subject:
+
+(defun message-wash-subject (subject)
+ "Remove junk like \"Re:\", \"(fwd)\", etc. that was added to the subject by previous forwarders, replyers, etc."
+ (with-temp-buffer
+ (insert-string subject)
+ (goto-char (point-min))
+ ;; strip Re/Fwd stuff off the beginning
+ (while (re-search-forward
+ "\\([Rr][Ee]:\\|[Ff][Ww][Dd]\\(\\[[0-9]*\\]\\)?:\\|[Ff][Ww]:\\)" nil t)
+ (replace-match ""))
+
+ ;; and gnus-style forwards [foo@bar.com] subject
+ (goto-char (point-min))
+ (while (re-search-forward "\\[[^ \t]*\\(@\\|\\.\\)[^ \t]*\\]" nil t)
+ (replace-match ""))
+
+ ;; and off the end
+ (goto-char (point-max))
+ (while (re-search-backward "([Ff][Ww][Dd])" nil t)
+ (replace-match ""))
+
+ ;; and finally, any whitespace that was left-over
+ (goto-char (point-min))
+ (while (re-search-forward "^[ \t]+" nil t)
+ (replace-match ""))
+ (goto-char (point-max))
+ (while (re-search-backward "[ \t]+$" nil t)
+ (replace-match ""))
+
+ (buffer-string)))
+
+;;; Forwarding messages.
+
+(defun message-forward-subject-author-subject (subject)
+ "Generate a subject for a forwarded message.
+The form is: [Source] Subject, where if the original message was mail,
+Source is the sender, and if the original message was news, Source is
+the list of newsgroups is was posted to."
+ (concat "["
+ (or (message-fetch-field
+ (if (message-news-p) "newsgroups" "from"))
+ "(nowhere)")
+ "] " subject))
+
+(defun message-forward-subject-fwd (subject)
+ "Generate a subject for a forwarded message.
+The form is: Fwd: Subject, where Subject is the original subject of
+the message."
+ (concat "Fwd: " subject))
+
+(defun message-make-forward-subject ()
+ "Return a Subject header suitable for the message in the current buffer."
+ (save-excursion
+ (save-restriction
+ (current-buffer)
+ (message-narrow-to-head)
+ (let ((funcs message-make-forward-subject-function)
+ (subject (if message-wash-forwarded-subjects
+ (message-wash-subject
+ (or (nnheader-decode-subject
+ (message-fetch-field "Subject"))
+ ""))
+ (or (nnheader-decode-subject
+ (message-fetch-field "Subject"))
+ ""))))
+ ;; Make sure funcs is a list.
+ (and funcs
+ (not (listp funcs))
+ (setq funcs (list funcs)))
+ ;; Apply funcs in order, passing subject generated by previous
+ ;; func to the next one.
+ (while funcs
+ (when (message-functionp (car funcs))
+ (setq subject (funcall (car funcs) subject)))
+ (setq funcs (cdr funcs)))
+ subject))))
+
+;;;###autoload
+(defun message-forward (&optional news)
+ "Forward the current message via mail.
+Optional NEWS will use news to forward instead of mail."
+ (interactive "P")
+ (let ((cur (current-buffer))
+ (subject (message-make-forward-subject))
+ art-beg)
+ (if news (message-news nil subject) (message-mail nil subject))
+ ;; Put point where we want it before inserting the forwarded
+ ;; message.
+ (if message-signature-before-forwarded-message
+ (goto-char (point-max))
+ (message-goto-body))
+ ;; Make sure we're at the start of the line.
+ (unless (eolp)
+ (insert "\n"))
+ ;; Narrow to the area we are to insert.
+ (narrow-to-region (point) (point))
+ ;; Insert the separators and the forwarded buffer.
+ (insert message-forward-start-separator)
+ (setq art-beg (point))
+ (insert-buffer-substring cur)
+ (goto-char (point-max))
+ (insert message-forward-end-separator)
+ (set-text-properties (point-min) (point-max) nil)
+ ;; Remove all unwanted headers.
+ (goto-char art-beg)
+ (narrow-to-region (point) (if (search-forward "\n\n" nil t)
+ (1- (point))
+ (point)))
+ (goto-char (point-min))
+ (message-remove-header message-included-forward-headers t nil t)
+ (widen)
+ (message-position-point)))
+
+;;;###autoload
+(defun message-resend (address)
+ "Resend the current article to ADDRESS."
+ (interactive "sResend message to: ")
+ (message "Resending message to %s..." address)
+ (save-excursion
+ (let ((cur (current-buffer))
+ beg)
+ ;; We first set up a normal mail buffer.
+ (set-buffer (get-buffer-create " *message resend*"))
+ (erase-buffer)
+ ;; avoid to turn-on-mime-edit
+ (let (message-setup-hook)
+ (message-setup `((To . ,address)))
+ )
+ ;; Insert our usual headers.
+ (message-generate-headers '(From Date To))
+ (message-narrow-to-headers)
+ ;; Rename them all to "Resent-*".
+ (while (re-search-forward "^[A-Za-z]" nil t)
+ (forward-char -1)
+ (insert "Resent-"))
+ (widen)
+ (forward-line)
+ (delete-region (point) (point-max))
+ (setq beg (point))
+ ;; Insert the message to be resent.
+ (insert-buffer-substring cur)
+ (goto-char (point-min))
+ (search-forward "\n\n")
+ (forward-char -1)
+ (save-restriction
+ (narrow-to-region beg (point))
+ (message-remove-header message-ignored-resent-headers t)
+ (goto-char (point-max)))
+ (insert mail-header-separator)
+ ;; Rename all old ("Also-")Resent headers.
+ (while (re-search-backward "^\\(Also-\\)*Resent-" beg t)
+ (beginning-of-line)
+ (insert "Also-"))
+ ;; Quote any "From " lines at the beginning.
+ (goto-char beg)
+ (when (looking-at "From ")
+ (replace-match "X-From-Line: "))
+ ;; Send it.
+ (let ((message-encoding-buffer (current-buffer))
+ (message-edit-buffer (current-buffer)))
+ (let (message-required-mail-headers)
+ (message-send-mail)))
+ (kill-buffer (current-buffer)))
+ (message "Resending message to %s...done" address)))
+
+(defun message-bounce-setup-for-mime-edit ()
+ (set (make-local-variable 'message-setup-hook) nil)
+ (mime-edit-again))
+
+;;;###autoload
+(defun message-bounce ()
+ "Re-mail the current message.
+This only makes sense if the current message is a bounce message than
+contains some mail you have written which has been bounced back to
+you."
+ (interactive)
+ (let ((cur (current-buffer))
+ boundary)
+ (message-pop-to-buffer (message-buffer-name "bounce"))
+ (insert-buffer-substring cur)
+ (undo-boundary)
+ (message-narrow-to-head)
+ (if (and (message-fetch-field "MIME-Version")
+ (setq boundary (message-fetch-field "Content-Type")))
+ (if (string-match "boundary=\"\\([^\"]+\\)\"" boundary)
+ (setq boundary (concat (match-string 1 boundary) " *\n"
+ "Content-Type: message/rfc822"))
+ (setq boundary nil)))
+ (widen)
+ (goto-char (point-min))
+ (search-forward "\n\n" nil t)
+ (or (and boundary
+ (re-search-forward boundary nil t)
+ (forward-line 2))
+ (and (re-search-forward message-unsent-separator nil t)
+ (forward-line 1))
+ (re-search-forward "^Return-Path:.*\n" nil t))
+ ;; We remove everything before the bounced mail.
+ (delete-region
+ (point-min)
+ (if (re-search-forward "^[^ \n\t]+:" nil t)
+ (match-beginning 0)
+ (point)))
+ (save-restriction
+ (message-narrow-to-head)
+ (message-remove-header message-ignored-bounced-headers t)
+ (goto-char (point-max))
+ (insert mail-header-separator))
+ (when message-bounce-setup-function
+ (funcall message-bounce-setup-function))
+ (run-hooks 'message-bounce-setup-hook)
+ (message-position-point)))
+
+;;;
+;;; Interactive entry points for new message buffers.
+;;;
+
+;;;###autoload
+(defun message-mail-other-window (&optional to subject)
+ "Like `message-mail' command, but display mail buffer in another window."
+ (interactive)
+ (let ((pop-up-windows t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "mail" to)))
+ (let ((message-this-is-mail t))
+ (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
+
+;;;###autoload
+(defun message-mail-other-frame (&optional to subject)
+ "Like `message-mail' command, but display mail buffer in another frame."
+ (interactive)
+ (let ((pop-up-frames t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "mail" to)))
+ (let ((message-this-is-mail t))
+ (message-setup `((To . ,(or to "")) (Subject . ,(or subject ""))))))
+
+;;;###autoload
+(defun message-news-other-window (&optional newsgroups subject)
+ "Start editing a news article to be sent."
+ (interactive)
+ (let ((pop-up-windows t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
+ (let ((message-this-is-news t))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (Subject . ,(or subject ""))))))
+
+;;;###autoload
+(defun message-news-other-frame (&optional newsgroups subject)
+ "Start editing a news article to be sent."
+ (interactive)
+ (let ((pop-up-frames t)
+ (special-display-buffer-names nil)
+ (special-display-regexps nil)
+ (same-window-buffer-names nil)
+ (same-window-regexps nil))
+ (message-pop-to-buffer (message-buffer-name "news" nil newsgroups)))
+ (let ((message-this-is-news t))
+ (message-setup `((Newsgroups . ,(or newsgroups ""))
+ (Subject . ,(or subject ""))))))
+
+;;; underline.el
+
+;; This code should be moved to underline.el (from which it is stolen).
+
+;;;###autoload
+(defun bold-region (start end)
+ "Bold all nonblank characters in the region.
+Works by overstriking characters.
+Called from program, takes two arguments START and END
+which specify the range to operate on."
+ (interactive "r")
+ (save-excursion
+ (let ((end1 (make-marker)))
+ (move-marker end1 (max start end))
+ (goto-char (min start end))
+ (while (< (point) end1)
+ (or (looking-at "[_\^@- ]")
+ (insert (char-after) "\b"))
+ (forward-char 1)))))
+
+;;;###autoload
+(defun unbold-region (start end)
+ "Remove all boldness (overstruck characters) in the region.
+Called from program, takes two arguments START and END
+which specify the range to operate on."
+ (interactive "r")
+ (save-excursion
+ (let ((end1 (make-marker)))
+ (move-marker end1 (max start end))
+ (goto-char (min start end))
+ (while (re-search-forward "\b" end1 t)
+ (if (eq (char-after) (char-after (- (point) 2)))
+ (delete-char -2))))))
+
+(defalias 'message-exchange-point-and-mark 'exchange-point-and-mark)
+
+;; Support for toolbar
+(when (string-match "XEmacs\\|Lucid" emacs-version)
+ (require 'messagexmas))
+
+;;; Group name completion.
+
+(defvar message-newgroups-header-regexp
+ "^\\(Newsgroups\\|Followup-To\\|Posted-To\\|Gcc\\):"
+ "Regexp that match headers that lists groups.")
+
+(defun message-tab ()
+ "Expand group names in Newsgroups and Followup-To headers.
+Do a `tab-to-tab-stop' if not in those headers."
+ (interactive)
+ (if (let ((mail-abbrev-mode-regexp message-newgroups-header-regexp))
+ (mail-abbrev-in-expansion-header-p))
+ (message-expand-group)
+ (tab-to-tab-stop)))
+
+(defvar gnus-active-hashtb)
+(defun message-expand-group ()
+ "Expand the group name under point."
+ (let* ((b (save-excursion
+ (save-restriction
+ (narrow-to-region
+ (save-excursion
+ (beginning-of-line)
+ (skip-chars-forward "^:")
+ (1+ (point)))
+ (point))
+ (skip-chars-backward "^, \t\n") (point))))
+ (completion-ignore-case t)
+ (string (buffer-substring b (progn (skip-chars-forward "^,\t\n ")
+ (point))))
+ (hashtb (and (boundp 'gnus-active-hashtb) gnus-active-hashtb))
+ (completions (all-completions string hashtb))
+ comp)
+ (delete-region b (point))
+ (cond
+ ((= (length completions) 1)
+ (if (string= (car completions) string)
+ (progn
+ (insert string)
+ (message "Only matching group"))
+ (insert (car completions))))
+ ((and (setq comp (try-completion string hashtb))
+ (not (string= comp string)))
+ (insert comp))
+ (t
+ (insert string)
+ (if (not comp)
+ (message "No matching groups")
+ (save-selected-window
+ (pop-to-buffer "*Completions*")
+ (buffer-disable-undo)
+ (let ((buffer-read-only nil))
+ (erase-buffer)
+ (let ((standard-output (current-buffer)))
+ (display-completion-list (sort completions 'string<)))
+ (goto-char (point-min))
+ (delete-region (point) (progn (forward-line 3) (point))))))))))
+
+;;; Help stuff.
+
+(defun message-talkative-question (ask question show &rest text)
+ "Call FUNCTION with argument QUESTION; optionally display TEXT... args.
+If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
+The following arguments may contain lists of values."
+ (if (and show
+ (setq text (message-flatten-list text)))
+ (save-window-excursion
+ (save-excursion
+ (with-output-to-temp-buffer " *MESSAGE information message*"
+ (set-buffer " *MESSAGE information message*")
+ (mapcar 'princ text)
+ (goto-char (point-min))))
+ (funcall ask question))
+ (funcall ask question)))
+
+(defun message-flatten-list (list)
+ "Return a new, flat list that contains all elements of LIST.
+
+\(message-flatten-list '(1 (2 3 (4 5 (6))) 7))
+=> (1 2 3 4 5 6 7)"
+ (cond ((consp list)
+ (apply 'append (mapcar 'message-flatten-list list)))
+ (list
+ (list list))))
+
+(defun message-generate-new-buffer-clone-locals (name &optional varstr)
+ "Create and return a buffer with a name based on NAME using generate-new-buffer.
+Then clone the local variables and values from the old buffer to the
+new one, cloning only the locals having a substring matching the
+regexp varstr."
+ (let ((oldbuf (current-buffer)))
+ (save-excursion
+ (set-buffer (generate-new-buffer name))
+ (message-clone-locals oldbuf)
+ (current-buffer))))
+
+(defun message-clone-locals (buffer)
+ "Clone the local variables from BUFFER to the current buffer."
+ (let ((locals (save-excursion
+ (set-buffer buffer)
+ (buffer-local-variables)))
+ (regexp
+ "^\\(gnus\\|nn\\|message\\|user-\\(mail-address\\|full-name\\)\\)"))
+ (mapcar
+ (lambda (local)
+ (when (and (consp local)
+ (car local)
+ (string-match regexp (symbol-name (car local))))
+ (ignore-errors
+ (set (make-local-variable (car local))
+ (cdr local)))))
+ locals)))
+
+
+;;; @ for MIME Edit mode
+;;;
+
+(defun message-maybe-encode ()
+ (when message-mime-mode
+ (run-hooks 'mime-edit-translate-hook)
+ (if (catch 'mime-edit-error
+ (save-excursion
+ (mime-edit-translate-body)
+ ))
+ (error "Translation error!")
+ )
+ (end-of-invisible)
+ (run-hooks 'mime-edit-exit-hook)
+ ))
+
+(defun message-mime-insert-article (&optional full-headers)
+ (interactive "P")
+ (let ((message-cite-function 'mime-edit-inserted-message-filter)
+ (message-reply-buffer
+ (message-get-parameter-with-eval 'original-buffer))
+ (start (point)))
+ (message-yank-original nil)
+ (save-excursion
+ (narrow-to-region (goto-char start)
+ (if (search-forward "\n\n" nil t)
+ (1- (point))
+ (point-max)))
+ (goto-char (point-min))
+ (let ((message-included-forward-headers
+ (if full-headers "" message-included-forward-headers)))
+ (message-remove-header message-included-forward-headers t nil t))
+ (widen))))
+
+(set-alist 'mime-edit-message-inserter-alist
+ 'message-mode (function message-mime-insert-article))
+
+;;; Miscellaneous functions
+
+;; stolen (and renamed) from nnheader.el
+(defun message-replace-chars-in-string (string from to)
+ "Replace characters in STRING from FROM to TO."
+ (let ((string (substring string 0)) ;Copy string.
+ (len (length string))
+ (idx 0))
+ ;; Replace all occurrences of FROM with TO.
+ (while (< idx len)
+ (when (= (aref string idx) from)
+ (aset string idx to))
+ (setq idx (1+ idx)))
+ string))
+
+;;;
+;;; MIME functions
+;;;
+
+(defun message-mime-query-file (prompt)
+ (let ((file (read-file-name prompt nil nil t)))
+ ;; Prevent some common errors. This is inspired by similar code in
+ ;; VM.
+ (when (file-directory-p file)
+ (error "%s is a directory, cannot attach" file))
+ (unless (file-exists-p file)
+ (error "No such file: %s" file))
+ (unless (file-readable-p file)
+ (error "Permission denied: %s" file))
+ file))
+
+(defun message-mime-query-type (file)
+ (let* ((default (or (mm-default-file-encoding file)
+ ;; Perhaps here we should check what the file
+ ;; looks like, and offer text/plain if it looks
+ ;; like text/plain.
+ "application/octet-stream"))
+ (string (completing-read
+ (format "Content type (default %s): " default)
+ (delete-duplicates
+ (mapcar (lambda (m) (list (cdr m))) mailcap-mime-extensions)
+ :test 'equal))))
+ (if (not (equal string ""))
+ string
+ default)))
+
+(defun message-mime-query-description ()
+ (let ((description (read-string "One line description: ")))
+ (when (string-match "\\`[ \t]*\\'" description)
+ (setq description nil))
+ description))
+
+(defun message-mime-attach-file (file &optional type description)
+ "Attach a file to the outgoing MIME message.
+The file is not inserted or encoded until you send the message with
+`\\[message-send-and-exit]' or `\\[message-send]'.
+
+FILE is the name of the file to attach. TYPE is its content-type, a
+string of the form \"type/subtype\". DESCRIPTION is a one-line
+description of the attachment."
+ (interactive
+ (let* ((file (message-mime-query-file "Attach file: "))
+ (type (message-mime-query-type file))
+ (description (message-mime-query-description)))
+ (list file type description)))
+ (insert (format
+ "<#part type=%s filename=%s%s disposition=attachment><#/part>\n"
+ type (prin1-to-string file)
+ (if description
+ (format " description=%s" (prin1-to-string description))
+ ""))))
+
+(defun message-mime-attach-external (file &optional type description)
+ "Attach an external file into the buffer.
+FILE is an ange-ftp/efs specification of the part location.
+TYPE is the MIME type to use."
+ (interactive
+ (let* ((file (message-mime-query-file "Attach external file: "))
+ (type (message-mime-query-type file))
+ (description (message-mime-query-description)))
+ (list file type description)))
+ (insert (format
+ "<#external type=%s name=%s disposition=attachment><#/external>\n"
+ type (prin1-to-string file))))
+
+(defun message-encode-message-body ()
+ (let ((mail-parse-charset message-default-charset)
+ (case-fold-search t)
+ lines multipart-p content-type-p)
+ (message-goto-body)
+ (save-restriction
+ (narrow-to-region (point) (point-max))
+ (let ((new (mml-generate-mime)))
+ (when new
+ (delete-region (point-min) (point-max))
+ (insert new)
+ (goto-char (point-min))
+ (if (eq (aref new 0) ?\n)
+ (delete-char 1)
+ (search-forward "\n\n")
+ (setq lines (buffer-substring (point-min) (1- (point))))
+ (delete-region (point-min) (point))))))
+ (save-restriction
+ (message-narrow-to-headers-or-head)
+ (message-remove-header "Mime-Version")
+ (goto-char (point-max))
+ (insert "Mime-Version: 1.0\n")
+ (when lines
+ (insert lines))
+ (setq multipart-p
+ (re-search-backward "^Content-Type: multipart/" nil t))
+ (goto-char (point-max))
+ (setq content-type-p
+ (re-search-backward "^Content-Type:" nil t)))
+ (save-restriction
+ (message-narrow-to-headers-or-head)
+ (message-remove-first-header "Content-Type")
+ (message-remove-first-header "Content-Transfer-Encoding"))
+ (when multipart-p
+ (save-restriction
+ (message-narrow-to-headers-or-head)
+ (message-remove-first-header "Content-Type")
+ (message-remove-first-header "Content-Transfer-Encoding"))
+ (message-goto-body)
+ (insert "This is a MIME multipart message. If you are reading\n")
+ (insert "this, you shouldn't.\n"))
+ ;; We always make sure that the message has a Content-Type header.
+ ;; This is because some broken MTAs and MUAs get awfully confused
+ ;; when confronted with a message with a MIME-Version header and
+ ;; without a Content-Type header. For instance, Solaris'
+ ;; /usr/bin/mail.
+ (unless content-type-p
+ (goto-char (point-min))
+ (re-search-forward "^MIME-Version:")
+ (forward-line 1)
+ (insert "Content-Type: text/plain; charset=us-ascii\n"))))
+
+(defvar message-save-buffer " *encoding")
+(defun message-save-drafts ()
+ (interactive)
+ (if (not (get-buffer message-save-buffer))
+ (get-buffer-create message-save-buffer))
+ (let ((filename buffer-file-name)
+ (buffer (current-buffer)))
+ (set-buffer message-save-buffer)
+ (erase-buffer)
+ (insert-buffer buffer)
+ (mime-edit-translate-buffer)
+ (write-region (point-min) (point-max) filename)
+ (set-buffer buffer)
+ (set-buffer-modified-p nil)))
+
+(provide 'message)
+
+(run-hooks 'message-load-hook)
+
+;;; message.el ends here