~ubuntu-branches/ubuntu/karmic/emacs-snapshot/karmic

« back to all changes in this revision

Viewing changes to lisp/mail/pmail.el

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2009-04-05 09:14:30 UTC
  • mto: This revision was merged to the branch mainline in revision 34.
  • Revision ID: james.westby@ubuntu.com-20090405091430-nw07lynn2arotjbe
Tags: upstream-20090320
ImportĀ upstreamĀ versionĀ 20090320

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
;;; pmail.el --- main code of "PMAIL" mail reader for Emacs
2
 
 
3
 
;; Copyright (C) 1985, 1986, 1987, 1988, 1993, 1994, 1995, 1996, 1997, 1998,
4
 
;;   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
5
 
;;   Free Software Foundation, Inc.
6
 
 
7
 
;; Maintainer: FSF
8
 
;; Keywords: mail
9
 
 
10
 
;; This file is part of GNU Emacs.
11
 
 
12
 
;; GNU Emacs is free software: you can redistribute it and/or modify
13
 
;; it under the terms of the GNU General Public License as published by
14
 
;; the Free Software Foundation, either version 3 of the License, or
15
 
;; (at your option) any later version.
16
 
 
17
 
;; GNU Emacs is distributed in the hope that it will be useful,
18
 
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 
;; GNU General Public License for more details.
21
 
 
22
 
;; You should have received a copy of the GNU General Public License
23
 
;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
24
 
 
25
 
;;; Commentary:
26
 
 
27
 
;;; Code:
28
 
 
29
 
;; Souped up by shane@mit-ajax based on ideas of rlk@athena.mit.edu
30
 
;;   New features include attribute and keyword support, message
31
 
;;   selection by dispatch table, summary by attributes and keywords,
32
 
;;   expunging by dispatch table, sticky options for file commands.
33
 
 
34
 
;; Extended by Bob Weiner of Motorola
35
 
;;   New features include: pmail and pmail-summary buffers remain
36
 
;;   synchronized and key bindings basically operate the same way in both
37
 
;;   buffers, summary by topic or by regular expression, pmail-reply-prefix
38
 
;;   variable, and a bury pmail buffer (wipe) command.
39
 
;;
40
 
 
41
 
