mail-source.el 39 KB

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