mail-source.el 39 KB

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