(require 'mail-utils)
42
 
(eval-when-compile (require 'mule-util)) ; for detect-coding-with-priority
43
 
 
44
 
(defconst pmail-attribute-header "X-RMAIL-ATTRIBUTES"
45
 
  "The header that stores the Pmail attribute data.")
46
 
 
47
 
(defconst pmail-keyword-header "X-RMAIL-KEYWORDS"
48
 
  "The header that stores the Pmail keyword data.")
49
 
 
50
 
;;; Attribute indexes
51
 
 
52
 
(defconst pmail-answered-attr-index 0
53
 
  "The index for the `answered' attribute.")
54
 
 
55
 
(defconst pmail-deleted-attr-index 1
56
 
  "The index for the `deleted' attribute.")
57
 
 
58
 
(defconst pmail-edited-attr-index 2
59
 
  "The index for the `edited' attribute.")
60
 
 
61
 
(defconst pmail-filed-attr-index 3
62
 
  "The index for the `filed' attribute.")
63
 
 
64
 
(defconst pmail-resent-attr-index 4
65
 
  "The index for the `resent' attribute.")
66
 
 
67
 
(defconst pmail-stored-attr-index 5
68
 
  "The index for the `stored' attribute.")
69
 
 
70
 
(defconst pmail-unseen-attr-index 6
71
 
  "The index for the `unseen' attribute.")
72
 
 
73
 
(defconst pmail-attr-array
74
 
  '[(?A "answered")
75
 
    (?D "deleted")
76
 
    (?E "edited")
77
 
    (?F "filed")
78
 
    (?R "replied")
79
 
    (?S "stored")
80
 
    (?U "unseen")]
81
 
  "An array that provides a mapping between an attribute index,
82
 
it's character representation and it's display representation.")
83
 
 
84
 
(defvar deleted-head)
85
 
(defvar font-lock-fontified)
86
 
(defvar mail-abbrev-syntax-table)
87
 
(defvar mail-abbrevs)
88
 
(defvar messages-head)
89
 
(defvar pmail-use-spam-filter)
90
 
(defvar rsf-beep)
91
 
(defvar rsf-sleep-after-message)
92
 
(defvar total-messages)
93
 
(defvar tool-bar-map)
94
 
 
95
 
(defvar pmail-buffers-swapped-p nil
96
 
  "A flag that is non-nil when the message view buffer and the
97
 
 message collection buffer are swapped, i.e. the Pmail buffer
98
 
 contains a single decoded message.")
99
 
 
100
 
(defvar pmail-header-style 'normal
101
 
  "The current header display style choice, one of
102
 
'normal (selected headers) or 'full (all headers).")
103
 
 
104
 
; These variables now declared in paths.el.
105
 
;(defvar pmail-spool-directory "/usr/spool/mail/"
106
 
;  "This is the name of the directory used by the system mailer for\n\
107
 
;delivering new mail.  Its name should end with a slash.")
108
 
;(defvar pmail-file-name
109
 
;  (expand-file-name "~/PMAIL")
110
 
;  "")
111
 
 
112
 
;; Temporary support for mbox.
113
 
(defcustom pmail-file-name "~/PMAIL"
114
 
  "*Name of user's primary mail file."
115
 
  :type 'string
116
 
  :group 'rmail
117
 
  :version "21.1")
118
 
 
119
 
(defgroup pmail nil
120
 
  "Mail reader for Emacs."
121
 
  :group 'mail)
122
 
 
123
 
(defgroup pmail-retrieve nil
124
 
  "Pmail retrieval options."
125
 
  :prefix "pmail-"
126
 
  :group 'pmail)
127
 
 
128
 
(defgroup pmail-files nil
129
 
  "Pmail files."
130
 
  :prefix "pmail-"
131
 
  :group 'pmail)
132
 
 
133
 
(defgroup pmail-headers nil
134
 
  "Pmail header options."
135
 
  :prefix "pmail-"
136
 
  :group 'pmail)
137
 
 
138
 
(defgroup pmail-reply nil
139
 
  "Pmail reply options."
140
 
  :prefix "pmail-"
141
 
  :group 'pmail)
142
 
 
143
 
(defgroup pmail-summary nil
144
 
  "Pmail summary options."
145
 
  :prefix "pmail-"
146
 
  :prefix "pmail-summary-"
147
 
  :group 'pmail)
148
 
 
149
 
(defgroup pmail-output nil
150
 
  "Output message to a file."
151
 
  :prefix "pmail-output-"
152
 
  :prefix "pmail-"
153
 
  :group 'pmail)
154
 
 
155
 
(defgroup pmail-edit nil
156
 
  "Pmail editing."
157
 
  :prefix "pmail-edit-"
158
 
  :group 'pmail)
159
 
 
160
 
(defgroup pmail-obsolete nil
161
 
  "Pmail obsolete customization variables."
162
 
  :group 'pmail)
163
 
 
164
 
(defcustom pmail-movemail-program nil
165
 
  "If non-nil, the file name of the `movemail' program."
166
 
  :group 'pmail-retrieve
167
 
  :type '(choice (const nil) string))
168
 
 
169
 
(defcustom pmail-pop-password nil
170
 
  "*Password to use when reading mail from POP server.
171
 
Please use `pmail-remote-password' instead."
172
 
  :type '(choice (string :tag "Password")
173
 
                 (const :tag "Not Required" nil))
174
 
  :group 'pmail-obsolete)
175
 
 
176
 
(defcustom pmail-pop-password-required nil
177
 
  "*Non-nil if a password is required when reading mail from a POP server.
178
 
Please use pmail-remote-password-required instead."
179
 
  :type 'boolean
180
 
  :group 'pmail-obsolete)
181
 
 
182
 
(defcustom pmail-remote-password nil
183
 
  "*Password to use when reading mail from a remote server.
184
 
This setting is ignored for mailboxes whose URL already contains a password."
185
 
  :type '(choice (string :tag "Password")
186
 
                 (const :tag "Not Required" nil))
187
 
  :set-after '(pmail-pop-password)
188
 
  :set #'(lambda (symbol value)
189
 
           (set-default symbol
190
 
                        (if (and (not value)
191
 
                                 (boundp 'pmail-pop-password)
192
 
                                 pmail-pop-password)
193
 
                            pmail-pop-password
194
 
                          value))
195
 
           (setq pmail-pop-password nil))
196
 
  :group 'pmail-retrieve
197
 
  :version "22.1")
198
 
 
199
 
(defcustom pmail-remote-password-required nil
200
 
  "*Non-nil if a password is required when reading mail from a remote server."
201
 
  :type 'boolean
202
 
  :set-after '(pmail-pop-password-required)
203
 
  :set #'(lambda (symbol value)
204
 
           (set-default symbol
205
 
                        (if (and (not value)
206
 
                                 (boundp 'pmail-pop-password-required)
207
 
                                 pmail-pop-password-required)
208
 
                            pmail-pop-password-required
209
 
                          value))
210
 
           (setq pmail-pop-password-required nil))
211
 
  :group 'pmail-retrieve
212
 
  :version "22.1")
213
 
 
214
 
(defcustom pmail-movemail-flags nil
215
 
  "*List of flags to pass to movemail.
216
 
Most commonly used to specify `-g' to enable GSS-API authentication
217
 
or `-k' to enable Kerberos authentication."
218
 
  :type '(repeat string)
219
 
  :group 'pmail-retrieve
220
 
  :version "20.3")
221
 
 
222
 
(defvar pmail-remote-password-error "invalid usercode or password\\|
223
 
unknown user name or bad password\\|Authentication failed\\|MU_ERR_AUTH_FAILURE"
224
 
  "Regular expression matching incorrect-password POP or IMAP server error
225
 
messages.
226
 
If you get an incorrect-password error that this expression does not match,
227
 
please report it with \\[report-emacs-bug].")
228
 
 
229
 
(defvar pmail-encoded-remote-password nil)
230
 
 
231
 
(defcustom pmail-preserve-inbox nil
232
 
  "*Non-nil means leave incoming mail in the user's inbox--don't delete it."
233
 
  :type 'boolean
234
 
  :group 'pmail-retrieve)
235
 
 
236
 
(defcustom pmail-movemail-search-path nil
237
 
    "*List of directories to search for movemail (in addition to `exec-path')."
238
 
    :group 'pmail-retrieve
239
 
    :type '(repeat (directory)))
240
 
 
241
 
(declare-function mail-position-on-field "sendmail" (field &optional soft))
242
 
(declare-function mail-text-start "sendmail" ())
243
 
(declare-function pmail-dont-reply-to "mail-utils" (destinations))
244
 
(declare-function pmail-update-summary "pmailsum" (&rest ignore))
245
 
 
246
 
(defun pmail-probe (prog)
247
 
  "Determine what flavor of movemail PROG is.
248
 
We do this by executing it with `--version' and analyzing its output."
249
 
  (with-temp-buffer
250
 
    (let ((tbuf (current-buffer)))
251
 
      (buffer-disable-undo tbuf)
252
 
      (call-process prog nil tbuf nil "--version")
253
 
      (if (not (buffer-modified-p tbuf))
254
 
          ;; Should not happen...
255
 
          nil
256
 
        (goto-char (point-min))
257
 
        (cond
258
 
         ((looking-at ".*movemail: invalid option")
259
 
          'emacs)    ;; Possibly...
260
 
         ((looking-at "movemail (GNU Mailutils .*)")
261
 
          'mailutils)
262
 
         (t
263
 
          ;; FIXME:
264
 
          'emacs))))))
265
 
 
266
 
(defun pmail-autodetect ()
267
 
  "Determine the file name of the `movemail' program and return its flavor.
268
 
If `pmail-movemail-program' is non-nil, use it.
269
 
Otherwise, look for `movemail' in the directories in
270
 
`pmail-movemail-search-path', those in `exec-path', and `exec-directory'."
271
 
  (if pmail-movemail-program
272
 
      (pmail-probe pmail-movemail-program)
273
 
    (catch 'scan
274
 
      (dolist (dir (append pmail-movemail-search-path exec-path
275
 
                           (list exec-directory)))
276
 
        (when (and dir (file-accessible-directory-p dir))
277
 
          ;; Previously, this didn't have to work on Windows, because
278
 
          ;; pmail-insert-inbox-text before r1.439 fell back to using
279
 
          ;; (expand-file-name "movemail" exec-directory) and just
280
 
          ;; assuming it would work.
281
 
          ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2008-02/msg00087.html
282
 
          (let ((progname (expand-file-name
283
 
                           (concat "movemail"
284
 
                                   (if (memq system-type '(ms-dos windows-nt))
285
 
                                       ".exe")) dir)))
286
 
            (when (and (not (file-directory-p progname))
287
 
                       (file-executable-p progname))
288
 
              (let ((x (pmail-probe progname)))
289
 
                (when x
290
 
                  (setq pmail-movemail-program progname)
291
 
                  (throw 'scan x))))))))))
292
 
 
293
 
(defvar pmail-movemail-variant-in-use nil
294
 
  "The movemail variant currently in use. Known variants are:
295
 
 
296
 
  `emacs'     Means any implementation, compatible with the native Emacs one.
297
 
              This is the default;
298
 
  `mailutils' Means GNU mailutils implementation, capable of handling full
299
 
mail URLs as the source mailbox.")
300
 
 
301
 
;;;###autoload
302
 
(defun pmail-movemail-variant-p (&rest variants)
303
 
  "Return t if the current movemail variant is any of VARIANTS.
304
 
Currently known variants are 'emacs and 'mailutils."
305
 
  (when (not pmail-movemail-variant-in-use)
306
 
    ;; Autodetect
307
 
    (setq pmail-movemail-variant-in-use (pmail-autodetect)))
308
 
  (not (null (member pmail-movemail-variant-in-use variants))))
309
 
 
310
 
;; Call for effect, to set pmail-movemail-program (if not set by the
311
 
;; user), and pmail-movemail-variant-in-use. Used by various functions.
312
 
;; I'm not sure if M-x pmail is the only entry point to this package.
313
 
;; If so, this can be moved there.
314
 
(pmail-movemail-variant-p)
315
 
 
316
 
;;;###autoload
317
 
(defcustom pmail-dont-reply-to-names nil "\
318
 
*A regexp specifying addresses to prune from a reply message.
319
 
A value of nil means exclude your own email address as an address
320
 
plus whatever is specified by `pmail-default-dont-reply-to-names'."
321
 
  :type '(choice regexp (const :tag "Your Name" nil))
322
 
  :group 'pmail-reply)
323
 
 
324
 
;;;###autoload
325
 
(defvar pmail-default-dont-reply-to-names "\\`info-" "\
326
 
A regular expression specifying part of the default value of the
327
 
variable `pmail-dont-reply-to-names', for when the user does not set
328
 
`pmail-dont-reply-to-names' explicitly.  (The other part of the default
329
 
value is the user's email address and name.)
330
 
It is useful to set this variable in the site customization file.")
331
 
 
332
 
;;;###autoload
333
 
(defcustom pmail-ignored-headers
334
 
  (concat "^via:\\|^mail-from:\\|^origin:\\|^references:\\|^sender:"
335
 
          "\\|^status:\\|^received:\\|^x400-originator:\\|^x400-recipients:"
336
 
          "\\|^x400-received:\\|^x400-mts-identifier:\\|^x400-content-type:"
337
 
          "\\|^\\(resent-\\|\\)message-id:\\|^summary-line:\\|^resent-date:"
338
 
          "\\|^nntp-posting-host:\\|^path:\\|^x-char.*:\\|^x-face:\\|^face:"
339
 
          "\\|^x-mailer:\\|^delivered-to:\\|^lines:"
340
 
          "\\|^content-transfer-encoding:\\|^x-coding-system:"
341
 
          "\\|^return-path:\\|^errors-to:\\|^return-receipt-to:"
342
 
          "\\|^precedence:\\|^list-help:\\|^list-post:\\|^list-subscribe:"
343
 
          "\\|^list-id:\\|^list-unsubscribe:\\|^list-archive:"
344
 
          "\\|^content-length:\\|^nntp-posting-date:\\|^user-agent"
345
 
          "\\|^importance:\\|^envelope-to:\\|^delivery-date\\|^openpgp:"
346
 
          "\\|^mbox-line:\\|^cancel-lock:\\|^DomainKey-Signature:"
347
 
          "\\|^resent-face:\\|^resent-x.*:\\|^resent-organization:\\|^resent-openpgp:"
348
 
          "\\|^x-.*:")
349
 
  "*Regexp to match header fields that Pmail should normally hide.
350
 
\(See also `pmail-nonignored-headers', which overrides this regexp.)
351
 
This variable is used for reformatting the message header,
352
 
which normally happens once for each message,
353
 
when you view the message for the first time in Pmail.
354
 
To make a change in this variable take effect
355
 
for a message that you have already viewed,
356
 
go to that message and type \\[pmail-toggle-header] twice."
357
 
  :type 'regexp
358
 
  :group 'pmail-headers)
359
 
 
360
 
(defcustom pmail-nonignored-headers "^x-spam-status:"
361
 
  "*Regexp to match X header fields that Pmail should show.
362
 
This regexp overrides `pmail-ignored-headers'; if both this regexp
363
 
and that one match a certain header field, Pmail shows the field.
364
 
If this is nil, ignore all header fields in `pmail-ignored-headers'.
365
 
 
366
 
This variable is used for reformatting the message header,
367
 
which normally happens once for each message,
368
 
when you view the message for the first time in Pmail.
369
 
To make a change in this variable take effect
370
 
for a message that you have already viewed,
371
 
go to that message and type \\[pmail-toggle-header] twice."
372
 
  :type '(choice (const nil) (regexp))
373
 
  :group 'pmail-headers)
374
 
 
375
 
;;;###autoload
376
 
(defcustom pmail-displayed-headers nil
377
 
  "*Regexp to match Header fields that Pmail should display.
378
 
If nil, display all header fields except those matched by
379
 
`pmail-ignored-headers'."
380
 
  :type '(choice regexp (const :tag "All"))
381
 
  :group 'pmail-headers)
382
 
 
383
 
;;;###autoload
384
 
(defcustom pmail-retry-ignored-headers "^x-authentication-warning:" "\
385
 
*Headers that should be stripped when retrying a failed message."
386
 
  :type '(choice regexp (const nil :tag "None"))
387
 
  :group 'pmail-headers)
388
 
 
389
 
;;;###autoload
390
 
(defcustom pmail-highlighted-headers "^From:\\|^Subject:" "\
391
 
*Regexp to match Header fields that Pmail should normally highlight.
392
 
A value of nil means don't highlight."
393
 
  :type 'regexp
394
 
  :group 'pmail-headers)
395
 
 
396
 
(defface pmail-highlight
397
 
  '((t (:inherit highlight)))
398
 
  "Face to use for highlighting the most important header fields."
399
 
  :group 'pmail-headers
400
 
  :version "22.1")
401
 
 
402
 
(defface pmail-header-name
403
 
  '((t (:inherit font-lock-function-name-face)))
404
 
  "Face to use for highlighting the header names."
405
 
  :group 'pmail-headers
406
 
  :version "23.1")
407
 
 
408
 
;;;###autoload
409
 
(defcustom pmail-delete-after-output nil "\
410
 
*Non-nil means automatically delete a message that is copied to a file."
411
 
  :type 'boolean
412
 
  :group 'pmail-files)
413
 
 
414
 
;;;###autoload
415
 
(defcustom pmail-primary-inbox-list nil "\
416
 
*List of files which are inboxes for user's primary mail file `~/PMAIL'.
417
 
nil means the default, which is (\"/usr/spool/mail/$USER\")
418
 
\(the name varies depending on the operating system,
419
 
and the value of the environment variable MAIL overrides it)."
420
 
  ;; Don't use backquote here, because we don't want to need it
421
 
  ;; at load time.
422
 
  :type (list 'choice '(const :tag "Default" nil)
423
 
              (list 'repeat ':value (list (or (getenv "MAIL")
424
 
                                              (concat "/var/spool/mail/"
425
 
                                                      (getenv "USER"))))
426
 
                    'file))
427
 
  :group 'pmail-retrieve
428
 
  :group 'pmail-files)
429
 
 
430
 
;;;###autoload
431
 
(defcustom pmail-mail-new-frame nil
432
 
  "*Non-nil means Pmail makes a new frame for composing outgoing mail.
433
 
This is handy if you want to preserve the window configuration of
434
 
the frame where you have the PMAIL buffer displayed."
435
 
  :type 'boolean
436
 
  :group 'pmail-reply)
437
 
 
438
 
;;;###autoload
439
 
(defcustom pmail-secondary-file-directory "~/"
440
 
  "*Directory for additional secondary Pmail files."
441
 
  :type 'directory
442
 
  :group 'pmail-files)
443
 
;;;###autoload
444
 
(defcustom pmail-secondary-file-regexp "\\.xmail$"
445
 
  "*Regexp for which files are secondary Pmail files."
446
 
  :type 'regexp
447
 
  :group 'pmail-files)
448
 
 
449
 
;;;###autoload
450
 
(defcustom pmail-confirm-expunge 'y-or-n-p
451
 
  "*Whether and how to ask for confirmation before expunging deleted messages."
452
 
  :type '(choice (const :tag "No confirmation" nil)
453
 
                 (const :tag "Confirm with y-or-n-p" y-or-n-p)
454
 
                 (const :tag "Confirm with yes-or-no-p" yes-or-no-p))
455
 
  :version "21.1"
456
 
  :group 'pmail-files)
457
 
 
458
 
;;;###autoload
459
 
(defvar pmail-mode-hook nil
460
 
  "List of functions to call when Pmail is invoked.")
461
 
 
462
 
;;;###autoload
463
 
(defvar pmail-get-new-mail-hook nil
464
 
  "List of functions to call when Pmail has retrieved new mail.")
465
 
 
466
 
;;;###autoload
467
 
(defcustom pmail-show-message-hook nil
468
 
  "List of functions to call when Pmail displays a message."
469
 
  :type 'hook
470
 
  :options '(goto-address)
471
 
  :group 'pmail)
472
 
 
473
 
;;;###autoload
474
 
(defvar pmail-quit-hook nil
475
 
  "List of functions to call when quitting out of Pmail.")
476
 
 
477
 
;;;###autoload
478
 
(defvar pmail-delete-message-hook nil
479
 
  "List of functions to call when Pmail deletes a message.
480
 
When the hooks are called, the message has been marked deleted but is
481
 
still the current message in the Pmail buffer.")
482
 
 
483
 
;; These may be altered by site-init.el to match the format of mmdf files
484
 
;;  delimiting used on a given host (delim1 and delim2 from the config
485
 
;;  files).
486
 
 
487
 
(defvar pmail-mmdf-delim1 "^\001\001\001\001\n"
488
 
  "Regexp marking the start of an mmdf message.")
489
 
(defvar pmail-mmdf-delim2 "^\001\001\001\001\n"
490
 
  "Regexp marking the end of an mmdf message.")
491
 
 
492
 
(defcustom pmail-message-filter nil
493
 
  "If non-nil, a filter function for new messages in PMAIL.
494
 
Called with region narrowed to the message, including headers,
495
 
before obeying `pmail-ignored-headers'."
496
 
  :group 'pmail-headers
497
 
  :type '(choice (const nil) function))
498
 
 
499
 
(defcustom pmail-automatic-folder-directives nil
500
 
  "List of directives specifying where to put a message.
501
 
Each element of the list is of the form:
502
 
 
503
 
  (FOLDERNAME FIELD REGEXP [ FIELD REGEXP ] ... )
504
 
 
505
 
Where FOLDERNAME is the name of a BABYL format folder to put the
506
 
message.  If any of the field regexp's are nil, then it is ignored.
507
 
 
508
 
If FOLDERNAME is \"/dev/null\", it is deleted.
509
 
If FOLDERNAME is nil then it is deleted, and skipped.
510
 
 
511
 
FIELD is the plain text name of a field in the message, such as
512
 
\"subject\" or \"from\".  A FIELD of \"to\" will automatically include
513
 
all text from the \"cc\" field as well.
514
 
 
515
 
REGEXP is an expression to match in the preceeding specified FIELD.
516
 
FIELD/REGEXP pairs continue in the list.
517
 
 
518
 
examples:
519
 
  (\"/dev/null\" \"from\" \"@spam.com\") ; delete all mail from spam.com
520
 
  (\"RMS\" \"from\" \"rms@\") ; save all mail from RMS."
521
 
  :group 'pmail
522
 
  :version "21.1"
523
 
  :type '(repeat (sexp :tag "Directive")))
524
 
 
525
 
(defvar pmail-reply-prefix "Re: "
526
 
  "String to prepend to Subject line when replying to a message.")
527
 
 
528
 
;; Some mailers use "Re(2):" or "Re^2:" or "Re: Re:" or "Re[2]:".
529
 
;; This pattern should catch all the common variants.
530
 
;; rms: I deleted the change to delete tags in square brackets
531
 
;; because they mess up RT tags.
532
 
(defvar pmail-reply-regexp "\\`\\(Re\\(([0-9]+)\\|\\[[0-9]+\\]\\|\\^[0-9]+\\)?: *\\)*"
533
 
  "Regexp to delete from Subject line before inserting `pmail-reply-prefix'.")
534
 
 
535
 
(defcustom pmail-display-summary nil
536
 
  "*If non-nil, Pmail always displays the summary buffer."
537
 
  :group 'pmail-summary
538
 
  :type 'boolean)
539
 
 
540
 
(defvar pmail-inbox-list nil)
541
 
(put 'pmail-inbox-list 'permanent-local t)
542
 
 
543
 
(defvar pmail-keywords nil)
544
 
(put 'pmail-keywords 'permanent-local t)
545
 
 
546
 
(defvar pmail-buffer nil
547
 
  "The PMAIL buffer related to the current buffer.
548
 
In an PMAIL buffer, this holds the PMAIL buffer itself.
549
 
In a summary buffer, this holds the PMAIL buffer it is a summary for.")
550
 
(put 'pmail-buffer 'permanent-local t)
551
 
 
552
 
;; Message counters and markers.  Deleted flags.
553
 
 
554
 
(defvar pmail-current-message nil)
555
 
(put 'pmail-current-message 'permanent-local t)
556
 
 
557
 
(defvar pmail-total-messages nil)
558
 
(put 'pmail-total-messages 'permanent-local t)
559
 
 
560
 
(defvar pmail-message-vector nil)
561
 
(put 'pmail-message-vector 'permanent-local t)
562
 
 
563
 
(defvar pmail-deleted-vector nil)
564
 
(put 'pmail-deleted-vector 'permanent-local t)
565
 
 
566
 
(defvar pmail-msgref-vector nil
567
 
  "In an Pmail buffer, a vector whose Nth element is a list (N).
568
 
When expunging renumbers messages, these lists are modified
569
 
by substituting the new message number into the existing list.")
570
 
(put 'pmail-msgref-vector 'permanent-local t)
571
 
 
572
 
(defvar pmail-overlay-list nil)
573
 
(put 'pmail-overlay-list 'permanent-local t)
574
 
 
575
 
;; These are used by autoloaded pmail-summary.
576
 
 
577
 
(defvar pmail-summary-buffer nil)
578
 
(put 'pmail-summary-buffer 'permanent-local t)
579
 
(defvar pmail-summary-vector nil)
580
 
(put 'pmail-summary-vector 'permanent-local t)
581
 
 
582
 
(defvar pmail-view-buffer nil
583
 
  "Buffer which holds PMAIL message for MIME displaying.")
584
 
(put 'pmail-view-buffer 'permanent-local t)
585
 
 
586
 
;; `Sticky' default variables.
587
 
 
588
 
;; Last individual label specified to a or k.
589
 
(defvar pmail-last-label nil)
590
 
(put 'pmail-last-label 'permanent-local t)
591
 
 
592
 
;; Last set of values specified to C-M-n, C-M-p, C-M-s or C-M-l.
593
 
(defvar pmail-last-multi-labels nil)
594
 
 
595
 
(defvar pmail-last-regexp nil)
596
 
(put 'pmail-last-regexp 'permanent-local t)
597
 
 
598
 
(defcustom pmail-default-file "~/xmail"
599
 
  "*Default file name for \\[pmail-output]."
600
 
  :type 'file
601
 
  :group 'pmail-files)
602
 
(defcustom pmail-default-pmail-file "~/XMAIL"
603
 
  "*Default file name for \\[pmail-output-to-pmail-file]."
604
 
  :type 'file
605
 
  :group 'pmail-files)
606
 
(defcustom pmail-default-body-file "~/mailout"
607
 
  "*Default file name for \\[pmail-output-body-to-file]."
608
 
  :type 'file
609
 
  :group 'pmail-files
610
 
  :version "20.3")
611
 
 
612
 
;; Mule and MIME related variables.
613
 
 
614
 
;;;###autoload
615
 
(defvar pmail-file-coding-system nil
616
 
  "Coding system used in PMAIL file.
617
 
 
618
 
This is set to nil by default.")
619
 
 
620
 
;;;###autoload
621
 
(defcustom pmail-enable-mime nil
622
 
  "*If non-nil, PMAIL uses MIME feature.
623
 
If the value is t, PMAIL automatically shows MIME decoded message.
624
 
If the value is neither t nor nil, PMAIL does not show MIME decoded message
625
 
until a user explicitly requires it.
626
 
 
627
 
Even if the value is non-nil, you can't use MIME feature
628
 
if the feature specified by `pmail-mime-feature' is not available
629
 
in your session."
630
 
  :type '(choice (const :tag "on" t)
631
 
                 (const :tag "off" nil)
632
 
                 (other :tag "when asked" ask))
633
 
  :group 'pmail)
634
 
 
635
 
(defvar pmail-enable-mime-composing nil
636
 
  "*If non-nil, PMAIL uses `pmail-insert-mime-forwarded-message-function' to forward.")
637
 
 
638
 
;;;###autoload
639
 
(defvar pmail-show-mime-function nil
640
 
  "Function to show MIME decoded message of PMAIL file.
641
 
This function is called when `pmail-enable-mime' is non-nil.
642
 
It is called with no argument.")
643
 
 
644
 
;;;###autoload
645
 
(defvar pmail-insert-mime-forwarded-message-function nil
646
 
  "Function to insert a message in MIME format so it can be forwarded.
647
 
This function is called if `pmail-enable-mime' or
648
 
`pmail-enable-mime-composing' is non-nil.
649
 
It is called with one argument FORWARD-BUFFER, which is a
650
 
buffer containing the message to forward.  The current buffer
651
 
is the outgoing mail buffer.")
652
 
 
653
 
;;;###autoload
654
 
(defvar pmail-insert-mime-resent-message-function nil
655
 
  "Function to insert a message in MIME format so it can be resent.
656
 
This function is called if `pmail-enable-mime' is non-nil.
657
 
It is called with one argument FORWARD-BUFFER, which is a
658
 
buffer containing the message to forward.  The current buffer
659
 
is the outgoing mail buffer.")
660
 
 
661
 
;;;###autoload
662
 
(defvar pmail-search-mime-message-function nil
663
 
  "Function to check if a regexp matches a MIME message.
664
 
This function is called if `pmail-enable-mime' is non-nil.
665
 
It is called with two arguments MSG and REGEXP, where
666
 
MSG is the message number, REGEXP is the regular expression.")
667
 
 
668
 
;;;###autoload
669
 
(defvar pmail-search-mime-header-function nil
670
 
  "Function to check if a regexp matches a header of MIME message.
671
 
This function is called if `pmail-enable-mime' is non-nil.
672
 
It is called with three arguments MSG, REGEXP, and LIMIT, where
673
 
MSG is the message number,
674
 
REGEXP is the regular expression,
675
 
LIMIT is the position specifying the end of header.")
676
 
 
677
 
;;;###autoload
678
 
(defvar pmail-mime-feature 'pmail-mime
679
 
  "Feature to require to load MIME support in Pmail.
680
 
When starting Pmail, if `pmail-enable-mime' is non-nil,
681
 
this feature is required with `require'.
682
 
 
683
 
The default value is `pmail-mime'.  This feature is provided by
684
 
the pmail-mime package available at <http://www.m17n.org/pmail-mime/>.")
685
 
 
686
 
;;;###autoload
687
 
(defvar pmail-decode-mime-charset t
688
 
  "*Non-nil means a message is decoded by MIME's charset specification.
689
 
If this variable is nil, or the message has not MIME specification,
690
 
the message is decoded as normal way.
691
 
 
692
 
If the variable `pmail-enable-mime' is non-nil, this variables is
693
 
ignored, and all the decoding work is done by a feature specified by
694
 
the variable `pmail-mime-feature'.")
695
 
 
696
 
;;;###autoload
697
 
(defvar pmail-mime-charset-pattern
698
 
  (concat "^content-type:[ \t]*text/plain;"
699
 
          "\\(?:[ \t\n]*\\(?:format\\|delsp\\)=\"?[-a-z0-9]+\"?;\\)*"
700
 
          "[ \t\n]*charset=\"?\\([^ \t\n\";]+\\)\"?")
701
 
  "Regexp to match MIME-charset specification in a header of message.
702
 
The first parenthesized expression should match the MIME-charset name.")
703
 
 
704
 
 
705
 
;;; Regexp matching the delimiter of messages in UNIX mail format
706
 
;;; (UNIX From lines), minus the initial ^.  Note that if you change
707
 
;;; this expression, you must change the code in pmail-nuke-pinhead-header
708
 
;;; that knows the exact ordering of the \\( \\) subexpressions.
709
 
(defvar pmail-unix-mail-delimiter
710
 
  (let ((time-zone-regexp
711
 
         (concat "\\([A-Z]?[A-Z]?[A-Z][A-Z]\\( DST\\)?"
712
 
                 "\\|[-+]?[0-9][0-9][0-9][0-9]"
713
 
                 "\\|"
714
 
                 "\\) *")))
715
 
    (concat
716
 
     "From "
717
 
 
718
 
     ;; Many things can happen to an RFC 822 mailbox before it is put into
719
 
     ;; a `From' line.  The leading phrase can be stripped, e.g.
720
 
     ;; `Joe <@w.x:joe@y.z>' -> `<@w.x:joe@y.z>'.  The <> can be stripped, e.g.
721
 
     ;; `<@x.y:joe@y.z>' -> `@x.y:joe@y.z'.  Everything starting with a CRLF
722
 
     ;; can be removed, e.g.
723
 
     ;;         From: joe@y.z (Joe      K
724
 
     ;;                 User)
725
 
     ;; can yield `From joe@y.z (Joe    K Fri Mar 22 08:11:15 1996', and
726
 
     ;;         From: Joe User
727
 
     ;;                 <joe@y.z>
728
 
     ;; can yield `From Joe User Fri Mar 22 08:11:15 1996'.
729
 
     ;; The mailbox can be removed or be replaced by white space, e.g.
730
 
     ;;         From: "Joe User"{space}{tab}
731
 
     ;;                 <joe@y.z>
732
 
     ;; can yield `From {space}{tab} Fri Mar 22 08:11:15 1996',
733
 
     ;; where {space} and {tab} represent the Ascii space and tab characters.
734
 
     ;; We want to match the results of any of these manglings.
735
 
     ;; The following regexp rejects names whose first characters are
736
 
     ;; obviously bogus, but after that anything goes.
737
 
     "\\([^\0-\b\n-\r\^?].*\\)? "
738
 
 
739
 
     ;; The time the message was sent.
740
 
     "\\([^\0-\r \^?]+\\) +"                            ; day of the week
741
 
     "\\([^\0-\r \^?]+\\) +"                            ; month
742
 
     "\\([0-3]?[0-9]\\) +"                              ; day of month
743
 
     "\\([0-2][0-9]:[0-5][0-9]\\(:[0-6][0-9]\\)?\\) *"  ; time of day
744
 
 
745
 
     ;; Perhaps a time zone, specified by an abbreviation, or by a
746
 
     ;; numeric offset.
747
 
     time-zone-regexp
748
 
 
749
 
     ;; The year.
750
 
     " \\([0-9][0-9]+\\) *"
751
 
 
752
 
     ;; On some systems the time zone can appear after the year, too.
753
 
     time-zone-regexp
754
 
 
755
 
     ;; Old uucp cruft.
756
 
     "\\(remote from .*\\)?"
757
 
 
758
 
     "\n"))
759
 
  nil)
760
 
 
761
 
(defvar pmail-font-lock-keywords
762
 
  ;; These are all matched case-insensitively.
763
 
  (eval-when-compile
764
 
    (let* ((cite-chars "[>|}]")
765
 
           (cite-prefix "a-z")
766
 
           (cite-suffix (concat cite-prefix "0-9_.@-`'\"")))
767
 
      (list '("^\\(From\\|Sender\\|Resent-From\\):"
768
 
              . 'pmail-header-name)
769
 
            '("^Reply-To:.*$" . 'pmail-header-name)
770
 
            '("^Subject:" . 'pmail-header-name)
771
 
            '("^X-Spam-Status:" . 'pmail-header-name)
772
 
            '("^\\(To\\|Apparently-To\\|Cc\\|Newsgroups\\):"
773
 
              . 'pmail-header-name)
774
 
            ;; Use MATCH-ANCHORED to effectively anchor the regexp left side.
775
 
            `(,cite-chars
776
 
              (,(concat "\\=[ \t]*"
777
 
                        "\\(\\(\\([" cite-prefix "]+[" cite-suffix "]*\\)?"
778
 
                        "\\(" cite-chars "[ \t]*\\)\\)+\\)"
779
 
                        "\\(.*\\)")
780
 
               (beginning-of-line) (end-of-line)
781
 
               (1 font-lock-comment-delimiter-face nil t)
782
 
               (5 font-lock-comment-face nil t)))
783
 
            '("^\\(X-[a-z0-9-]+\\|In-reply-to\\|Date\\):.*\\(\n[ \t]+.*\\)*$"
784
 
              . 'pmail-header-name))))
785
 
  "Additional expressions to highlight in Pmail mode.")
786
 
 
787
 
;; Perform BODY in the summary buffer
788
 
;; in such a way that its cursor is properly updated in its own window.
789
 
(defmacro pmail-select-summary (&rest body)
790
 
  `(let ((total pmail-total-messages))
791
 
     (if (pmail-summary-displayed)
792
 
         (let ((window (selected-window)))
793
 
           (save-excursion
794
 
             (unwind-protect
795
 
                 (progn
796
 
                   (pop-to-buffer pmail-summary-buffer)
797
 
                   ;; pmail-total-messages is a buffer-local var
798
 
                   ;; in the pmail buffer.
799
 
                   ;; This way we make it available for the body
800
 
                   ;; even tho the pmail buffer is not current.
801
 
                   (let ((pmail-total-messages total))
802
 
                     ,@body))
803
 
               (select-window window))))
804
 
       (save-excursion
805
 
         (set-buffer pmail-summary-buffer)
806
 
         (let ((pmail-total-messages total))
807
 
           ,@body)))
808
 
     (pmail-maybe-display-summary)))
809
 
 
810
 
;;;; *** Pmail Mode ***
811
 
 
812
 
;; This variable is dynamically bound.  The defvar is here to placate
813
 
;; the byte compiler.
814
 
 
815
 
(defvar pmail-enable-multibyte nil)
816
 
 
817
 
 
818
 
(defun pmail-require-mime-maybe ()
819
 
  "Require `pmail-mime-feature' if that is non-nil.
820
 
Signal an error and set `pmail-mime-feature' to nil if the feature
821
 
isn't provided."
822
 
  (when pmail-enable-mime
823
 
    (condition-case err
824
 
        (require pmail-mime-feature)
825
 
      (error
826
 
       (display-warning
827
 
        :warning
828
 
        (format "Although MIME support is requested
829
 
by setting `pmail-enable-mime' to non-nil, the required feature
830
 
`%s' (the value of `pmail-mime-feature')
831
 
is not available in the current session.
832
 
So, the MIME support is turned off for the moment." 
833
 
                pmail-mime-feature))
834
 
       (setq pmail-enable-mime nil)))))
835
 
 
836
 
 
837
 
;;;###autoload
838
 
(defun pmail (&optional file-name-arg)
839
 
  "Read and edit incoming mail.
840
 
Moves messages into file named by `pmail-file-name' (a babyl format file)
841
 
 and edits that file in PMAIL Mode.
842
 
Type \\[describe-mode] once editing that file, for a list of PMAIL commands.
843
 
 
844
 
May be called with file name as argument; then performs pmail editing on
845
 
that file, but does not copy any new mail into the file.
846
 
Interactively, if you supply a prefix argument, then you
847
 
have a chance to specify a file name with the minibuffer.
848
 
 
849
 
If `pmail-display-summary' is non-nil, make a summary for this PMAIL file."
850
 
  (interactive (if current-prefix-arg
851
 
                   (list (read-file-name "Run pmail on PMAIL file: "))))
852
 
  (pmail-require-mime-maybe)
853
 
  (let* ((file-name (expand-file-name (or file-name-arg pmail-file-name)))
854
 
         ;; Use find-buffer-visiting, not get-file-buffer, for those users
855
 
         ;; who have find-file-visit-truename set to t.
856
 
         (existed (find-buffer-visiting file-name))
857
 
         run-mail-hook msg-shown)
858
 
    ;; Like find-file, but in the case where a buffer existed
859
 
    ;; and the file was reverted, recompute the message-data.
860
 
    ;; We used to bind enable-local-variables to nil here,
861
 
    ;; but that should not be needed now that pmail-mode
862
 
    ;; sets it locally to nil.
863
 
    ;; (Binding a variable locally with let is not safe if it has
864
 
    ;; buffer-local bindings.)
865
 
    (if (and existed (not (verify-visited-file-modtime existed)))
866
 
        (progn
867
 
          (find-file file-name)
868
 
          (when (and (verify-visited-file-modtime existed)
869
 
                     (eq major-mode 'pmail-mode))
870
 
            (pmail-forget-messages)
871
 
            (pmail-set-message-counters)))
872
 
      (switch-to-buffer
873
 
       (let ((enable-local-variables nil))
874
 
         (find-file-noselect file-name))))
875
 
    (setq pmail-buffers-swapped-p nil)
876
 
    (if (eq major-mode 'pmail-edit-mode)
877
 
        (error "Exit Pmail Edit mode before getting new mail"))
878
 
    (if (and existed (> (buffer-size) 0))
879
 
        ;; Buffer not new and not empty; ensure in proper mode, but that's all.
880
 
        (or (eq major-mode 'pmail-mode)
881
 
            (progn (pmail-mode-2)
882
 
                   (setq run-mail-hook t)))
883
 
      (setq run-mail-hook t)
884
 
      (pmail-mode-2)
885
 
      (pmail-convert-file-maybe)
886
 
      (goto-char (point-max)))
887
 
    ;; As we have read a file by raw-text, the buffer is set to
888
 
    ;; unibyte.  We must make it multibyte if necessary.
889
 
    (if (and pmail-enable-multibyte
890
 
             (not enable-multibyte-characters))
891
 
        (set-buffer-multibyte t))
892
 
    ;; If necessary, scan to find all the messages.
893
 
    (pmail-maybe-set-message-counters)
894
 
    (unwind-protect
895
 
        (unless (and (not file-name-arg) (pmail-get-new-mail))
896
 
          (pmail-show-message-maybe (pmail-first-unseen-message)))
897
 
      (progn
898
 
        (if pmail-display-summary (pmail-summary))
899
 
        (pmail-construct-io-menu)
900
 
        (if run-mail-hook
901
 
            (run-hooks 'pmail-mode-hook))))))
902
 
 
903
 
;; Given the value of MAILPATH, return a list of inbox file names.
904
 
;; This is turned off because it is not clear that the user wants
905
 
;; all these inboxes to feed into the primary pmail file.
906
 
; (defun pmail-convert-mailpath (string)
907
 
;   (let (idx list)
908
 
;     (while (setq idx (string-match "[%:]" string))
909
 
;       (let ((this (substring string 0 idx)))
910
 
;       (setq string (substring string (1+ idx)))
911
 
;       (setq list (cons (if (string-match "%" this)
912
 
;                            (substring this 0 (string-match "%" this))
913
 
;                          this)
914
 
;                        list))))
915
 
;     list))
916
 
 
917
 
; I have checked that adding "-*- pmail -*-" to the BABYL OPTIONS line
918
 
; will not cause emacs 18.55 problems.
919
 
 
920
 
;; This calls pmail-decode-babyl-format if the file is already Babyl.
921
 
 
922
 
(defun pmail-convert-file-maybe ()
923
 
  "Determine if the file needs to be converted to mbox format."
924
 
  (widen)
925
 
  (goto-char (point-min))
926
 
  ;; Detect previous Babyl format files.
927
 
  (cond ((looking-at "BABYL OPTIONS:")
928
 
         ;; The file is Babyl version 5.  Use unrmail to convert
929
 
         ;; it.
930
 
         (pmail-convert-babyl-to-mbox))
931
 
        ((looking-at "Version: 5\n")
932
 
         ;; Losing babyl file made by old version of Pmail.  Fix the
933
 
         ;; babyl file header and use unrmail to convert to mbox
934
 
         ;; format.
935
 
         (let ((buffer-read-only nil))
936
 
           (insert "BABYL OPTIONS: -*- pmail -*-\n")
937
 
           (pmail-convert-babyl-to-mbox)))
938
 
        ((equal (point-min) (point-max))
939
 
         (message "Empty Pmail file."))
940
 
        ((looking-at "From "))
941
 
        (t (pmail-error-bad-format))))
942
 
 
943
 
(defun pmail-error-bad-format (&optional msgnum)
944
 
  "Report that the buffer contains a message that is not RFC2822
945
 
compliant.
946
 
MSGNUM, if present, indicates the malformed message."
947
 
  (if msgnum
948
 
      (error "Message %s is not a valid RFC2822 message." msgnum)
949
 
    (error "Invalid mbox format mail file.")))
950
 
 
951
 
(defun pmail-convert-babyl-to-mbox ()
952
 
  "Convert the mail file from Babyl version 5 to mbox."
953
 
  (let ((old-file (make-temp-file "pmail"))
954
 
        (new-file (make-temp-file "pmail")))
955
 
    (unwind-protect
956
 
        (progn
957
 
          (write-region (point-min) (point-max) old-file)
958
 
          (unrmail old-file new-file)
959
 
          (message "Replacing BABYL format with mbox format...")
960
 
          (let ((inhibit-read-only t))
961
 
            (erase-buffer)
962
 
            (insert-file-contents-literally new-file))
963
 
          (message "Replacing BABYL format with mbox format...done"))
964
 
      (delete-file old-file)
965
 
      (delete-file new-file))))
966
 
 
967
 
(defun pmail-insert-pmail-file-header ()
968
 
  (let ((buffer-read-only nil))
969
 
    ;; -*-pmail-*- is here so that visiting the file normally
970
 
    ;; recognizes it as an Pmail file.
971
 
    (insert "BABYL OPTIONS: -*- pmail -*-
972
 
Version: 5
973
 
Labels:
974
 
Note:   This is the header of an pmail file.
975
 
Note:   If you are seeing it in pmail,
976
 
Note:    it means the file has no messages in it.\n\^_")))
977
 
 
978
 
(defun pmail-get-coding-system ()
979
 
  "Return a suitable coding system to use for the mail message in
980
 
the region."
981
 
  (let ((content-type-header (mail-fetch-field "content-type"))
982
 
        separator)
983
 
    (save-excursion
984
 
      (setq separator (search-forward "\n\n")))
985
 
    (if (and content-type-header
986
 
             (string-match pmail-mime-charset-pattern content-type-header))
987
 
        (substring content-type-header (match-beginning 1) (match-end 1))
988
 
      'undecided)))
989
 
 
990
 
;; Decode Babyl formatted part at the head of current buffer by
991
 
;; pmail-file-coding-system, or if it is nil, do auto conversion.
992
 
 
993
 
(defun pmail-decode-babyl-format ()
994
 
  (let ((modifiedp (buffer-modified-p))
995
 
        (buffer-read-only nil)
996
 
        (coding-system pmail-file-coding-system)
997
 
        from to)
998
 
    (goto-char (point-min))
999
 
    (search-forward "\n\^_" nil t)      ; Skip BABYL header.
1000
 
    (setq from (point))
1001
 
    (goto-char (point-max))
1002
 
    (search-backward "\n\^_" from 'mv)
1003
 
    (setq to (point))
1004
 
    (unless (and coding-system
1005
 
                 (coding-system-p coding-system))
1006
 
      (setq coding-system
1007
 
            ;; If pmail-file-coding-system is nil, Emacs 21 writes
1008
 
            ;; PMAIL files in emacs-mule, Emacs 22 in utf-8, but
1009
 
            ;; earlier versions did that with the current buffer's
1010
 
            ;; encoding.  So we want to favor detection of emacs-mule
1011
 
            ;; (whose normal priority is quite low) and utf-8, but
1012
 
            ;; still allow detection of other encodings if they won't
1013
 
            ;; fit.  The call to with-coding-priority below achieves
1014
 
            ;; that.
1015
 
            (with-coding-priority '(emacs-mule utf-8)
1016
 
              (detect-coding-region from to 'highest))))
1017
 
    (unless (eq (coding-system-type coding-system) 'undecided)
1018
 
      (set-buffer-modified-p t)         ; avoid locking when decoding
1019
 
      (let ((buffer-undo-list t))
1020
 
        (decode-coding-region from to coding-system))
1021
 
      (setq coding-system last-coding-system-used))
1022
 
    (set-buffer-modified-p modifiedp)
1023
 
    (setq buffer-file-coding-system nil)
1024
 
    (setq save-buffer-coding-system
1025
 
          (or coding-system 'undecided))))
1026
 
 
1027
 
(defvar pmail-mode-map nil)
1028
 
(if pmail-mode-map
1029
 
    nil
1030
 
  (setq pmail-mode-map (make-keymap))
1031
 
  (suppress-keymap pmail-mode-map)
1032
 
  (define-key pmail-mode-map "a"      'pmail-add-label)
1033
 
  (define-key pmail-mode-map "b"      'pmail-bury)
1034
 
  (define-key pmail-mode-map "c"      'pmail-continue)
1035
 
  (define-key pmail-mode-map "d"      'pmail-delete-forward)
1036
 
  (define-key pmail-mode-map "\C-d"   'pmail-delete-backward)
1037
 
  (define-key pmail-mode-map "e"      'pmail-edit-current-message)
1038
 
  (define-key pmail-mode-map "f"      'pmail-forward)
1039
 
  (define-key pmail-mode-map "g"      'pmail-get-new-mail)
1040
 
  (define-key pmail-mode-map "h"      'pmail-summary)
1041
 
  (define-key pmail-mode-map "i"      'pmail-input)
1042
 
  (define-key pmail-mode-map "j"      'pmail-show-message-maybe)
1043
 
  (define-key pmail-mode-map "k"      'pmail-kill-label)
1044
 
  (define-key pmail-mode-map "l"      'pmail-summary-by-labels)
1045
 
  (define-key pmail-mode-map "\e\C-h" 'pmail-summary)
1046
 
  (define-key pmail-mode-map "\e\C-l" 'pmail-summary-by-labels)
1047
 
  (define-key pmail-mode-map "\e\C-r" 'pmail-summary-by-recipients)
1048
 
  (define-key pmail-mode-map "\e\C-s" 'pmail-summary-by-regexp)
1049
 
  (define-key pmail-mode-map "\e\C-t" 'pmail-summary-by-topic)
1050
 
  (define-key pmail-mode-map "m"      'pmail-mail)
1051
 
  (define-key pmail-mode-map "\em"    'pmail-retry-failure)
1052
 
  (define-key pmail-mode-map "n"      'pmail-next-undeleted-message)
1053
 
  (define-key pmail-mode-map "\en"    'pmail-next-message)
1054
 
  (define-key pmail-mode-map "\e\C-n" 'pmail-next-labeled-message)
1055
 
  (define-key pmail-mode-map "o"      'pmail-output-to-pmail-file)
1056
 
  (define-key pmail-mode-map "\C-o"   'pmail-output)
1057
 
  (define-key pmail-mode-map "p"      'pmail-previous-undeleted-message)
1058
 
  (define-key pmail-mode-map "\ep"    'pmail-previous-message)
1059
 
  (define-key pmail-mode-map "\e\C-p" 'pmail-previous-labeled-message)
1060
 
  (define-key pmail-mode-map "q"      'pmail-quit)
1061
 
  (define-key pmail-mode-map "r"      'pmail-reply)
1062
 
;; I find I can't live without the default M-r command -- rms.
1063
 
;;  (define-key pmail-mode-map "\er"  'pmail-search-backwards)
1064
 
  (define-key pmail-mode-map "s"      'pmail-expunge-and-save)
1065
 
  (define-key pmail-mode-map "\es"    'pmail-search)
1066
 
  (define-key pmail-mode-map "t"      'pmail-toggle-header)
1067
 
  (define-key pmail-mode-map "u"      'pmail-undelete-previous-message)
1068
 
  (define-key pmail-mode-map "w"      'pmail-output-body-to-file)
1069
 
  (define-key pmail-mode-map "x"      'pmail-expunge)
1070
 
  (define-key pmail-mode-map "."      'pmail-beginning-of-message)
1071
 
  (define-key pmail-mode-map "/"      'pmail-end-of-message)
1072
 
  (define-key pmail-mode-map "<"      'pmail-first-message)
1073
 
  (define-key pmail-mode-map ">"      'pmail-last-message)
1074
 
  (define-key pmail-mode-map " "      'scroll-up)
1075
 
  (define-key pmail-mode-map "\177"   'scroll-down)
1076
 
  (define-key pmail-mode-map "?"      'describe-mode)
1077
 
  (define-key pmail-mode-map "\C-c\C-s\C-d" 'pmail-sort-by-date)
1078
 
  (define-key pmail-mode-map "\C-c\C-s\C-s" 'pmail-sort-by-subject)
1079
 
  (define-key pmail-mode-map "\C-c\C-s\C-a" 'pmail-sort-by-author)
1080
 
  (define-key pmail-mode-map "\C-c\C-s\C-r" 'pmail-sort-by-recipient)
1081
 
  (define-key pmail-mode-map "\C-c\C-s\C-c" 'pmail-sort-by-correspondent)
1082
 
  (define-key pmail-mode-map "\C-c\C-s\C-l" 'pmail-sort-by-lines)
1083
 
  (define-key pmail-mode-map "\C-c\C-s\C-k" 'pmail-sort-by-labels)
1084
 
  (define-key pmail-mode-map "\C-c\C-n" 'pmail-next-same-subject)
1085
 
  (define-key pmail-mode-map "\C-c\C-p" 'pmail-previous-same-subject)
1086
 
  )
1087
 
 
1088
 
(define-key pmail-mode-map [menu-bar] (make-sparse-keymap))
1089
 
 
1090
 
(define-key pmail-mode-map [menu-bar classify]
1091
 
  (cons "Classify" (make-sparse-keymap "Classify")))
1092
 
 
1093
 
(define-key pmail-mode-map [menu-bar classify input-menu]
1094
 
  nil)
1095
 
 
1096
 
(define-key pmail-mode-map [menu-bar classify output-menu]
1097
 
  nil)
1098
 
 
1099
 
(define-key pmail-mode-map [menu-bar classify output-body]
1100
 
  '("Output body to file..." . pmail-output-body-to-file))
1101
 
 
1102
 
(define-key pmail-mode-map [menu-bar classify output-inbox]
1103
 
  '("Output (inbox)..." . pmail-output))
1104
 
 
1105
 
(define-key pmail-mode-map [menu-bar classify output]
1106
 
  '("Output (Pmail)..." . pmail-output-to-pmail-file))
1107
 
 
1108
 
(define-key pmail-mode-map [menu-bar classify kill-label]
1109
 
  '("Kill Label..." . pmail-kill-label))
1110
 
 
1111
 
(define-key pmail-mode-map [menu-bar classify add-label]
1112
 
  '("Add Label..." . pmail-add-label))
1113
 
 
1114
 
(define-key pmail-mode-map [menu-bar summary]
1115
 
  (cons "Summary" (make-sparse-keymap "Summary")))
1116
 
 
1117
 
(define-key pmail-mode-map [menu-bar summary senders]
1118
 
  '("By Senders..." . pmail-summary-by-senders))
1119
 
 
1120
 
(define-key pmail-mode-map [menu-bar summary labels]
1121
 
  '("By Labels..." . pmail-summary-by-labels))
1122
 
 
1123
 
(define-key pmail-mode-map [menu-bar summary recipients]
1124
 
  '("By Recipients..." . pmail-summary-by-recipients))
1125
 
 
1126
 
(define-key pmail-mode-map [menu-bar summary topic]
1127
 
  '("By Topic..." . pmail-summary-by-topic))
1128
 
 
1129
 
(define-key pmail-mode-map [menu-bar summary regexp]
1130
 
  '("By Regexp..." . pmail-summary-by-regexp))
1131
 
 
1132
 
(define-key pmail-mode-map [menu-bar summary all]
1133
 
  '("All" . pmail-summary))
1134
 
 
1135
 
(define-key pmail-mode-map [menu-bar mail]
1136
 
  (cons "Mail" (make-sparse-keymap "Mail")))
1137
 
 
1138
 
(define-key pmail-mode-map [menu-bar mail pmail-get-new-mail]
1139
 
  '("Get New Mail" . pmail-get-new-mail))
1140
 
 
1141
 
(define-key pmail-mode-map [menu-bar mail lambda]
1142
 
  '("----"))
1143
 
 
1144
 
(define-key pmail-mode-map [menu-bar mail continue]
1145
 
  '("Continue" . pmail-continue))
1146
 
 
1147
 
(define-key pmail-mode-map [menu-bar mail resend]
1148
 
  '("Re-send..." . pmail-resend))
1149
 
 
1150
 
(define-key pmail-mode-map [menu-bar mail forward]
1151
 
  '("Forward" . pmail-forward))
1152
 
 
1153
 
(define-key pmail-mode-map [menu-bar mail retry]
1154
 
  '("Retry" . pmail-retry-failure))
1155
 
 
1156
 
(define-key pmail-mode-map [menu-bar mail reply]
1157
 
  '("Reply" . pmail-reply))
1158
 
 
1159
 
(define-key pmail-mode-map [menu-bar mail mail]
1160
 
  '("Mail" . pmail-mail))
1161
 
 
1162
 
(define-key pmail-mode-map [menu-bar delete]
1163
 
  (cons "Delete" (make-sparse-keymap "Delete")))
1164
 
 
1165
 
(define-key pmail-mode-map [menu-bar delete expunge/save]
1166
 
  '("Expunge/Save" . pmail-expunge-and-save))
1167
 
 
1168
 
(define-key pmail-mode-map [menu-bar delete expunge]
1169
 
  '("Expunge" . pmail-expunge))
1170
 
 
1171
 
(define-key pmail-mode-map [menu-bar delete undelete]
1172
 
  '("Undelete" . pmail-undelete-previous-message))
1173
 
 
1174
 
(define-key pmail-mode-map [menu-bar delete delete]
1175
 
  '("Delete" . pmail-delete-forward))
1176
 
 
1177
 
(define-key pmail-mode-map [menu-bar move]
1178
 
  (cons "Move" (make-sparse-keymap "Move")))
1179
 
 
1180
 
(define-key pmail-mode-map [menu-bar move search-back]
1181
 
  '("Search Back..." . pmail-search-backwards))
1182
 
 
1183
 
(define-key pmail-mode-map [menu-bar move search]
1184
 
  '("Search..." . pmail-search))
1185
 
 
1186
 
(define-key pmail-mode-map [menu-bar move previous]
1187
 
  '("Previous Nondeleted" . pmail-previous-undeleted-message))
1188
 
 
1189
 
(define-key pmail-mode-map [menu-bar move next]
1190
 
  '("Next Nondeleted" . pmail-next-undeleted-message))
1191
 
 
1192
 
(define-key pmail-mode-map [menu-bar move last]
1193
 
  '("Last" . pmail-last-message))
1194
 
 
1195
 
(define-key pmail-mode-map [menu-bar move first]
1196
 
  '("First" . pmail-first-message))
1197
 
 
1198
 
(define-key pmail-mode-map [menu-bar move previous]
1199
 
  '("Previous" . pmail-previous-message))
1200
 
 
1201
 
(define-key pmail-mode-map [menu-bar move next]
1202
 
  '("Next" . pmail-next-message))
1203
 
 
1204
 
;; Pmail toolbar
1205
 
(defvar pmail-tool-bar-map
1206
 
  (let ((map (make-sparse-keymap)))
1207
 
    (tool-bar-local-item-from-menu 'pmail-get-new-mail "mail/inbox"
1208
 
                                   map pmail-mode-map)
1209
 
    (tool-bar-local-item-from-menu 'pmail-next-undeleted-message "right-arrow"
1210
 
                                   map pmail-mode-map)
1211
 
    (tool-bar-local-item-from-menu 'pmail-previous-undeleted-message "left-arrow"
1212
 
                                   map pmail-mode-map)
1213
 
    (tool-bar-local-item-from-menu 'pmail-search "search"
1214
 
                                   map pmail-mode-map)
1215
 
    (tool-bar-local-item-from-menu 'pmail-input "open"
1216
 
                                   map pmail-mode-map)
1217
 
    (tool-bar-local-item-from-menu 'pmail-mail "mail/compose"
1218
 
                                   map pmail-mode-map)
1219
 
    (tool-bar-local-item-from-menu 'pmail-reply "mail/reply-all"
1220
 
                                   map pmail-mode-map)
1221
 
    (tool-bar-local-item-from-menu 'pmail-forward "mail/forward"
1222
 
                                   map pmail-mode-map)
1223
 
    (tool-bar-local-item-from-menu 'pmail-delete-forward "close"
1224
 
                                   map pmail-mode-map)
1225
 
    (tool-bar-local-item-from-menu 'pmail-output "mail/move"
1226
 
                                   map pmail-mode-map)
1227
 
    (tool-bar-local-item-from-menu 'pmail-output-body-to-file "mail/save"
1228
 
                                   map pmail-mode-map)
1229
 
    (tool-bar-local-item-from-menu 'pmail-expunge "delete"
1230
 
                                   map pmail-mode-map)
1231
 
    map))
1232
 
 
1233
 
 
1234
 
 
1235
 
;; Pmail mode is suitable only for specially formatted data.
1236
 
(put 'pmail-mode 'mode-class 'special)
1237
 
 
1238
 
(defun pmail-mode-kill-summary ()
1239
 
  (if pmail-summary-buffer (kill-buffer pmail-summary-buffer)))
1240
 
 
1241
 
;;;###autoload
1242
 
(defun pmail-mode ()
1243
 
  "Pmail Mode is used by \\<pmail-mode-map>\\[pmail] for editing Pmail files.
1244
 
All normal editing commands are turned off.
1245
 
Instead, these commands are available:
1246
 
 
1247
 
\\[pmail-beginning-of-message]  Move point to front of this message.
1248
 
\\[pmail-end-of-message]        Move point to bottom of this message.
1249
 
\\[scroll-up]   Scroll to next screen of this message.
1250
 
\\[scroll-down] Scroll to previous screen of this message.
1251
 
\\[pmail-next-undeleted-message]        Move to Next non-deleted message.
1252
 
\\[pmail-previous-undeleted-message]    Move to Previous non-deleted message.
1253
 
\\[pmail-next-message]  Move to Next message whether deleted or not.
1254
 
\\[pmail-previous-message]      Move to Previous message whether deleted or not.
1255
 
\\[pmail-first-message] Move to the first message in Pmail file.
1256
 
\\[pmail-last-message]  Move to the last message in Pmail file.
1257
 
\\[pmail-show-message-maybe]    Jump to message specified by numeric position in file.
1258
 
\\[pmail-search]        Search for string and show message it is found in.
1259
 
\\[pmail-delete-forward]        Delete this message, move to next nondeleted.
1260
 
\\[pmail-delete-backward]       Delete this message, move to previous nondeleted.
1261
 
\\[pmail-undelete-previous-message]     Undelete message.  Tries current message, then earlier messages
1262
 
        till a deleted message is found.
1263
 
\\[pmail-edit-current-message]  Edit the current message.  \\[pmail-cease-edit] to return to Pmail.
1264
 
\\[pmail-expunge]       Expunge deleted messages.
1265
 
\\[pmail-expunge-and-save]      Expunge and save the file.
1266
 
\\[pmail-quit]       Quit Pmail: expunge, save, then switch to another buffer.
1267
 
\\[save-buffer] Save without expunging.
1268
 
\\[pmail-get-new-mail]  Move new mail from system spool directory into this file.
1269
 
\\[pmail-mail]  Mail a message (same as \\[mail-other-window]).
1270
 
\\[pmail-continue]      Continue composing outgoing message started before.
1271
 
\\[pmail-reply] Reply to this message.  Like \\[pmail-mail] but initializes some fields.
1272
 
\\[pmail-retry-failure] Send this message again.  Used on a mailer failure message.
1273
 
\\[pmail-forward]       Forward this message to another user.
1274
 
\\[pmail-output-to-pmail-file]       Output this message to an Pmail file (append it).
1275
 
\\[pmail-output]        Output this message to a Unix-format mail file (append it).
1276
 
\\[pmail-output-body-to-file]   Save message body to a file.  Default filename comes from Subject line.
1277
 
\\[pmail-input] Input Pmail file.  Run Pmail on that file.
1278
 
\\[pmail-add-label]     Add label to message.  It will be displayed in the mode line.
1279
 
\\[pmail-kill-label]    Kill label.  Remove a label from current message.
1280
 
\\[pmail-next-labeled-message]   Move to Next message with specified label
1281
 
          (label defaults to last one specified).
1282
 
          Standard labels: filed, unseen, answered, forwarded, deleted.
1283
 
          Any other label is present only if you add it with \\[pmail-add-label].
1284
 
\\[pmail-previous-labeled-message]   Move to Previous message with specified label
1285
 
\\[pmail-summary]       Show headers buffer, with a one line summary of each message.
1286
 
\\[pmail-summary-by-labels]     Summarize only messages with particular label(s).
1287
 
\\[pmail-summary-by-recipients]   Summarize only messages with particular recipient(s).
1288
 
\\[pmail-summary-by-regexp]   Summarize only messages with particular regexp(s).
1289
 
\\[pmail-summary-by-topic]   Summarize only messages with subject line regexp(s).
1290
 
\\[pmail-toggle-header] Toggle display of complete header."
1291
 
  (interactive)
1292
 
  (let ((finding-pmail-file (not (eq major-mode 'pmail-mode))))
1293
 
    (pmail-mode-2)
1294
 
    (when (and finding-pmail-file
1295
 
               (null coding-system-for-read)
1296
 
               default-enable-multibyte-characters)
1297
 
      (let ((pmail-enable-multibyte t))
1298
 
        (pmail-require-mime-maybe)
1299
 
        (pmail-convert-file-maybe)
1300
 
        (goto-char (point-max))
1301
 
        (set-buffer-multibyte t)))
1302
 
    (pmail-set-message-counters)
1303
 
    (pmail-show-message-maybe pmail-total-messages)
1304
 
    (when finding-pmail-file
1305
 
      (when pmail-display-summary
1306
 
        (pmail-summary))
1307
 
      (pmail-construct-io-menu))
1308
 
    (run-mode-hooks 'pmail-mode-hook)))
1309
 
 
1310
 
(defun pmail-mode-2 ()
1311
 
  (kill-all-local-variables)
1312
 
  (pmail-mode-1)
1313
 
  (pmail-perm-variables)
1314
 
  (pmail-variables))
1315
 
 
1316
 
(defun pmail-mode-1 ()
1317
 
  (setq major-mode 'pmail-mode)
1318
 
  (setq mode-name "PMAIL")
1319
 
  (setq buffer-read-only t)
1320
 
  ;; No need to auto save PMAIL files in normal circumstances
1321
 
  ;; because they contain no info except attribute changes
1322
 
  ;; and deletion of messages.
1323
 
  ;; The one exception is when messages are copied into an Pmail mode buffer.
1324
 
  ;; pmail-output-to-pmail-file enables auto save when you do that.
1325
 
  (setq buffer-auto-save-file-name nil)
1326
 
  (setq mode-line-modified "--")
1327
 
  (use-local-map pmail-mode-map)
1328
 
  (set-syntax-table text-mode-syntax-table)
1329
 
  (setq local-abbrev-table text-mode-abbrev-table))
1330
 
 
1331
 
(defun pmail-generate-viewer-buffer ()
1332
 
  "Return a newly created buffer suitable for viewing messages."
1333
 
  (let ((suffix (file-name-nondirectory (or buffer-file-name (buffer-name)))))
1334
 
    (generate-new-buffer (format " *message-viewer %s*" suffix))))
1335
 
 
1336
 
;; Set up the permanent locals associated with an Pmail file.
1337
 
(defun pmail-perm-variables ()
1338
 
  (make-local-variable 'pmail-last-label)
1339
 
  (make-local-variable 'pmail-last-regexp)
1340
 
  (make-local-variable 'pmail-deleted-vector)
1341
 
  (make-local-variable 'pmail-buffer)
1342
 
  (setq pmail-buffer (current-buffer))
1343
 
  (set-buffer-multibyte nil)
1344
 
  (make-local-variable 'pmail-view-buffer)
1345
 
  (save-excursion
1346
 
    (setq pmail-view-buffer (pmail-generate-viewer-buffer))
1347
 
    (set-buffer pmail-view-buffer)
1348
 
    (set-buffer-multibyte t))
1349
 
  (make-local-variable 'pmail-summary-buffer)
1350
 
  (make-local-variable 'pmail-summary-vector)
1351
 
  (make-local-variable 'pmail-current-message)
1352
 
  (make-local-variable 'pmail-total-messages)
1353
 
  (make-local-variable 'pmail-overlay-list)
1354
 
  (setq pmail-overlay-list nil)
1355
 
  (make-local-variable 'pmail-message-vector)
1356
 
  (make-local-variable 'pmail-msgref-vector)
1357
 
  (make-local-variable 'pmail-inbox-list)
1358
 
  (setq pmail-inbox-list (pmail-parse-file-inboxes))
1359
 
  ;; Provide default set of inboxes for primary mail file ~/PMAIL.
1360
 
  (and (null pmail-inbox-list)
1361
 
       (or (equal buffer-file-name (expand-file-name pmail-file-name))
1362
 
           (equal buffer-file-truename
1363
 
                  (abbreviate-file-name (file-truename pmail-file-name))))
1364
 
       (setq pmail-inbox-list
1365
 
             (or pmail-primary-inbox-list
1366
 
                 (list (or (getenv "MAIL")
1367
 
                           (concat rmail-spool-directory
1368
 
                                   (user-login-name)))))))
1369
 
  (make-local-variable 'pmail-keywords)
1370
 
  (set (make-local-variable 'tool-bar-map) pmail-tool-bar-map)
1371
 
  (make-local-variable 'pmail-buffers-swapped-p)
1372
 
  ;; this gets generated as needed
1373
 
  (setq pmail-keywords nil))
1374
 
 
1375
 
;; Set up the non-permanent locals associated with Pmail mode.
1376
 
(defun pmail-variables ()
1377
 
  (make-local-variable 'save-buffer-coding-system)
1378
 
  ;; If we don't already have a value for save-buffer-coding-system,
1379
 
  ;; get it from buffer-file-coding-system, and clear that
1380
 
  ;; because it should be determined in pmail-show-message.
1381
 
  (unless save-buffer-coding-system
1382
 
    (setq save-buffer-coding-system (or buffer-file-coding-system 'undecided))
1383
 
    (setq buffer-file-coding-system nil))
1384
 
  ;; Don't let a local variables list in a message cause confusion.
1385
 
  (make-local-variable 'local-enable-local-variables)
1386
 
  (setq local-enable-local-variables nil)
1387
 
  (make-local-variable 'revert-buffer-function)
1388
 
  (setq revert-buffer-function 'pmail-revert)
1389
 
  (make-local-variable 'font-lock-defaults)
1390
 
  (setq font-lock-defaults
1391
 
        '(pmail-font-lock-keywords
1392
 
          t t nil nil
1393
 
          (font-lock-maximum-size . nil)
1394
 
          (font-lock-fontify-buffer-function . pmail-fontify-buffer-function)
1395
 
          (font-lock-unfontify-buffer-function . pmail-unfontify-buffer-function)
1396
 
          (font-lock-inhibit-thing-lock . (lazy-lock-mode fast-lock-mode))))
1397
 
  (make-local-variable 'require-final-newline)
1398
 
  (setq require-final-newline nil)
1399
 
  (make-local-variable 'version-control)
1400
 
  (setq version-control 'never)
1401
 
  (make-local-variable 'kill-buffer-hook)
1402
 
  (add-hook 'kill-buffer-hook 'pmail-mode-kill-summary)
1403
 
  (make-local-variable 'file-precious-flag)
1404
 
  (setq file-precious-flag t)
1405
 
  (make-local-variable 'desktop-save-buffer)
1406
 
  (setq desktop-save-buffer t))
1407
 
 
1408
 
;; Handle M-x revert-buffer done in an pmail-mode buffer.
1409
 
(defun pmail-revert (arg noconfirm)
1410
 
  (set-buffer pmail-buffer)
1411
 
  (let* ((revert-buffer-function (default-value 'revert-buffer-function))
1412
 
         (pmail-enable-multibyte enable-multibyte-characters)
1413
 
         ;; See similar code in `pmail'.
1414
 
         (coding-system-for-read (and pmail-enable-multibyte 'raw-text)))
1415
 
    ;; Call our caller again, but this time it does the default thing.
1416
 
    (when (revert-buffer arg noconfirm)
1417
 
      ;; If the user said "yes", and we changed something,
1418
 
      ;; reparse the messages.
1419
 
      (set-buffer pmail-buffer)
1420
 
      (pmail-mode-2)
1421
 
      ;; Convert all or part to Babyl file if possible.
1422
 
      (pmail-convert-file-maybe)
1423
 
      ;; We have read the file as raw-text, so the buffer is set to
1424
 
      ;; unibyte.  Make it multibyte if necessary.
1425
 
      (if (and pmail-enable-multibyte
1426
 
               (not enable-multibyte-characters))
1427
 
          (set-buffer-multibyte t))
1428
 
      (goto-char (point-max))
1429
 
      (pmail-set-message-counters)
1430
 
      (pmail-show-message-maybe pmail-total-messages)
1431
 
      (run-hooks 'pmail-mode-hook))))
1432
 
 
1433
 
;; Return a list of files from this buffer's Mail: option.
1434
 
;; Does not assume that messages have been parsed.
1435
 
;; Just returns nil if buffer does not look like Babyl format.
1436
 
(defun pmail-parse-file-inboxes ()
1437
 
  (save-excursion
1438
 
    (save-restriction
1439
 
      (widen)
1440
 
      (goto-char 1)
1441
 
      (cond ((looking-at "BABYL OPTIONS:")
1442
 
             (search-forward "\n\^_" nil 'move)
1443
 
             (narrow-to-region 1 (point))
1444
 
             (goto-char 1)
1445
 
             (when (search-forward "\nMail:" nil t)
1446
 
               (narrow-to-region (point) (progn (end-of-line) (point)))
1447
 
               (goto-char (point-min))
1448
 
               (mail-parse-comma-list)))))))
1449
 
 
1450
 
(defun pmail-expunge-and-save ()
1451
 
  "Expunge and save PMAIL file."
1452
 
  (interactive)
1453
 
  (pmail-expunge)
1454
 
  (set-buffer pmail-buffer)
1455
 
  (save-buffer)
1456
 
  (if (pmail-summary-exists)
1457
 
      (pmail-select-summary (set-buffer-modified-p nil))))
1458
 
 
1459
 
(defun pmail-quit ()
1460
 
  "Quit out of PMAIL.
1461
 
Hook `pmail-quit-hook' is run after expunging."
1462
 
  (interactive)
1463
 
  ;; Determine if the buffers need to be swapped.
1464
 
  (pmail-swap-buffers-maybe)
1465
 
  (pmail-expunge-and-save)
1466
 
  (when (boundp 'pmail-quit-hook)
1467
 
    (run-hooks 'pmail-quit-hook))
1468
 
  ;; Don't switch to the summary buffer even if it was recently visible.
1469
 
  (when pmail-summary-buffer
1470
 
    (replace-buffer-in-windows pmail-summary-buffer)
1471
 
    (bury-buffer pmail-summary-buffer))
1472
 
  (if pmail-enable-mime
1473
 
      (let ((obuf pmail-buffer)
1474
 
            (ovbuf pmail-view-buffer))
1475
 
        (set-buffer pmail-view-buffer)
1476
 
        (quit-window)
1477
 
        (replace-buffer-in-windows ovbuf)
1478
 
        (replace-buffer-in-windows obuf)
1479
 
        (bury-buffer obuf))
1480
 
    (let ((obuf (current-buffer)))
1481
 
      (quit-window)
1482
 
      (replace-buffer-in-windows obuf))))
1483
 
 
1484
 
(defun pmail-bury ()
1485
 
  "Bury current Pmail buffer and its summary buffer."
1486
 
  (interactive)
1487
 
  ;; This let var was called pmail-buffer, but that interfered
1488
 
  ;; with the buffer-local var used in summary buffers.
1489
 
  (let ((buffer-to-bury (current-buffer)))
1490
 
    (if (pmail-summary-exists)
1491
 
        (let (window)
1492
 
          (while (setq window (get-buffer-window pmail-summary-buffer))
1493
 
            (quit-window nil window))
1494
 
          (bury-buffer pmail-summary-buffer)))
1495
 
    (quit-window)))
1496
 
 
1497
 
(defun pmail-duplicate-message ()
1498
 
  "Create a duplicated copy of the current message.
1499
 
The duplicate copy goes into the Pmail file just after the
1500
 
original copy."
1501
 
  (interactive)
1502
 
  (widen)
1503
 
  (let ((buffer-read-only nil)
1504
 
        (number pmail-current-message)
1505
 
        (string (buffer-substring (pmail-msgbeg pmail-current-message)
1506
 
                                  (pmail-msgend pmail-current-message))))
1507
 
    (goto-char (pmail-msgend pmail-current-message))
1508
 
    (insert string)
1509
 
    (pmail-forget-messages)
1510
 
    (pmail-show-message-maybe number)
1511
 
    (message "Message duplicated")))
1512
 
 
1513
 
;;;###autoload
1514
 
(defun pmail-input (filename)
1515
 
  "Run Pmail on file FILENAME."
1516
 
  (interactive "FRun pmail on PMAIL file: ")
1517
 
  (pmail filename))
1518
 
 
1519
 
 
1520
 
;; This used to scan subdirectories recursively, but someone pointed out
1521
 
;; that if the user wants that, person can put all the files in one dir.
1522
 
;; And the recursive scan was slow.  So I took it out.
1523
 
;; rms, Sep 1996.
1524
 
(defun pmail-find-all-files (start)
1525
 
  "Return list of file in dir START that match `pmail-secondary-file-regexp'."
1526
 
  (if (file-accessible-directory-p start)
1527
 
      ;; Don't sort here.
1528
 
      (let* ((case-fold-search t)
1529
 
             (files (directory-files start t pmail-secondary-file-regexp)))
1530
 
        ;; Sort here instead of in directory-files
1531
 
        ;; because this list is usually much shorter.
1532
 
        (sort files 'string<))))
1533
 
 
1534
 
(defun pmail-list-to-menu (menu-name l action &optional full-name)
1535
 
  (let ((menu (make-sparse-keymap menu-name)))
1536
 
    (mapc
1537
 
     (function (lambda (item)
1538
 
                 (let (command)
1539
 
                   (if (consp item)
1540
 
                       (progn
1541
 
                         (setq command
1542
 
                               (pmail-list-to-menu (car item) (cdr item)
1543
 
                                                   action
1544
 
                                                   (if full-name
1545
 
                                                       (concat full-name "/"
1546
 
                                                               (car item))
1547
 
                                                     (car item))))
1548
 
                         (setq name (car item)))
1549
 
                     (progn
1550
 
                       (setq name item)
1551
 
                       (setq command
1552
 
                             (list 'lambda () '(interactive)
1553
 
                                   (list action
1554
 
                                         (expand-file-name
1555
 
                                          (if full-name
1556
 
                                              (concat full-name "/" item)
1557
 
                                            item)
1558
 
                                          pmail-secondary-file-directory))))))
1559
 
                   (define-key menu (vector (intern name))
1560
 
                     (cons name command)))))
1561
 
     (reverse l))
1562
 
    menu))
1563
 
 
1564
 
;; This command is always "disabled" when it appears in a menu.
1565
 
(put 'pmail-disable-menu 'menu-enable ''nil)
1566
 
 
1567
 
(defun pmail-construct-io-menu ()
1568
 
  (let ((files (pmail-find-all-files pmail-secondary-file-directory)))
1569
 
    (if files
1570
 
        (progn
1571
 
          (define-key pmail-mode-map [menu-bar classify input-menu]
1572
 
            (cons "Input Pmail File"
1573
 
                  (pmail-list-to-menu "Input Pmail File"
1574
 
                                      files
1575
 
                                      'pmail-input)))
1576
 
          (define-key pmail-mode-map [menu-bar classify output-menu]
1577
 
            (cons "Output Pmail File"
1578
 
                  (pmail-list-to-menu "Output Pmail File"
1579
 
                                      files
1580
 
                                      'pmail-output-to-pmail-file))))
1581
 
 
1582
 
      (define-key pmail-mode-map [menu-bar classify input-menu]
1583
 
        '("Input Pmail File" . pmail-disable-menu))
1584
 
      (define-key pmail-mode-map [menu-bar classify output-menu]
1585
 
        '("Output Pmail File" . pmail-disable-menu)))))
1586
 
 
1587
 
 
1588
 
;;;; *** Pmail input ***
1589
 
 
1590
 
(declare-function pmail-spam-filter "pmail-spam-filter" (msg))
1591
 
(declare-function pmail-summary-goto-msg "pmailsum" (&optional n nowarn skip-pmail))
1592
 
(declare-function pmail-summary-mark-undeleted "pmailsum" (n))
1593
 
(declare-function pmail-summary-mark-deleted "pmailsum" (&optional n undel))
1594
 
(declare-function rfc822-addresses "rfc822" (header-text))
1595
 
(declare-function mail-abbrev-make-syntax-table "mailabbrev.el" ())
1596
 
(declare-function mail-sendmail-delimit-header "sendmail" ())
1597
 
(declare-function mail-header-end "sendmail" ())
1598
 
 
1599
 
;; RLK feature not added in this version:
1600
 
;; argument specifies inbox file or files in various ways.
1601
 
 
1602
 
(defun pmail-get-new-mail (&optional file-name)
1603
 
  "Move any new mail from this PMAIL file's inbox files.
1604
 
The inbox files can be specified with the file's Mail: option.  The
1605
 
variable `pmail-primary-inbox-list' specifies the inboxes for your
1606
 
primary PMAIL file if it has no Mail: option.  By default, this is
1607
 
your /usr/spool/mail/$USER.
1608
 
 
1609
 
You can also specify the file to get new mail from.  In this case, the
1610
 
file of new mail is not changed or deleted.  Noninteractively, you can
1611
 
pass the inbox file name as an argument.  Interactively, a prefix
1612
 
argument causes us to read a file name and use that file as the inbox.
1613
 
 
1614
 
If the variable `pmail-preserve-inbox' is non-nil, new mail will
1615
 
always be left in inbox files rather than deleted.
1616
 
 
1617
 
This function runs `pmail-get-new-mail-hook' before saving the updated file.
1618
 
It returns t if it got any new messages."
1619
 
  (interactive
1620
 
   (list (if current-prefix-arg
1621
 
             (read-file-name "Get new mail from file: "))))
1622
 
  (run-hooks 'pmail-before-get-new-mail-hook)
1623
 
  ;; If the disk file has been changed from under us,
1624
 
  ;; revert to it before we get new mail.
1625
 
  (or (verify-visited-file-modtime (current-buffer))
1626
 
      (find-file (buffer-file-name)))
1627
 
  (set-buffer pmail-buffer)
1628
 
  (pmail-maybe-set-message-counters)
1629
 
  (widen)
1630
 
  ;; Get rid of all undo records for this buffer.
1631
 
  (or (eq buffer-undo-list t)
1632
 
      (setq buffer-undo-list nil))
1633
 
  (pmail-get-new-mail-1 file-name))
1634
 
 
1635
 
(defun pmail-get-new-mail-1 (file-name)
1636
 
  "Continuation of 'pmail-get-new-mail.  Sort of a procedural
1637
 
abstraction kind of thing to manage the code size.  Return t if
1638
 
new messages are found, nil otherwise."
1639
 
  (let ((all-files (if file-name (list file-name)
1640
 
                     pmail-inbox-list))
1641
 
        (pmail-enable-multibyte (default-value 'enable-multibyte-characters))
1642
 
        found)
1643
 
    (unwind-protect
1644
 
        (when all-files
1645
 
          (let ((opoint (point))
1646
 
                (delete-files ())
1647
 
                ;; If buffer has not changed yet, and has not been
1648
 
                ;; saved yet, don't replace the old backup file now.
1649
 
                (make-backup-files (and make-backup-files (buffer-modified-p)))
1650
 
                (buffer-read-only nil)
1651
 
                ;; Don't make undo records for what we do in getting
1652
 
                ;; mail.
1653
 
                (buffer-undo-list t)
1654
 
                success files file-last-names)
1655
 
            ;; Pull files off all-files onto files as long as there is
1656
 
            ;; no name conflict.  A conflict happens when two inbox
1657
 
            ;; file names have the same last component.
1658
 
            (while (and all-files
1659
 
                        (not (member (file-name-nondirectory (car all-files))
1660
 
                                     file-last-names)))
1661
 
              (setq files (cons (car all-files) files)
1662
 
                    file-last-names
1663
 
                    (cons (file-name-nondirectory (car all-files)) files))
1664
 
              (setq all-files (cdr all-files)))
1665
 
            ;; Put them back in their original order.
1666
 
            (setq files (nreverse files))
1667
 
            (goto-char (point-max))
1668
 
            (skip-chars-backward " \t\n") ; just in case of brain damage
1669
 
            (delete-region (point) (point-max)) ; caused by require-final-newline
1670
 
            (setq found (pmail-get-new-mail-2 file-name files delete-files))))
1671
 
      found)
1672
 
    ;; Don't leave the buffer screwed up if we get a disk-full error.
1673
 
    (or found (pmail-show-message-maybe))))
1674
 
 
1675
 
(defun pmail-get-new-mail-2 (file-name files delete-files)
1676
 
  "Return t if new messages are detected without error, nil otherwise."
1677
 
  (save-excursion
1678
 
    (save-restriction
1679
 
      (let ((new-messages 0)
1680
 
            (spam-filter-p (and (featurep 'pmail-spam-filter) pmail-use-spam-filter))
1681
 
            blurb success suffix)
1682
 
        (narrow-to-region (point) (point))
1683
 
        ;; Read in the contents of the inbox files, renaming them as
1684
 
        ;; necessary, and adding to the list of files to delete
1685
 
        ;; eventually.
1686
 
        (if file-name
1687
 
            (pmail-insert-inbox-text files nil)
1688
 
          (setq delete-files (pmail-insert-inbox-text files t)))
1689
 
        ;; Scan the new text and convert each message to
1690
 
        ;; Pmail/mbox format.
1691
 
        (goto-char (point-min))
1692
 
        (unwind-protect
1693
 
            (setq new-messages (pmail-add-mbox-headers)
1694
 
                  success t)
1695
 
          ;; Try to delete the garbage just inserted.
1696
 
          (or success (delete-region (point-min) (point-max)))
1697
 
          ;; If we could not convert the file's inboxes, rename the
1698
 
          ;; files we tried to read so we won't over and over again.
1699
 
          (if (and (not file-name) (not success))
1700
 
              (let ((delfiles delete-files)
1701
 
                    (count 0))
1702
 
                (while delfiles
1703
 
                  (while (file-exists-p (format "PMAILOSE.%d" count))
1704
 
                    (setq count (1+ count)))
1705
 
                  (rename-file (car delfiles) (format "PMAILOSE.%d" count))
1706
 
                  (setq delfiles (cdr delfiles))))))
1707
 
        ;; Determine if there are messages.
1708
 
        (unless (zerop new-messages)
1709
 
          ;; There are.  Process them.
1710
 
          (goto-char (point-min))
1711
 
          (pmail-count-new-messages)
1712
 
          (run-hooks 'pmail-get-new-mail-hook)
1713
 
          (save-buffer))
1714
 
        ;; Delete the old files, now that the Pmail file is saved.
1715
 
        (while delete-files
1716
 
          (condition-case ()
1717
 
              ;; First, try deleting.
1718
 
              (condition-case ()
1719
 
                  (delete-file (car delete-files))
1720
 
                (file-error
1721
 
                 ;; If we can't delete it, truncate it.
1722
 
                 (write-region (point) (point) (car delete-files))))
1723
 
            (file-error nil))
1724
 
          (setq delete-files (cdr delete-files)))
1725
 
        (if (zerop new-messages)
1726
 
            (when (or file-name pmail-inbox-list)
1727
 
              (message "(No new mail has arrived)"))
1728
 
          ;; Generate the spam message.
1729
 
          (setq blurb (if spam-filter-p
1730
 
                          (pmail-get-new-mail-filter-spam new-messages)
1731
 
                        "")))
1732
 
        (if (pmail-summary-exists)
1733
 
            (pmail-select-summary (pmail-update-summary)))
1734
 
        (setq suffix (if (= 1 new-messages) "" "s"))
1735
 
        (message "%d new message%s read%s" new-messages suffix blurb)
1736
 
        (when spam-filter-p
1737
 
          (if rsf-beep (beep t))
1738
 
          (sleep-for rsf-sleep-after-message))
1739
 
    
1740
 
        ;; Move to the first new message
1741
 
        ;; unless we have other unseen messages before it.
1742
 
        (pmail-show-message-maybe (pmail-first-unseen-message))
1743
 
        (run-hooks 'pmail-after-get-new-mail-hook)
1744
 
        (> new-messages 0)))))
1745
 
 
1746
 
(defun pmail-get-new-mail-filter-spam (new-message-count)
1747
 
  "Process new messages for spam."
1748
 
  (let* ((old-messages (- pmail-total-messages new-message-count))
1749
 
         (rsf-number-of-spam 0)
1750
 
         (rsf-scanned-message-number (1+ old-messages))
1751
 
         ;; save deletion flags of old messages: vector starts at zero
1752
 
         ;; (is one longer that no of messages), therefore take 1+
1753
 
         ;; old-messages
1754
 
         (save-deleted (substring pmail-deleted-vector 0 (1+ old-messages)))
1755
 
         blurb)
1756
 
    ;; set all messages to undeleted
1757
 
    (setq pmail-deleted-vector (make-string (1+ pmail-total-messages) ?\ ))
1758
 
    (while (<= rsf-scanned-message-number pmail-total-messages)
1759
 
      (progn
1760
 
        (if (not (pmail-spam-filter rsf-scanned-message-number))
1761
 
            (progn (setq rsf-number-of-spam (1+ rsf-number-of-spam))))
1762
 
        (setq rsf-scanned-message-number (1+ rsf-scanned-message-number))))
1763
 
    (if (> rsf-number-of-spam 0)
1764
 
        (progn
1765
 
          (when (pmail-expunge-confirmed)
1766
 
            (pmail-only-expunge t))))
1767
 
    (setq pmail-deleted-vector
1768
 
          (concat save-deleted
1769
 
                  (make-string (- pmail-total-messages old-messages) ?\ )))
1770
 
    ;; Generate a return value message based on the number of spam
1771
 
    ;; messages found.
1772
 
    (cond
1773
 
     ((zerop rsf-number-of-spam) "")
1774
 
     ((= 1 new-message-count) ", and appears to be spam")
1775
 
     ((= rsf-number-of-spam new-message-count) ", and all appear to be spam")
1776
 
     ((> rsf-number-of-spam 1)
1777
 
      (format ", and %d appear to be spam" rsf-number-of-spam))
1778
 
     (t ", and 1 appears to be spam"))))
1779
 
 
1780
 
(defun pmail-parse-url (file)
1781
 
  "Parse the supplied URL. Return (list MAILBOX-NAME REMOTE PASSWORD GOT-PASSWORD)
1782
 
WHERE MAILBOX-NAME is the name of the mailbox suitable as argument to the
1783
 
actual version of `movemail', REMOTE is non-nil if MAILBOX-NAME refers to
1784
 
a remote mailbox, PASSWORD is the password if it should be
1785
 
supplied as a separate argument to `movemail' or nil otherwise, GOT-PASSWORD
1786
 
is non-nil if the user has supplied the password interactively.
1787
 
"
1788
 
  (cond
1789
 
   ((string-match "^\\([^:]+\\)://\\(\\([^:@]+\\)\\(:\\([^@]+\\)\\)?@\\)?.*" file)
1790
 
      (let (got-password supplied-password
1791
 
            (proto (match-string 1 file))
1792
 
            (user  (match-string 3 file))
1793
 
            (pass  (match-string 5 file))
1794
 
            (host  (substring file (or (match-end 2)
1795
 
                                       (+ 3 (match-end 1))))))
1796
 
 
1797
 
        (if (not pass)
1798
 
            (when pmail-remote-password-required
1799
 
              (setq got-password (not (pmail-have-password)))
1800
 
              (setq supplied-password (pmail-get-remote-password
1801
 
                                       (string-equal proto "imap"))))
1802
 
          ;; The password is embedded.  Strip it out since movemail
1803
 
          ;; does not really like it, in spite of the movemail spec.
1804
 
          (setq file (concat proto "://" user "@" host)))
1805
 
 
1806
 
        (if (pmail-movemail-variant-p 'emacs)
1807
 
            (if (string-equal proto "pop")
1808
 
                (list (concat "po:" user ":" host)
1809
 
                      t
1810
 
                      (or pass supplied-password)
1811
 
                      got-password)
1812
 
              (error "Emacs movemail does not support %s protocol" proto))
1813
 
          (list file
1814
 
                (or (string-equal proto "pop") (string-equal proto "imap"))
1815
 
                (or supplied-password pass)
1816
 
                got-password))))
1817
 
 
1818
 
   ((string-match "^po:\\([^:]+\\)\\(:\\(.*\\)\\)?" file)
1819
 
    (let (got-password supplied-password
1820
 
          (proto "pop")
1821
 
          (user  (match-string 1 file))
1822
 
          (host  (match-string 3 file)))
1823
 
 
1824
 
      (when pmail-remote-password-required
1825
 
        (setq got-password (not (pmail-have-password)))
1826
 
        (setq supplied-password (pmail-get-remote-password nil)))
1827
 
 
1828
 
      (list file "pop" supplied-password got-password)))
1829
 
 
1830
 
   (t
1831
 
    (list file nil nil nil))))
1832
 
 
1833
 
(defun pmail-insert-inbox-text (files renamep)
1834
 
  ;; Detect a locked file now, so that we avoid moving mail
1835
 
  ;; out of the real inbox file.  (That could scare people.)
1836
 
  (or (memq (file-locked-p buffer-file-name) '(nil t))
1837
 
      (error "PMAIL file %s is locked"
1838
 
             (file-name-nondirectory buffer-file-name)))
1839
 
  (let (file tofile delete-files movemail popmail got-password password)
1840
 
    (while files
1841
 
      ;; Handle remote mailbox names specially; don't expand as filenames
1842
 
      ;; in case the userid contains a directory separator.
1843
 
      (setq file (car files))
1844
 
      (let ((url-data (pmail-parse-url file)))
1845
 
        (setq file (nth 0 url-data))
1846
 
        (setq popmail (nth 1 url-data))
1847
 
        (setq password (nth 2 url-data))
1848
 
        (setq got-password (nth 3 url-data)))
1849
 
 
1850
 
      (if popmail
1851
 
          (setq renamep t)
1852
 
        (setq file (file-truename
1853
 
                    (substitute-in-file-name (expand-file-name file)))))
1854
 
      (setq tofile (expand-file-name
1855
 
                    ;; Generate name to move to from inbox name,
1856
 
                    ;; in case of multiple inboxes that need moving.
1857
 
                    (concat ".newmail-"
1858
 
                            (file-name-nondirectory
1859
 
                             (if (memq system-type '(windows-nt cygwin ms-dos))
1860
 
                                 ;; cannot have colons in file name
1861
 
                                 (replace-regexp-in-string ":" "-" file)
1862
 
                               file)))
1863
 
                    ;; Use the directory of this pmail file
1864
 
                    ;; because it's a nuisance to use the homedir
1865
 
                    ;; if that is on a full disk and this pmail
1866
 
                    ;; file isn't.
1867
 
                    (file-name-directory
1868
 
                     (expand-file-name buffer-file-name))))
1869
 
      ;; Always use movemail to rename the file,
1870
 
      ;; since there can be mailboxes in various directories.
1871
 
      (when (not popmail)
1872
 
        ;; On some systems, /usr/spool/mail/foo is a directory
1873
 
        ;; and the actual inbox is /usr/spool/mail/foo/foo.
1874
 
        (if (file-directory-p file)
1875
 
            (setq file (expand-file-name (user-login-name)
1876
 
                                         file))))
1877
 
      (cond (popmail
1878
 
             (message "Getting mail from the remote server ..."))
1879
 
            ((and (file-exists-p tofile)
1880
 
                  (/= 0 (nth 7 (file-attributes tofile))))
1881
 
             (message "Getting mail from %s..." tofile))
1882
 
            ((and (file-exists-p file)
1883
 
                  (/= 0 (nth 7 (file-attributes file))))
1884
 
             (message "Getting mail from %s..." file)))
1885
 
      ;; Set TOFILE if have not already done so, and
1886
 
      ;; rename or copy the file FILE to TOFILE if and as appropriate.
1887
 
      (cond ((not renamep)
1888
 
             (setq tofile file))
1889
 
            ((or (file-exists-p tofile) (and (not popmail)
1890
 
                                             (not (file-exists-p file))))
1891
 
             nil)
1892
 
            (t
1893
 
             (with-temp-buffer
1894
 
               (let ((errors (current-buffer)))
1895
 
                 (buffer-disable-undo errors)
1896
 
                 (let ((args
1897
 
                        (append
1898
 
                         (list (or pmail-movemail-program "movemail") nil errors nil)
1899
 
                         (if pmail-preserve-inbox
1900
 
                             (list "-p")
1901
 
                           nil)
1902
 
                         (if (pmail-movemail-variant-p 'mailutils)
1903
 
                             (append (list "--emacs") pmail-movemail-flags)
1904
 
                           pmail-movemail-flags)
1905
 
                         (list file tofile)
1906
 
                         (if password (list password) nil))))
1907
 
                   (apply 'call-process args))
1908
 
                 (if (not (buffer-modified-p errors))
1909
 
                     ;; No output => movemail won
1910
 
                     nil
1911
 
                   (set-buffer errors)
1912
 
                   (subst-char-in-region (point-min) (point-max)
1913
 
                                         ?\n ?\  )
1914
 
                   (goto-char (point-max))
1915
 
                   (skip-chars-backward " \t")
1916
 
                   (delete-region (point) (point-max))
1917
 
                   (goto-char (point-min))
1918
 
                   (if (looking-at "movemail: ")
1919
 
                       (delete-region (point-min) (match-end 0)))
1920
 
                   (beep t)
1921
 
                   ;; If we just read the password, most likely it is
1922
 
                   ;; wrong.  Otherwise, see if there is a specific
1923
 
                   ;; reason to think that the problem is a wrong passwd.
1924
 
                   (if (or got-password
1925
 
                           (re-search-forward pmail-remote-password-error
1926
 
                                              nil t))
1927
 
                       (pmail-set-remote-password nil))
1928
 
 
1929
 
                   ;; If using Mailutils, remove initial error code
1930
 
                   ;; abbreviation
1931
 
                   (when (pmail-movemail-variant-p 'mailutils)
1932
 
                     (goto-char (point-min))
1933
 
                     (when (looking-at "[A-Z][A-Z0-9_]*:")
1934
 
                       (delete-region (point-min) (match-end 0))))
1935
 
 
1936
 
                   (message "movemail: %s"
1937
 
                            (buffer-substring (point-min)
1938
 
                                              (point-max)))
1939
 
 
1940
 
                   (sit-for 3)
1941
 
                   nil)))))
1942
 
 
1943
 
      ;; At this point, TOFILE contains the name to read:
1944
 
      ;; Either the alternate name (if we renamed)
1945
 
      ;; or the actual inbox (if not renaming).
1946
 
      (if (file-exists-p tofile)
1947
 
          (let ((coding-system-for-read 'no-conversion)
1948
 
                size)
1949
 
            (goto-char (point-max))
1950
 
            (setq size (nth 1 (insert-file-contents tofile)))
1951
 
            ;; Determine if a pair of newline message separators need
1952
 
            ;; to be added to the new collection of messages.  This is
1953
 
            ;; the case for all new message collections added to a
1954
 
            ;; non-empty mail file.
1955
 
            (unless (zerop size)
1956
 
              (save-restriction
1957
 
                (let ((start (point-min)))
1958
 
                  (widen)
1959
 
                  (unless (eq start (point-min))
1960
 
                    (goto-char start)
1961
 
                    (insert "\n\n")
1962
 
                    (setq size (+ 2 size))))))
1963
 
            (goto-char (point-max))
1964
 
            (or (= (preceding-char) ?\n)
1965
 
                (zerop size)
1966
 
                (insert ?\n))
1967
 
            (if (not (and pmail-preserve-inbox (string= file tofile)))
1968
 
                (setq delete-files (cons tofile delete-files)))))
1969
 
      (message "")
1970
 
      (setq files (cdr files)))
1971
 
    delete-files))
1972
 
 
1973
 
;; Decode the region specified by FROM and TO by CODING.
1974
 
;; If CODING is nil or an invalid coding system, decode by `undecided'.
1975
 
(defun pmail-decode-region (from to coding &optional destination)
1976
 
  (if (or (not coding) (not (coding-system-p coding)))
1977
 
      (setq coding 'undecided))
1978
 
  ;; Use -dos decoding, to remove ^M characters left from base64 or
1979
 
  ;; rogue qp-encoded text.
1980
 
  (decode-coding-region
1981
 
   from to (coding-system-change-eol-conversion coding 1) destination)
1982
 
  ;; Don't reveal the fact we used -dos decoding, as users generally
1983
 
  ;; will not expect the PMAIL buffer to use DOS EOL format.
1984
 
  (setq buffer-file-coding-system
1985
 
        (setq last-coding-system-used
1986
 
              (coding-system-change-eol-conversion coding 0))))
1987
 
 
1988
 
(defun pmail-add-header (name value)
1989
 
  "Add a message header named NAME with value VALUE.
1990
 
The current buffer is narrowed to the headers for some
1991
 
message (including the blank line separator)."
1992
 
  ;; Position point at the end of the headers but before the blank
1993
 
  ;; line separating the headers from the body.
1994
 
  (goto-char (point-max))
1995
 
  (forward-char -1)
1996
 
  (insert name ": " value "\n"))
1997
 
  
1998
 
(defun pmail-add-mbox-headers ()
1999
 
  "Validate the RFC2822 format for the new messages.  Point, at
2000
 
entry should be looking at the first new message.  An error will
2001
 
be thrown if the new messages are not RCC2822 compliant.  Lastly,
2002
 
unless one already exists, add an Rmail attribute header to the
2003
 
new messages in the region.  Return the number of new messages."
2004
 
  (save-excursion
2005
 
    (let ((count 0)
2006
 
          (start (point))
2007
 
          (value "------U")
2008
 
          limit)
2009
 
      ;; Detect an empty inbox file.
2010
 
      (unless (= start (point-max))
2011
 
        ;; Scan the new messages to establish a count and to insure that
2012
 
        ;; an attribute header is present.
2013
 
        (while (looking-at "From ")
2014
 
          ;; Determine if a new attribute header needs to be added to
2015
 
          ;; the message.
2016
 
          (if (search-forward "\n\n" nil t)
2017
 
              (progn
2018
 
                (setq count (1+ count))
2019
 
                (narrow-to-region start (point))
2020
 
                (unless (mail-fetch-field pmail-attribute-header)
2021
 
                  (pmail-add-header pmail-attribute-header value))
2022
 
                (widen))
2023
 
            (pmail-error-bad-format))
2024
 
          ;; Move to the next message.
2025
 
          (if (search-forward "\n\nFrom " nil 'move)
2026
 
              (forward-char -5))
2027
 
          (setq start (point))))
2028
 
      count)))
2029
 
 
2030
 
;; the  pmail-break-forwarded-messages  feature is not implemented
2031
 
(defun pmail-convert-to-babyl-format ()
2032
 
  (let ((count 0) start
2033
 
        (case-fold-search nil)
2034
 
        (buffer-undo-list t)
2035
 
        (invalid-input-resync
2036
 
         (function (lambda ()
2037
 
                     (message "Invalid Babyl format in inbox!")
2038
 
                     (sit-for 3)
2039
 
                     ;; Try to get back in sync with a real message.
2040
 
                     (if (re-search-forward
2041
 
                          (concat pmail-mmdf-delim1 "\\|^From") nil t)
2042
 
                         (beginning-of-line)
2043
 
                       (goto-char (point-max)))))))
2044
 
    (goto-char (point-min))
2045
 
    (save-restriction
2046
 
      (while (not (eobp))
2047
 
        (setq start (point))
2048
 
        (cond ((looking-at "BABYL OPTIONS:")    ;Babyl header
2049
 
               (if (search-forward "\n\^_" nil t)
2050
 
                   ;; If we find the proper terminator, delete through there.
2051
 
                   (delete-region (point-min) (point))
2052
 
                 (funcall invalid-input-resync)
2053
 
                 (delete-region (point-min) (point))))
2054
 
              ;; Babyl format message
2055
 
              ((looking-at "\^L")
2056
 
               (or (search-forward "\n\^_" nil t)
2057
 
                   (funcall invalid-input-resync))
2058
 
               (setq count (1+ count))
2059
 
               ;; Make sure there is no extra white space after the ^_
2060
 
               ;; at the end of the message.
2061
 
               ;; Narrowing will make sure that whatever follows the junk
2062
 
               ;; will be treated properly.
2063
 
               (delete-region (point)
2064
 
                              (save-excursion
2065
 
                                (skip-chars-forward " \t\n")
2066
 
                                (point)))
2067
 
               ;; The following let* form was wrapped in a `save-excursion'
2068
 
               ;; which in one case caused infinite looping, see:
2069
 
               ;; http://lists.gnu.org/archive/html/emacs-devel/2008-01/msg00968.html
2070
 
               ;; Removing that form leaves `point' at the end of the
2071
 
               ;; region decoded by `pmail-decode-region' which should
2072
 
               ;; be correct.
2073
 
               (let* ((header-end
2074
 
                       (progn
2075
 
                         (save-excursion
2076
 
                           (goto-char start)
2077
 
                           (forward-line 1)
2078
 
                           (if (looking-at "0")
2079
 
                               (forward-line 1)
2080
 
                             (forward-line 2))
2081
 
                           (save-restriction
2082
 
                             (narrow-to-region (point) (point-max))
2083
 
                             (rfc822-goto-eoh)
2084
 
                             (point)))))
2085
 
                      (case-fold-search t)
2086
 
                      (quoted-printable-header-field-end
2087
 
                       (save-excursion
2088
 
                         (goto-char start)
2089
 
                         (re-search-forward
2090
 
                          "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
2091
 
                          header-end t)))
2092
 
                      (base64-header-field-end
2093
 
                       (save-excursion
2094
 
                         (goto-char start)
2095
 
                         ;; Don't try to decode non-text data.
2096
 
                         (and (re-search-forward
2097
 
                               "^content-type:\\(\n?[\t ]\\)\\(text\\|message\\)/"
2098
 
                               header-end t)
2099
 
                              (goto-char start)
2100
 
                              (re-search-forward
2101
 
                               "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
2102
 
                               header-end t)))))
2103
 
                 (if quoted-printable-header-field-end
2104
 
                     (save-excursion
2105
 
                       (unless
2106
 
                           (mail-unquote-printable-region header-end (point) nil t t)
2107
 
                         (message "Malformed MIME quoted-printable message"))
2108
 
                       ;; Change "quoted-printable" to "8bit",
2109
 
                       ;; to reflect the decoding we just did.
2110
 
                       (goto-char quoted-printable-header-field-end)
2111
 
                       (delete-region (point) (search-backward ":"))
2112
 
                       (insert ": 8bit")))
2113
 
                 (if base64-header-field-end
2114
 
                     (save-excursion
2115
 
                       (when
2116
 
                           (condition-case nil
2117
 
                               (progn
2118
 
                                 (base64-decode-region (1+ header-end)
2119
 
                                                       (- (point) 2))
2120
 
                                 t)
2121
 
                             (error nil))
2122
 
                         ;; Change "base64" to "8bit", to reflect the
2123
 
                         ;; decoding we just did.
2124
 
                         (goto-char base64-header-field-end)
2125
 
                         (delete-region (point) (search-backward ":"))
2126
 
                         (insert ": 8bit"))))
2127
 
                 (setq last-coding-system-used nil)
2128
 
                 (or pmail-enable-mime
2129
 
                     (not pmail-enable-multibyte)
2130
 
                     (let ((mime-charset
2131
 
                            (if (and pmail-decode-mime-charset
2132
 
                                     (save-excursion
2133
 
                                       (goto-char start)
2134
 
                                       (search-forward "\n\n" nil t)
2135
 
                                       (let ((case-fold-search t))
2136
 
                                         (re-search-backward
2137
 
                                          pmail-mime-charset-pattern
2138
 
                                          start t))))
2139
 
                                (intern (downcase (match-string 1))))))
2140
 
                       (pmail-decode-region start (point) mime-charset))))
2141
 
               ;; Add an X-Coding-System: header if we don't have one.
2142
 
               (save-excursion
2143
 
                 (goto-char start)
2144
 
                 (forward-line 1)
2145
 
                 (if (looking-at "0")
2146
 
                     (forward-line 1)
2147
 
                   (forward-line 2))
2148
 
                 (or (save-restriction
2149
 
                       (narrow-to-region (point) (point-max))
2150
 
                       (rfc822-goto-eoh)
2151
 
                       (goto-char (point-min))
2152
 
                       (re-search-forward "^X-Coding-System:" nil t))
2153
 
                     (insert "X-Coding-System: "
2154
 
                             (symbol-name last-coding-system-used)
2155
 
                             "\n")))
2156
 
               (narrow-to-region (point) (point-max))
2157
 
               (and (= 0 (% count 10))
2158
 
                    (message "Converting to Babyl format...%d" count)))
2159
 
              ;;*** MMDF format
2160
 
              ((let ((case-fold-search t))
2161
 
                 (looking-at pmail-mmdf-delim1))
2162
 
               (let ((case-fold-search t))
2163
 
                 (replace-match "\^L\n0, unseen,,\n*** EOOH ***\n")
2164
 
                 (re-search-forward pmail-mmdf-delim2 nil t)
2165
 
                 (replace-match "\^_"))
2166
 
               (save-excursion
2167
 
                 (save-restriction
2168
 
                   (narrow-to-region start (1- (point)))
2169
 
                   (goto-char (point-min))
2170
 
                   (while (search-forward "\n\^_" nil t) ; single char "\^_"
2171
 
                     (replace-match "\n^_"))))  ; 2 chars: "^" and "_"
2172
 
               (setq last-coding-system-used nil)
2173
 
               (or pmail-enable-mime
2174
 
                   (not pmail-enable-multibyte)
2175
 
                   (decode-coding-region start (point) 'undecided))
2176
 
               (save-excursion
2177
 
                 (goto-char start)
2178
 
                 (forward-line 3)
2179
 
                 (insert "X-Coding-System: "
2180
 
                         (symbol-name last-coding-system-used)
2181
 
                         "\n"))
2182
 
               (narrow-to-region (point) (point-max))
2183
 
               (setq count (1+ count))
2184
 
               (and (= 0 (% count 10))
2185
 
                    (message "Converting to Babyl format...%d" count)))
2186
 
              ;;*** Mail format
2187
 
              ((looking-at "^From ")
2188
 
               (insert "\^L\n0, unseen,,\n*** EOOH ***\n")
2189
 
               (pmail-nuke-pinhead-header)
2190
 
               ;; If this message has a Content-Length field,
2191
 
               ;; skip to the end of the contents.
2192
 
               (let* ((header-end (save-excursion
2193
 
                                    (and (re-search-forward "\n\n" nil t)
2194
 
                                         (1- (point)))))
2195
 
                      (case-fold-search t)
2196
 
                      (quoted-printable-header-field-end
2197
 
                       (save-excursion
2198
 
                         (re-search-forward
2199
 
                          "^content-transfer-encoding:\\(\n?[\t ]\\)*quoted-printable\\(\n?[\t ]\\)*"
2200
 
                          header-end t)))
2201
 
                      (base64-header-field-end
2202
 
                       (and
2203
 
                        ;; Don't decode non-text data.
2204
 
                        (save-excursion
2205
 
                          (re-search-forward
2206
 
                           "^content-type:\\(\n?[\t ]\\)\\(text\\|message\\)/"
2207
 
                           header-end t))
2208
 
                        (save-excursion
2209
 
                          (re-search-forward
2210
 
                           "^content-transfer-encoding:\\(\n?[\t ]\\)*base64\\(\n?[\t ]\\)*"
2211
 
                           header-end t))))
2212
 
                      (size
2213
 
                       ;; Get the numeric value from the Content-Length field.
2214
 
                       (save-excursion
2215
 
                         ;; Back up to end of prev line,
2216
 
                         ;; in case the Content-Length field comes first.
2217
 
                         (forward-char -1)
2218
 
                         (and (search-forward "\ncontent-length: "
2219
 
                                              header-end t)
2220
 
                              (let ((beg (point))
2221
 
                                    (eol (progn (end-of-line) (point))))
2222
 
                                (string-to-number (buffer-substring beg eol)))))))
2223
 
                 (and size
2224
 
                      (if (and (natnump size)
2225
 
                               (<= (+ header-end size) (point-max))
2226
 
                               ;; Make sure this would put us at a position
2227
 
                               ;; that we could continue from.
2228
 
                               (save-excursion
2229
 
                                 (goto-char (+ header-end size))
2230
 
                                 (skip-chars-forward "\n")
2231
 
                                 (or (eobp)
2232
 
                                     (and (looking-at "BABYL OPTIONS:")
2233
 
                                          (search-forward "\n\^_" nil t))
2234
 
                                     (and (looking-at "\^L")
2235
 
                                          (search-forward "\n\^_" nil t))
2236
 
                                     (let ((case-fold-search t))
2237
 
                                       (looking-at pmail-mmdf-delim1))
2238
 
                                     (looking-at "From "))))
2239
 
                          (goto-char (+ header-end size))
2240
 
                        (message "Ignoring invalid Content-Length field")
2241
 
                        (sit-for 1 0 t)))
2242
 
                 (if (let ((case-fold-search nil))
2243
 
                       (re-search-forward
2244
 
                        (concat "^[\^_]?\\("
2245
 
                                pmail-unix-mail-delimiter
2246
 
                                "\\|"
2247
 
                                pmail-mmdf-delim1 "\\|"
2248
 
                                "^BABYL OPTIONS:\\|"
2249
 
                                "\^L\n[01],\\)") nil t))
2250
 
                     (goto-char (match-beginning 1))
2251
 
                   (goto-char (point-max)))
2252
 
                 (setq count (1+ count))
2253
 
                 (if quoted-printable-header-field-end
2254
 
                     (save-excursion
2255
 
                       (unless
2256
 
                           (mail-unquote-printable-region header-end (point) nil t t)
2257
 
                         (message "Malformed MIME quoted-printable message"))
2258
 
                       ;; Change "quoted-printable" to "8bit",
2259
 
                       ;; to reflect the decoding we just did.
2260
 
                       (goto-char quoted-printable-header-field-end)
2261
 
                       (delete-region (point) (search-backward ":"))
2262
 
                       (insert ": 8bit")))
2263
 
                 (if base64-header-field-end
2264
 
                     (save-excursion
2265
 
                       (when
2266
 
                           (condition-case nil
2267
 
                               (progn
2268
 
                                 (base64-decode-region
2269
 
                                  (1+ header-end)
2270
 
                                  (save-excursion
2271
 
                                    ;; Prevent base64-decode-region
2272
 
                                    ;; from removing newline characters.
2273
 
                                    (skip-chars-backward "\n\t ")
2274
 
                                    (point)))
2275
 
                                 t)
2276
 
                             (error nil))
2277
 
                         ;; Change "base64" to "8bit", to reflect the
2278
 
                         ;; decoding we just did.
2279
 
                         (goto-char base64-header-field-end)
2280
 
                         (delete-region (point) (search-backward ":"))
2281
 
                         (insert ": 8bit")))))
2282
 
 
2283
 
               (save-excursion
2284
 
                 (save-restriction
2285
 
                   (narrow-to-region start (point))
2286
 
                   (goto-char (point-min))
2287
 
                   (while (search-forward "\n\^_" nil t) ; single char
2288
 
                     (replace-match "\n^_"))))  ; 2 chars: "^" and "_"
2289
 
               ;; This is for malformed messages that don't end in newline.
2290
 
               ;; There shouldn't be any, but some users say occasionally
2291
 
               ;; there are some.
2292
 
               (or (bolp) (newline))
2293
 
               (insert ?\^_)
2294
 
               (setq last-coding-system-used nil)
2295
 
               (or pmail-enable-mime
2296
 
                   (not pmail-enable-multibyte)
2297
 
                   (let ((mime-charset
2298
 
                          (if (and pmail-decode-mime-charset
2299
 
                                   (save-excursion
2300
 
                                     (goto-char start)
2301
 
                                     (search-forward "\n\n" nil t)
2302
 
                                     (let ((case-fold-search t))
2303
 
                                       (re-search-backward
2304
 
                                        pmail-mime-charset-pattern
2305
 
                                        start t))))
2306
 
                              (intern (downcase (match-string 1))))))
2307
 
                     (pmail-decode-region start (point) mime-charset)))
2308
 
               (save-excursion
2309
 
                 (goto-char start)
2310
 
                 (forward-line 3)
2311
 
                 (insert "X-Coding-System: "
2312
 
                         (symbol-name last-coding-system-used)
2313
 
                         "\n"))
2314
 
               (narrow-to-region (point) (point-max))
2315
 
               (and (= 0 (% count 10))
2316
 
                    (message "Converting to Babyl format...%d" count)))
2317
 
              ;;
2318
 
              ;; This kludge is because some versions of sendmail.el
2319
 
              ;; insert an extra newline at the beginning that shouldn't
2320
 
              ;; be there.  sendmail.el has been fixed, but old versions
2321
 
              ;; may still be in use.  -- rms, 7 May 1993.
2322
 
              ((eolp) (delete-char 1))
2323
 
              (t (error "Cannot convert to babyl format")))))
2324
 
    (setq buffer-undo-list nil)
2325
 
    count))
2326
 
 
2327
 
;; Delete the "From ..." line, creating various other headers with
2328
 
;; information from it if they don't already exist.  Now puts the
2329
 
;; original line into a mail-from: header line for debugging and for
2330
 
;; use by the pmail-output function.
2331
 
(defun pmail-nuke-pinhead-header ()
2332
 
  (save-excursion
2333
 
    (save-restriction
2334
 
      (let ((start (point))
2335
 
            (end (progn
2336
 
                   (condition-case ()
2337
 
                       (search-forward "\n\n")
2338
 
                     (error
2339
 
                      (goto-char (point-max))
2340
 
                      (insert "\n\n")))
2341
 
                   (point)))
2342
 
            has-from has-date)
2343
 
        (narrow-to-region start end)
2344
 
        (let ((case-fold-search t))
2345
 
          (goto-char start)
2346
 
          (setq has-from (search-forward "\nFrom:" nil t))
2347
 
          (goto-char start)
2348
 
          (setq has-date (and (search-forward "\nDate:" nil t) (point)))
2349
 
          (goto-char start))
2350
 
        (let ((case-fold-search nil))
2351
 
          (if (re-search-forward (concat "^" pmail-unix-mail-delimiter) nil t)
2352
 
              (replace-match
2353
 
                (concat
2354
 
                  "Mail-from: \\&"
2355
 
                  ;; Keep and reformat the date if we don't
2356
 
                  ;;  have a Date: field.
2357
 
                  (if has-date
2358
 
                      ""
2359
 
                    (concat
2360
 
                     "Date: \\2, \\4 \\3 \\9 \\5 "
2361
 
 
2362
 
                     ;; The timezone could be matched by group 7 or group 10.
2363
 
                     ;; If neither of them matched, assume EST, since only
2364
 
                     ;; Easterners would be so sloppy.
2365
 
                     ;; It's a shame the substitution can't use "\\10".
2366
 
                     (cond
2367
 
                      ((/= (match-beginning 7) (match-end 7)) "\\7")
2368
 
                      ((/= (match-beginning 10) (match-end 10))
2369
 
                       (buffer-substring (match-beginning 10)
2370
 
                                         (match-end 10)))
2371
 
                      (t "EST"))
2372
 
                     "\n"))
2373
 
                  ;; Keep and reformat the sender if we don't
2374
 
                  ;; have a From: field.
2375
 
                  (if has-from
2376
 
                      ""
2377
 
                    "From: \\1\n"))
2378
 
                t)))))))
2379
 
 
2380
 
;;;; *** Pmail Message Formatting and Header Manipulation ***
2381
 
 
2382
 
(defun pmail-copy-headers (beg end &optional ignored-headers)
2383
 
  "Copy displayed header fields to the message viewer buffer.
2384
 
BEG and END marks the start and end positions of the message in
2385
 
the mail buffer.  If the optional argument IGNORED-HEADERS is
2386
 
non-nil, ignore all header fields whose names match that regexp.
2387
 
Otherwise, if `rmail-displayed-headers' is non-nil, copy only
2388
 
those header fields whose names match that regexp.  Otherwise,
2389
 
copy all header fields whose names do not match
2390
 
`rmail-ignored-headers' (unless they also match
2391
 
`rmail-nonignored-headers')."
2392
 
  (let ((header-start-regexp "\n[^ \t]")
2393
 
        lim)
2394
 
    (with-current-buffer pmail-buffer
2395
 
      (when (search-forward "\n\n" nil t)
2396
 
        (forward-char -1)
2397
 
        (save-restriction
2398
 
          ;; Put point right after the From header line.
2399
 
          (narrow-to-region beg (point))
2400
 
          (goto-char (point-min))
2401
 
          (unless (re-search-forward header-start-regexp nil t)
2402
 
            (pmail-error-bad-format))
2403
 
          (forward-char -1)
2404
 
          (cond
2405
 
           ;; Handle the case where all headers should be copied.
2406
 
           ((eq pmail-header-style 'full)
2407
 
            (prepend-to-buffer pmail-view-buffer beg (point-max)))
2408
 
           ;; Handle the case where the headers matching the diplayed
2409
 
           ;; headers regexp should be copied.
2410
 
           ((and pmail-displayed-headers (null ignored-headers))
2411
 
            (while (not (eobp))
2412
 
              (save-excursion
2413
 
                (setq lim (if (re-search-forward header-start-regexp nil t)
2414
 
                              (1+ (match-beginning 0))
2415
 
                            (point-max))))
2416
 
              (when (looking-at pmail-displayed-headers)
2417
 
                (append-to-buffer pmail-view-buffer (point) lim))
2418
 
              (goto-char lim)))
2419
 
           ;; Handle the ignored headers.
2420
 
           ((or ignored-headers (setq ignored-headers pmail-ignored-headers))
2421
 
            (while (and ignored-headers (not (eobp)))
2422
 
              (save-excursion
2423
 
                (setq lim (if (re-search-forward header-start-regexp nil t)
2424
 
                              (1+ (match-beginning 0))
2425
 
                            (point-max))))
2426
 
              (if (and (looking-at ignored-headers)
2427
 
                       (not (looking-at pmail-nonignored-headers)))
2428
 
                  (goto-char lim)
2429
 
                (append-to-buffer pmail-view-buffer (point) lim)
2430
 
                (goto-char lim))))
2431
 
           (t (error "No headers selected for display!"))))))))
2432
 
 
2433
 
(defun pmail-toggle-header (&optional arg)
2434
 
  "Show original message header if pruned header currently shown, or vice versa.
2435
 
With argument ARG, show the message header pruned if ARG is greater than zero;
2436
 
otherwise, show it in full."
2437
 
  (interactive "P")
2438
 
  (setq pmail-header-style
2439
 
        (cond
2440
 
         ((and (numberp arg) (> arg 0)) 'normal)
2441
 
         ((eq pmail-header-style 'full) 'normal)
2442
 
         (t 'full)))
2443
 
  (pmail-show-message-maybe))
2444
 
 
2445
 
;; Lifted from repos-count-screen-lines.
2446
 
;; Return number of screen lines between START and END.
2447
 
(defun pmail-count-screen-lines (start end)
2448
 
  (save-excursion
2449
 
    (save-restriction
2450
 
      (narrow-to-region start end)
2451
 
      (goto-char (point-min))
2452
 
      (vertical-motion (- (point-max) (point-min))))))
2453
 
 
2454
 
;;;; *** Pmail Attributes and Keywords ***
2455
 
 
2456
 
(defun pmail-get-header (name &optional msg)
2457
 
  "Return the value of message header NAME, nil if no such header
2458
 
exists.  MSG, if set identifies the message number to use.  The
2459
 
current mail message will be used otherwise."
2460
 
  (save-excursion
2461
 
    (save-restriction
2462
 
      (with-current-buffer pmail-buffer
2463
 
        (widen)
2464
 
        (let* ((n (or msg pmail-current-message))
2465
 
               (beg (pmail-msgbeg n))
2466
 
               end)
2467
 
          (goto-char beg)
2468
 
          (setq end (search-forward "\n\n" nil t))
2469
 
          (if end
2470
 
              (progn
2471
 
                (narrow-to-region beg end)
2472
 
                (mail-fetch-field name))
2473
 
            (pmail-error-bad-format msg)))))))
2474
 
  
2475
 
(defun pmail-get-attr-names (&optional msg)
2476
 
  "Return the message attributes in a comma separated string.
2477
 
MSG, if set identifies the message number to use.  The current
2478
 
mail message will be used otherwise."
2479
 
  (let ((value (pmail-get-header pmail-attribute-header msg))
2480
 
        result temp)
2481
 
    (dotimes (index (length value))
2482
 
      (setq temp (and (not (= ?- (aref value index)))
2483
 
                      (nth 1 (aref pmail-attr-array index)))
2484
 
            result
2485
 
            (cond
2486
 
             ((and temp result) (format "%s, %s" result temp))
2487
 
             (temp temp)
2488
 
             (t result))))
2489
 
    result))
2490
 
 
2491
 
(defun pmail-get-keywords (&optional msg)
2492
 
  "Return the message keywords in a comma separated string.
2493
 
MSG, if set identifies the message number to use.  The current
2494
 
mail message will be used otherwise."
2495
 
  (pmail-get-header pmail-keyword-header msg))
2496
 
 
2497
 
(defun pmail-display-labels ()
2498
 
  "Update the mode line with the (set) attributes and keywords
2499
 
for the current message."
2500
 
  (let (blurb attr-names keywords)
2501
 
    ;; Combine the message attributes and keywords into a comma
2502
 
    ;; separated list.
2503
 
    (setq attr-names (pmail-get-attr-names pmail-current-message)
2504
 
          keywords (pmail-get-keywords pmail-current-message))
2505
 
    (setq blurb
2506
 
          (cond
2507
 
           ((and attr-names keywords) (concat attr-names ", " keywords))
2508
 
           (attr-names attr-names)
2509
 
           (keywords keywords)
2510
 
           (t "")))
2511
 
    (setq mode-line-process
2512
 
          (format " %d/%d%s"
2513
 
                  pmail-current-message pmail-total-messages blurb))
2514
 
    ;; If pmail-enable-mime is non-nil, we may have to update
2515
 
    ;; `mode-line-process' of pmail-view-buffer too.
2516
 
    (if (and pmail-enable-mime
2517
 
             (not (eq (current-buffer) pmail-view-buffer))
2518
 
             (buffer-live-p pmail-view-buffer))
2519
 
        (let ((mlp mode-line-process))
2520
 
          (with-current-buffer pmail-view-buffer
2521
 
            (setq mode-line-process mlp))))))
2522
 
 
2523
 
(defun pmail-get-attr-value (attr state)
2524
 
  "Return the character value for ATTR.
2525
 
ATTR is a (numeric) index, an offset into the mbox attribute
2526
 
header value. STATE is one of nil, t, or a character value."
2527
 
  (cond
2528
 
   ((numberp state) state)
2529
 
   ((not state) ?-)
2530
 
   (t (nth 0 (aref pmail-attr-array attr)))))
2531
 
 
2532
 
(defun pmail-set-attribute (attr state &optional msgnum)
2533
 
  "Turn an attribute of a message on or off according to STATE.
2534
 
STATE is either nil or the character (numeric) value associated
2535
 
with the state (nil represents off and non-nil represents on).
2536
 
ATTR is the index of the attribute.  MSGNUM is message number to
2537
 
change; nil means current message."
2538
 
  (set-buffer pmail-buffer)
2539
 
  (let ((value (pmail-get-attr-value attr state))
2540
 
        (omax (point-max-marker))
2541
 
        (omin (point-min-marker))
2542
 
        (buffer-read-only nil)
2543
 
        limit)
2544
 
    (or msgnum (setq msgnum pmail-current-message))
2545
 
    (if (> msgnum 0)
2546
 
        (unwind-protect
2547
 
            (save-excursion
2548
 
              ;; Determine if the current state is the desired state.
2549
 
              (widen)
2550
 
              (goto-char (pmail-msgbeg msgnum))
2551
 
              (save-excursion
2552
 
                (setq limit (search-forward "\n\n" nil t)))
2553
 
              (when (search-forward (concat pmail-attribute-header ": ") limit t)
2554
 
                (forward-char attr)
2555
 
                (when (/= value (char-after))
2556
 
                  (delete-char 1)
2557
 
                  (insert value)))
2558
 
              (if (= attr pmail-deleted-attr-index)
2559
 
                  (pmail-set-message-deleted-p msgnum state)))
2560
 
          ;; Note: we don't use save-restriction because that does not work right
2561
 
          ;; if changes are made outside the saved restriction
2562
 
          ;; before that restriction is restored.
2563
 
          (narrow-to-region omin omax)
2564
 
          (set-marker omin nil)
2565
 
          (set-marker omax nil)
2566
 
          (if (= msgnum pmail-current-message)
2567
 
              (pmail-display-labels))))))
2568
 
 
2569
 
(defun pmail-message-attr-p (msg attrs)
2570
 
  "Return t if the attributes header for message MSG contains a
2571
 
match for the regexp ATTRS."
2572
 
  (save-excursion
2573
 
    (save-restriction
2574
 
      (let ((start (pmail-msgbeg msg))
2575
 
            limit)
2576
 
        (widen)
2577
 
        (goto-char start)
2578
 
        (setq limit (search-forward "\n\n" (pmail-msgend msg) t))
2579
 
        (goto-char start)
2580
 
        (and limit
2581
 
             (search-forward (concat pmail-attribute-header ": ") limit t)
2582
 
             (looking-at attrs))))))
2583
 
 
2584
 
(defun pmail-message-unseen-p (msgnum)
2585
 
  "Test the unseen attribute for message MSGNUM.
2586
 
Return non-nil if the unseen attribute is set, nil otherwise."
2587
 
  (pmail-message-attr-p msgnum "......U"))
2588
 
 
2589
 
 
2590
 
;;;; *** Pmail Message Selection And Support ***
2591
 
 
2592
 
;; (defun pmail-get-collection-buffer ()
2593
 
;;   "Return the buffer containing the mbox formatted messages."
2594
 
;;   (if (eq major-mode 'pmail-mode)
2595
 
;;       (if pmail-buffers-swapped-p
2596
 
;;        pmail-view-buffer
2597
 
;;      pmail-buffer)
2598
 
;;     (error "The current buffer must be in Pmail mode.")))
2599
 
 
2600
 
(defun pmail-use-collection-buffer ()
2601
 
  "Insure that the Pmail buffer contains the message collection.
2602
 
Return the current message number if the Pmail buffer is in a
2603
 
swapped state, i.e. it currently contains a single decoded
2604
 
message rather than an entire message collection, nil otherwise."
2605
 
  (let (result)
2606
 
    (when pmail-buffers-swapped-p
2607
 
      (buffer-swap-text pmail-view-buffer)
2608
 
      (setq pmail-buffers-swapped-p nil
2609
 
            result pmail-current-message))
2610
 
    result))
2611
 
 
2612
 
(defun pmail-use-viewer-buffer (&optional msgnum)
2613
 
  "Insure that the Pmail buffer contains the current message.
2614
 
If message MSGNUM is non-nil make it the current message and
2615
 
display it.  Return nil."
2616
 
  (let (result)
2617
 
    (cond
2618
 
     ((not pmail-buffers-swapped-p)
2619
 
      (let ((message (or msgnum pmail-current-message)))
2620
 
        (pmail-show-message message)))
2621
 
     ((and msgnum (/= msgnum pmail-current-message))
2622
 
      (pmail-show-message msgnum))
2623
 
     (t))
2624
 
    result))
2625
 
 
2626
 
(defun pmail-msgend (n)
2627
 
  (marker-position (aref pmail-message-vector (1+ n))))
2628
 
 
2629
 
(defun pmail-msgbeg (n)
2630
 
  (marker-position (aref pmail-message-vector n)))
2631
 
 
2632
 
(defun pmail-widen-to-current-msgbeg (function)
2633
 
  "Call FUNCTION with point at start of internal data of current message.
2634
 
Assumes that bounds were previously narrowed to display the message in Pmail.
2635
 
The bounds are widened enough to move point where desired, then narrowed
2636
 
again afterward.
2637
 
 
2638
 
FUNCTION may not change the visible text of the message, but it may
2639
 
change the invisible header text."
2640
 
  (save-excursion
2641
 
    (unwind-protect
2642
 
        (progn
2643
 
          (narrow-to-region (pmail-msgbeg pmail-current-message)
2644
 
                            (point-max))
2645
 
          (goto-char (point-min))
2646
 
          (funcall function))
2647
 
        ;; Note: we don't use save-restriction because that does not work right
2648
 
        ;; if changes are made outside the saved restriction
2649
 
        ;; before that restriction is restored.
2650
 
      (narrow-to-region (pmail-msgbeg pmail-current-message)
2651
 
                        (pmail-msgend pmail-current-message)))))
2652
 
 
2653
 
(defun pmail-forget-messages ()
2654
 
  (unwind-protect
2655
 
      (if (vectorp pmail-message-vector)
2656
 
          (let* ((i 0)
2657
 
                 (v pmail-message-vector)
2658
 
                 (n (length v)))
2659
 
            (while (< i n)
2660
 
              (move-marker (aref v i)  nil)
2661
 
              (setq i (1+ i)))))
2662
 
    (setq pmail-message-vector nil)
2663
 
    (setq pmail-msgref-vector nil)
2664
 
    (setq pmail-deleted-vector nil)))
2665
 
 
2666
 
(defun pmail-maybe-set-message-counters ()
2667
 
  (if (not (and pmail-deleted-vector
2668
 
                pmail-message-vector
2669
 
                pmail-current-message
2670
 
                pmail-total-messages))
2671
 
      (pmail-set-message-counters)))
2672
 
 
2673
 
(defun pmail-count-new-messages (&optional nomsg)
2674
 
  "Count the number of new messages in the region.
2675
 
Output a helpful message unless NOMSG is non-nil."
2676
 
  (let* ((case-fold-search nil)
2677
 
         (total-messages 0)
2678
 
         (messages-head nil)
2679
 
         (deleted-head nil))
2680
 
    (or nomsg (message "Counting new messages..."))
2681
 
    (goto-char (point-max))
2682
 
    ;; Put at the end of messages-head
2683
 
    ;; the entry for message N+1, which marks
2684
 
    ;; the end of message N.  (N = number of messages).
2685
 
    (setq messages-head (list (point-marker)))
2686
 
    (pmail-set-message-counters-counter (point-min))
2687
 
    (setq pmail-current-message (1+ pmail-total-messages))
2688
 
    (setq pmail-total-messages
2689
 
          (+ pmail-total-messages total-messages))
2690
 
    (setq pmail-message-vector
2691
 
          (vconcat pmail-message-vector (cdr messages-head)))
2692
 
    (aset pmail-message-vector
2693
 
          pmail-current-message (car messages-head))
2694
 
    (setq pmail-deleted-vector
2695
 
          (concat pmail-deleted-vector deleted-head))
2696
 
    (setq pmail-summary-vector
2697
 
          (vconcat pmail-summary-vector (make-vector total-messages nil)))
2698
 
    (setq pmail-msgref-vector
2699
 
          (vconcat pmail-msgref-vector (make-vector total-messages nil)))
2700
 
    ;; Fill in the new elements of pmail-msgref-vector.
2701
 
    (let ((i (1+ (- pmail-total-messages total-messages))))
2702
 
      (while (<= i pmail-total-messages)
2703
 
        (aset pmail-msgref-vector i (list i))
2704
 
        (setq i (1+ i))))
2705
 
    (goto-char (point-min))
2706
 
    (or nomsg (message "Counting new messages...done (%d)" total-messages))))
2707
 
 
2708
 
(defun pmail-set-message-counters ()
2709
 
  (pmail-forget-messages)
2710
 
  (save-excursion
2711
 
    (save-restriction
2712
 
      (widen)
2713
 
      (let* ((point-save (point))
2714
 
             (total-messages 0)
2715
 
             (messages-after-point)
2716
 
             (case-fold-search nil)
2717
 
             (messages-head nil)
2718
 
             (deleted-head nil))
2719
 
        ;; Determine how many messages follow point.
2720
 
        (message "Counting messages...")
2721
 
        (goto-char (point-max))
2722
 
        ;; Put at the end of messages-head
2723
 
        ;; the entry for message N+1, which marks
2724
 
        ;; the end of message N.  (N = number of messages).
2725
 
        (setq messages-head (list (point-marker)))
2726
 
        (pmail-set-message-counters-counter (min (point) point-save))
2727
 
        (setq messages-after-point total-messages)
2728
 
 
2729
 
        ;; Determine how many precede point.
2730
 
        (pmail-set-message-counters-counter)
2731
 
        (setq pmail-total-messages total-messages)
2732
 
        (setq pmail-current-message
2733
 
              (min total-messages
2734
 
                   (max 1 (- total-messages messages-after-point))))
2735
 
        (setq pmail-message-vector
2736
 
              (apply 'vector (cons (point-min-marker) messages-head))
2737
 
              pmail-deleted-vector (concat "0" deleted-head)
2738
 
              pmail-summary-vector (make-vector pmail-total-messages nil)
2739
 
              pmail-msgref-vector (make-vector (1+ pmail-total-messages) nil))
2740
 
        (let ((i 0))
2741
 
          (while (<= i pmail-total-messages)
2742
 
            (aset pmail-msgref-vector i (list i))
2743
 
            (setq i (1+ i))))
2744
 
        (message "Counting messages...done")))))
2745
 
 
2746
 
 
2747
 
(defsubst pmail-collect-deleted (message-end)
2748
 
  "Collect the message deletion flags for each message.
2749
 
MESSAGE-END is the buffer position corresponding to the end of
2750
 
the message.  Point is at the beginning of the message."
2751
 
  ;; NOTE: This piece of code will be executed on a per-message basis.
2752
 
  ;; In the face of thousands of messages, it has to be as fast as
2753
 
  ;; possible, hence some brute force constant use is employed in
2754
 
  ;; addition to inlining.
2755
 
  (save-excursion
2756
 
    (setq deleted-head
2757
 
          (cons (if (and (search-forward (concat pmail-attribute-header ": ") message-end t)
2758
 
                         (looking-at "?D"))
2759
 
                    ?D
2760
 
                  ?\ ) deleted-head))))
2761
 
 
2762
 
(defun pmail-set-message-counters-counter (&optional stop)
2763
 
  ;; Collect the start position for each message into 'messages-head.
2764
 
  (let ((start (point)))
2765
 
    (while (search-backward "\n\nFrom " stop t)
2766
 
      (forward-char 2)
2767
 
      (pmail-collect-deleted start)
2768
 
      ;; Show progress after every 20 messages or so.
2769
 
      (setq messages-head (cons (point-marker) messages-head)
2770
 
            total-messages (1+ total-messages)
2771
 
            start (point))
2772
 
      (if (zerop (% total-messages 20))
2773
 
          (message "Counting messages...%d" total-messages)))
2774
 
    ;; Handle the first message, maybe.
2775
 
    (if stop
2776
 
        (goto-char stop)
2777
 
      (goto-char (point-min)))
2778
 
    (unless (not (looking-at "From "))
2779
 
      (pmail-collect-deleted start)
2780
 
      (setq messages-head (cons (point-marker) messages-head)
2781
 
            total-messages (1+ total-messages)))))
2782
 
 
2783
 
(defun pmail-beginning-of-message ()
2784
 
  "Show current message starting from the beginning."
2785
 
  (interactive)
2786
 
  (let ((pmail-show-message-hook
2787
 
         (list (function (lambda ()
2788
 
                           (goto-char (point-min)))))))
2789
 
    (pmail-show-message-maybe pmail-current-message)))
2790
 
 
2791
 
(defun pmail-end-of-message ()
2792
 
  "Show bottom of current message."
2793
 
  (interactive)
2794
 
  (let ((pmail-show-message-hook
2795
 
         (list (function (lambda ()
2796
 
                           (goto-char (point-max))
2797
 
                           (recenter (1- (window-height))))))))
2798
 
    (pmail-show-message-maybe pmail-current-message)))
2799
 
 
2800
 
(defun pmail-unknown-mail-followup-to ()
2801
 
  "Handle a \"Mail-Followup-To\" header field with an unknown mailing list.
2802
 
Ask the user whether to add that list name to `mail-mailing-lists'."
2803
 
   (save-restriction
2804
 
     (let ((mail-followup-to (mail-fetch-field "mail-followup-to" nil t)))
2805
 
       (when mail-followup-to
2806
 
         (let ((addresses
2807
 
                (split-string
2808
 
                 (mail-strip-quoted-names mail-followup-to)
2809
 
                 ",[[:space:]]+" t)))
2810
 
           (dolist (addr addresses)
2811
 
             (when (and (not (member addr mail-mailing-lists))
2812
 
                        (not
2813
 
                         ;; taken from pmailsum.el
2814
 
                         (string-match
2815
 
                          (or pmail-user-mail-address-regexp
2816
 
                              (concat "^\\("
2817
 
                                      (regexp-quote (user-login-name))
2818
 
                                      "\\($\\|@\\)\\|"
2819
 
                                      (regexp-quote
2820
 
                                       (or user-mail-address
2821
 
                                           (concat (user-login-name) "@"
2822
 
                                                   (or mail-host-address
2823
 
                                                       (system-name)))))
2824
 
                                      "\\>\\)"))
2825
 
                          addr))
2826
 
                        (y-or-n-p
2827
 
                         (format "Add `%s' to `mail-mailing-lists'? "
2828
 
                                 addr)))
2829
 
               (customize-save-variable 'mail-mailing-lists
2830
 
                                        (cons addr mail-mailing-lists)))))))))
2831
 
 
2832
 
(defun pmail-swap-buffers-maybe ()
2833
 
  "Determine if the Pmail buffer is showing a message.
2834
 
If so restore the actual mbox message collection."
2835
 
  (unless (not pmail-buffers-swapped-p)
2836
 
    (with-current-buffer pmail-buffer
2837
 
      (buffer-swap-text pmail-view-buffer)
2838
 
      (setq pmail-buffers-swapped-p nil))))
2839
 
 
2840
 
(defun pmail-show-message-maybe (&optional n no-summary)
2841
 
  "Show message number N (prefix argument), counting from start of file.
2842
 
If summary buffer is currently displayed, update current message there also."
2843
 
  (interactive "p")
2844
 
  (or (eq major-mode 'pmail-mode)
2845
 
      (switch-to-buffer pmail-buffer))
2846
 
  (pmail-swap-buffers-maybe)
2847
 
  (pmail-maybe-set-message-counters)
2848
 
  (widen)
2849
 
  (let (blurb)
2850
 
    (if (zerop pmail-total-messages)
2851
 
        (save-excursion
2852
 
          (with-current-buffer pmail-view-buffer
2853
 
            (erase-buffer)
2854
 
            (setq blurb "No mail.")))
2855
 
      (setq blurb (pmail-show-message n))
2856
 
      (when mail-mailing-lists
2857
 
        (pmail-unknown-mail-followup-to))
2858
 
      (if transient-mark-mode (deactivate-mark))
2859
 
      ;; If there is a summary buffer, try to move to this message
2860
 
      ;; in that buffer.  But don't complain if this message
2861
 
      ;; is not mentioned in the summary.
2862
 
      ;; Don't do this at all if we were called on behalf
2863
 
      ;; of cursor motion in the summary buffer.
2864
 
      (and (pmail-summary-exists) (not no-summary)
2865
 
           (let ((curr-msg pmail-current-message))
2866
 
             (pmail-select-summary
2867
 
              (pmail-summary-goto-msg curr-msg t t))))
2868
 
      (with-current-buffer pmail-buffer
2869
 
        (pmail-auto-file)))
2870
 
    (if blurb
2871
 
        (message blurb))))
2872
 
 
2873
 
(defun pmail-is-text-p ()
2874
 
  "Return t if the region contains a text message, nil
2875
 
otherwise."
2876
 
  (save-excursion
2877
 
    (let ((text-regexp "\\(text\\|message\\)/")
2878
 
          (content-type-header (mail-fetch-field "content-type")))
2879
 
      ;; The message is text if either there is no content type header
2880
 
      ;; (a default of "text/plain; charset=US-ASCII" is assumed) or
2881
 
      ;; the base content type is either text or message.
2882
 
      (or (not content-type-header)
2883
 
          (string-match text-regexp content-type-header)))))
2884
 
 
2885
 
(defun pmail-show-message (&optional msg)
2886
 
  "Show message MSG using a special view buffer.
2887
 
Return text to display in the minibuffer if MSG is out of
2888
 
range (displaying a reasonable choice as well), nil otherwise.
2889
 
The current mail message becomes the message displayed."
2890
 
  (let ((mbox-buf pmail-buffer)
2891
 
        (view-buf pmail-view-buffer)
2892
 
        blurb beg end body-start coding-system character-coding is-text-message)
2893
 
    (if (not msg)
2894
 
        (setq msg pmail-current-message))
2895
 
    (cond ((<= msg 0)
2896
 
           (setq msg 1
2897
 
                 pmail-current-message 1
2898
 
                 blurb "No previous message"))
2899
 
          ((> msg pmail-total-messages)
2900
 
           (setq msg pmail-total-messages
2901
 
                 pmail-current-message pmail-total-messages
2902
 
                 blurb "No following message"))
2903
 
          (t (setq pmail-current-message msg)))
2904
 
    (with-current-buffer pmail-buffer
2905
 
      ;; Mark the message as seen, bracket the message in the mail
2906
 
      ;; buffer and determine the coding system the transfer encoding.
2907
 
      (pmail-set-attribute pmail-unseen-attr-index nil)
2908
 
      (setq beg (pmail-msgbeg msg)
2909
 
            end (pmail-msgend msg))
2910
 
      (widen)
2911
 
      (narrow-to-region beg end)
2912
 
      (goto-char beg)
2913
 
      (setq body-start (search-forward "\n\n" nil t))
2914
 
      (narrow-to-region beg (point))
2915
 
      (goto-char beg)
2916
 
      (setq character-coding (mail-fetch-field "content-transfer-encoding")
2917
 
            is-text-message (pmail-is-text-p)
2918
 
            coding-system (pmail-get-coding-system))
2919
 
      (widen)
2920
 
      (narrow-to-region beg end)
2921
 
      ;; Decode the message body into an empty view buffer using a
2922
 
      ;; unibyte temporary buffer where the character decoding takes
2923
 
      ;; place.
2924
 
      (with-current-buffer pmail-view-buffer
2925
 
        (erase-buffer))
2926
 
      (with-temp-buffer
2927
 
        (set-buffer-multibyte nil)
2928
 
        (insert-buffer-substring mbox-buf body-start end)
2929
 
        (cond
2930
 
         ((string= character-coding "quoted-printable")
2931
 
          (mail-unquote-printable-region (point-min) (point-max)))
2932
 
         ((and (string= character-coding "base64") is-text-message)
2933
 
          (base64-decode-region (point-min) (point-max)))
2934
 
         ((eq character-coding 'uuencode)
2935
 
          (error "Not supported yet."))
2936
 
         (t))
2937
 
        (pmail-decode-region (point-min) (point-max) coding-system view-buf))
2938
 
      ;; Copy the headers to the front of the message view buffer.
2939
 
      (with-current-buffer pmail-view-buffer
2940
 
        (goto-char (point-min)))
2941
 
      (pmail-copy-headers beg end)
2942
 
      ;; Add the separator (blank line) between headers and body;
2943
 
      ;; highlight the message, activate any URL like text and add
2944
 
      ;; special highlighting for and quoted material.
2945
 
      (with-current-buffer pmail-view-buffer
2946
 
        (insert "\n")
2947
 
        (goto-char (point-min))
2948
 
        (pmail-highlight-headers)
2949
 
        ;(pmail-activate-urls)
2950
 
        ;(pmail-process-quoted-material)
2951
 
        )
2952
 
      ;; Update the mode-line with message status information and swap
2953
 
      ;; the view buffer/mail buffer contents.
2954
 
      (pmail-display-labels)
2955
 
      (buffer-swap-text pmail-view-buffer)
2956
 
      (setq pmail-buffers-swapped-p t)
2957
 
      (run-hooks 'pmail-show-message-hook))
2958
 
    blurb))
2959
 
 
2960
 
;; Find all occurrences of certain fields, and highlight them.
2961
 
(defun pmail-highlight-headers ()
2962
 
  ;; Do this only if the system supports faces.
2963
 
  (if (and (fboundp 'internal-find-face)
2964
 
           pmail-highlighted-headers)
2965
 
      (save-excursion
2966
 
        (search-forward "\n\n" nil 'move)
2967
 
        (save-restriction
2968
 
          (narrow-to-region (point-min) (point))
2969
 
          (let ((case-fold-search t)
2970
 
                (inhibit-read-only t)
2971
 
                ;; Highlight with boldface if that is available.
2972
 
                ;; Otherwise use the `highlight' face.
2973
 
                (face (or 'pmail-highlight
2974
 
                          (if (face-differs-from-default-p 'bold)
2975
 
                              'bold 'highlight)))
2976
 
                ;; List of overlays to reuse.
2977
 
                (overlays pmail-overlay-list))
2978
 
            (goto-char (point-min))
2979
 
            (while (re-search-forward pmail-highlighted-headers nil t)
2980
 
              (skip-chars-forward " \t")
2981
 
              (let ((beg (point))
2982
 
                    overlay)
2983
 
                (while (progn (forward-line 1)
2984
 
                              (looking-at "[ \t]")))
2985
 
                ;; Back up over newline, then trailing spaces or tabs
2986
 
                (forward-char -1)
2987
 
                (while (member (preceding-char) '(?  ?\t))
2988
 
                  (forward-char -1))
2989
 
                (if overlays
2990
 
                    ;; Reuse an overlay we already have.
2991
 
                    (progn
2992
 
                      (setq overlay (car overlays)
2993
 
                            overlays (cdr overlays))
2994
 
                      (overlay-put overlay 'face face)
2995
 
                      (move-overlay overlay beg (point)))
2996
 
                  ;; Make a new overlay and add it to
2997
 
                  ;; pmail-overlay-list.
2998
 
                  (setq overlay (make-overlay beg (point)))
2999
 
                  (overlay-put overlay 'face face)
3000
 
                  (setq pmail-overlay-list
3001
 
                        (cons overlay pmail-overlay-list))))))))))
3002
 
 
3003
 
(defun pmail-auto-file ()
3004
 
  "Automatically move a message into a sub-folder based on criteria.
3005
 
Called when a new message is displayed."
3006
 
  (if (or (zerop pmail-total-messages)
3007
 
          (pmail-message-attr-p pmail-current-message "...F...")
3008
 
          (not (string= (buffer-file-name)
3009
 
                        (expand-file-name pmail-file-name))))
3010
 
      ;; Do nothing if the message has already been filed or if there
3011
 
      ;; are no messages.
3012
 
      nil
3013
 
    ;; Find out some basics (common fields)
3014
 
    (let ((from (mail-fetch-field "from"))
3015
 
          (subj (mail-fetch-field "subject"))
3016
 
          (to   (concat (mail-fetch-field "to") "," (mail-fetch-field "cc")))
3017
 
          (d pmail-automatic-folder-directives)
3018
 
          (directive-loop nil)
3019
 
          (folder nil))
3020
 
      (while d
3021
 
        (setq folder (car (car d))
3022
 
              directive-loop (cdr (car d)))
3023
 
        (while (and (car directive-loop)
3024
 
                    (let ((f (cond
3025
 
                              ((string= (car directive-loop) "from") from)
3026
 
                              ((string= (car directive-loop) "to") to)
3027
 
                              ((string= (car directive-loop) "subject") subj)
3028
 
                              (t (mail-fetch-field (car directive-loop))))))
3029
 
                      (and f (string-match (car (cdr directive-loop)) f))))
3030
 
          (setq directive-loop (cdr (cdr directive-loop))))
3031
 
        ;; If there are no directives left, then it was a complete match.
3032
 
        (if (null directive-loop)
3033
 
            (if (null folder)
3034
 
                (pmail-delete-forward)
3035
 
              (if (string= "/dev/null" folder)
3036
 
                  (pmail-delete-message)
3037
 
                (pmail-output-to-pmail-file folder 1 t)
3038
 
                (setq d nil))))
3039
 
        (setq d (cdr d))))))
3040
 
 
3041
 
(defun pmail-next-message (n)
3042
 
  "Show following message whether deleted or not.
3043
 
With prefix arg N, moves forward N messages, or backward if N is negative."
3044
 
  (interactive "p")
3045
 
  (set-buffer pmail-buffer)
3046
 
  (pmail-maybe-set-message-counters)
3047
 
  (pmail-show-message-maybe (+ pmail-current-message n)))
3048
 
 
3049
 
(defun pmail-previous-message (n)
3050
 
  "Show previous message whether deleted or not.
3051
 
With prefix arg N, moves backward N messages, or forward if N is negative."
3052
 
  (interactive "p")
3053
 
  (pmail-next-message (- n)))
3054
 
 
3055
 
(defun pmail-next-undeleted-message (n)
3056
 
  "Show following non-deleted message.
3057
 
With prefix arg N, moves forward N non-deleted messages,
3058
 
or backward if N is negative.
3059
 
 
3060
 
Returns t if a new message is being shown, nil otherwise."
3061
 
  (interactive "p")
3062
 
  (set-buffer pmail-buffer)
3063
 
  (pmail-maybe-set-message-counters)
3064
 
  (let ((lastwin pmail-current-message)
3065
 
        (current pmail-current-message))
3066
 
    (while (and (> n 0) (< current pmail-total-messages))
3067
 
      (setq current (1+ current))
3068
 
      (if (not (pmail-message-deleted-p current))
3069
 
          (setq lastwin current n (1- n))))
3070
 
    (while (and (< n 0) (> current 1))
3071
 
      (setq current (1- current))
3072
 
      (if (not (pmail-message-deleted-p current))
3073
 
          (setq lastwin current n (1+ n))))
3074
 
    (if (/= lastwin pmail-current-message)
3075
 
        (progn (pmail-show-message-maybe lastwin)
3076
 
               t)
3077
 
      (if (< n 0)
3078
 
          (message "No previous nondeleted message"))
3079
 
      (if (> n 0)
3080
 
          (message "No following nondeleted message"))
3081
 
      nil)))
3082
 
 
3083
 
(defun pmail-previous-undeleted-message (n)
3084
 
  "Show previous non-deleted message.
3085
 
With prefix argument N, moves backward N non-deleted messages,
3086
 
or forward if N is negative."
3087
 
  (interactive "p")
3088
 
  (pmail-next-undeleted-message (- n)))
3089
 
 
3090
 
(defun pmail-first-message ()
3091
 
  "Show first message in file."
3092
 
  (interactive)
3093
 
  (pmail-maybe-set-message-counters)
3094
 
  (pmail-show-message-maybe (< 1 pmail-total-messages)))
3095
 
 
3096
 
(defun pmail-last-message ()
3097
 
  "Show last message in file."
3098
 
  (interactive)
3099
 
  (pmail-maybe-set-message-counters)
3100
 
  (pmail-show-message-maybe pmail-total-messages))
3101
 
 
3102
 
(defun pmail-what-message ()
3103
 
  (let ((where (point))
3104
 
        (low 1)
3105
 
        (high pmail-total-messages)
3106
 
        (mid (/ pmail-total-messages 2)))
3107
 
    (while (> (- high low) 1)
3108
 
      (if (>= where (pmail-msgbeg mid))
3109
 
          (setq low mid)
3110
 
        (setq high mid))
3111
 
      (setq mid (+ low (/ (- high low) 2))))
3112
 
    (if (>= where (pmail-msgbeg high)) high low)))
3113
 
 
3114
 
(defun pmail-message-recipients-p (msg recipients &optional primary-only)
3115
 
  (save-restriction
3116
 
    (goto-char (pmail-msgbeg msg))
3117
 
    (search-forward "\n*** EOOH ***\n")
3118
 
    (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
3119
 
    (or (string-match recipients (or (mail-fetch-field "To") ""))
3120
 
        (string-match recipients (or (mail-fetch-field "From") ""))
3121
 
        (if (not primary-only)
3122
 
            (string-match recipients (or (mail-fetch-field "Cc") ""))))))
3123
 
 
3124
 
(defun pmail-message-regexp-p (n regexp)
3125
 
  "Return t, if for message number N, regexp REGEXP matches in the header."
3126
 
  (let ((beg (pmail-msgbeg n))
3127
 
        (end (pmail-msgend n)))
3128
 
    (goto-char beg)
3129
 
    (forward-line 1)
3130
 
    (save-excursion
3131
 
      (save-restriction
3132
 
        (if (prog1 (= (following-char) ?0)
3133
 
              (forward-line 2)
3134
 
              ;; If there's a Summary-line in the (otherwise empty)
3135
 
              ;; header, we didn't yet get past the EOOH line.
3136
 
              (when (looking-at "^\\*\\*\\* EOOH \\*\\*\\*\n")
3137
 
                (forward-line 1))
3138
 
              (setq beg (point))
3139
 
              (narrow-to-region (point) end))
3140
 
            (progn
3141
 
              (rfc822-goto-eoh)
3142
 
              (setq end (point)))
3143
 
          (setq beg (point))
3144
 
          (search-forward "\n*** EOOH ***\n" end t)
3145
 
          (setq end (1+ (match-beginning 0)))))
3146
 
        (goto-char beg)
3147
 
        (if pmail-enable-mime
3148
 
            (funcall pmail-search-mime-header-function n regexp end)
3149
 
          (re-search-forward regexp end t)))))
3150
 
 
3151
 
(defun pmail-search-message (msg regexp)
3152
 
  "Return non-nil, if for message number MSG, regexp REGEXP matches."
3153
 
  (goto-char (pmail-msgbeg msg))
3154
 
  (if pmail-enable-mime
3155
 
      (funcall pmail-search-mime-message-function msg regexp)
3156
 
    (re-search-forward regexp (pmail-msgend msg) t)))
3157
 
 
3158
 
(defvar pmail-search-last-regexp nil)
3159
 
(defun pmail-search (regexp &optional n)
3160
 
  "Show message containing next match for REGEXP (but not the current msg).
3161
 
Prefix argument gives repeat count; negative argument means search
3162
 
backwards (through earlier messages).
3163
 
Interactively, empty argument means use same regexp used last time."
3164
 
  (interactive
3165
 
    (let* ((reversep (< (prefix-numeric-value current-prefix-arg) 0))
3166
 
           (prompt
3167
 
            (concat (if reversep "Reverse " "") "Pmail search (regexp"))
3168
 
           regexp)
3169
 
      (setq prompt
3170
 
            (concat prompt
3171
 
                    (if pmail-search-last-regexp
3172
 
                        (concat ", default "
3173
 
                                pmail-search-last-regexp "): ")
3174
 
                      "): ")))
3175
 
      (setq regexp (read-string prompt))
3176
 
      (cond ((not (equal regexp ""))
3177
 
             (setq pmail-search-last-regexp regexp))
3178
 
            ((not pmail-search-last-regexp)
3179
 
             (error "No previous Pmail search string")))
3180
 
      (list pmail-search-last-regexp
3181
 
            (prefix-numeric-value current-prefix-arg))))
3182
 
  (or n (setq n 1))
3183
 
  (message "%sPmail search for %s..."
3184
 
           (if (< n 0) "Reverse " "")
3185
 
           regexp)
3186
 
  (set-buffer pmail-buffer)
3187
 
  (pmail-maybe-set-message-counters)
3188
 
  (let ((omin (point-min))
3189
 
        (omax (point-max))
3190
 
        (opoint (point))
3191
 
        win
3192
 
        (reversep (< n 0))
3193
 
        (msg pmail-current-message))
3194
 
    (unwind-protect
3195
 
        (progn
3196
 
          (widen)
3197
 
          (while (/= n 0)
3198
 
            ;; Check messages one by one, advancing message number up or down
3199
 
            ;; but searching forward through each message.
3200
 
            (if reversep
3201
 
                (while (and (null win) (> msg 1))
3202
 
                  (setq msg (1- msg)
3203
 
                        win (pmail-search-message msg regexp)))
3204
 
              (while (and (null win) (< msg pmail-total-messages))
3205
 
                (setq msg (1+ msg)
3206
 
                      win (pmail-search-message msg regexp))))
3207
 
            (setq n (+ n (if reversep 1 -1)))))
3208
 
      (if win
3209
 
          (progn
3210
 
            (pmail-show-message-maybe msg)
3211
 
            ;; Search forward (if this is a normal search) or backward
3212
 
            ;; (if this is a reverse search) through this message to
3213
 
            ;; position point.  This search may fail because REGEXP
3214
 
            ;; was found in the hidden portion of this message.  In
3215
 
            ;; that case, move point to the beginning of visible
3216
 
            ;; portion.
3217
 
            (if reversep
3218
 
                (progn
3219
 
                  (goto-char (point-max))
3220
 
                  (re-search-backward regexp nil 'move))
3221
 
              (goto-char (point-min))
3222
 
              (re-search-forward regexp nil t))
3223
 
            (message "%sPmail search for %s...done"
3224
 
                     (if reversep "Reverse " "")
3225
 
                     regexp))
3226
 
        (goto-char opoint)
3227
 
        (narrow-to-region omin omax)
3228
 
        (ding)
3229
 
        (message "Search failed: %s" regexp)))))
3230
 
 
3231
 
(defun pmail-search-backwards (regexp &optional n)
3232
 
  "Show message containing previous match for REGEXP.
3233
 
Prefix argument gives repeat count; negative argument means search
3234
 
forward (through later messages).
3235
 
Interactively, empty argument means use same regexp used last time."
3236
 
  (interactive
3237
 
    (let* ((reversep (>= (prefix-numeric-value current-prefix-arg) 0))
3238
 
           (prompt
3239
 
            (concat (if reversep "Reverse " "") "Pmail search (regexp"))
3240
 
           regexp)
3241
 
      (setq prompt
3242
 
            (concat prompt
3243
 
                    (if pmail-search-last-regexp
3244
 
                        (concat ", default "
3245
 
                                pmail-search-last-regexp "): ")
3246
 
                      "): ")))
3247
 
      (setq regexp (read-string prompt))
3248
 
      (cond ((not (equal regexp ""))
3249
 
             (setq pmail-search-last-regexp regexp))
3250
 
            ((not pmail-search-last-regexp)
3251
 
             (error "No previous Pmail search string")))
3252
 
      (list pmail-search-last-regexp
3253
 
            (prefix-numeric-value current-prefix-arg))))
3254
 
  (pmail-search regexp (- (or n 1))))
3255
 
 
3256
 
 
3257
 
(defun pmail-first-unseen-message ()
3258
 
  "Return the message index for the first message which has the
3259
 
`unseen' attribute."
3260
 
  (pmail-maybe-set-message-counters)
3261
 
  (let ((current 1)
3262
 
        found)
3263
 
    (save-restriction
3264
 
      (widen)
3265
 
      (while (and (not found) (<= current pmail-total-messages))
3266
 
        (if (pmail-message-attr-p current "......U")
3267
 
            (setq found current))
3268
 
        (setq current (1+ current))))
3269
 
    found))
3270
 
 
3271
 
(defun pmail-current-subject ()
3272
 
  "Return the current subject.
3273
 
The subject is stripped of leading and trailing whitespace, and
3274
 
of typical reply prefixes such as Re:."
3275
 
  (let ((subject (or (mail-fetch-field "Subject") "")))
3276
 
    (if (string-match "\\`[ \t]+" subject)
3277
 
        (setq subject (substring subject (match-end 0))))
3278
 
    (if (string-match pmail-reply-regexp subject)
3279
 
        (setq subject (substring subject (match-end 0))))
3280
 
    (if (string-match "[ \t]+\\'" subject)
3281
 
        (setq subject (substring subject 0 (match-beginning 0))))
3282
 
    subject))
3283
 
 
3284
 
(defun pmail-current-subject-regexp ()
3285
 
  "Return a regular expression matching the current subject.
3286
 
The regular expression matches the subject header line of
3287
 
messages about the same subject.  The subject itself is stripped
3288
 
of leading and trailing whitespace, of typical reply prefixes
3289
 
such as Re: and whitespace within the subject is replaced by a
3290
 
regular expression matching whitespace in general in order to
3291
 
take into account that subject header lines may include newlines
3292
 
and more whitespace.  The returned regular expressions contains
3293
 
`pmail-reply-regexp' and ends with a newline."
3294
 
  (let ((subject (pmail-current-subject)))
3295
 
    ;; If Subject is long, mailers will break it into several lines at
3296
 
    ;; arbitrary places, so replace whitespace with a regexp that will
3297
 
    ;; match any sequence of spaces, TABs, and newlines.
3298
 
    (setq subject (regexp-quote subject))
3299
 
    (setq subject
3300
 
          (replace-regexp-in-string "[ \t\n]+" "[ \t\n]+" subject t t))
3301
 
    ;; Some mailers insert extra spaces after "Subject:", so allow any
3302
 
    ;; amount of them.
3303
 
    (concat "^Subject:[ \t]+"
3304
 
            (if (string= "\\`" (substring pmail-reply-regexp 0 2))
3305
 
                (substring pmail-reply-regexp 2)
3306
 
              pmail-reply-regexp)
3307
 
            subject "[ \t]*\n")))
3308
 
 
3309
 
(defun pmail-next-same-subject (n)
3310
 
  "Go to the next mail message having the same subject header.
3311
 
With prefix argument N, do this N times.
3312
 
If N is negative, go backwards instead."
3313
 
  (interactive "p")
3314
 
  (let ((search-regexp (pmail-current-subject-regexp))
3315
 
        (forward (> n 0))
3316
 
        (i pmail-current-message)
3317
 
        (case-fold-search t)
3318
 
        found)
3319
 
    (save-excursion
3320
 
      (save-restriction
3321
 
        (widen)
3322
 
        (while (and (/= n 0)
3323
 
                    (if forward
3324
 
                        (< i pmail-total-messages)
3325
 
                      (> i 1)))
3326
 
          (let (done)
3327
 
            (while (and (not done)
3328
 
                        (if forward
3329
 
                            (< i pmail-total-messages)
3330
 
                          (> i 1)))
3331
 
              (setq i (if forward (1+ i) (1- i)))
3332
 
              (goto-char (pmail-msgbeg i))
3333
 
              (search-forward "\n*** EOOH ***\n")
3334
 
              (let ((beg (point)) end)
3335
 
                (search-forward "\n\n")
3336
 
                (setq end (point))
3337
 
                (goto-char beg)
3338
 
                (setq done (re-search-forward search-regexp end t))))
3339
 
            (if done (setq found i)))
3340
 
          (setq n (if forward (1- n) (1+ n))))))
3341
 
    (if found
3342
 
        (pmail-show-message-maybe found)
3343
 
      (error "No %s message with same subject"
3344
 
             (if forward "following" "previous")))))
3345
 
 
3346
 
(defun pmail-previous-same-subject (n)
3347
 
  "Go to the previous mail message having the same subject header.
3348
 
With prefix argument N, do this N times.
3349
 
If N is negative, go forwards instead."
3350
 
  (interactive "p")
3351
 
  (pmail-next-same-subject (- n)))
3352
 
 
3353
 
;;;; *** Pmail Message Deletion Commands ***
3354
 
 
3355
 
(defun pmail-message-deleted-p (n)
3356
 
  (= (aref pmail-deleted-vector n) ?D))
3357
 
 
3358
 
(defun pmail-set-message-deleted-p (n state)
3359
 
  (aset pmail-deleted-vector n (if state ?D ?\ )))
3360
 
 
3361
 
(defun pmail-delete-message ()
3362
 
  "Delete this message and stay on it."
3363
 
  (interactive)
3364
 
  (pmail-set-attribute pmail-deleted-attr-index t)
3365
 
  (run-hooks 'pmail-delete-message-hook))
3366
 
 
3367
 
(defun pmail-undelete-previous-message ()
3368
 
  "Back up to deleted message, select it, and undelete it."
3369
 
  (interactive)
3370
 
  (set-buffer pmail-buffer)
3371
 
  (let ((msg pmail-current-message))
3372
 
    (while (and (> msg 0)
3373
 
                (not (pmail-message-deleted-p msg)))
3374
 
      (setq msg (1- msg)))
3375
 
    (if (= msg 0)
3376
 
        (error "No previous deleted message")
3377
 
      (if (/= msg pmail-current-message)
3378
 
          (pmail-show-message-maybe msg))
3379
 
      (pmail-set-attribute pmail-deleted-attr-index nil)
3380
 
      (if (pmail-summary-exists)
3381
 
          (save-excursion
3382
 
            (set-buffer pmail-summary-buffer)
3383
 
            (pmail-summary-mark-undeleted msg)))
3384
 
      (pmail-maybe-display-summary))))
3385
 
 
3386
 
(defun pmail-delete-forward (&optional backward)
3387
 
  "Delete this message and move to next nondeleted one.
3388
 
Deleted messages stay in the file until the \\[pmail-expunge] command is given.
3389
 
With prefix argument, delete and move backward.
3390
 
 
3391
 
Returns t if a new message is displayed after the delete, or nil otherwise."
3392
 
  (interactive "P")
3393
 
  (pmail-set-attribute pmail-deleted-attr-index t)
3394
 
  (run-hooks 'pmail-delete-message-hook)
3395
 
  (let ((del-msg pmail-current-message))
3396
 
    (if (pmail-summary-exists)
3397
 
        (pmail-select-summary
3398
 
         (pmail-summary-mark-deleted del-msg)))
3399
 
    (prog1 (pmail-next-undeleted-message (if backward -1 1))
3400
 
      (pmail-maybe-display-summary))))
3401
 
 
3402
 
(defun pmail-delete-backward ()
3403
 
  "Delete this message and move to previous nondeleted one.
3404
 
Deleted messages stay in the file until the \\[pmail-expunge] command is given."
3405
 
  (interactive)
3406
 
  (pmail-delete-forward t))
3407
 
 
3408
 
;; Compute the message number a given message would have after expunging.
3409
 
;; The present number of the message is OLDNUM.
3410
 
;; DELETEDVEC should be pmail-deleted-vector.
3411
 
;; The value is nil for a message that would be deleted.
3412
 
(defun pmail-msg-number-after-expunge (deletedvec oldnum)
3413
 
  (if (or (null oldnum) (= (aref deletedvec oldnum) ?D))
3414
 
      nil
3415
 
    (let ((i 0)
3416
 
          (newnum 0))
3417
 
      (while (< i oldnum)
3418
 
        (if (/= (aref deletedvec i) ?D)
3419
 
            (setq newnum (1+ newnum)))
3420
 
        (setq i (1+ i)))
3421
 
      newnum)))
3422
 
 
3423
 
(defun pmail-expunge-confirmed ()
3424
 
  "Return t if deleted message should be expunged. If necessary, ask the user.
3425
 
See also user-option `pmail-confirm-expunge'."
3426
 
  (set-buffer pmail-buffer)
3427
 
  (or (not (stringp pmail-deleted-vector))
3428
 
      (not (string-match "D" pmail-deleted-vector))
3429
 
      (null pmail-confirm-expunge)
3430
 
      (funcall pmail-confirm-expunge
3431
 
               "Erase deleted messages from Pmail file? ")))
3432
 
 
3433
 
(defun pmail-only-expunge (&optional dont-show)
3434
 
  "Actually erase all deleted messages in the file."
3435
 
  (interactive)
3436
 
  (set-buffer pmail-buffer)
3437
 
  (message "Expunging deleted messages...")
3438
 
  ;; Discard all undo records for this buffer.
3439
 
  (or (eq buffer-undo-list t)
3440
 
      (setq buffer-undo-list nil))
3441
 
  (pmail-maybe-set-message-counters)
3442
 
  (let* ((omax (- (buffer-size) (point-max)))
3443
 
         (omin (- (buffer-size) (point-min)))
3444
 
         (opoint (if (and (> pmail-current-message 0)
3445
 
                          (pmail-message-deleted-p pmail-current-message))
3446
 
                     0
3447
 
                   (if pmail-enable-mime
3448
 
                       (with-current-buffer pmail-view-buffer
3449
 
                         (- (point)(point-min)))
3450
 
                     (- (point) (point-min)))))
3451
 
         (messages-head (cons (aref pmail-message-vector 0) nil))
3452
 
         (messages-tail messages-head)
3453
 
         ;; Don't make any undo records for the expunging.
3454
 
         (buffer-undo-list t)
3455
 
         (win))
3456
 
    (unwind-protect
3457
 
        (save-excursion
3458
 
          (widen)
3459
 
          (goto-char (point-min))
3460
 
          (let ((counter 0)
3461
 
                (number 1)
3462
 
                (total pmail-total-messages)
3463
 
                (new-message-number pmail-current-message)
3464
 
                (new-summary nil)
3465
 
                (new-msgref (list (list 0)))
3466
 
                (pmailbuf (current-buffer))
3467
 
                (buffer-read-only nil)
3468
 
                (messages pmail-message-vector)
3469
 
                (deleted pmail-deleted-vector)
3470
 
                (summary pmail-summary-vector))
3471
 
            (setq pmail-total-messages nil
3472
 
                  pmail-current-message nil
3473
 
                  pmail-message-vector nil
3474
 
                  pmail-deleted-vector nil
3475
 
                  pmail-summary-vector nil)
3476
 
 
3477
 
            (while (<= number total)
3478
 
              (if (= (aref deleted number) ?D)
3479
 
                  (progn
3480
 
                    (delete-region
3481
 
                      (marker-position (aref messages number))
3482
 
                      (marker-position (aref messages (1+ number))))
3483
 
                    (move-marker (aref messages number) nil)
3484
 
                    (if (> new-message-number counter)
3485
 
                        (setq new-message-number (1- new-message-number))))
3486
 
                (setq counter (1+ counter))
3487
 
                (setq messages-tail
3488
 
                      (setcdr messages-tail
3489
 
                              (cons (aref messages number) nil)))
3490
 
                (setq new-summary
3491
 
                      (cons (if (= counter number) (aref summary (1- number)))
3492
 
                            new-summary))
3493
 
                (setq new-msgref
3494
 
                      (cons (aref pmail-msgref-vector number)
3495
 
                            new-msgref))
3496
 
                (setcar (car new-msgref) counter))
3497
 
              (if (zerop (% (setq number (1+ number)) 20))
3498
 
                  (message "Expunging deleted messages...%d" number)))
3499
 
            (setq messages-tail
3500
 
                  (setcdr messages-tail
3501
 
                          (cons (aref messages number) nil)))
3502
 
            (setq pmail-current-message new-message-number
3503
 
                  pmail-total-messages counter
3504
 
                  pmail-message-vector (apply 'vector messages-head)
3505
 
                  pmail-deleted-vector (make-string (1+ counter) ?\ )
3506
 
                  pmail-summary-vector (vconcat (nreverse new-summary))
3507
 
                  pmail-msgref-vector (apply 'vector (nreverse new-msgref))
3508
 
                  win t)))
3509
 
      (message "Expunging deleted messages...done")
3510
 
      (if (not win)
3511
 
          (narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax)))
3512
 
      (if (not dont-show)
3513
 
          (pmail-show-message-maybe (< pmail-current-message pmail-total-messages)))
3514
 
      (pmail-swap-buffers-maybe)
3515
 
      (if pmail-enable-mime
3516
 
          (goto-char (+ (point-min) opoint))
3517
 
        (goto-char (+ (point) opoint))))))
3518
 
 
3519
 
(defun pmail-expunge ()
3520
 
  "Erase deleted messages from Pmail file and summary buffer."
3521
 
  (interactive)
3522
 
  (when (pmail-expunge-confirmed)
3523
 
    (pmail-only-expunge)
3524
 
    (if (pmail-summary-exists)
3525
 
        (pmail-select-summary (pmail-update-summary)))))
3526
 
 
3527
 
;;;; *** Pmail Mailing Commands ***
3528
 
 
3529
 
(defun pmail-start-mail (&optional noerase to subject in-reply-to cc
3530
 
                                   replybuffer sendactions same-window others)
3531
 
  (let (yank-action)
3532
 
    (if replybuffer
3533
 
        (setq yank-action (list 'insert-buffer replybuffer)))
3534
 
    (setq others (cons (cons "cc" cc) others))
3535
 
    (setq others (cons (cons "in-reply-to" in-reply-to) others))
3536
 
    (if same-window
3537
 
        (compose-mail to subject others
3538
 
                      noerase nil
3539
 
                      yank-action sendactions)
3540
 
      (if pmail-mail-new-frame
3541
 
          (prog1
3542
 
              (compose-mail to subject others
3543
 
                            noerase 'switch-to-buffer-other-frame
3544
 
                            yank-action sendactions)
3545
 
            ;; This is not a standard frame parameter;
3546
 
            ;; nothing except sendmail.el looks at it.
3547
 
            (modify-frame-parameters (selected-frame)
3548
 
                                     '((mail-dedicated-frame . t))))
3549
 
        (compose-mail to subject others
3550
 
                      noerase 'switch-to-buffer-other-window
3551
 
                      yank-action sendactions)))))
3552
 
 
3553
 
(defun pmail-mail ()
3554
 
  "Send mail in another window.
3555
 
While composing the message, use \\[mail-yank-original] to yank the
3556
 
original message into it."
3557
 
  (interactive)
3558
 
  (pmail-start-mail nil nil nil nil nil pmail-view-buffer))
3559
 
 
3560
 
(defun pmail-continue ()
3561
 
  "Continue composing outgoing message previously being composed."
3562
 
  (interactive)
3563
 
  (pmail-start-mail t))
3564
 
 
3565
 
(defun pmail-reply (just-sender)
3566
 
  "Reply to the current message.
3567
 
Normally include CC: to all other recipients of original message;
3568
 
prefix argument means ignore them.  While composing the reply,
3569
 
use \\[mail-yank-original] to yank the original message into it."
3570
 
  (interactive "P")
3571
 
  (let (from reply-to cc subject date to message-id references
3572
 
             resent-to resent-cc resent-reply-to
3573
 
             (msgnum pmail-current-message))
3574
 
    (save-excursion
3575
 
      (save-restriction
3576
 
        (if pmail-enable-mime
3577
 
            (narrow-to-region
3578
 
             (goto-char (point-min))
3579
 
             (if (search-forward "\n\n" nil 'move)
3580
 
                 (1+ (match-beginning 0))
3581
 
               (point)))
3582
 
          (widen)
3583
 
          (goto-char (pmail-msgbeg pmail-current-message))
3584
 
          (forward-line 1)
3585
 
          (if (= (following-char) ?0)
3586
 
              (narrow-to-region
3587
 
               (progn (forward-line 2)
3588
 
                      (point))
3589
 
               (progn (search-forward "\n\n" (pmail-msgend pmail-current-message)
3590
 
                                      'move)
3591
 
                      (point)))
3592
 
            (narrow-to-region (point)
3593
 
                              (progn (search-forward "\n*** EOOH ***\n")
3594
 
                                     (beginning-of-line) (point)))))
3595
 
        (setq from (mail-fetch-field "from")
3596
 
              reply-to (or (mail-fetch-field "mail-reply-to" nil t)
3597
 
                           (mail-fetch-field "reply-to" nil t)
3598
 
                           from)
3599
 
              subject (mail-fetch-field "subject")
3600
 
              date (mail-fetch-field "date")
3601
 
              message-id (mail-fetch-field "message-id")
3602
 
              references (mail-fetch-field "references" nil nil t)
3603
 
              resent-reply-to (mail-fetch-field "resent-reply-to" nil t)
3604
 
              resent-cc (and (not just-sender)
3605
 
                             (mail-fetch-field "resent-cc" nil t))
3606
 
              resent-to (or (mail-fetch-field "resent-to" nil t) "")
3607
 
;;;           resent-subject (mail-fetch-field "resent-subject")
3608
 
;;;           resent-date (mail-fetch-field "resent-date")
3609
 
;;;           resent-message-id (mail-fetch-field "resent-message-id")
3610
 
              )
3611
 
        (unless just-sender
3612
 
          (if (mail-fetch-field "mail-followup-to" nil t)
3613
 
              ;; If this header field is present, use it instead of the To and CC fields.
3614
 
              (setq to (mail-fetch-field "mail-followup-to" nil t))
3615
 
            (setq cc (or (mail-fetch-field "cc" nil t) "")
3616
 
                  to (or (mail-fetch-field "to" nil t) ""))))
3617
 
 
3618
 
        ))
3619
 
 
3620
 
    ;; Merge the resent-to and resent-cc into the to and cc.
3621
 
    (if (and resent-to (not (equal resent-to "")))
3622
 
        (if (not (equal to ""))
3623
 
            (setq to (concat to ", " resent-to))
3624
 
          (setq to resent-to)))
3625
 
    (if (and resent-cc (not (equal resent-cc "")))
3626
 
        (if (not (equal cc ""))
3627
 
            (setq cc (concat cc ", " resent-cc))
3628
 
          (setq cc resent-cc)))
3629
 
    ;; Add `Re: ' to subject if not there already.
3630
 
    (and (stringp subject)
3631
 
         (setq subject
3632
 
               (concat pmail-reply-prefix
3633
 
                       (if (let ((case-fold-search t))
3634
 
                             (string-match pmail-reply-regexp subject))
3635
 
                           (substring subject (match-end 0))
3636
 
                         subject))))
3637
 
    (pmail-start-mail
3638
 
     nil
3639
 
     ;; Using mail-strip-quoted-names is undesirable with newer mailers
3640
 
     ;; since they can handle the names unstripped.
3641
 
     ;; I don't know whether there are other mailers that still
3642
 
     ;; need the names to be stripped.
3643
 
;;;     (mail-strip-quoted-names reply-to)
3644
 
     ;; Remove unwanted names from reply-to, since Mail-Followup-To
3645
 
     ;; header causes all the names in it to wind up in reply-to, not
3646
 
     ;; in cc.  But if what's left is an empty list, use the original.
3647
 
     (let* ((reply-to-list (pmail-dont-reply-to reply-to)))
3648
 
       (if (string= reply-to-list "") reply-to reply-to-list))
3649
 
     subject
3650
 
     (pmail-make-in-reply-to-field from date message-id)
3651
 
     (if just-sender
3652
 
         nil
3653
 
       ;; mail-strip-quoted-names is NOT necessary for pmail-dont-reply-to
3654
 
       ;; to do its job.
3655
 
       (let* ((cc-list (pmail-dont-reply-to
3656
 
                        (mail-strip-quoted-names
3657
 
                         (if (null cc) to (concat to ", " cc))))))
3658
 
         (if (string= cc-list "") nil cc-list)))
3659
 
     pmail-view-buffer
3660
 
     (list (list 'pmail-mark-message
3661
 
                 pmail-buffer
3662
 
                 (with-current-buffer pmail-buffer
3663
 
                   (aref pmail-msgref-vector msgnum))
3664
 
                 "answered"))
3665
 
     nil
3666
 
     (list (cons "References" (concat (mapconcat 'identity references " ")
3667
 
                                      " " message-id))))))
3668
 
 
3669
 
(defun pmail-mark-message (buffer msgnum-list attribute)
3670
 
  "Give BUFFER's message number in MSGNUM-LIST the attribute ATTRIBUTE.
3671
 
This is use in the send-actions for message buffers.
3672
 
MSGNUM-LIST is a list of the form (MSGNUM)
3673
 
which is an element of pmail-msgref-vector."
3674
 
  (save-excursion
3675
 
    (set-buffer buffer)
3676
 
    (if (car msgnum-list)
3677
 
        (pmail-set-attribute attribute t (car msgnum-list)))))
3678
 
 
3679
 
(defun pmail-make-in-reply-to-field (from date message-id)
3680
 
  (cond ((not from)
3681
 
         (if message-id
3682
 
             message-id
3683
 
             nil))
3684
 
        (mail-use-rfc822
3685
 
         (require 'rfc822)
3686
 
         (let ((tem (car (rfc822-addresses from))))
3687
 
           (if message-id
3688
 
               (if (or (not tem)
3689
 
                       (string-match
3690
 
                        (regexp-quote (if (string-match "@[^@]*\\'" tem)
3691
 
                                          (substring tem 0
3692
 
                                                     (match-beginning 0))
3693
 
                                        tem))
3694
 
                        message-id))
3695
 
                   ;; missing From, or Message-ID is sufficiently informative
3696
 
                   message-id
3697
 
                   (concat message-id " (" tem ")"))
3698
 
             ;; Copy TEM, discarding text properties.
3699
 
             (setq tem (copy-sequence tem))
3700
 
             (set-text-properties 0 (length tem) nil tem)
3701
 
             (setq tem (copy-sequence tem))
3702
 
             ;; Use prin1 to fake RFC822 quoting
3703
 
             (let ((field (prin1-to-string tem)))
3704
 
               (if date
3705
 
                   (concat field "'s message of " date)
3706
 
                   field)))))
3707
 
        ((let* ((foo "[^][\000-\037()<>@,;:\\\" ]+")
3708
 
                (bar "[^][\000-\037()<>@,;:\\\"]+"))
3709
 
           ;; These strings both match all non-ASCII characters.
3710
 
           (or (string-match (concat "\\`[ \t]*\\(" bar
3711
 
                                     "\\)\\(<" foo "@" foo ">\\)?[ \t]*\\'")
3712
 
                             ;; "Unix Loser <Foo@bar.edu>" => "Unix Loser"
3713
 
                             from)
3714
 
               (string-match (concat "\\`[ \t]*<" foo "@" foo ">[ \t]*(\\("
3715
 
                                     bar "\\))[ \t]*\\'")
3716
 
                             ;; "<Bugs@bar.edu>" (Losing Unix) => "Losing Unix"
3717
 
                             from)))
3718
 
         (let ((start (match-beginning 1))
3719
 
               (end (match-end 1)))
3720
 
           ;; Trim whitespace which above regexp match allows
3721
 
           (while (and (< start end)
3722
 
                       (memq (aref from start) '(?\t ?\ )))
3723
 
             (setq start (1+ start)))
3724
 
           (while (and (< start end)
3725
 
                       (memq (aref from (1- end)) '(?\t ?\ )))
3726
 
             (setq end (1- end)))
3727
 
           (let ((field (substring from start end)))
3728
 
             (if date (setq field (concat "message from " field " on " date)))
3729
 
             (if message-id
3730
 
                 ;; "<AA259@bar.edu> (message from Unix Loser on 1-Apr-89)"
3731
 
                 (concat message-id " (" field ")")
3732
 
                 field))))
3733
 
        (t
3734
 
         ;; If we can't kludge it simply, do it correctly
3735
 
         (let ((mail-use-rfc822 t))
3736
 
           (pmail-make-in-reply-to-field from date message-id)))))
3737
 
 
3738
 
(defun pmail-forward (resend)
3739
 
  "Forward the current message to another user.
3740
 
With prefix argument, \"resend\" the message instead of forwarding it;
3741
 
see the documentation of `pmail-resend'."
3742
 
  (interactive "P")
3743
 
  (if resend
3744
 
      (call-interactively 'pmail-resend)
3745
 
    (let ((forward-buffer pmail-buffer)
3746
 
          (msgnum pmail-current-message)
3747
 
          (subject (concat "["
3748
 
                           (let ((from (or (mail-fetch-field "From")
3749
 
                                           (mail-fetch-field ">From"))))
3750
 
                             (if from
3751
 
                                 (concat (mail-strip-quoted-names from) ": ")
3752
 
                               ""))
3753
 
                           (or (mail-fetch-field "Subject") "")
3754
 
                           "]")))
3755
 
      (if (pmail-start-mail
3756
 
           nil nil subject nil nil nil
3757
 
           (list (list 'pmail-mark-message
3758
 
                       forward-buffer
3759
 
                       (with-current-buffer pmail-buffer
3760
 
                         (aref pmail-msgref-vector msgnum))
3761
 
                       "forwarded"))
3762
 
           ;; If only one window, use it for the mail buffer.
3763
 
           ;; Otherwise, use another window for the mail buffer
3764
 
           ;; so that the Pmail buffer remains visible
3765
 
           ;; and sending the mail will get back to it.
3766
 
           (and (not pmail-mail-new-frame) (one-window-p t)))
3767
 
          ;; The mail buffer is now current.
3768
 
          (save-excursion
3769
 
            ;; Insert after header separator--before signature if any.
3770
 
            (goto-char (mail-text-start))
3771
 
            (if (or pmail-enable-mime pmail-enable-mime-composing)
3772
 
                (funcall pmail-insert-mime-forwarded-message-function
3773
 
                         forward-buffer)
3774
 
              (insert "------- Start of forwarded message -------\n")
3775
 
              ;; Quote lines with `- ' if they start with `-'.
3776
 
              (let ((beg (point)) end)
3777
 
                (setq end (point-marker))
3778
 
                (set-marker-insertion-type end t)
3779
 
                (insert-buffer-substring forward-buffer)
3780
 
                (goto-char beg)
3781
 
                (while (re-search-forward "^-" end t)
3782
 
                  (beginning-of-line)
3783
 
                  (insert "- ")
3784
 
                  (forward-line 1))
3785
 
                (goto-char end)
3786
 
                (skip-chars-backward "\n")
3787
 
                (if (< (point) end)
3788
 
                    (forward-char 1))
3789
 
                (delete-region (point) end)
3790
 
                (set-marker end nil))
3791
 
              (insert "------- End of forwarded message -------\n"))
3792
 
            (push-mark))))))
3793
 
 
3794
 
(defun pmail-resend (address &optional from comment mail-alias-file)
3795
 
  "Resend current message to ADDRESSES.
3796
 
ADDRESSES should be a single address, a string consisting of several
3797
 
addresses separated by commas, or a list of addresses.
3798
 
 
3799
 
Optional FROM is the address to resend the message from, and
3800
 
defaults from the value of `user-mail-address'.
3801
 
Optional COMMENT is a string to insert as a comment in the resent message.
3802
 
Optional ALIAS-FILE is alternate aliases file to be used by sendmail,
3803
 
typically for purposes of moderating a list."
3804
 
  (interactive "sResend to: ")
3805
 
  (require 'sendmail)
3806
 
  (require 'mailalias)
3807
 
  (unless (or (eq pmail-view-buffer (current-buffer))
3808
 
              (eq pmail-buffer (current-buffer)))
3809
 
    (error "Not an Pmail buffer"))
3810
 
  (if (not from) (setq from user-mail-address))
3811
 
  (let ((tembuf (generate-new-buffer " sendmail temp"))
3812
 
        (case-fold-search nil)
3813
 
        (mail-personal-alias-file
3814
 
         (or mail-alias-file mail-personal-alias-file))
3815
 
        (mailbuf pmail-buffer))
3816
 
    (unwind-protect
3817
 
        (with-current-buffer tembuf
3818
 
          ;;>> Copy message into temp buffer
3819
 
          (if pmail-enable-mime
3820
 
              (funcall pmail-insert-mime-resent-message-function mailbuf)
3821
 
            (insert-buffer-substring mailbuf))
3822
 
          (goto-char (point-min))
3823
 
          ;; Delete any Sender field, since that's not specifiable.
3824
 
          ; Only delete Sender fields in the actual header.
3825
 
          (re-search-forward "^$" nil 'move)
3826
 
          ; Using "while" here rather than "if" because some buggy mail
3827
 
          ; software may have inserted multiple Sender fields.
3828
 
          (while (re-search-backward "^Sender:" nil t)
3829
 
            (let (beg)
3830
 
              (setq beg (point))
3831
 
              (forward-line 1)
3832
 
              (while (looking-at "[ \t]")
3833
 
                (forward-line 1))
3834
 
              (delete-region beg (point))))
3835
 
          ; Go back to the beginning of the buffer so the Resent- fields
3836
 
          ; are inserted there.
3837
 
          (goto-char (point-min))
3838
 
          ;;>> Insert resent-from:
3839
 
          (insert "Resent-From: " from "\n")
3840
 
          (insert "Resent-Date: " (mail-rfc822-date) "\n")
3841
 
          ;;>> Insert resent-to: and bcc if need be.
3842
 
          (let ((before (point)))
3843
 
            (if mail-self-blind
3844
 
                (insert "Resent-Bcc: " (user-login-name) "\n"))
3845
 
            (insert "Resent-To: " (if (stringp address)
3846
 
                               address
3847
 
                             (mapconcat 'identity address ",\n\t"))
3848
 
                    "\n")
3849
 
            ;; Expand abbrevs in the recipients.
3850
 
            (save-excursion
3851
 
              (if (featurep 'mailabbrev)
3852
 
                  (let ((end (point-marker))
3853
 
                        (local-abbrev-table mail-abbrevs)
3854
 
                        (old-syntax-table (syntax-table)))
3855
 
                    (if (and (not (vectorp mail-abbrevs))
3856
 
                             (file-exists-p mail-personal-alias-file))
3857
 
                        (build-mail-abbrevs))
3858
 
                    (unless mail-abbrev-syntax-table
3859
 
                      (mail-abbrev-make-syntax-table))
3860
 
                    (set-syntax-table mail-abbrev-syntax-table)
3861
 
                    (goto-char before)
3862
 
                    (while (and (< (point) end)
3863
 
                                (progn (forward-word 1)
3864
 
                                       (<= (point) end)))
3865
 
                      (expand-abbrev))
3866
 
                    (set-syntax-table old-syntax-table))
3867
 
                (expand-mail-aliases before (point)))))
3868
 
          ;;>> Set up comment, if any.
3869
 
          (if (and (sequencep comment) (not (zerop (length comment))))
3870
 
              (let ((before (point))
3871
 
                    after)
3872
 
                (insert comment)
3873
 
                (or (eolp) (insert "\n"))
3874
 
                (setq after (point))
3875
 
                (goto-char before)
3876
 
                (while (< (point) after)
3877
 
                  (insert "Resent-Comment: ")
3878
 
                  (forward-line 1))))
3879
 
          ;; Don't expand aliases in the destination fields
3880
 
          ;; of the original message.
3881
 
          (let (mail-aliases)
3882
 
            (funcall send-mail-function)))
3883
 
      (kill-buffer tembuf))
3884
 
    (with-current-buffer pmail-buffer
3885
 
      (pmail-set-attribute pmail-resent-attr-index t pmail-current-message))))
3886
 
 
3887
 
(defvar mail-unsent-separator
3888
 
  (concat "^ *---+ +Unsent message follows +---+ *$\\|"
3889
 
          "^ *---+ +Returned message +---+ *$\\|"
3890
 
          "^ *---+ *Returned mail follows *---+ *$\\|"
3891
 
          "^Start of returned message$\\|"
3892
 
          "^---+ Below this line is a copy of the message.$\\|"
3893
 
          "^ *---+ +Original message +---+ *$\\|"
3894
 
          "^ *--+ +begin message +--+ *$\\|"
3895
 
          "^ *---+ +Original message follows +---+ *$\\|"
3896
 
          "^ *---+ +Your message follows +---+ *$\\|"
3897
 
          "^|? *---+ +Message text follows: +---+ *|?$\\|"
3898
 
          "^ *---+ +This is a copy of \\w+ message, including all the headers.*---+ *$")
3899
 
  "A regexp that matches the separator before the text of a failed message.")
3900
 
 
3901
 
(defvar mail-mime-unsent-header "^Content-Type: message/rfc822 *$"
3902
 
 "A regexp that matches the header of a MIME body part with a failed message.")
3903
 
 
3904
 
(defun pmail-retry-failure ()
3905
 
  "Edit a mail message which is based on the contents of the current message.
3906
 
For a message rejected by the mail system, extract the interesting headers and
3907
 
the body of the original message.
3908
 
If the failed message is a MIME multipart message, it is searched for a
3909
 
body part with a header which matches the variable `mail-mime-unsent-header'.
3910
 
Otherwise, the variable `mail-unsent-separator' should match the string that
3911
 
delimits the returned original message.
3912
 
The variable `pmail-retry-ignored-headers' is a regular expression
3913
 
specifying headers which should not be copied into the new message."
3914
 
  (interactive)
3915
 
  (require 'mail-utils)
3916
 
  (let ((pmail-this-buffer (current-buffer))
3917
 
        (msgnum pmail-current-message)
3918
 
        bounce-start bounce-end bounce-indent resending
3919
 
        ;; Fetch any content-type header in current message
3920
 
        ;; Must search thru the whole unpruned header.
3921
 
        (content-type
3922
 
         (save-excursion
3923
 
           (save-restriction
3924
 
             (mail-fetch-field "Content-Type") ))))
3925
 
    (save-excursion
3926
 
      (goto-char (point-min))
3927
 
      (let ((case-fold-search t))
3928
 
        (if (and content-type
3929
 
                 (string-match
3930
 
                  ";[\n\t ]*boundary=\"?\\([-0-9a-z'()+_,./:=? ]+\\)\"?"
3931
 
                  content-type))
3932
 
            ;; Handle a MIME multipart bounce message.
3933
 
            (let ((codestring
3934
 
                   (concat "\n--"
3935
 
                           (substring content-type (match-beginning 1)
3936
 
                                      (match-end 1)))))
3937
 
              (unless (re-search-forward mail-mime-unsent-header nil t)
3938
 
                (error "Cannot find beginning of header in failed message"))
3939
 
              (unless (search-forward "\n\n" nil t)
3940
 
                (error "Cannot find start of Mime data in failed message"))
3941
 
              (setq bounce-start (point))
3942
 
              (if (search-forward codestring nil t)
3943
 
                  (setq bounce-end (match-beginning 0))
3944
 
                (setq bounce-end (point-max))))
3945
 
          ;; Non-MIME bounce.
3946
 
          (or (re-search-forward mail-unsent-separator nil t)
3947
 
              (error "Cannot parse this as a failure message"))
3948
 
          (skip-chars-forward "\n")
3949
 
          ;; Support a style of failure message in which the original
3950
 
          ;; message is indented, and included within lines saying
3951
 
          ;; `Start of returned message' and `End of returned message'.
3952
 
          (if (looking-at " +Received:")
3953
 
              (progn
3954
 
                (setq bounce-start (point))
3955
 
                (skip-chars-forward " ")
3956
 
                (setq bounce-indent (- (current-column)))
3957
 
                (goto-char (point-max))
3958
 
                (re-search-backward "^End of returned message$" nil t)
3959
 
                (setq bounce-end (point)))
3960
 
            ;; One message contained a few random lines before
3961
 
            ;; the old message header.  The first line of the
3962
 
            ;; message started with two hyphens.  A blank line
3963
 
            ;; followed these random lines.  The same line
3964
 
            ;; beginning with two hyphens was possibly marking
3965
 
            ;; the end of the message.
3966
 
            (if (looking-at "^--")
3967
 
                (let ((boundary (buffer-substring-no-properties
3968
 
                                 (point)
3969
 
                                 (progn (end-of-line) (point)))))
3970
 
                  (search-forward "\n\n")
3971
 
                  (skip-chars-forward "\n")
3972
 
                  (setq bounce-start (point))
3973
 
                  (goto-char (point-max))
3974
 
                  (search-backward (concat "\n\n" boundary) bounce-start t)
3975
 
                  (setq bounce-end (point)))
3976
 
              (setq bounce-start (point)
3977
 
                    bounce-end (point-max)))
3978
 
            (unless (search-forward "\n\n" nil t)
3979
 
              (error "Cannot find end of header in failed message"))))))
3980
 
    ;; We have found the message that bounced, within the current message.
3981
 
    ;; Now start sending new message; default header fields from original.
3982
 
    ;; Turn off the usual actions for initializing the message body
3983
 
    ;; because we want to get only the text from the failure message.
3984
 
    (let (mail-signature mail-setup-hook)
3985
 
      (if (pmail-start-mail nil nil nil nil nil pmail-this-buffer
3986
 
                            (list (list 'pmail-mark-message
3987
 
                                        pmail-this-buffer
3988
 
                                        (aref pmail-msgref-vector msgnum)
3989
 
                                        "retried")))
3990
 
          ;; Insert original text as initial text of new draft message.
3991
 
          ;; Bind inhibit-read-only since the header delimiter
3992
 
          ;; of the previous message was probably read-only.
3993
 
          (let ((inhibit-read-only t)
3994
 
                pmail-displayed-headers
3995
 
                pmail-ignored-headers)
3996
 
            (erase-buffer)
3997
 
            (insert-buffer-substring pmail-this-buffer
3998
 
                                     bounce-start bounce-end)
3999
 
            (goto-char (point-min))
4000
 
            (if bounce-indent
4001
 
                (indent-rigidly (point-min) (point-max) bounce-indent))
4002
 
            (pmail-clear-headers pmail-retry-ignored-headers)
4003
 
            (pmail-clear-headers "^sender:\\|^return-path:\\|^received:")
4004
 
            (mail-sendmail-delimit-header)
4005
 
            (save-restriction
4006
 
              (narrow-to-region (point-min) (mail-header-end))
4007
 
              (setq resending (mail-fetch-field "resent-to"))
4008
 
              (if mail-self-blind
4009
 
                  (if resending
4010
 
                      (insert "Resent-Bcc: " (user-login-name) "\n")
4011
 
                    (insert "BCC: " (user-login-name) "\n"))))
4012
 
            (goto-char (point-min))
4013
 
            (mail-position-on-field (if resending "Resent-To" "To") t))))))
4014
 
 
4015
 
(defun pmail-summary-exists ()
4016
 
  "Non-nil if in an PMAIL buffer and an associated summary buffer exists.
4017
 
In fact, the non-nil value returned is the summary buffer itself."
4018
 
  (and pmail-summary-buffer (buffer-name pmail-summary-buffer)
4019
 
       pmail-summary-buffer))
4020
 
 
4021
 
(defun pmail-summary-displayed ()
4022
 
  "t if in PMAIL buffer and an associated summary buffer is displayed."
4023
 
  (and pmail-summary-buffer (get-buffer-window pmail-summary-buffer)))
4024
 
 
4025
 
(defcustom pmail-redisplay-summary nil
4026
 
  "*Non-nil means Pmail should show the summary when it changes.
4027
 
This has an effect only if a summary buffer exists."
4028
 
  :type 'boolean
4029
 
  :group 'pmail-summary)
4030
 
 
4031
 
(defcustom pmail-summary-window-size nil
4032
 
  "*Non-nil means specify the height for an Pmail summary window."
4033
 
  :type '(choice (const :tag "Disabled" nil) integer)
4034
 
  :group 'pmail-summary)
4035
 
 
4036
 
;; Put the summary buffer back on the screen, if user wants that.
4037
 
(defun pmail-maybe-display-summary ()
4038
 
  (let ((selected (selected-window))
4039
 
        window)
4040
 
    ;; If requested, make sure the summary is displayed.
4041
 
    (and pmail-summary-buffer (buffer-name pmail-summary-buffer)
4042
 
         pmail-redisplay-summary
4043
 
         (if (get-buffer-window pmail-summary-buffer 0)
4044
 
             ;; It's already in some frame; show that one.
4045
 
             (let ((frame (window-frame
4046
 
                           (get-buffer-window pmail-summary-buffer 0))))
4047
 
               (make-frame-visible frame)
4048
 
               (raise-frame frame))
4049
 
           (display-buffer pmail-summary-buffer)))
4050
 
    ;; If requested, set the height of the summary window.
4051
 
    (and pmail-summary-buffer (buffer-name pmail-summary-buffer)
4052
 
         pmail-summary-window-size
4053
 
         (setq window (get-buffer-window pmail-summary-buffer))
4054
 
         ;; Don't try to change the size if just one window in frame.
4055
 
         (not (eq window (frame-root-window (window-frame window))))
4056
 
         (unwind-protect
4057
 
             (progn
4058
 
               (select-window window)
4059
 
               (enlarge-window (- pmail-summary-window-size (window-height))))
4060
 
           (select-window selected)))))
4061
 
 
4062
 
;;;; *** Pmail Local Fontification ***
4063
 
 
4064
 
(defun pmail-fontify-buffer-function ()
4065
 
  ;; This function's symbol is bound to font-lock-fontify-buffer-function.
4066
 
  (add-hook 'pmail-show-message-hook 'pmail-fontify-message nil t)
4067
 
  ;; If we're already showing a message, fontify it now.
4068
 
  (if pmail-current-message (pmail-fontify-message))
4069
 
  ;; Prevent Font Lock mode from kicking in.
4070
 
  (setq font-lock-fontified t))
4071
 
 
4072
 
(defun pmail-unfontify-buffer-function ()
4073
 
  ;; This function's symbol is bound to font-lock-fontify-unbuffer-function.
4074
 
  (let ((modified (buffer-modified-p))
4075
 
        (buffer-undo-list t) (inhibit-read-only t)
4076
 
        before-change-functions after-change-functions
4077
 
        buffer-file-name buffer-file-truename)
4078
 
    (save-restriction
4079
 
      (widen)
4080
 
      (remove-hook 'pmail-show-message-hook 'pmail-fontify-message t)
4081
 
      (remove-text-properties (point-min) (point-max) '(pmail-fontified nil))
4082
 
      (font-lock-default-unfontify-buffer)
4083
 
      (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))
4084
 
 
4085
 
(defun pmail-fontify-message ()
4086
 
  ;; Fontify the current message if it is not already fontified.
4087
 
  (if (text-property-any (point-min) (point-max) 'pmail-fontified nil)
4088
 
      (let ((modified (buffer-modified-p))
4089
 
            (buffer-undo-list t) (inhibit-read-only t)
4090
 
            before-change-functions after-change-functions
4091
 
            buffer-file-name buffer-file-truename)
4092
 
        (save-excursion
4093
 
          (save-match-data
4094
 
            (add-text-properties (point-min) (point-max) '(pmail-fontified t))
4095
 
            (font-lock-fontify-region (point-min) (point-max))
4096
 
            (and (not modified) (buffer-modified-p) (set-buffer-modified-p nil)))))))
4097
 
 
4098
 
;;; Speedbar support for PMAIL files.
4099
 
(eval-when-compile (require 'speedbar))
4100
 
 
4101
 
(defvar pmail-speedbar-match-folder-regexp "^[A-Z0-9]+\\(\\.[A-Z0-9]+\\)?$"
4102
 
  "*This regex is used to match folder names to be displayed in speedbar.
4103
 
Enabling this will permit speedbar to display your folders for easy
4104
 
browsing, and moving of messages.")
4105
 
 
4106
 
(defvar pmail-speedbar-last-user nil
4107
 
  "The last user to be displayed in the speedbar.")
4108
 
 
4109
 
(defvar pmail-speedbar-key-map nil
4110
 
  "Keymap used when in pmail display mode.")
4111
 
 
4112
 
(defun pmail-install-speedbar-variables ()
4113
 
  "Install those variables used by speedbar to enhance pmail."
4114
 
  (if pmail-speedbar-key-map
4115
 
      nil
4116
 
    (setq pmail-speedbar-key-map (speedbar-make-specialized-keymap))
4117
 
 
4118
 
    (define-key pmail-speedbar-key-map "e" 'speedbar-edit-line)
4119
 
    (define-key pmail-speedbar-key-map "r" 'speedbar-edit-line)
4120
 
    (define-key pmail-speedbar-key-map "\C-m" 'speedbar-edit-line)
4121
 
    (define-key pmail-speedbar-key-map "M"
4122
 
      'pmail-speedbar-move-message-to-folder-on-line)))
4123
 
 
4124
 
(defvar pmail-speedbar-menu-items
4125
 
  '(["Read Folder" speedbar-edit-line t]
4126
 
    ["Move message to folder" pmail-speedbar-move-message-to-folder-on-line
4127
 
     (save-excursion (beginning-of-line)
4128
 
                     (looking-at "<M> "))])
4129
 
  "Additional menu-items to add to speedbar frame.")
4130
 
 
4131
 
;; Make sure our special speedbar major mode is loaded
4132
 
(if (featurep 'speedbar)
4133
 
    (pmail-install-speedbar-variables)
4134
 
  (add-hook 'speedbar-load-hook 'pmail-install-speedbar-variables))
4135
 
 
4136
 
(defun pmail-speedbar-buttons (buffer)
4137
 
  "Create buttons for BUFFER containing pmail messages.
4138
 
Click on the address under Reply to: to reply to this person.
4139
 
Under Folders: Click a name to read it, or on the <M> to move the
4140
 
current message into that PMAIL folder."
4141
 
  (let ((from nil))
4142
 
    (save-excursion
4143
 
      (set-buffer buffer)
4144
 
      (goto-char (point-min))
4145
 
      (if (not (re-search-forward "^Reply-To: " nil t))
4146
 
          (if (not (re-search-forward "^From:? " nil t))
4147
 
              (setq from t)))
4148
 
      (if from
4149
 
          nil
4150
 
        (setq from (buffer-substring (point) (save-excursion
4151
 
                                               (end-of-line)
4152
 
                                               (point))))))
4153
 
    (goto-char (point-min))
4154
 
    (if (and (looking-at "Reply to:")
4155
 
             (equal from pmail-speedbar-last-user))
4156
 
        nil
4157
 
      (setq pmail-speedbar-last-user from)
4158
 
      (erase-buffer)
4159
 
      (insert "Reply To:\n")
4160
 
      (if (stringp from)
4161
 
          (speedbar-insert-button from 'speedbar-directory-face 'highlight
4162
 
                                  'pmail-speedbar-button 'pmail-reply))
4163
 
      (insert "Folders:\n")
4164
 
      (let* ((case-fold-search nil)
4165
 
             (df (directory-files (save-excursion (set-buffer buffer)
4166
 
                                                  default-directory)
4167
 
                                  nil pmail-speedbar-match-folder-regexp)))
4168
 
        (while df
4169
 
          (speedbar-insert-button "<M>" 'speedbar-button-face 'highlight
4170
 
                                  'pmail-speedbar-move-message (car df))
4171
 
          (speedbar-insert-button (car df) 'speedbar-file-face 'highlight
4172
 
                                  'pmail-speedbar-find-file nil t)
4173
 
          (setq df (cdr df)))))))
4174
 
 
4175
 
(defun pmail-speedbar-button (text token indent)
4176
 
  "Execute an pmail command specified by TEXT.
4177
 
The command used is TOKEN.  INDENT is not used."
4178
 
  (speedbar-with-attached-buffer
4179
 
   (funcall token t)))
4180
 
 
4181
 
(defun pmail-speedbar-find-file (text token indent)
4182
 
  "Load in the pmail file TEXT.
4183
 
TOKEN and INDENT are not used."
4184
 
  (speedbar-with-attached-buffer
4185
 
   (message "Loading in PMAIL file %s..." text)
4186
 
   (find-file text)))
4187
 
 
4188
 
(defun pmail-speedbar-move-message-to-folder-on-line ()
4189
 
  "If the current line is a folder, move current message to it."
4190
 
  (interactive)
4191
 
  (save-excursion
4192
 
    (beginning-of-line)
4193
 
    (if (re-search-forward "<M> " (save-excursion (end-of-line) (point)) t)
4194
 
        (progn
4195
 
          (forward-char -2)
4196
 
          (speedbar-do-function-pointer)))))
4197
 
 
4198
 
(defun pmail-speedbar-move-message (text token indent)
4199
 
  "From button TEXT, copy current message to the pmail file specified by TOKEN.
4200
 
TEXT and INDENT are not used."
4201
 
  (speedbar-with-attached-buffer
4202
 
   (message "Moving message to %s" token)
4203
 
   (pmail-output-to-pmail-file token)))
4204
 
 
4205
 
; Functions for setting, getting and encoding the POP password.
4206
 
; The password is encoded to prevent it from being easily accessible
4207
 
; to "prying eyes."  Obviously, this encoding isn't "real security,"
4208
 
; nor is it meant to be.
4209
 
 
4210
 
;;;###autoload
4211
 
(defun pmail-set-remote-password (password)
4212
 
  "Set PASSWORD to be used for retrieving mail from a POP or IMAP server."
4213
 
  (interactive "sPassword: ")
4214
 
  (if password
4215
 
      (setq pmail-encoded-remote-password
4216
 
            (pmail-encode-string password (emacs-pid)))
4217
 
    (setq pmail-remote-password nil)
4218
 
    (setq pmail-encoded-remote-password nil)))
4219
 
 
4220
 
(defun pmail-get-remote-password (imap)
4221
 
  "Get the password for retrieving mail from a POP or IMAP server.  If none
4222
 
has been set, then prompt the user for one."
4223
 
  (when (not pmail-encoded-remote-password)
4224
 
    (if (not pmail-remote-password)
4225
 
        (setq pmail-remote-password
4226
 
              (read-passwd (if imap
4227
 
                               "IMAP password: "
4228
 
                             "POP password: "))))
4229
 
    (pmail-set-remote-password pmail-remote-password)
4230
 
    (setq pmail-remote-password nil))
4231
 
  (pmail-encode-string pmail-encoded-remote-password (emacs-pid)))
4232
 
 
4233
 
(defun pmail-have-password ()
4234
 
  (or pmail-remote-password pmail-encoded-remote-password))
4235
 
 
4236
 
(defun pmail-encode-string (string mask)
4237
 
 "Encode STRING with integer MASK, by taking the exclusive OR of the
4238
 
lowest byte in the mask with the first character of string, the
4239
 
second-lowest-byte with the second character of the string, etc.,
4240
 
restarting at the lowest byte of the mask whenever it runs out.
4241
 
Returns the encoded string.  Calling the function again with an
4242
 
encoded string (and the same mask) will decode the string."
4243
 
 (setq mask (abs mask))                 ; doesn't work if negative
4244
 
 (let* ((string-vector (string-to-vector string)) (i 0)
4245
 
        (len (length string-vector)) (curmask mask) charmask)
4246
 
   (while (< i len)
4247
 
     (if (= curmask 0)
4248
 
         (setq curmask mask))
4249
 
     (setq charmask (% curmask 256))
4250
 
     (setq curmask (lsh curmask -8))
4251
 
     (aset string-vector i (logxor charmask (aref string-vector i)))
4252
 
     (setq i (1+ i)))
4253
 
   (concat string-vector)))
4254
 
 
4255
 
;;;;  Desktop support
4256
 
 
4257
 
(defun pmail-restore-desktop-buffer (desktop-buffer-file-name
4258
 
                                     desktop-buffer-name
4259
 
                                     desktop-buffer-misc)
4260
 
  "Restore an pmail buffer specified in a desktop file."
4261
 
  (condition-case error
4262
 
      (progn
4263
 
        (pmail-input desktop-buffer-file-name)
4264
 
        (if (eq major-mode 'pmail-mode)
4265
 
            (current-buffer)
4266
 
          pmail-buffer))
4267
 
    (file-locked
4268
 
      (kill-buffer (current-buffer))
4269
 
      nil)))
4270
 
 
4271
 
(add-to-list 'desktop-buffer-mode-handlers
4272
 
             '(pmail-mode . pmail-restore-desktop-buffer))
4273
 
 
4274
 
 
4275
 
(provide 'pmail)
4276
 
 
4277
 
;; Local Variables:
4278
 
;; change-log-default-name: "ChangeLog.pmail"
4279
 
;; End:
4280
 
 
4281
 
;; arch-tag: 65d257d3-c281-4a65-9c38-e61af95af2f0
4282
 
;;; pmail.el ends here