;;; message.el --- composing mail and news messages
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;; 2005 Free Software Foundation, Inc.
+;; 2005, 2006 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: mail, news
(require 'canlock)
(require 'mailheader)
(require 'nnheader)
+(require 'gmm-utils)
;; This is apparently necessary even though things are autoloaded.
;; Because we dynamically bind mail-abbrev-mode-regexp, we'd better
;; require mailabbrev here.
;;; Suggested by Jonas Steverud @ www.dtek.chalmers.se/~d4jonas/
-;;;###autoload
(defun message-change-subject (new-subject)
"Ask for NEW-SUBJECT header, append (was: <Old Subject>)."
;; <URL:http://www.landfield.com/usefor/drafts/draft-ietf-usefor-useage--1.02.unpaged>
" (was: "
old-subject ")\n")))))))))
-;;;###autoload
(defun message-mark-inserted-region (beg end &optional verbatim)
"Mark some region in the current article with enclosing tags.
See `message-mark-insert-begin' and `message-mark-insert-end'.
(goto-char beg)
(insert (if verbatim "#v+\n" message-mark-insert-begin))))
-;;;###autoload
(defun message-mark-insert-file (file &optional verbatim)
"Insert FILE at point, marking it with enclosing tags.
See `message-mark-insert-begin' and `message-mark-insert-end'.
(goto-char p)
(insert (if verbatim "#v+\n" message-mark-insert-begin))))
-;;;###autoload
(defun message-add-archive-header ()
"Insert \"X-No-Archive: Yes\" in the header and a note in the body.
The note can be customized using `message-archive-note'. When called with a
(message-add-header message-archive-header)
(message-sort-headers)))
-;;;###autoload
(defun message-cross-post-followup-to-header (target-group)
"Mangles FollowUp-To and Newsgroups header to point to TARGET-GROUP.
With prefix-argument just set Follow-Up, don't cross-post."
(insert (concat "\nFollowup-To: " target-group)))
(setq message-cross-post-old-target target-group))
-;;;###autoload
(defun message-cross-post-insert-note (target-group cross-post in-old
old-groups)
"Insert a in message body note about a set Followup or Crosspost.
(insert (concat message-followup-to-note target-group "\n"))
(insert (concat message-cross-post-note target-group "\n")))))
-;;;###autoload
(defun message-cross-post-followup-to (target-group)
"Crossposts message and set Followup-To to TARGET-GROUP.
With prefix-argument just set Follow-Up, don't cross-post."
;;; Reduce To: to Cc: or Bcc: header
-;;;###autoload
(defun message-reduce-to-to-cc ()
"Replace contents of To: header with contents of Cc: or Bcc: header."
(interactive)
;;; End of functions adopted from `message-utils.el'.
-(defun message-remove-duplicates (list)
- (let (new)
- (while list
- (or (member (car list) new)
- (setq new (cons (car list) new)))
- (setq list (cdr list)))
- (nreverse new)))
-
(defun message-remove-header (header &optional is-regexp first reverse)
"Remove HEADER in the narrowed buffer.
If IS-REGEXP, HEADER is a regular expression.
(message-skip-to-next-address)
(kill-region start (point))))
+
+(defun message-info (&optional arg)
+ "Display the Message manual.
+
+Prefixed with one \\[universal-argument], display the Emacs MIME manual.
+Prefixed with two \\[universal-argument]'s, display the PGG manual."
+ (interactive "p")
+ (cond ((eq arg 16) (Info-goto-node "(pgg)Top"))
+ ((eq arg 4) (Info-goto-node "(emacs-mime)Top"))
+ (t (Info-goto-node "(message)Top"))))
+
\f
;;;
(define-key message-mode-map "\C-c\C-fw" 'message-insert-wide-reply)
(define-key message-mode-map "\C-c\C-n" 'message-insert-newsgroups)
(define-key message-mode-map "\C-c\C-l" 'message-to-list-only)
+ (define-key message-mode-map "\C-c\C-f\C-e" 'message-insert-expires)
(define-key message-mode-map "\C-c\C-u" 'message-insert-or-toggle-importance)
(define-key message-mode-map "\C-c\M-n"
'(:help "Ask, then arrange to send message at that time"))]
["Kill Message" message-kill-buffer
,@(if (featurep 'xemacs) '(t)
- '(:help "Delete this message without sending"))]))
+ '(:help "Delete this message without sending"))]
+ "----"
+ ["Message manual" message-info
+ ,@(if (featurep 'xemacs) '(t)
+ '(:help "Display the Message manual"))]))
(easy-menu-define
message-mode-field-menu message-mode-map ""
;; ["Followup-To (with note in body)" message-cross-post-followup-to t]
["Crosspost / Followup-To..." message-cross-post-followup-to t]
["Distribution" message-goto-distribution t]
- ["X-No-Archive:" message-add-archive-header t ]
+ ["Expires" message-insert-expires t ]
+ ["X-No-Archive" message-add-archive-header t ]
"----"
;; (typical) mailing-lists stuff
["Fetch To" message-insert-to
"----"
["Sort Headers" message-sort-headers t]
["Encode non-ASCII domain names" message-idna-to-ascii-rhs t]
+ ;; We hide `message-hidden-headers' by narrowing the buffer.
+ ["Show Hidden Headers" widen t]
["Goto Body" message-goto-body t]
["Goto Signature" message-goto-signature t]))
C-c C-f C-o move to From (\"Originator\")
C-c C-f C-f move to Followup-To
C-c C-f C-m move to Mail-Followup-To
+ C-c C-f C-e move to Expires
C-c C-f C-i cycle through Importance values
C-c C-f s change subject and append \"(was: <Old Subject>)\"
C-c C-f x crossposting with FollowUp-To header and note in body
(set (make-local-variable 'font-lock-defaults)
'(message-font-lock-keywords t))
(if (boundp 'tool-bar-map)
- (set (make-local-variable 'tool-bar-map) (message-tool-bar-map))))
+ (set (make-local-variable 'tool-bar-map) (message-make-tool-bar))))
(easy-menu-add message-mode-menu message-mode-map)
(easy-menu-add message-mode-field-menu message-mode-map)
(gnus-make-local-hook 'after-change-functions)
(message-goto-body)
(forward-line -1))
+(defun message-in-body-p ()
+ "Return t if point is in the message body."
+ (let ((body (save-excursion (message-goto-body) (point))))
+ (>= (point) body)))
+
(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
"Kill all text up to the signature.
If a numberic argument or prefix arg is given, leave that number
of lines before the signature intact."
- (interactive "p")
+ (interactive "P")
(save-excursion
(save-restriction
(let ((point (point)))
(defun message-insert-citation-line ()
"Insert a simple citation line."
(when message-reply-headers
- (insert (mail-header-from message-reply-headers) " writes:\n\n")))
+ (insert (mail-header-from message-reply-headers) " writes:")
+ (newline)
+ (newline)))
(defun message-position-on-field (header &rest afters)
(let ((case-fold-search t))
(when (let ((char (char-after)))
(or (< (mm-char-int char) 128)
(and (mm-multibyte-p)
- ;; Fixme: Wrong for Emacs 22 and for things
- ;; like undecable utf-8. Should at least
+ ;; FIXME: Wrong for Emacs 23 (unicode) and for
+ ;; things like undecable utf-8. Should at least
;; use find-coding-systems-region.
(memq (char-charset char)
'(eight-bit-control eight-bit-graphic
(let ((system-time-locale "C"))
(format-time-string "%a, %d %b %Y %T %z" now)))
+(defun message-insert-expires (days)
+ "Insert the Expires header. Expiry in DAYS days."
+ (interactive "NExpire article in how many days? ")
+ (save-excursion
+ (message-position-on-field "Expires" "X-Draft-From")
+ (insert (message-make-expires-date days))))
+
+(defun message-make-expires-date (days)
+ "Make date string for the Expires header. Expiry in DAYS days.
+
+In posting styles use `(\"Expires\" (make-expires-date 30))'."
+ (let* ((cur (decode-time (current-time)))
+ (nday (+ days (nth 3 cur))))
+ (setf (nth 3 cur) nday)
+ (message-make-date (apply 'encode-time cur))))
+
(defun message-make-message-id ()
"Make a unique Message-ID."
(concat "<" (message-unique-id)
(* 25 25)))
(let ((tm (current-time)))
(concat
- (if (memq system-type '(ms-dos emx vax-vms))
+ (if (or (memq system-type '(ms-dos emx vax-vms))
+ ;; message-number-base36 doesn't handle bigints.
+ (floatp (user-uid)))
(let ((user (downcase (user-login-name))))
(while (string-match "[^a-z0-9_]" user)
(aset user (match-beginning 0) ?_))
rhs ace address)
(when field
(dolist (rhs
- (message-remove-duplicates
+ (mm-delete-duplicates
(mapcar (lambda (rhs) (or (cadr (split-string rhs "@")) ""))
(mapcar 'downcase
(mapcar
;; The element is a symbol. We insert the value
;; of this symbol, if any.
(symbol-value header))
- ((not (message-check-element header))
+ ((not (message-check-element
+ (intern (downcase (symbol-name header)))))
;; We couldn't generate a value for this header,
;; so we just ask the user.
(read-from-minibuffer
;; totally and insert the new value.
(delete-region (point) (point-at-eol))
;; If the header is optional, and the header was
- ;; empty, we con't insert it anyway.
+ ;; empty, we can't insert it anyway.
(unless optionalp
(push header-string message-inserted-headers)
(insert value)
(prefix
(if group
(gnus-group-decoded-name group)
- (or (and from (car (gnus-extract-address-components from)))
+ (or (and from (or
+ (car (gnus-extract-address-components from))
+ (cadr (gnus-extract-address-components from))))
"(nowhere)"))))
(concat "["
(if message-forward-decoded-p