comment
[bpt/emacs.git] / lisp / gnus / mail-source.el
CommitLineData
c113de23
GM
1;;; mail-source.el --- functions for fetching mail
2;; Copyright (C) 1999, 2000 Free Software Foundation, Inc.
3
4;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
4f926b3e 5;; Maintainer: bugs@gnus.org
c113de23
GM
6;; Keywords: news, mail
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
24
25;;; Commentary:
26
27;;; Code:
28
4f926b3e
DL
29(eval-when-compile
30 (require 'cl)
31 (require 'imap)
32 (eval-when-compile (defvar display-time-mail-function)))
c113de23
GM
33(eval-and-compile
34 (autoload 'pop3-movemail "pop3")
4f926b3e
DL
35 (autoload 'pop3-get-message-count "pop3")
36 (autoload 'nnheader-cancel-timer "nnheader"))
c113de23 37(require 'format-spec)
4f926b3e 38(require 'mm-util)
c113de23
GM
39
40(defgroup mail-source nil
41 "The mail-fetching library."
ce9401f3 42 :version "21.1"
c113de23
GM
43 :group 'gnus)
44
4f926b3e
DL
45;; Define these at compile time to avoid dragging in imap always.
46(defconst mail-source-imap-authenticators
47 (eval-when-compile
48 (mapcar (lambda (a)
49 (list 'const (car a)))
50 imap-authenticator-alist)))
51(defconst mail-source-imap-streams
52 (eval-when-compile
53 (mapcar (lambda (a)
54 (list 'const (car a)))
55 imap-stream-alist)))
56
72fc0418 57(defcustom mail-sources nil
c113de23 58 "*Where the mail backends will look for incoming mail.
ce9401f3
DL
59This variable is a list of mail source specifiers.
60See Info node `(gnus)Mail Source Specifiers'."
c113de23 61 :group 'mail-source
ce9401f3 62 :type `(repeat
4f926b3e
DL
63 (choice :format "%[Value Menu%] %v"
64 :value (file)
65 (cons :tag "Spool file"
66 (const :format "" file)
67 (checklist :tag "Options" :greedy t
68 (group :inline t
69 (const :format "" :value :path)
70 file)))
ce9401f3 71 (cons :tag "Several files in a directory"
4f926b3e
DL
72 (const :format "" directory)
73 (checklist :tag "Options" :greedy t
74 (group :inline t
75 (const :format "" :value :path)
76 (directory :tag "Path"))
77 (group :inline t
78 (const :format "" :value :suffix)
79 (string :tag "Suffix"))
80 (group :inline t
81 (const :format "" :value :predicate)
82 (function :tag "Predicate"))
83 (group :inline t
84 (const :format "" :value :prescript)
85 (string :tag "Prescript"))
86 (group :inline t
87 (const :format "" :value :postscript)
88 (string :tag "Postscript"))
89 (group :inline t
90 (const :format "" :value :plugged)
91 (boolean :tag "Plugged"))))
ce9401f3 92 (cons :tag "POP3 server"
4f926b3e
DL
93 (const :format "" pop)
94 (checklist :tag "Options" :greedy t
95 (group :inline t
96 (const :format "" :value :server)
97 (string :tag "Server"))
98 (group :inline t
99 (const :format "" :value :port)
100 (choice :tag "Port"
101 :value "pop3"
102 (number :format "%v")
103 (string :format "%v")))
104 (group :inline t
105 (const :format "" :value :user)
106 (string :tag "User"))
107 (group :inline t
108 (const :format "" :value :password)
109 (string :tag "Password"))
110 (group :inline t
111 (const :format "" :value :program)
112 (string :tag "Program"))
113 (group :inline t
114 (const :format "" :value :prescript)
115 (string :tag "Prescript"))
116 (group :inline t
117 (const :format "" :value :postscript)
118 (string :tag "Postscript"))
119 (group :inline t
120 (const :format "" :value :function)
121 (function :tag "Function"))
122 (group :inline t
123 (const :format ""
124 :value :authentication)
125 (choice :tag "Authentication"
126 :value apop
127 (const password)
128 (const apop)))
129 (group :inline t
130 (const :format "" :value :plugged)
131 (boolean :tag "Plugged"))))
ce9401f3 132 (cons :tag "Maildir (qmail, postfix...)"
4f926b3e
DL
133 (const :format "" maildir)
134 (checklist :tag "Options" :greedy t
135 (group :inline t
136 (const :format "" :value :path)
137 (directory :tag "Path"))
138 (group :inline t
139 (const :format "" :value :plugged)
140 (boolean :tag "Plugged"))))
ce9401f3 141 (cons :tag "IMAP server"
4f926b3e
DL
142 (const :format "" imap)
143 (checklist :tag "Options" :greedy t
144 (group :inline t
145 (const :format "" :value :server)
146 (string :tag "Server"))
147 (group :inline t
148 (const :format "" :value :port)
149 (choice :tag "Port"
150 :value 143
151 number string))
152 (group :inline t
153 (const :format "" :value :user)
154 (string :tag "User"))
155 (group :inline t
156 (const :format "" :value :password)
157 (string :tag "Password"))
158 (group :inline t
159 (const :format "" :value :stream)
160 (choice :tag "Stream"
161 :value network
162 ,@mail-source-imap-streams))
163 (group :inline t
164 (const :format ""
165 :value :authenticator)
166 (choice :tag "Authenticator"
167 :value login
168 ,@mail-source-imap-authenticators))
169 (group :inline t
170 (const :format "" :value :mailbox)
5dd7fd91
DL
171 (string :tag "Mailbox"
172 :value "INBOX"))
4f926b3e
DL
173 (group :inline t
174 (const :format "" :value :predicate)
5dd7fd91
DL
175 (string :tag "Predicate"
176 :value "UNSEEN UNDELETED"))
4f926b3e
DL
177 (group :inline t
178 (const :format "" :value :fetchflag)
5dd7fd91
DL
179 (string :tag "Fetchflag"
180 :value "\\Deleted"))
4f926b3e
DL
181 (group :inline t
182 (const :format ""
183 :value :dontexpunge)
184 (boolean :tag "Dontexpunge"))
185 (group :inline t
186 (const :format "" :value :plugged)
187 (boolean :tag "Plugged"))))
ce9401f3 188 (cons :tag "Webmail server"
4f926b3e
DL
189 (const :format "" webmail)
190 (checklist :tag "Options" :greedy t
191 (group :inline t
192 (const :format "" :value :subtype)
193 ;; Should be generated from
194 ;; `webmail-type-definition', but we
195 ;; can't require webmail without W3.
196 (choice :tag "Subtype"
197 :value hotmail
198 (const hotmail)
199 (const yahoo)
200 (const netaddress)
201 (const netscape)
202 (const my-deja)))
203 (group :inline t
204 (const :format "" :value :user)
205 (string :tag "User"))
206 (group :inline t
207 (const :format "" :value :password)
208 (string :tag "Password"))
209 (group :inline t
210 (const :format ""
211 :value :dontexpunge)
212 (boolean :tag "Dontexpunge"))
213 (group :inline t
214 (const :format "" :value :plugged)
215 (boolean :tag "Plugged")))))))
c113de23
GM
216
217(defcustom mail-source-primary-source nil
218 "*Primary source for incoming mail.
219If non-nil, this maildrop will be checked periodically for new mail."
220 :group 'mail-source
221 :type 'sexp)
222
223(defcustom mail-source-crash-box "~/.emacs-mail-crash-box"
224 "File where mail will be stored while processing it."
225 :group 'mail-source
226 :type 'file)
227
228(defcustom mail-source-directory "~/Mail/"
229 "Directory where files (if any) will be stored."
230 :group 'mail-source
231 :type 'directory)
232
233(defcustom mail-source-default-file-modes 384
234 "Set the mode bits of all new mail files to this integer."
235 :group 'mail-source
236 :type 'integer)
237
805b7fc0 238(defcustom mail-source-delete-incoming t
c113de23
GM
239 "*If non-nil, delete incoming files after handling."
240 :group 'mail-source
241 :type 'boolean)
242
243(defcustom mail-source-incoming-file-prefix "Incoming"
244 "Prefix for file name for storing incoming mail"
245 :group 'mail-source
246 :type 'string)
247
248(defcustom mail-source-report-new-mail-interval 5
249 "Interval in minutes between checks for new mail."
250 :group 'mail-source
251 :type 'number)
252
253(defcustom mail-source-idle-time-delay 5
254 "Number of idle seconds to wait before checking for new mail."
255 :group 'mail-source
256 :type 'number)
257
258;;; Internal variables.
259
260(defvar mail-source-string ""
261 "A dynamically bound string that says what the current mail source is.")
262
263(defvar mail-source-new-mail-available nil
264 "Flag indicating when new mail is available.")
265
266(eval-and-compile
267 (defvar mail-source-common-keyword-map
268 '((:plugged))
269 "Mapping from keywords to default values.
270Common keywords should be listed here.")
271
272 (defvar mail-source-keyword-map
273 '((file
274 (:prescript)
275 (:prescript-delay)
276 (:postscript)
277 (:path (or (getenv "MAIL")
4f926b3e 278 (expand-file-name (user-login-name) rmail-spool-directory))))
c113de23
GM
279 (directory
280 (:path)
281 (:suffix ".spool")
282 (:predicate identity))
283 (pop
284 (:prescript)
285 (:prescript-delay)
286 (:postscript)
287 (:server (getenv "MAILHOST"))
288 (:port 110)
289 (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER")))
290 (:program)
291 (:function)
292 (:password)
293 (:authentication password))
294 (maildir
295 (:path (or (getenv "MAILDIR") "~/Maildir/"))
296 (:subdirs ("new" "cur"))
297 (:function))
298 (imap
299 (:server (getenv "MAILHOST"))
300 (:port)
301 (:stream)
302 (:authentication)
303 (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER")))
304 (:password)
305 (:mailbox "INBOX")
306 (:predicate "UNSEEN UNDELETED")
307 (:fetchflag "\\Deleted")
308 (:dontexpunge))
309 (webmail
310 (:subtype hotmail)
311 (:user (or (user-login-name) (getenv "LOGNAME") (getenv "USER")))
312 (:password)
313 (:dontexpunge)
314 (:authentication password)))
315 "Mapping from keywords to default values.
316All keywords that can be used must be listed here."))
317
318(defvar mail-source-fetcher-alist
319 '((file mail-source-fetch-file)
320 (directory mail-source-fetch-directory)
321 (pop mail-source-fetch-pop)
322 (maildir mail-source-fetch-maildir)
323 (imap mail-source-fetch-imap)
324 (webmail mail-source-fetch-webmail))
325 "A mapping from source type to fetcher function.")
326
327(defvar mail-source-password-cache nil)
328
329(defvar mail-source-plugged t)
330
331;;; Functions
332
333(eval-and-compile
334 (defun mail-source-strip-keyword (keyword)
335 "Strip the leading colon off the KEYWORD."
336 (intern (substring (symbol-name keyword) 1))))
337
338(eval-and-compile
339 (defun mail-source-bind-1 (type)
340 (let* ((defaults (cdr (assq type mail-source-keyword-map)))
341 default bind)
342 (while (setq default (pop defaults))
343 (push (list (mail-source-strip-keyword (car default))
344 nil)
345 bind))
346 bind)))
347
348(defmacro mail-source-bind (type-source &rest body)
349 "Return a `let' form that binds all variables in source TYPE.
350TYPE-SOURCE is a list where the first element is the TYPE, and
351the second variable is the SOURCE.
352At run time, the mail source specifier SOURCE will be inspected,
353and the variables will be set according to it. Variables not
354specified will be given default values.
355
356After this is done, BODY will be executed in the scope
357of the `let' form.
358
359The variables bound and their default values are described by
360the `mail-source-keyword-map' variable."
361 `(let ,(mail-source-bind-1 (car type-source))
362 (mail-source-set-1 ,(cadr type-source))
363 ,@body))
364
365(put 'mail-source-bind 'lisp-indent-function 1)
366(put 'mail-source-bind 'edebug-form-spec '(form body))
367
368(defun mail-source-set-1 (source)
369 (let* ((type (pop source))
370 (defaults (cdr (assq type mail-source-keyword-map)))
371 default value keyword)
372 (while (setq default (pop defaults))
373 (set (mail-source-strip-keyword (setq keyword (car default)))
374 (if (setq value (plist-get source keyword))
375 (mail-source-value value)
376 (mail-source-value (cadr default)))))))
377
378(eval-and-compile
379 (defun mail-source-bind-common-1 ()
380 (let* ((defaults mail-source-common-keyword-map)
381 default bind)
382 (while (setq default (pop defaults))
383 (push (list (mail-source-strip-keyword (car default))
384 nil)
385 bind))
386 bind)))
387
388(defun mail-source-set-common-1 (source)
389 (let* ((type (pop source))
390 (defaults mail-source-common-keyword-map)
391 (defaults-1 (cdr (assq type mail-source-keyword-map)))
392 default value keyword)
393 (while (setq default (pop defaults))
394 (set (mail-source-strip-keyword (setq keyword (car default)))
395 (if (setq value (plist-get source keyword))
396 (mail-source-value value)
397 (if (setq value (assq keyword defaults-1))
398 (mail-source-value (cadr value))
399 (mail-source-value (cadr default))))))))
400
401(defmacro mail-source-bind-common (source &rest body)
402 "Return a `let' form that binds all common variables.
403See `mail-source-bind'."
404 `(let ,(mail-source-bind-common-1)
405 (mail-source-set-common-1 source)
406 ,@body))
407
408(put 'mail-source-bind-common 'lisp-indent-function 1)
409(put 'mail-source-bind-common 'edebug-form-spec '(form body))
410
411(defun mail-source-value (value)
412 "Return the value of VALUE."
413 (cond
414 ;; String
415 ((stringp value)
416 value)
417 ;; Function
418 ((and (listp value)
419 (functionp (car value)))
420 (eval value))
421 ;; Just return the value.
422 (t
423 value)))
424
425(defun mail-source-fetch (source callback)
426 "Fetch mail from SOURCE and call CALLBACK zero or more times.
427CALLBACK will be called with the name of the file where (some of)
428the mail from SOURCE is put.
429Return the number of files that were found."
430 (mail-source-bind-common source
431 (if (or mail-source-plugged plugged)
432 (save-excursion
433 (let ((function (cadr (assq (car source) mail-source-fetcher-alist)))
434 (found 0))
435 (unless function
436 (error "%S is an invalid mail source specification" source))
437 ;; If there's anything in the crash box, we do it first.
438 (when (file-exists-p mail-source-crash-box)
439 (message "Processing mail from %s..." mail-source-crash-box)
440 (setq found (mail-source-callback
441 callback mail-source-crash-box)))
442 (+ found
443 (condition-case err
444 (funcall function source callback)
445 (error
446 (unless (yes-or-no-p
447 (format "Mail source error (%s). Continue? " err))
448 (error "Cannot get new mail."))
449 0))))))))
450
451(defun mail-source-make-complex-temp-name (prefix)
452 (let ((newname (make-temp-name prefix))
453 (newprefix prefix))
454 (while (file-exists-p newname)
455 (setq newprefix (concat newprefix "x"))
456 (setq newname (make-temp-name newprefix)))
457 newname))
458
459(defun mail-source-callback (callback info)
460 "Call CALLBACK on the mail file, and then remove the mail file.
461Pass INFO on to CALLBACK."
462 (if (or (not (file-exists-p mail-source-crash-box))
463 (zerop (nth 7 (file-attributes mail-source-crash-box))))
464 (progn
465 (when (file-exists-p mail-source-crash-box)
466 (delete-file mail-source-crash-box))
467 0)
468 (prog1
469 (funcall callback mail-source-crash-box info)
470 (when (file-exists-p mail-source-crash-box)
471 ;; Delete or move the incoming mail out of the way.
472 (if mail-source-delete-incoming
473 (delete-file mail-source-crash-box)
474 (let ((incoming
475 (mail-source-make-complex-temp-name
476 (expand-file-name
477 mail-source-incoming-file-prefix
478 mail-source-directory))))
479 (unless (file-exists-p (file-name-directory incoming))
480 (make-directory (file-name-directory incoming) t))
481 (rename-file mail-source-crash-box incoming t)))))))
482
483(defun mail-source-movemail (from to)
484 "Move FROM to TO using movemail."
485 (if (not (file-writable-p to))
486 (error "Can't write to crash box %s. Not moving mail" to)
487 (let ((to (file-truename (expand-file-name to)))
488 errors result)
489 (setq to (file-truename to)
490 from (file-truename from))
491 ;; Set TO if have not already done so, and rename or copy
492 ;; the file FROM to TO if and as appropriate.
493 (cond
494 ((file-exists-p to)
495 ;; The crash box exists already.
496 t)
497 ((not (file-exists-p from))
498 ;; There is no inbox.
499 (setq to nil))
500 ((zerop (nth 7 (file-attributes from)))
501 ;; Empty file.
502 (setq to nil))
503 (t
504 ;; If getting from mail spool directory, use movemail to move
505 ;; rather than just renaming, so as to interlock with the
506 ;; mailer.
507 (unwind-protect
508 (save-excursion
509 (setq errors (generate-new-buffer " *mail source loss*"))
510 (let ((default-directory "/"))
511 (setq result
512 (apply
513 'call-process
514 (append
515 (list
516 (expand-file-name "movemail" exec-directory)
517 nil errors nil from to)))))
518 (when (file-exists-p to)
519 (set-file-modes to mail-source-default-file-modes))
520 (if (and (not (buffer-modified-p errors))
521 (zerop result))
522 ;; No output => movemail won.
523 t
524 (set-buffer errors)
525 ;; There may be a warning about older revisions. We
526 ;; ignore that.
527 (goto-char (point-min))
528 (if (search-forward "older revision" nil t)
529 t
530 ;; Probably a real error.
531 (subst-char-in-region (point-min) (point-max) ?\n ?\ )
532 (goto-char (point-max))
533 (skip-chars-backward " \t")
534 (delete-region (point) (point-max))
535 (goto-char (point-min))
536 (when (looking-at "movemail: ")
537 (delete-region (point-min) (match-end 0)))
538 (unless (yes-or-no-p
539 (format "movemail: %s (%d return). Continue? "
540 (buffer-string) result))
541 (error "%s" (buffer-string)))
542 (setq to nil)))))))
543 (when (and errors
544 (buffer-name errors))
545 (kill-buffer errors))
546 ;; Return whether we moved successfully or not.
547 to)))
548
549(defun mail-source-movemail-and-remove (from to)
550 "Move FROM to TO using movemail, then remove FROM if empty."
551 (or (not (mail-source-movemail from to))
552 (not (zerop (nth 7 (file-attributes from))))
553 (delete-file from)))
554
555(defvar mail-source-read-passwd nil)
556(defun mail-source-read-passwd (prompt &rest args)
557 "Read a password using PROMPT.
558If ARGS, PROMPT is used as an argument to `format'."
559 (let ((prompt
560 (if args
561 (apply 'format prompt args)
562 prompt)))
563 (unless mail-source-read-passwd
564 (if (or (fboundp 'read-passwd) (load "passwd" t))
565 (setq mail-source-read-passwd 'read-passwd)
566 (unless (fboundp 'ange-ftp-read-passwd)
567 (autoload 'ange-ftp-read-passwd "ange-ftp"))
568 (setq mail-source-read-passwd 'ange-ftp-read-passwd)))
569 (funcall mail-source-read-passwd prompt)))
570
571(defun mail-source-fetch-with-program (program)
572 (zerop (call-process shell-file-name nil nil nil
ce9401f3 573 shell-command-switch program)))
c113de23
GM
574
575(defun mail-source-run-script (script spec &optional delay)
576 (when script
577 (if (and (symbolp script) (fboundp script))
578 (funcall script)
579 (mail-source-call-script
580 (format-spec script spec))))
581 (when delay
582 (sleep-for delay)))
583
584(defun mail-source-call-script (script)
585 (let ((background nil))
586 (when (string-match "& *$" script)
587 (setq script (substring script 0 (match-beginning 0))
588 background 0))
589 (call-process shell-file-name nil background nil
590 shell-command-switch script)))
591
592;;;
593;;; Different fetchers
594;;;
595
596(defun mail-source-fetch-file (source callback)
597 "Fetcher for single-file sources."
598 (mail-source-bind (file source)
599 (mail-source-run-script
600 prescript (format-spec-make ?t mail-source-crash-box)
601 prescript-delay)
602 (let ((mail-source-string (format "file:%s" path)))
603 (if (mail-source-movemail path mail-source-crash-box)
604 (prog1
605 (mail-source-callback callback path)
606 (mail-source-run-script
607 postscript (format-spec-make ?t mail-source-crash-box)))
608 0))))
609
610(defun mail-source-fetch-directory (source callback)
611 "Fetcher for directory sources."
612 (mail-source-bind (directory source)
613 (let ((found 0)
614 (mail-source-string (format "directory:%s" path)))
615 (dolist (file (directory-files
616 path t (concat (regexp-quote suffix) "$")))
617 (when (and (file-regular-p file)
618 (funcall predicate file)
619 (mail-source-movemail file mail-source-crash-box))
620 (incf found (mail-source-callback callback file))))
621 found)))
622
623(defun mail-source-fetch-pop (source callback)
624 "Fetcher for single-file sources."
625 (mail-source-bind (pop source)
626 (mail-source-run-script
627 prescript
628 (format-spec-make ?p password ?t mail-source-crash-box
629 ?s server ?P port ?u user)
630 prescript-delay)
631 (let ((from (format "%s:%s:%s" server user port))
632 (mail-source-string (format "pop:%s@%s" user server))
633 result)
634 (when (eq authentication 'password)
635 (setq password
636 (or password
637 (cdr (assoc from mail-source-password-cache))
638 (mail-source-read-passwd
639 (format "Password for %s at %s: " user server)))))
640 (when server
641 (setenv "MAILHOST" server))
642 (setq result
643 (cond
644 (program
645 (mail-source-fetch-with-program
646 (format-spec
647 program
648 (format-spec-make ?p password ?t mail-source-crash-box
649 ?s server ?P port ?u user))))
650 (function
651 (funcall function mail-source-crash-box))
652 ;; The default is to use pop3.el.
653 (t
654 (let ((pop3-password password)
655 (pop3-maildrop user)
656 (pop3-mailhost server)
657 (pop3-port port)
658 (pop3-authentication-scheme
659 (if (eq authentication 'apop) 'apop 'pass)))
660 (save-excursion (pop3-movemail mail-source-crash-box))))))
661 (if result
662 (progn
663 (when (eq authentication 'password)
664 (unless (assoc from mail-source-password-cache)
665 (push (cons from password) mail-source-password-cache)))
666 (prog1
667 (mail-source-callback callback server)
668 ;; Update display-time's mail flag, if relevant.
669 (if (equal source mail-source-primary-source)
670 (setq mail-source-new-mail-available nil))
671 (mail-source-run-script
672 postscript
673 (format-spec-make ?p password ?t mail-source-crash-box
674 ?s server ?P port ?u user))))
675 ;; We nix out the password in case the error
676 ;; was because of a wrong password being given.
677 (setq mail-source-password-cache
678 (delq (assoc from mail-source-password-cache)
679 mail-source-password-cache))
680 0))))
681
682(defun mail-source-check-pop (source)
683 "Check whether there is new mail."
684 (mail-source-bind (pop source)
685 (let ((from (format "%s:%s:%s" server user port))
686 (mail-source-string (format "pop:%s@%s" user server))
687 result)
688 (when (eq authentication 'password)
689 (setq password
690 (or password
691 (cdr (assoc from mail-source-password-cache))
692 (mail-source-read-passwd
693 (format "Password for %s at %s: " user server))))
694 (unless (assoc from mail-source-password-cache)
695 (push (cons from password) mail-source-password-cache)))
696 (when server
697 (setenv "MAILHOST" server))
698 (setq result
699 (cond
700 ;; No easy way to check whether mail is waiting for these.
701 (program)
702 (function)
703 ;; The default is to use pop3.el.
704 (t
705 (let ((pop3-password password)
706 (pop3-maildrop user)
707 (pop3-mailhost server)
708 (pop3-port port)
709 (pop3-authentication-scheme
710 (if (eq authentication 'apop) 'apop 'pass)))
711 (save-excursion (pop3-get-message-count))))))
712 (if result
713 ;; Inform display-time that we have new mail.
714 (setq mail-source-new-mail-available (> result 0))
715 ;; We nix out the password in case the error
716 ;; was because of a wrong password being given.
717 (setq mail-source-password-cache
718 (delq (assoc from mail-source-password-cache)
719 mail-source-password-cache)))
720 result)))
721
722(defun mail-source-new-mail-p ()
723 "Handler for `display-time' to indicate when new mail is available."
724 ;; Only report flag setting; flag is updated on a different schedule.
725 mail-source-new-mail-available)
726
727
728(defvar mail-source-report-new-mail nil)
729(defvar mail-source-report-new-mail-timer nil)
730(defvar mail-source-report-new-mail-idle-timer nil)
731
126cbb42
DL
732(eval-when-compile
733 (if (featurep 'xemacs)
734 (require 'itimer)
735 (require 'timer)))
c113de23
GM
736
737(defun mail-source-start-idle-timer ()
738 ;; Start our idle timer if necessary, so we delay the check until the
739 ;; user isn't typing.
740 (unless mail-source-report-new-mail-idle-timer
741 (setq mail-source-report-new-mail-idle-timer
742 (run-with-idle-timer
743 mail-source-idle-time-delay
744 nil
745 (lambda ()
746 (setq mail-source-report-new-mail-idle-timer nil)
747 (mail-source-check-pop mail-source-primary-source))))
748 ;; Since idle timers created when Emacs is already in the idle
749 ;; state don't get activated until Emacs _next_ becomes idle, we
750 ;; need to force our timer to be considered active now. We do
751 ;; this by being naughty and poking the timer internals directly
752 ;; (element 0 of the vector is nil if the timer is active).
753 (aset mail-source-report-new-mail-idle-timer 0 nil)))
754
755(defun mail-source-report-new-mail (arg)
756 "Toggle whether to report when new mail is available.
757This only works when `display-time' is enabled."
758 (interactive "P")
759 (if (not mail-source-primary-source)
760 (error "Need to set `mail-source-primary-source' to check for new mail."))
761 (let ((on (if (null arg)
762 (not mail-source-report-new-mail)
763 (> (prefix-numeric-value arg) 0))))
764 (setq mail-source-report-new-mail on)
765 (and mail-source-report-new-mail-timer
72fc0418 766 (nnheader-cancel-timer mail-source-report-new-mail-timer))
c113de23 767 (and mail-source-report-new-mail-idle-timer
72fc0418 768 (nnheader-cancel-timer mail-source-report-new-mail-idle-timer))
c113de23
GM
769 (setq mail-source-report-new-mail-timer nil)
770 (setq mail-source-report-new-mail-idle-timer nil)
771 (if on
772 (progn
773 (require 'time)
ce9401f3 774 ;; display-time-mail-function is an Emacs 21 feature.
c113de23
GM
775 (setq display-time-mail-function #'mail-source-new-mail-p)
776 ;; Set up the main timer.
777 (setq mail-source-report-new-mail-timer
778 (run-at-time t (* 60 mail-source-report-new-mail-interval)
779 #'mail-source-start-idle-timer))
780 ;; When you get new mail, clear "Mail" from the mode line.
781 (add-hook 'nnmail-post-get-new-mail-hook
782 'display-time-event-handler)
783 (message "Mail check enabled"))
784 (setq display-time-mail-function nil)
785 (remove-hook 'nnmail-post-get-new-mail-hook
786 'display-time-event-handler)
787 (message "Mail check disabled"))))
788
789(defun mail-source-fetch-maildir (source callback)
790 "Fetcher for maildir sources."
791 (mail-source-bind (maildir source)
792 (let ((found 0)
793 mail-source-string)
794 (unless (string-match "/$" path)
795 (setq path (concat path "/")))
796 (dolist (subdir subdirs)
797 (when (file-directory-p (concat path subdir))
798 (setq mail-source-string (format "maildir:%s%s" path subdir))
799 (dolist (file (directory-files (concat path subdir) t))
800 (when (and (not (file-directory-p file))
801 (not (if function
802 (funcall function file mail-source-crash-box)
803 (let ((coding-system-for-write
804 mm-text-coding-system)
805 (coding-system-for-read
806 mm-text-coding-system))
807 (with-temp-file mail-source-crash-box
808 (insert-file-contents file)
809 (goto-char (point-min))
810;;; ;; Unix mail format
811;;; (unless (looking-at "\n*From ")
812;;; (insert "From maildir "
813;;; (current-time-string) "\n"))
814;;; (while (re-search-forward "^From " nil t)
815;;; (replace-match ">From "))
126cbb42
DL
816;;; (goto-char (point-max))
817;;; (insert "\n\n")
c113de23 818 ;; MMDF mail format
126cbb42 819 (insert "\001\001\001\001\n"))
c113de23
GM
820 (delete-file file)))))
821 (incf found (mail-source-callback callback file))))))
822 found)))
823
824(eval-and-compile
825 (autoload 'imap-open "imap")
826 (autoload 'imap-authenticate "imap")
827 (autoload 'imap-mailbox-select "imap")
828 (autoload 'imap-mailbox-unselect "imap")
829 (autoload 'imap-mailbox-close "imap")
830 (autoload 'imap-search "imap")
831 (autoload 'imap-fetch "imap")
832 (autoload 'imap-close "imap")
833 (autoload 'imap-error-text "imap")
834 (autoload 'imap-message-flags-add "imap")
835 (autoload 'imap-list-to-message-set "imap")
72fc0418 836 (autoload 'imap-range-to-message-set "imap")
c113de23
GM
837 (autoload 'nnheader-ms-strip-cr "nnheader"))
838
72fc0418
DL
839(defvar mail-source-imap-file-coding-system 'binary
840 "Coding system for the crashbox made by `mail-source-fetch-imap'.")
841
c113de23
GM
842(defun mail-source-fetch-imap (source callback)
843 "Fetcher for imap sources."
844 (mail-source-bind (imap source)
845 (let ((from (format "%s:%s:%s" server user port))
846 (found 0)
847 (buf (get-buffer-create (generate-new-buffer-name " *imap source*")))
848 (mail-source-string (format "imap:%s:%s" server mailbox))
849 remove)
850 (if (and (imap-open server port stream authentication buf)
851 (imap-authenticate
852 user (or (cdr (assoc from mail-source-password-cache))
853 password) buf)
854 (imap-mailbox-select mailbox nil buf))
4f926b3e 855 (let ((coding-system-for-write mail-source-imap-file-coding-system)
126cbb42 856 str)
c113de23 857 (with-temp-file mail-source-crash-box
4f926b3e
DL
858 ;; Avoid converting 8-bit chars from inserted strings to
859 ;; multibyte.
860 (mm-disable-multibyte)
c113de23
GM
861 ;; remember password
862 (with-current-buffer buf
863 (when (or imap-password
864 (assoc from mail-source-password-cache))
865 (push (cons from imap-password) mail-source-password-cache)))
866 ;; if predicate is nil, use all uids
867 (dolist (uid (imap-search (or predicate "1:*") buf))
868 (when (setq str (imap-fetch uid "RFC822.PEEK" 'RFC822 nil buf))
869 (push uid remove)
870 (insert "From imap " (current-time-string) "\n")
871 (save-excursion
872 (insert str "\n\n"))
873 (while (re-search-forward "^From " nil t)
874 (replace-match ">From "))
875 (goto-char (point-max))))
876 (nnheader-ms-strip-cr))
877 (incf found (mail-source-callback callback server))
878 (when (and remove fetchflag)
879 (imap-message-flags-add
72fc0418
DL
880 (imap-range-to-message-set (gnus-compress-sequence remove))
881 fetchflag nil buf))
c113de23
GM
882 (if dontexpunge
883 (imap-mailbox-unselect buf)
884 (imap-mailbox-close buf))
885 (imap-close buf))
886 (imap-close buf)
887 ;; We nix out the password in case the error
888 ;; was because of a wrong password being given.
889 (setq mail-source-password-cache
890 (delq (assoc from mail-source-password-cache)
891 mail-source-password-cache))
892 (error (imap-error-text buf)))
893 (kill-buffer buf)
894 found)))
895
896(eval-and-compile
897 (autoload 'webmail-fetch "webmail"))
898
899(defun mail-source-fetch-webmail (source callback)
900 "Fetch for webmail source."
901 (mail-source-bind (webmail source)
902 (let ((mail-source-string (format "webmail:%s:%s" subtype user))
903 (webmail-newmail-only dontexpunge)
904 (webmail-move-to-trash-can (not dontexpunge)))
905 (when (eq authentication 'password)
906 (setq password
907 (or password
908 (cdr (assoc (format "webmail:%s:%s" subtype user)
909 mail-source-password-cache))
910 (mail-source-read-passwd
911 (format "Password for %s at %s: " user subtype))))
912 (when (and password
913 (not (assoc (format "webmail:%s:%s" subtype user)
914 mail-source-password-cache)))
915 (push (cons (format "webmail:%s:%s" subtype user) password)
916 mail-source-password-cache)))
917 (webmail-fetch mail-source-crash-box subtype user password)
918 (mail-source-callback callback (symbol-name subtype)))))
919
920(provide 'mail-source)
921
922;;; mail-source.el ends here