getmail.scm 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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
  103. "Name or IP address of the server to retrieve mail from.")
  104. (username
  105. string
  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
  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-configuration getmail-configuration-file
  202. (retriever
  203. (getmail-retriever-configuration (getmail-retriever-configuration))
  204. "What mail account to retrieve mail from, and how to access that account.")
  205. (destination
  206. (getmail-destination-configuration (getmail-destination-configuration))
  207. "What to do with retrieved messages.")
  208. (options
  209. (getmail-options-configuration (getmail-options-configuration))
  210. "Configure getmail."))
  211. (define (serialize-getmail-configuration-file field-name val)
  212. (match-record val <getmail-configuration-file>
  213. (retriever destination options)
  214. #~(string-append
  215. "[retriever]\n"
  216. #$(serialize-getmail-retriever-configuration #f retriever)
  217. "\n[destination]\n"
  218. #$(serialize-getmail-destination-configuration #f destination)
  219. "\n[options]\n"
  220. #$(serialize-getmail-options-configuration #f options))))
  221. (define (serialize-symbol field-name val) "")
  222. (define (serialize-getmail-configuration field-name val) "")
  223. (define-configuration getmail-configuration
  224. (name
  225. (symbol "unset")
  226. "A symbol to identify the getmail service.")
  227. (package
  228. (file-like getmail)
  229. "The getmail package to use.")
  230. (user
  231. (string "getmail")
  232. "The user to run getmail as.")
  233. (group
  234. (string "getmail")
  235. "The group to run getmail as.")
  236. (directory
  237. (string "/var/lib/getmail/default")
  238. "The getmail directory to use.")
  239. (rcfile
  240. (getmail-configuration-file (getmail-configuration-file))
  241. "The getmail configuration file to use.")
  242. (idle
  243. (list '())
  244. "A list of mailboxes that getmail should wait on the server for new mail
  245. notifications. This depends on the server supporting the IDLE extension.")
  246. (environment-variables
  247. (list '())
  248. "Environment variables to set for getmail."))
  249. (define (generate-getmail-documentation)
  250. (generate-documentation
  251. `((getmail-configuration
  252. ,getmail-configuration-fields
  253. (rcfile getmail-configuration-file))
  254. (getmail-configuration-file
  255. ,getmail-configuration-file-fields
  256. (retriever getmail-retriever-configuration)
  257. (destination getmail-destination-configuration)
  258. (options getmail-options-configuration))
  259. (getmail-retriever-configuration ,getmail-retriever-configuration-fields)
  260. (getmail-destination-configuration ,getmail-destination-configuration-fields)
  261. (getmail-options-configuration ,getmail-options-configuration-fields))
  262. 'getmail-configuration))
  263. (define-gexp-compiler (getmail-configuration-file-compiler
  264. (rcfile <getmail-configuration-file>) system target)
  265. (gexp->derivation
  266. "getmailrc"
  267. #~(call-with-output-file #$output
  268. (lambda (port)
  269. (display #$(serialize-getmail-configuration-file #f rcfile)
  270. port)))
  271. #:system system
  272. #:target target))
  273. (define (getmail-accounts configs)
  274. (let ((users (delete-duplicates
  275. (map getmail-configuration-user
  276. configs)))
  277. (groups (delete-duplicates
  278. (map getmail-configuration-group
  279. configs))))
  280. (append
  281. (map (lambda (group)
  282. (user-group
  283. (name group)
  284. (system? #t)))
  285. groups)
  286. (map (lambda (user)
  287. (user-account
  288. (name user)
  289. (group (getmail-configuration-group
  290. (find (lambda (config)
  291. (and
  292. (string=? user (getmail-configuration-user config))
  293. (getmail-configuration-group config)))
  294. configs)))
  295. (system? #t)
  296. (comment "Getmail user")
  297. (home-directory "/var/empty")
  298. (shell (file-append shadow "/sbin/nologin"))))
  299. users))))
  300. (define (getmail-activation configs)
  301. "Return the activation GEXP for CONFIGS."
  302. (with-imported-modules '((guix build utils))
  303. #~(begin
  304. (use-modules (guix build utils))
  305. #$@(map
  306. (lambda (config)
  307. #~(let* ((pw (getpw #$(getmail-configuration-user config)))
  308. (uid (passwd:uid pw))
  309. (gid (passwd:gid pw))
  310. (getmaildir #$(getmail-configuration-directory config)))
  311. (mkdir-p getmaildir)
  312. (chown getmaildir uid gid)))
  313. configs))))
  314. (define (getmail-shepherd-services configs)
  315. "Return a list of <shepherd-service> for CONFIGS."
  316. (map (lambda (config)
  317. (match-record config <getmail-configuration>
  318. (name package user group directory rcfile idle environment-variables)
  319. (shepherd-service
  320. (documentation "Run getmail.")
  321. (provision (list (symbol-append 'getmail- name)))
  322. (requirement '(networking))
  323. (start #~(make-forkexec-constructor
  324. `(#$(file-append package "/bin/getmail")
  325. ,(string-append "--getmaildir=" #$directory)
  326. #$@(map (lambda (idle)
  327. (string-append "--idle=" idle))
  328. idle)
  329. ,(string-append "--rcfile=" #$rcfile))
  330. #:user #$user
  331. #:group #$group
  332. #:environment-variables
  333. (list #$@environment-variables)
  334. #:log-file
  335. #$(string-append "/var/log/getmail-"
  336. (symbol->string name))))
  337. (stop #~(make-kill-destructor)))))
  338. configs))
  339. (define getmail-service-type
  340. (service-type
  341. (name 'getmail)
  342. (extensions
  343. (list (service-extension shepherd-root-service-type
  344. getmail-shepherd-services)
  345. (service-extension activation-service-type
  346. getmail-activation)
  347. (service-extension account-service-type
  348. getmail-accounts)))
  349. (description
  350. "Run @command{getmail}, a mail retriever program.")
  351. (default-value '())
  352. (compose concatenate)
  353. (extend append)))