getmail.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2019 Christopher Baines <mail@cbaines.net>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (gnu services getmail)
  19. #:use-module (gnu services)
  20. #:use-module (gnu services base)
  21. #:use-module (gnu services configuration)
  22. #:use-module (gnu services shepherd)
  23. #:use-module (gnu system pam)
  24. #:use-module (gnu system shadow)
  25. #:use-module (gnu packages mail)
  26. #:use-module (gnu packages admin)
  27. #:use-module (gnu packages tls)
  28. #:use-module (guix records)
  29. #:use-module (guix store)
  30. #:use-module (guix packages)
  31. #:use-module (guix gexp)
  32. #:use-module (ice-9 match)
  33. #:use-module (ice-9 format)
  34. #:use-module (srfi srfi-1)
  35. #:export (getmail-retriever-configuration
  36. getmail-retriever-configuration-extra-parameters
  37. getmail-destination-configuration
  38. getmail-options-configuration
  39. getmail-configuration-file
  40. getmail-configuration
  41. getmail-service-type))
  42. ;;; Commentary:
  43. ;;;
  44. ;;; Service for the getmail mail retriever.
  45. ;;;
  46. ;;; Code:
  47. (define (uglify-field-name field-name)
  48. (let ((str (symbol->string field-name)))
  49. (string-join (string-split (if (string-suffix? "?" str)
  50. (substring str 0 (1- (string-length str)))
  51. str)
  52. #\-)
  53. "_")))
  54. (define (serialize-field field-name val)
  55. #~(let ((val '#$val))
  56. (format #f "~a = ~a\n"
  57. #$(uglify-field-name field-name)
  58. (cond
  59. ((list? val)
  60. (string-append
  61. "("
  62. (string-concatenate
  63. (map (lambda (list-val)
  64. (format #f "\"~a\", " list-val))
  65. val))
  66. ")"))
  67. (else
  68. val)))))
  69. (define (serialize-string field-name val)
  70. (if (string=? val "")
  71. ""
  72. (serialize-field field-name val)))
  73. (define (string-or-filelike? val)
  74. (or (string? val)
  75. (file-like? val)))
  76. (define (serialize-string-or-filelike field-name val)
  77. (if (equal? val "")
  78. ""
  79. (serialize-field field-name val)))
  80. (define (serialize-boolean field-name val)
  81. (serialize-field field-name (if val "true" "false")))
  82. (define (non-negative-integer? val)
  83. (and (exact-integer? val) (not (negative? val))))
  84. (define (serialize-non-negative-integer field-name val)
  85. (serialize-field field-name val))
  86. (define serialize-list serialize-field)
  87. (define parameter-alist? list?)
  88. (define (serialize-parameter-alist field-name val)
  89. #~(string-append
  90. #$@(map (match-lambda
  91. ((key . value)
  92. (serialize-field key value)))
  93. val)))
  94. (define (serialize-getmail-retriever-configuration field-name val)
  95. (serialize-configuration val getmail-retriever-configuration-fields))
  96. (define-configuration getmail-retriever-configuration
  97. (type
  98. (string "SimpleIMAPSSLRetriever")
  99. "The type of mail retriever to use. Valid values include
  100. @samp{passwd} and @samp{static}.")
  101. (server
  102. (string 'unset)
  103. "Name or IP address of the server to retrieve mail from.")
  104. (username
  105. (string 'unset)
  106. "Username to login to the mail server with.")
  107. (port
  108. (non-negative-integer #f)
  109. "Port number to connect to.")
  110. (password
  111. (string "")
  112. "Override fields from passwd.")
  113. (password-command
  114. (list '())
  115. "Override fields from passwd.")
  116. (keyfile
  117. (string "")
  118. "PEM-formatted key file to use for the TLS negotiation.")
  119. (certfile
  120. (string "")
  121. "PEM-formatted certificate file to use for the TLS negotiation.")
  122. (ca-certs
  123. (string "")
  124. "CA certificates to use.")
  125. (extra-parameters
  126. (parameter-alist '())
  127. "Extra retriever parameters."))
  128. (define (serialize-getmail-destination-configuration field-name val)
  129. (serialize-configuration val getmail-destination-configuration-fields))
  130. (define-configuration getmail-destination-configuration
  131. (type
  132. (string 'unset)
  133. "The type of mail destination. Valid values include @samp{Maildir},
  134. @samp{Mboxrd} and @samp{MDA_external}.")
  135. (path
  136. (string-or-filelike "")
  137. "The path option for the mail destination. The behaviour depends on the
  138. chosen type.")
  139. (extra-parameters
  140. (parameter-alist '())
  141. "Extra destination parameters"))
  142. (define (serialize-getmail-options-configuration field-name val)
  143. (serialize-configuration val getmail-options-configuration-fields))
  144. (define-configuration getmail-options-configuration
  145. (verbose
  146. (non-negative-integer 1)
  147. "If set to @samp{0}, getmail will only print warnings and errors. A value
  148. of @samp{1} means that messages will be printed about retrieving and deleting
  149. messages. If set to @samp{2}, getmail will print messages about each of it's
  150. actions.")
  151. (read-all
  152. (boolean #t)
  153. "If true, getmail will retrieve all available messages. Otherwise it will
  154. only retrieve messages it hasn't seen previously.")
  155. (delete
  156. (boolean #f)
  157. "If set to true, messages will be deleted from the server after retrieving
  158. and successfully delivering them. Otherwise, messages will be left on the
  159. server.")
  160. (delete-after
  161. (non-negative-integer 0)
  162. "Getmail will delete messages this number of days after seeing them, if
  163. they have been delivered. This means messages will be left on the server this
  164. number of days after delivering them. A value of @samp{0} disabled this
  165. feature.")
  166. (delete-bigger-than
  167. (non-negative-integer 0)
  168. "Delete messages larger than this of bytes after retrieving them, even if
  169. the delete and delete-after options are disabled. A value of @samp{0}
  170. disables this feature.")
  171. (max-bytes-per-session
  172. (non-negative-integer 0)
  173. "Retrieve messages totalling up to this number of bytes before closing the
  174. session with the server. A value of @samp{0} disables this feature.")
  175. (max-message-size
  176. (non-negative-integer 0)
  177. "Don't retrieve messages larger than this number of bytes. A value of
  178. @samp{0} disables this feature.")
  179. (delivered-to
  180. (boolean #t)
  181. "If true, getmail will add a Delivered-To header to messages.")
  182. (received
  183. (boolean #t)
  184. "If set, getmail adds a Received header to the messages.")
  185. (message-log
  186. (string "")
  187. "Getmail will record a log of its actions to the named file. A value of
  188. @samp{\"\"} disables this feature.")
  189. (message-log-syslog
  190. (boolean #f)
  191. "If true, getmail will record a log of its actions using the system
  192. logger.")
  193. (message-log-verbose
  194. (boolean #f)
  195. "If true, getmail will log information about messages not retrieved and the
  196. reason for not retrieving them, as well as starting and ending information
  197. lines.")
  198. (extra-parameters
  199. (parameter-alist '())
  200. "Extra options to include."))
  201. (define (serialize-getmail-configuration-file field-name val)
  202. (match val
  203. (($ <getmail-configuration-file> location
  204. retriever destination options)
  205. #~(string-append
  206. "[retriever]\n"
  207. #$(serialize-getmail-retriever-configuration #f retriever)
  208. "\n[destination]\n"
  209. #$(serialize-getmail-destination-configuration #f destination)
  210. "\n[options]\n"
  211. #$(serialize-getmail-options-configuration #f options)))))
  212. (define-configuration getmail-configuration-file
  213. (retriever
  214. (getmail-retriever-configuration (getmail-retriever-configuration))
  215. "What mail account to retrieve mail from, and how to access that account.")
  216. (destination
  217. (getmail-destination-configuration (getmail-destination-configuration))
  218. "What to do with retrieved messages.")
  219. (options
  220. (getmail-options-configuration (getmail-options-configuration))
  221. "Configure getmail."))
  222. (define (serialize-symbol field-name val) "")
  223. (define (serialize-getmail-configuration field-name val) "")
  224. (define-configuration getmail-configuration
  225. (name
  226. (symbol "unset")
  227. "A symbol to identify the getmail service.")
  228. (package
  229. (file-like getmail)
  230. "The getmail package to use.")
  231. (user
  232. (string "getmail")
  233. "The user to run getmail as.")
  234. (group
  235. (string "getmail")
  236. "The group to run getmail as.")
  237. (directory
  238. (string "/var/lib/getmail/default")
  239. "The getmail directory to use.")
  240. (rcfile
  241. (getmail-configuration-file (getmail-configuration-file))
  242. "The getmail configuration file to use.")
  243. (idle
  244. (list '())
  245. "A list of mailboxes that getmail should wait on the server for new mail
  246. notifications. This depends on the server supporting the IDLE extension.")
  247. (environment-variables
  248. (list '())
  249. "Environment variables to set for getmail."))
  250. (define (generate-getmail-documentation)
  251. (generate-documentation
  252. `((getmail-configuration
  253. ,getmail-configuration-fields
  254. (rcfile getmail-configuration-file))
  255. (getmail-configuration-file
  256. ,getmail-configuration-file-fields
  257. (retriever getmail-retriever-configuration)
  258. (destination getmail-destination-configuration)
  259. (options getmail-options-configuration))
  260. (getmail-retriever-configuration ,getmail-retriever-configuration-fields)
  261. (getmail-destination-configuration ,getmail-destination-configuration-fields)
  262. (getmail-options-configuration ,getmail-options-configuration-fields))
  263. 'getmail-configuration))
  264. (define-gexp-compiler (getmail-configuration-file-compiler
  265. (rcfile <getmail-configuration-file>) system target)
  266. (gexp->derivation
  267. "getmailrc"
  268. #~(call-with-output-file #$output
  269. (lambda (port)
  270. (display #$(serialize-getmail-configuration-file #f rcfile)
  271. port)))
  272. #:system system
  273. #:target target))
  274. (define (getmail-accounts configs)
  275. (let ((users (delete-duplicates
  276. (map getmail-configuration-user
  277. configs)))
  278. (groups (delete-duplicates
  279. (map getmail-configuration-group
  280. configs))))
  281. (append
  282. (map (lambda (group)
  283. (user-group
  284. (name group)
  285. (system? #t)))
  286. groups)
  287. (map (lambda (user)
  288. (user-account
  289. (name user)
  290. (group (getmail-configuration-group
  291. (find (lambda (config)
  292. (and
  293. (string=? user (getmail-configuration-user config))
  294. (getmail-configuration-group config)))
  295. configs)))
  296. (system? #t)
  297. (comment "Getmail user")
  298. (home-directory "/var/empty")
  299. (shell (file-append shadow "/sbin/nologin"))))
  300. users))))
  301. (define (getmail-activation configs)
  302. "Return the activation GEXP for CONFIGS."
  303. (with-imported-modules '((guix build utils))
  304. #~(begin
  305. (use-modules (guix build utils))
  306. #$@(map
  307. (lambda (config)
  308. #~(let* ((pw (getpw #$(getmail-configuration-user config)))
  309. (uid (passwd:uid pw))
  310. (gid (passwd:gid pw))
  311. (getmaildir #$(getmail-configuration-directory config)))
  312. (mkdir-p getmaildir)
  313. (chown getmaildir uid gid)))
  314. configs))))
  315. (define (getmail-shepherd-services configs)
  316. "Return a list of <shepherd-service> for CONFIGS."
  317. (map (match-lambda
  318. (($ <getmail-configuration> location name package
  319. user group directory rcfile idle
  320. environment-variables)
  321. (shepherd-service
  322. (documentation "Run getmail.")
  323. (provision (list (symbol-append 'getmail- name)))
  324. (requirement '(networking))
  325. (start #~(make-forkexec-constructor
  326. `(#$(file-append package "/bin/getmail")
  327. ,(string-append "--getmaildir=" #$directory)
  328. #$@(map (lambda (idle)
  329. (string-append "--idle=" idle))
  330. idle)
  331. ,(string-append "--rcfile=" #$rcfile))
  332. #:user #$user
  333. #:group #$group
  334. #:environment-variables
  335. (list #$@environment-variables)
  336. #:log-file
  337. #$(string-append "/var/log/getmail-"
  338. (symbol->string name))))
  339. (stop #~(make-kill-destructor)))))
  340. configs))
  341. (define getmail-service-type
  342. (service-type
  343. (name 'getmail)
  344. (extensions
  345. (list (service-extension shepherd-root-service-type
  346. getmail-shepherd-services)
  347. (service-extension activation-service-type
  348. getmail-activation)
  349. (service-extension account-service-type
  350. getmail-accounts)))
  351. (description
  352. "Run @command{getmail}, a mail retriever program.")
  353. (default-value '())
  354. (compose concatenate)
  355. (extend append)))