X-Git-Url: http://cgit.sxemacs.org/?p=gnus;a=blobdiff_plain;f=lisp%2Fmessage.el;h=828dfdeb53d13eed0f0d99528a837d013c18cb95;hp=2374314a56c6f99124d140a2c281ed7309444605;hb=54b3844ec0d9b1fd25b4f00f927853ff72ba5274;hpb=8e3b3fa6d8b9a97c183d9f7ae79657fe11c5af29 diff --git a/lisp/message.el b/lisp/message.el index 2374314a5..828dfdeb5 100644 --- a/lisp/message.el +++ b/lisp/message.el @@ -1,6 +1,6 @@ ;;; message.el --- composing mail and news messages -;; Copyright (C) 1996-2013 Free Software Foundation, Inc. +;; Copyright (C) 1996-2014 Free Software Foundation, Inc. ;; Author: Lars Magne Ingebrigtsen ;; Keywords: mail, news @@ -28,9 +28,6 @@ ;;; Code: -;; For Emacs <22.2 and XEmacs. -(eval-and-compile - (unless (fboundp 'declare-function) (defmacro declare-function (&rest r)))) (eval-when-compile (require 'cl)) @@ -50,6 +47,7 @@ (require 'mml) (require 'rfc822) (require 'format-spec) +(require 'dired) (autoload 'mailclient-send-it "mailclient") ;; Emacs 22 or contrib/ @@ -268,7 +266,7 @@ This is a list of regexps and regexp matches." :type 'sexp) (defcustom message-ignored-news-headers - "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:\\|^X-Message-SMTP-Method:" + "^NNTP-Posting-Host:\\|^Xref:\\|^[BGF]cc:\\|^Resent-Fcc:\\|^X-Draft-From:\\|^X-Gnus-Agent-Meta-Information:\\|^X-Message-SMTP-Method:\\|^X-Gnus-Delayed:" "*Regexp of headers to be removed unconditionally before posting." :group 'message-news :group 'message-headers @@ -614,7 +612,8 @@ Done before generating the new subject of a forward." regexp)) (defcustom message-forward-ignored-headers "^Content-Transfer-Encoding:\\|^X-Gnus" - "*All headers that match this regexp will be deleted when forwarding a message." + "*All headers that match this regexp will be deleted when forwarding a message. +This may also be a list of regexps." :version "21.1" :group 'message-forwarding :type '(repeat :value-to-internal (lambda (widget value) @@ -624,6 +623,19 @@ Done before generating the new subject of a forward." (widget-editable-list-match widget value))) regexp)) +(defcustom message-forward-included-headers nil + "If non-nil, delete non-matching headers when forwarding a message. +Only headers that match this regexp will be included. This +variable should be a regexp or a list of regexps." + :version "25.1" + :group 'message-forwarding + :type '(repeat :value-to-internal (lambda (widget value) + (custom-split-regexp-maybe value)) + :match (lambda (widget value) + (or (stringp value) + (widget-editable-list-match widget value))) + regexp)) + (defcustom message-ignored-cited-headers "." "*Delete these headers from the messages you yank." :group 'message-insertion @@ -972,6 +984,8 @@ the signature is inserted." (set-keymap-parent map minibuffer-local-map) map) "Keymap for `message-read-from-minibuffer'." + ;; FIXME improve type. + :type '(restricted-sexp :match-alternatives (symbolp keymapp)) :version "22.1" :group 'message-various) @@ -996,8 +1010,8 @@ configuration. See the variable `gnus-cite-attribution-suffix'." (defcustom message-citation-line-format "On %a, %b %d %Y, %N wrote:\n" "Format of the \"Whomever writes:\" line. -The string is formatted using `format-spec'. The following -constructs are replaced: +The string is formatted using `format-spec'. The following constructs +are replaced: %f The full From, e.g. \"John Doe \". %n The mail address, e.g. \"john.doe@example.invalid\". @@ -1005,11 +1019,14 @@ constructs are replaced: back to the mail address. %F The first name if present, e.g.: \"John\". %L The last name if present, e.g.: \"Doe\". + %Z, %z The time zone in the numeric form, e.g.:\"+0000\". All other format specifiers are passed to `format-time-string' -which is called using the date from the article your replying to. -Extracting the first (%F) and last name (%L) is done -heuristically, so you should always check it yourself. +which is called using the date from the article your replying to, but +the date in the formatted string will be expressed in the author's +time zone as much as possible. +Extracting the first (%F) and last name (%L) is done heuristically, +so you should always check it yourself. Please also read the note in the documentation of `message-citation-line-function'." @@ -1783,13 +1800,17 @@ no, only reply back to the author." :type '(radio (const :format "%v " nil) (string :format "FQDN: %v"))) -(defcustom message-use-idna (and (condition-case nil (require 'idna) - (file-error)) - (mm-coding-system-p 'utf-8) - (executable-find idna-program) - (string= (idna-to-ascii "räksmörgås") - "xn--rksmrgs-5wao1o") - t) +(defcustom message-use-idna + (and (or (mm-coding-system-p 'utf-8) + (condition-case nil + (let (mucs-ignore-version-incompatibilities) + (require 'un-define)) + (error))) + (condition-case nil (require 'idna) (file-error)) + idna-program + (executable-find idna-program) + (string= (idna-to-ascii "räksmörgås") "xn--rksmrgs-5wao1o") + t) "Whether to encode non-ASCII in domain names into ASCII according to IDNA. GNU Libidn, and in particular the elisp package \"idna.el\" and the external program \"idn\", must be installed for this @@ -1946,7 +1967,45 @@ You must have the \"hashcash\" binary installed, see `hashcash-path'." "cat\\|com\\|coop\\|edu\\|gov\\|" "info\\|int\\|jobs\\|" "mil\\|mobi\\|museum\\|name\\|net\\|" - "org\\|pro\\|tel\\|travel\\|uucp\\)") + "org\\|pro\\|tel\\|travel\\|uucp\\|" + ;; ICANN-era generic top-level domains + "academy\\|actor\\|agency\\|airforce\\|archi\\|associates\\|axa\\|" + "bar\\|bargains\\|bayern\\|beer\\|berlin\\|best\\|bid\\|bike\\|" + "biz\\|black\\|blackfriday\\|blue\\|boutique\\|build\\|builders\\|" + "buzz\\|cab\\|camera\\|camp\\|capital\\|cards\\|care\\|career\\|" + "careers\\|cash\\|catering\\|center\\|ceo\\|cheap\\|christmas\\|" + "church\\|citic\\|cleaning\\|clinic\\|clothing\\|club\\|codes\\|" + "coffee\\|college\\|cologne\\|com\\|community\\|company\\|computer\\|" + "construction\\|contractors\\|cooking\\|cool\\|country\\|creditcard\\|" + "cruises\\|dance\\|dating\\|democrat\\|dental\\|desi\\|design\\|" + "diamonds\\|directory\\|discount\\|domains\\|education\\|email\\|" + "engineering\\|enterprises\\|equipment\\|estate\\|eus\\|events\\|" + "exchange\\|expert\\|exposed\\|fail\\|farm\\|feedback\\|finance\\|" + "financial\\|fish\\|fishing\\|fitness\\|flights\\|florist\\|foo\\|" + "foundation\\|frogans\\|fund\\|furniture\\|futbol\\|gal\\|" + "gallery\\|gift\\|glass\\|globo\\|gmo\\|gop\\|graphics\\|gratis\\|" + "gripe\\|guide\\|guitars\\|guru\\|hamburg\\|haus\\|hiphop\\|" + "holdings\\|holiday\\|homes\\|horse\\|house\\|immobilien\\|" + "industries\\|info\\|ink\\|institute\\|insure\\|international\\|" + "investments\\|jetzt\\|juegos\\|kaufen\\|kim\\|kitchen\\|kiwi\\|" + "koeln\\|kred\\|land\\|lat\\|latino\\|lease\\|life\\|lighting\\|" + "limited\\|limo\\|link\\|loans\\|london\\|luxe\\|luxury\\|" + "management\\|mango\\|marketing\\|media\\|meet\\|menu\\|miami\\|" + "moda\\|moe\\|monash\\|moscow\\|motorcycles\\|nagoya\\|name\\|" + "net\\|neustar\\|ninja\\|nyc\\|okinawa\\|onl\\|org\\|paris\\|" + "partners\\|parts\\|photo\\|photography\\|photos\\|pics\\|" + "pictures\\|pink\\|plumbing\\|pro\\|productions\\|properties\\|" + "pub\\|qpon\\|quebec\\|recipes\\|red\\|reisen\\|ren\\|rentals\\|" + "repair\\|report\\|rest\\|reviews\\|rich\\|rocks\\|rodeo\\|" + "ruhr\\|ryukyu\\|saarland\\|schule\\|scot\\|services\\|sexy\\|" + "shiksha\\|shoes\\|singles\\|social\\|sohu\\|solar\\|solutions\\|" + "soy\\|supplies\\|supply\\|support\\|surgery\\|systems\\|tattoo\\|" + "tax\\|technology\\|tienda\\|tips\\|today\\|tokyo\\|tools\\|" + "town\\|toys\\|trade\\|training\\|university\\|uno\\|vacations\\|" + "vegas\\|ventures\\|viajes\\|villas\\|vision\\|vodka\\|vote\\|" + "voting\\|voto\\|voyage\\|wang\\|watch\\|webcam\\|wed\\|wien\\|" + "wiki\\|works\\|wtc\\|wtf\\|xyz\\|yachts\\|yokohama\\|you\\|" + "zone\\)") ;; http://en.wikipedia.org/wiki/List_of_Internet_top-level_domains ;; http://en.wikipedia.org/wiki/GTLD ;; `approved, but not yet in operation': .xxx @@ -2493,6 +2552,7 @@ With prefix-argument just set Follow-Up, don't cross-post." "Remove HEADER in the narrowed buffer. If IS-REGEXP, HEADER is a regular expression. If FIRST, only remove the first instance of the header. +If REVERSE, remove headers that doesn't match HEADER. Return the number of headers removed." (goto-char (point-min)) (let ((regexp (if is-regexp header (concat "^" (regexp-quote header) ":"))) @@ -3557,15 +3617,16 @@ Message buffers and is not meant to be called directly." (goto-char (point-max)) ;; Insert the signature. (unless (bolp) - (insert "\n")) + (newline)) (when message-signature-insert-empty-line - (insert "\n")) - (insert "-- \n") + (newline)) + (insert "-- ") + (newline) (if (eq signature t) (insert-file-contents signature-file) (insert signature)) (goto-char (point-max)) - (or (bolp) (insert "\n"))))) + (or (bolp) (newline))))) (defun message-insert-importance-high () "Insert header to mark message as important." @@ -3949,9 +4010,13 @@ This function uses `mail-citation-hook' if that is non-nil." (defvar gnus-extract-address-components) (autoload 'format-spec "format-spec") +(autoload 'gnus-date-get-time "gnus-util") -(defun message-insert-formatted-citation-line (&optional from date) +(defun message-insert-formatted-citation-line (&optional from date tz) "Function that inserts a formatted citation line. +The optional FROM, and DATE are strings containing the contents of +the From header and the Date header respectively. The optional TZ +is a number of seconds, overrides the time zone of DATE. See `message-citation-line-format'." ;; The optional args are for testing/debugging. They will disappear later. @@ -3959,7 +4024,7 @@ See `message-citation-line-format'." ;; (with-temp-buffer ;; (message-insert-formatted-citation-line ;; "John Doe " - ;; (current-time)) + ;; (message-make-date)) ;; (buffer-string)) (when (or message-reply-headers (and from date)) (unless from @@ -3976,28 +4041,46 @@ See `message-citation-line-format'." (net (car (cdr data))) (name-or-net (or (car data) (car (cdr data)) from)) - (replydate - (or - date - ;; We need Gnus functionality if the user wants date or time from - ;; the original article: - (when (string-match "%[^fnNFL]" message-citation-line-format) - (autoload 'gnus-date-get-time "gnus-util") - (gnus-date-get-time (mail-header-date message-reply-headers))))) + (time + (when (string-match "%[^fnNFL]" message-citation-line-format) + (cond ((numberp (car-safe date)) date) ;; backward compatibility + (date (gnus-date-get-time date)) + (t + (gnus-date-get-time + (setq date (mail-header-date message-reply-headers))))))) + (tz (or tz + (when (stringp date) + (nth 8 (parse-time-string date))))) (flist (let ((i ?A) lst) (when (stringp name) ;; Guess first name and last name: - (let* ((names (delq nil (mapcar (lambda (x) - (if (string-match "\\`\\(\\w\\|[-.]\\)+\\'" x) x nil)) - (split-string name "[ \t]+")))) - (count (length names))) - (cond ((= count 1) (setq fname (car names) - lname "")) - ((or (= count 2) (= count 3)) (setq fname (car names) - lname (mapconcat 'identity (cdr names) " "))) - ((> count 3) (setq fname (mapconcat 'identity (butlast names (- count 2)) " ") - lname (mapconcat 'identity (nthcdr 2 names) " "))) ))) + (let* ((names (delq + nil + (mapcar + (lambda (x) + (if (string-match "\\`\\(\\w\\|[-.]\\)+\\'" + x) + x + nil)) + (split-string name "[ \t]+")))) + (count (length names))) + (cond ((= count 1) + (setq fname (car names) + lname "")) + ((or (= count 2) (= count 3)) + (setq fname (car names) + lname (mapconcat 'identity (cdr names) " "))) + ((> count 3) + (setq fname (mapconcat 'identity + (butlast names (- count 2)) + " ") + lname (mapconcat 'identity + (nthcdr 2 names) + " ")))) + (when (string-match "\\(.*\\),\\'" fname) + (let ((newlname (match-string 1 fname))) + (setq fname lname lname newlname))))) ;; The following letters are not used in `format-time-string': (push ?E lst) (push "" lst) (push ?F lst) (push fname lst) @@ -4024,7 +4107,7 @@ See `message-citation-line-format'." (>= i ?a))) (push i lst) (push (condition-case nil - (format-time-string (format "%%%c" i) replydate) + (gmm-format-time-string (format "%%%c" i) time tz) (error (format ">%c<" i))) lst)) (setq i (1+ i))) @@ -4138,11 +4221,12 @@ Instead, just auto-save the buffer and then bury it." (defun message-bury (buffer) "Bury this mail BUFFER." + ;; Note that this is not quite the same as (bury-buffer buffer), + ;; since bury-buffer does extra stuff with a nil argument. + ;; Eg http://lists.gnu.org/archive/html/emacs-devel/2014-01/msg00539.html + (with-current-buffer buffer (bury-buffer)) (if message-return-action - (progn - (bury-buffer buffer) - (apply (car message-return-action) (cdr message-return-action))) - (with-current-buffer buffer (bury-buffer)))) + (apply (car message-return-action) (cdr message-return-action)))) (defun message-send (&optional arg) "Send the message in the current buffer. @@ -4780,7 +4864,9 @@ that instead." (list resend-to-addresses) '("-t")))))) (unless (or (null cpr) (and (numberp cpr) (zerop cpr))) - (if errbuf (pop-to-buffer errbuf)) + (when errbuf + (pop-to-buffer errbuf) + (setq errbuf nil)) (error "Sending...failed with exit value %d" cpr))) (when message-interactive (with-current-buffer errbuf @@ -5550,7 +5636,7 @@ If NOW, use that time instead." "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))) + (let* ((cur (decode-time)) (nday (+ days (nth 3 cur)))) (setf (nth 3 cur) nday) (message-make-date (apply 'encode-time cur)))) @@ -6312,6 +6398,9 @@ they are." :link '(custom-manual "(message)Movement") :type 'boolean) +(defvar visual-line-mode) +(declare-function beginning-of-visual-line "simple" (&optional n)) + (defun message-beginning-of-line (&optional n) "Move point to beginning of header value or to beginning of line. The prefix argument N is passed directly to `beginning-of-line'. @@ -6338,7 +6427,9 @@ between beginning of field and beginning of line." (goto-char (if (and eoh (or (< eoh here) (= bol here))) eoh bol))) - (beginning-of-line n))) + (if (and (boundp 'visual-line-mode) visual-line-mode) + (beginning-of-visual-line n) + (beginning-of-line n)))) (defun message-buffer-name (type &optional to group) "Return a new (unique) buffer name based on TYPE and TO." @@ -7186,7 +7277,7 @@ If ARG, allow editing of the cancellation message." (erase-buffer) (insert "Newsgroups: " newsgroups "\n" "From: " from "\n" - "Subject: cmsg cancel " message-id "\n" + "Subject: cancel " message-id "\n" "Control: cancel " message-id "\n" (if distribution (concat "Distribution: " distribution "\n") @@ -7246,7 +7337,7 @@ header line with the old Message-ID." (let ((buffer-read-only nil)) (erase-buffer) (insert-file-contents file-name nil))) - (t (error "message-recover cancelled"))))) + (t (error "message-recover canceled"))))) ;;; Washing Subject: @@ -7405,17 +7496,25 @@ Optional DIGEST will use digest to forward." (message-remove-ignored-headers b e))) (defun message-remove-ignored-headers (b e) - (when message-forward-ignored-headers + (when (or message-forward-ignored-headers + message-forward-included-headers) (save-restriction (narrow-to-region b e) (goto-char b) (narrow-to-region (point) (or (search-forward "\n\n" nil t) (point))) - (let ((ignored (if (stringp message-forward-ignored-headers) - (list message-forward-ignored-headers) - message-forward-ignored-headers))) - (dolist (elem ignored) - (message-remove-header elem t)))))) + (when message-forward-ignored-headers + (let ((ignored (if (stringp message-forward-ignored-headers) + (list message-forward-ignored-headers) + message-forward-ignored-headers))) + (dolist (elem ignored) + (message-remove-header elem t)))) + (when message-forward-included-headers + (message-remove-header + (if (listp message-forward-included-headers) + (regexp-opt message-forward-included-headers) + message-forward-included-headers) + t nil t))))) (defun message-forward-make-body-mime (forward-buffer &optional beg end) (let ((b (point))) @@ -7463,8 +7562,7 @@ Optional DIGEST will use digest to forward." (goto-char (point-max)))) (setq e (point)) (insert "<#/mml>\n") - (when (and (not message-forward-decoded-p) - message-forward-ignored-headers) + (when (not message-forward-decoded-p) (message-remove-ignored-headers b e)))) (defun message-forward-make-body-digest-plain (forward-buffer) @@ -7959,8 +8057,9 @@ If nil, the function bound in `text-mode-map' or `global-map' is executed." (defun message-tab () "Complete names according to `message-completion-alist'. -Execute function specified by `message-tab-body-function' when not in -those headers." +Execute function specified by `message-tab-body-function' when +not in those headers. If that variable is nil, indent with the +regular text mode tabbing command." (interactive) (cond ((if (and (boundp 'completion-fail-discreetly) @@ -7988,17 +8087,6 @@ those headers." ;; falling back to message-tab-body-function. (lambda () (funcall fun) 'completion-attempted))))) -(eval-and-compile - (condition-case nil - (with-temp-buffer - (let ((standard-output (current-buffer))) - (eval '(display-completion-list nil ""))) - (defalias 'message-display-completion-list 'display-completion-list)) - (error ;; Don't use `wrong-number-of-arguments' here because of XEmacs. - (defun message-display-completion-list (completions &optional ignore) - "Display the list of completions, COMPLETIONS, using `standard-output'." - (display-completion-list completions))))) - (defun message-expand-group () "Expand the group name under point." (let ((b (save-excursion @@ -8023,12 +8111,12 @@ those headers." group) collection)) gnus-active-hashtb)) - (message-completion-in-region e b collection))) + (message-completion-in-region b e collection))) (defalias 'message-completion-in-region (if (fboundp 'completion-in-region) 'completion-in-region - (lambda (e b hashtb) + (lambda (b e hashtb) (let* ((string (buffer-substring b e)) (completions (all-completions string hashtb)) comp) @@ -8053,8 +8141,7 @@ those headers." (let ((buffer-read-only nil)) (erase-buffer) (let ((standard-output (current-buffer))) - (message-display-completion-list (sort completions 'string<) - string)) + (display-completion-list (sort completions 'string<))) (setq buffer-read-only nil) (goto-char (point-min)) (delete-region (point) @@ -8464,6 +8551,17 @@ Used in `message-simplify-recipients'." (message-fetch-field hdr) t)) ", ")))) +;;; multipart/related and HTML support. + +(defun message-make-html-message-with-image-files (files) + (interactive (list (dired-get-marked-files nil current-prefix-arg))) + (message-mail) + (message-goto-body) + (insert "<#part type=text/html>\n\n") + (dolist (file files) + (insert (format "\n\n" file))) + (message-goto-to)) + (when (featurep 'xemacs) (require 'messagexmas) (message-xmas-redefine))