opensmtpd-records.scm 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
  1. ;; sudo guix system -L ../linode/ reconfigure sway.scm
  2. ;; use the above to test this code on an actual system.
  3. (define-module (opensmtpd-records)
  4. #:use-module (gnu services)
  5. #:use-module (gnu services base)
  6. #:use-module (gnu services configuration)
  7. #:use-module (gnu services shepherd)
  8. #:use-module (gnu system pam)
  9. #:use-module (gnu system shadow)
  10. #:use-module (gnu packages mail)
  11. #:use-module (gnu packages admin)
  12. #:use-module (gnu packages dav)
  13. #:use-module (gnu packages tls)
  14. #:use-module (guix records)
  15. #:use-module (guix packages)
  16. #:use-module (guix gexp)
  17. #:use-module (ice-9 match)
  18. #:use-module (ice-9 format)
  19. #:use-module (srfi srfi-1)
  20. #:export (opensmtpd-service-type
  21. opensmtpd-table
  22. opensmtpd-table?
  23. opensmtpd-table-name
  24. opensmtpd-table-file
  25. opensmtpd-table-file-db
  26. opensmtpd-table-values
  27. opensmtpd-ca
  28. opensmtpd-ca?
  29. opensmtpd-name
  30. opensmtpd-file
  31. opensmtpd-pki
  32. opensmtpd-pki?
  33. opensmtpd-pki-domain
  34. opensmtpd-pki-cert
  35. opensmtpd-pki-key
  36. opensmtpd-local-delivery-configuration
  37. opensmtpd-local-delivery-configuration?
  38. opensmtpd-local-delivery-configuration-method
  39. opensmtpd-local-delivery-configuration-alias
  40. opensmtpd-local-delivery-configuration-ttl
  41. opensmtpd-local-delivery-configuration-user
  42. opensmtpd-local-delivery-configuration-userbase
  43. opensmtpd-local-delivery-configuration-virtual
  44. opensmtpd-local-delivery-configuration-wrapper
  45. opensmtpd-maildir-configuration
  46. opensmtpd-maildir-configuration?
  47. opensmtpd-maildir-configuration-pathname
  48. opensmtpd-maildir-configuration-junk
  49. opensmtpd-mda-configuration
  50. opensmtpd-mda-configuration-command
  51. opensmtpd-relay-configuration
  52. opensmtpd-relay-configuration?
  53. opensmtpd-relay-configuration-backup
  54. opensmtpd-relay-configuration-backup-mx
  55. opensmtpd-relay-configuration-helo
  56. opensmtpd-relay-configuration-domain
  57. opensmtpd-relay-configuration-host
  58. opensmtpd-relay-configuration-pki
  59. opensmtpd-relay-configuration-srs
  60. opensmtpd-relay-configuration-tls
  61. opensmtpd-relay-configuration-protocols
  62. opensmtpd-relay-configuration-ciphers
  63. opensmtpd-relay-configuration-auth
  64. opensmtpd-relay-configuration-mail-from
  65. opensmtpd-relay-configuration-src
  66. opensmtpd-action
  67. opensmtpd-action?
  68. opensmtpd-action-name
  69. opensmtpd-action-method
  70. opensmtpd-filter-chain
  71. opensmtpd-filter-chain?
  72. opensmtpd-filter-chain-name
  73. opensmtpd-filter-chain-filter-names
  74. opensmtpd-filter-phase
  75. opensmtpd-filter-phase?
  76. opensmtpd-filter-phase-name
  77. opensmtpd-filter-phase-phase-name
  78. opensmtpd-filter-phase-conditions
  79. opensmtpd-filter-phase-decision
  80. opensmtpd-filter-phase-message
  81. opensmtpd-filter-phase-value
  82. opensmtpd-filter-proc
  83. opensmtpd-filter-proc?
  84. opensmtpd-filter-proc-name
  85. opensmtpd-filter-proc-command
  86. opensmtpd-filter-proc-exec
  87. opensmtpd-filter-proc-exec?
  88. opensmtpd-filter-proc-exec-name
  89. opensmtpd-filter-proc-exec-command
  90. opensmtpd-listen-on
  91. opensmtpd-listen-on?
  92. opensmtpd-listen-on-interface
  93. opensmtpd-listen-on-auth
  94. opensmtpd-listen-on-auth-optional
  95. opensmtpd-listen-on-filter
  96. opensmtpd-listen-on-hostname
  97. opensmtpd-listen-on-hostnames
  98. opensmtpd-listen-on-mask-src
  99. opensmtpd-listen-on-no-dsn
  100. opensmtpd-listen-on-pki
  101. opensmtpd-listen-on-port
  102. opensmtpd-listen-on-proxy-v2
  103. opensmtpd-listen-on-received-auth
  104. opensmtpd-listen-on-senders
  105. opensmtpd-listen-on-secure-connection
  106. opensmtpd-listen-on-tag
  107. opensmtpd-listen-on-protocols
  108. opensmtpd-listen-on-ciphers
  109. opensmtpd-listen-on-socket
  110. opensmtpd-listen-on-socket?
  111. opensmtpd-listen-on-socket-filter
  112. opensmtpd-listen-on-socket-mask-src
  113. opensmtpd-listen-on-socket-tag
  114. opensmtpd-match
  115. opensmtpd-match?
  116. opensmtpd-match-name
  117. opensmtpd-match-for
  118. opensmtpd-match-from
  119. opensmtpd-match-auth
  120. opensmtpd-match-helo
  121. opensmtpd-match-mail-from
  122. opensmtpd-match-rcpt-to
  123. opensmtpd-match-tag
  124. opensmtpd-match-tls
  125. opensmtpd-proc
  126. opensmtpd-proc?
  127. opensmtpd-proc-name
  128. opensmtpd-proc-command
  129. opensmtpd-smtp-configuration
  130. opensmtpd-smtp-configuration?
  131. opensmtpd-smtp-configuration-ciphers
  132. opensmtpd-smtp-configuration-limit-max-mails
  133. opensmtpd-smtp-configuration-limit-max-rcpt
  134. opensmtpd-smtp-configuration-max-message-size
  135. opensmtpd-smtp-configuration-sub-addr-delim character
  136. opensmtpd-srs-configuration
  137. opensmtpd-srs-configuration?
  138. opensmtpd-srs-configuration-key
  139. opensmtpd-srs-configuration-backup-key
  140. opensmtpd-srs-configuration-ttl-delay
  141. opensmtpd-queue-configuration
  142. opensmtpd-queue-configuration?
  143. opensmtpd-queue-configuration-compression
  144. opensmtpd-queue-configuration-encryption
  145. opensmtpd-queue-configuration-ttl-delay
  146. opensmtpd-configuration
  147. opensmtpd-configuration?
  148. opensmtpd-package
  149. opensmtpd-config-file
  150. opensmtpd-configuration-actions
  151. opensmtpd-configuration-bounce
  152. opensmtpd-configuration-filter-chains
  153. opensmtpd-configuration-filter-phases
  154. opensmtpd-configuration-filter-procs
  155. opensmtpd-configuration-filter-proc-execs
  156. opensmtpd-configuration-listen-ons
  157. opensmtpd-configuration-listen-on-socket
  158. opensmtpd-configuration-includes
  159. opensmtpd-configuration-matches
  160. opensmtpd-configuration-mda-wrappers
  161. opensmtpd-configuration-mta-max-deferred
  162. opensmtpd-configuration-procs
  163. opensmtpd-configuration-tables
  164. opensmtpd-configuration-srs
  165. opensmtpd-configuration-smtp
  166. opensmtpd-configuration-queue
  167. ))
  168. ;; some fieldnames have a default value of #f, which is ok. They cannot have a value of #t.
  169. ;; for example opensmtpd-table-values can be #f, BUT NOT true.
  170. ;; my/sanitize procedure tests values to see if they are of the right kind.
  171. ;; procedure false? is needed to allow fields like 'values' to be blank, (empty), or #f BUT also
  172. ;; have a value like a list of strings.
  173. (define (false? value)
  174. (eq? #f value))
  175. (define (is-value-right-type? value list-of-procedures)
  176. (let loop ([list-of-procedures list-of-procedures])
  177. (if (null? list-of-procedures)
  178. #f
  179. (begin
  180. (if ((car list-of-procedures) value)
  181. #t
  182. (loop (cdr list-of-procedures)))))))
  183. ;; converts strings like this:
  184. ;; "apple, ham, cherry" -> "apple, ham, or cherry"
  185. ;; "pineapple" -> "pinneapple".
  186. ;; "cheese, grapefruit, or jam" -> "cheese, grapefruit, or jam"
  187. (define (add-comma-or string)
  188. (define last-comma-location (string-rindex string #\,))
  189. (if last-comma-location
  190. (if (string-contains string ", or" last-comma-location)
  191. string
  192. (string-replace string ", or" last-comma-location (+ 1 last-comma-location)))
  193. string))
  194. ;; I could test for read-ability of a file, but then I would have to
  195. ;; test the program as root everytime instead of as a normal user...
  196. (define (file-exists? file)
  197. (if (string? file)
  198. (access? file F_OK)
  199. #f))
  200. (define (list-of-procedures->string procedures)
  201. (define string
  202. (let loop ([procedures procedures])
  203. (if (null? procedures)
  204. ""
  205. (begin
  206. (string-append
  207. ;; FIXME/TODO add a guess function that takes a look at the
  208. ;; function... eg: list-of-opensmtpd-proc? -> "a list of
  209. ;; <opensmtpd-proc>s, "
  210. (cond [(eq? false? (car procedures))
  211. "#f , "]
  212. [(eq? boolean? (car procedures))
  213. "boolean, "]
  214. [(eq? string? (car procedures))
  215. "string, "]
  216. [(eq? integer? (car procedures))
  217. "integer, "]
  218. [(eq? list-of-strings? (car procedures))
  219. "list of strings, "]
  220. [(eq? assoc-list? (car procedures))
  221. "an association list, "]
  222. [(eq? opensmtpd-pki? (car procedures))
  223. "an <opensmtpd-pki> record, "]
  224. [(eq? file-exists? (car procedures))
  225. "file, "]
  226. [(eq? list-of-opensmtpd-filter-phase? (car procedures))
  227. "a list of <opensmtpd-filter-phase>s, "]
  228. [(eq? list-of-opensmtpd-filter-chain? (car procedures))
  229. "a list of <opensmtpd-filter-chain>s, "]
  230. [(eq? list-of-opensmtpd-proc? (car procedures))
  231. "a list of <opensmtpd-proc>s, "]
  232. [else
  233. (display "You've got some procedure that you don't know about.\n")
  234. (display (car procedures))
  235. (display "\n")
  236. (throw 'bad! (car procedures))])
  237. (loop (cdr procedures)))))))
  238. (add-comma-or (string-append (string-drop-right string 2) ".\n")))
  239. ;; TODO/FIXME? write a procedure (define (string-in-list? string
  240. ;; list)) The procedure will be used to sanitize the few values that
  241. ;; whose strings can be certain contrained
  242. ;; strings. opensmtpd-listen-on would use such a procedure.
  243. ;; There are a perhaps three places that would use this function:
  244. ;; search for (or (string=?
  245. ;; To use this function, I would have to modify list-of-procedures->string
  246. ;; to accept a function like (quote (string-in-strings? string list)) and I would
  247. ;; later have to evaluate that s-expression.
  248. (define (string-in-strings? string list)
  249. (if (null? list)
  250. #f
  251. (if (string=? string (car list))
  252. #t
  253. (string-in-strings? string (cdr list)))))
  254. (define (my/sanitize value table fieldname list-of-procedures)
  255. (if (is-value-right-type? value list-of-procedures)
  256. value
  257. (begin
  258. (display (string-append "<" table "> fieldname: '" fieldname "' is of type "
  259. (list-of-procedures->string list-of-procedures) "\n"))
  260. (throw 'bad! value))))
  261. ;; FIXME/TODO? It is possible to create a table like this (opensmtpd-table (name "table")),
  262. ;; which is a table with no values or file, which is no table at all, and will result
  263. ;; in the service not starting. This is probably not worth fixing. Only silly users
  264. ;; would try to create a table with no values or file.
  265. (define-record-type* <opensmtpd-table>
  266. opensmtpd-table make-opensmtpd-table
  267. opensmtpd-table?
  268. this-record
  269. ;; string
  270. (name opensmtpd-table-name
  271. (default #f)
  272. (sanitize (lambda (value)
  273. (my/sanitize value "opensmtpd-table" "name" (list string?)))))
  274. (file opensmtpd-table-file
  275. (default #f)
  276. (sanitize (lambda (value)
  277. (my/sanitize value "opensmtpd-table" "file" (list false? string?)))))
  278. ;; is a list of values or key values
  279. ;; eg: (list "mysite.me" "your-site.com")
  280. ;; eg: (list ("joshua" . "joshua@gnu.org") ("james" . "james@gnu.org"))
  281. ;; I am currently making these values be as assocation list of strings only.
  282. ;; FIXME should I allow a value like this?
  283. ;; (list (cons "gnucode.me" 234.949.392.23))
  284. (file-db opensmtpd-table-file-db
  285. (default #f)
  286. (sanitize (lambda (value)
  287. (my/sanitize value "opensmtpd-table" "file-db" (list boolean?)))))
  288. ;; FIXME support an aliasing table as described here:
  289. ;; https://man.openbsd.org/table.5
  290. ;; One may have to use the record file for this. I don't think tables support a table like this:
  291. ;; table "name" { joshua = joshua@gnucode.me,joshua@gnu-hurd.com,joshua@propernaming.org, root = root@gnucode.me }
  292. (values opensmtpd-table-values
  293. (default #f)
  294. (sanitize (lambda (value)
  295. (my/sanitize value "opensmtpd-table" "values" (list false? list-of-strings? assoc-list?))
  296. )))
  297. ;; can be of type: (quote list-of-strings) or (quote assoc-list)
  298. ;; (opensmtpd-table-type record) returns the values' type. The user SHOULD NEVER set the type.
  299. ;; TODO jpoiret: on irc reccomends that I just use an outside function to determine fieldname 'values', type.
  300. ;; it would be "simpler" and possibly easier for the next person working on this code to understand what is happening.
  301. (type opensmtpd-table-type
  302. (default #f)
  303. (thunked)
  304. (sanitize (lambda (value)
  305. (cond [(opensmtpd-table-values this-record)
  306. (if (list-of-strings? (opensmtpd-table-values this-record))
  307. (quote list-of-strings)
  308. (quote assoc-list))]
  309. [(opensmtpd-table-file this-record)
  310. (if (opensmtpd-table-file-db this-record)
  311. (quote db)
  312. (quote file))]
  313. [else
  314. (display "opensmtpd-table-type is broke\n")
  315. (throw 'bad! value)])))))
  316. (define-record-type* <opensmtpd-ca>
  317. opensmtpd-ca make-opensmtpd-ca
  318. opensmtpd-ca?
  319. (name opensmtpd-ca-name
  320. (default #f)
  321. (sanitize (lambda (value)
  322. (my/sanitize value "opensmtpd-ca" "name" (list string?)))))
  323. (file opensmtpd-ca-file
  324. (default #f)
  325. (sanitize (lambda (value)
  326. (my/sanitize value "opensmtpd-ca" "file" (list string? file-exists?))))))
  327. (define-record-type* <opensmtpd-pki>
  328. opensmtpd-pki make-opensmtpd-pki
  329. opensmtpd-pki?
  330. (domain opensmtpd-pki-domain
  331. (default #f)
  332. (sanitize (lambda (value)
  333. (my/sanitize value "opensmtpd-pki" "domain" (list string?)))))
  334. ;; TODO/FIXME this should probably be a list of files. The opensmtpd documentation says
  335. ;; that you could have a list of files.
  336. (cert opensmtpd-pki-cert
  337. (default #f)
  338. (sanitize (lambda (value)
  339. (my/sanitize value "opensmtpd-pki" "cert" (list file-exists?)))))
  340. (key opensmtpd-pki-key
  341. (default #f)
  342. (sanitize (lambda (value)
  343. (my/sanitize value "opensmtpd-pki" "key" (list file-exists?)))))
  344. (dhe opensmtpd-pki-dhe
  345. (default #f)
  346. (sanitize (lambda (value)
  347. (my/sanitize value "opensmtpd-dhe" "dhe" (list false? string?))))))
  348. (define-record-type* <opensmtpd-lmtp-configuration>
  349. opensmtpd-lmtp-configuration make-opensmtpd-lmtp-configuration
  350. opensmtpd-lmtp-configuration?
  351. (destination opensmtpd-lmtp-configuration-destination
  352. (default #f)
  353. (sanitize (lambda (value)
  354. (my/sanitize value "opensmtpd-lmtp-configuration" "destination" (list string?)))))
  355. (rcpt-to opensmtpd-lmtp-configuration-rcpt-to
  356. (default #f)
  357. (sanitize (lambda (value)
  358. (my/sanitize value "opensmtpd-lmtp-configuration" "rcpt-to" (list false? string?))))))
  359. (define-record-type* <opensmtpd-mda-configuration>
  360. opensmtpd-mda-configuration make-opensmtpd-mda-configuration
  361. opensmtpd-mda-configuration?
  362. (command opensmtpd-mda-configuration-command
  363. (default #f)
  364. (sanitize (lambda (value)
  365. (my/sanitize value "opensmtpd-mda-configuration" "command" (list string?))))))
  366. (define-record-type* <opensmtpd-maildir-configuration>
  367. opensmtpd-maildir-configuration make-opensmtpd-maildir-configuration
  368. opensmtpd-maildir-configuration?
  369. (pathname opensmtpd-maildir-configuration-pathname
  370. (default #f)
  371. (sanitize (lambda (value)
  372. (my/sanitize value "opensmtpd-maildir-configuration" "pathname" (list false? string?)))))
  373. (junk opensmtpd-maildir-configuration-junk
  374. (default #f)
  375. (sanitize (lambda (value)
  376. (my/sanitize value "opensmtpd-maildir-configuration" "junk" (list boolean?))))))
  377. (define-record-type* <opensmtpd-local-delivery-configuration>
  378. opensmtpd-local-delivery-configuration make-opensmtpd-local-delivery-configuration
  379. opensmtpd-local-delivery-configuration?
  380. ;; method only be "mbox", expand-only, forward-only,
  381. ;; <lmtp-configuration>, <maildir-configuration>, or
  382. ;; <mda-configuration>. They all support various other values like alias, which is why they are
  383. ;; lumped togethor in the <opensmtpd-local-delivery-configuration>
  384. (method opensmtpd-local-delivery-configuration-method
  385. (default "mbox")
  386. (sanitize (lambda (value)
  387. (cond
  388. [(or (opensmtpd-lmtp-configuration? value)
  389. (opensmtpd-maildir-configuration? value)
  390. (opensmtpd-mda-configuration? value)
  391. (string=? value "mbox")
  392. (string=? value "expand-only")
  393. (string=? value "forward-only"))
  394. value]
  395. [else
  396. (begin
  397. (display (string-append "<opensmtpd-local-delivery-configuration> fieldname 'method' must be of type "
  398. "\"mbox\", \"expand-only\", \"forward-only\" "
  399. "<opensmtpd-lmtp-configuration>, <opensmtpd-maildir-configuration>, "
  400. "or <opensmtpd-mda-configuration>.\n"))
  401. (throw 'bad! value))]))))
  402. ;; string opensmtpd-table-configuration-name
  403. (alias opensmtpd-local-delivery-configuration-alias
  404. (default #f)
  405. (sanitize (lambda (value)
  406. (my/sanitize value "opensmtpd-local-delivery-configuration" "alias" (list false? string?)))))
  407. ;; string
  408. (ttl opensmtpd-local-delivery-configuration-ttl
  409. (default #f)
  410. (sanitize (lambda (value)
  411. (my/sanitize value "opensmtpd-local-delivery-configuration" "ttl" (list false? string?)))))
  412. (user opensmtpd-local-delivery-configuration-user
  413. (default #f)
  414. (sanitize (lambda (value)
  415. (my/sanitize value "opensmtpd-local-delivery-configuration" "user" (list false? string?)))))
  416. ;; needs to be of type string
  417. (userbase opensmtpd-local-delivery-configuration-userbase
  418. (default #f)
  419. (sanitize (lambda (value)
  420. (my/sanitize value "opensmtpd-local-delivery-configuration" "userbase" (list false? string?)))))
  421. (virtual opensmtpd-local-delivery-configuration-virtual
  422. (default #f)
  423. (sanitize (lambda (value)
  424. (my/sanitize value "opensmtpd-local-delivery-configuration" "virtual" (list false? string?)))))
  425. (wrapper opensmtpd-local-delivery-configuration-wrapper
  426. (default #f)
  427. (sanitize (lambda (value)
  428. (my/sanitize value "opensmtpd-local-delivery-configuration" "wrapper" (list false? string?))))))
  429. ;; FIXME/TODO this is a valid opensmtpd-relay record
  430. ;; (opensmtpd-relay-configuration
  431. ;; (pki (opensmtpd-pki
  432. ;; (domain "gnucode.me")
  433. ;; (cert "opensmtpd.scm")
  434. ;; (key "opensmtpd.scm"))))
  435. ;; BUT how does it relay the email? What host does it use?
  436. ;; I think opensmtpd-relay-configuration needs "method" field.
  437. ;; the method field might need to be another record...BUT basically the relay has to have a 'backup', 'backup-mx',
  438. ;; or 'domain', or 'host' defined.
  439. (define-record-type* <opensmtpd-relay-configuration>
  440. opensmtpd-relay-configuration make-opensmtpd-relay-configuration
  441. opensmtpd-relay-configuration?
  442. (backup opensmtpd-relay-configuration-backup ;; boolean
  443. (default #f)
  444. (sanitize (lambda (value)
  445. (my/sanitize value "opensmtpd-relay-configuration" "backup" (list boolean?)))))
  446. (backup-mx opensmtpd-relay-configuration-backup-mx ;; string mx name
  447. (default #f)
  448. (sanitize (lambda (value)
  449. (my/sanitize value "opensmtpd-relay-configuration" "backup-mx" (list false? string?)))))
  450. ;; string or heloname or <table name>
  451. ;; this combines options helo or helo-src
  452. (helo opensmtpd-relay-configuration-helo
  453. (default #f))
  454. ;; add helo-src and sanitize the value so that it does NOT include "<" or ">".
  455. ;; string sanitize the value so that it does NOT include "<" or ">". This MUST correspond to a <opensmtpd-table>.
  456. (domain opensmtpd-relay-configuration-domain
  457. (default #f))
  458. ;; string
  459. (host opensmtpd-relay-configuration-host
  460. (default #f))
  461. ;; <opensmtpd-pki-configuration>
  462. (pki opensmtpd-relay-configuration-pki
  463. (default #f)
  464. (sanitize (lambda (value)
  465. (my/sanitize value "opensmtpd-relay-configuration" "pki" (list false? opensmtpd-pki?)))))
  466. ;; boolean
  467. (srs opensmtpd-relay-configuration-srs
  468. (default #f)
  469. (lambda (value)
  470. (my/sanitize value "opensmtpd-relay-configuration" "srs" (list boolean?))))
  471. ;; boolean or no-verify
  472. (tls opensmtpd-relay-configuration-tls
  473. (default #f)
  474. (sanitize (lambda (value)
  475. (my/sanitize value "opensmtpd-relay-configuration" "tls" (list false? string?)))))
  476. ;; string
  477. (protocols opensmtpd-relay-configuration-protocols
  478. (default #f)
  479. (sanitize (lambda (value)
  480. (my/sanitize value "opensmtpd-relay-configuration" "protocols" (list false? string?)))))
  481. ;; string
  482. (ciphers opensmtpd-relay-configuration-ciphers
  483. (default #f)
  484. (sanitize (lambda (value)
  485. (my/sanitize value "opensmtpd-relay-configuration" "ciphers" (list false? string?)))))
  486. ;; string "table". Sanitize this value so that it does not include "<" or ">".
  487. (auth opensmtpd-relay-configuration-auth
  488. (default #f))
  489. (mail-from opensmtpd-relay-configuration-mail-from
  490. (default #f))
  491. ;; string "sourceaddr" or "<sourceadd>"
  492. (src opensmtpd-relay-configuration-src
  493. (default #f)))
  494. (define-record-type* <opensmtpd-action>
  495. opensmtpd-action make-opensmtpd-action
  496. opensmtpd-action?
  497. this-record
  498. (name opensmtpd-action-name
  499. (default "local"))
  500. ;; type <opensmtpd-local-delivery-configuration> or <opensmtpd-relay-configuration>
  501. (method opensmtpd-action-method
  502. (default (opensmtpd-local-delivery-configuration))
  503. (sanitize (lambda (value)
  504. (my/sanitize value "opensmtpd-action" "method"
  505. (list opensmtpd-relay-configuration?
  506. opensmtpd-local-delivery-configuration?))))))
  507. ;; FIXME/TODO Perhaps it would be nice in the future to change filter names to "filters".
  508. ;; Then "filters" could either be a list of filter names, OR it could be a list of types
  509. ;; <opensmtpd-filter-phase>, <opensmtpd-filter-proc>, or <opensmtpd-filter-proc-exec>.
  510. ;; BUT then I would have to write more code...and it would get complicated. Seems like
  511. ;; to much work.
  512. ;;list of many records of type opensmtpd-filter-chain
  513. ;; FIXME/TODO? Perhaps I could make filter-chains accept a list like this:
  514. ;; (list (filter-chain (name "chain-filter")
  515. ;; (filters (list (filter-phase
  516. ;; (name "phase")
  517. ;; (conditions (list "rdns")))
  518. ;; (filter-proc-exec
  519. ;; (name "process")
  520. ;; (command "this command"))))))
  521. ;; then I could get rid of fieldnames filter-phases, filter-procs, and filter-proc-execs from
  522. ;; opensmtpd-configuration
  523. (define-record-type* <opensmtpd-filter-chain>
  524. opensmtpd-filter-chain make-opensmtpd-filter-chain
  525. opensmtpd-filter-chain?
  526. ;; string chain name
  527. (name opensmtpd-filter-chain-name
  528. (default "filter-chain")
  529. (sanitize
  530. (lambda (value)
  531. (my/sanitize value "opensmtpd-filter-chain" "chain-name" (list string?)))))
  532. ;; list of strings of filter-name
  533. ;; maybe someday this could support record types
  534. ;; <opensmtpd-filter-proc>, <opensmtpd-filter-proc-exec>, or <opensmtpd-filter-phase>
  535. (filter-names opensmtpd-filter-chain-filter-names
  536. (default #f)
  537. (sanitize
  538. (lambda (value)
  539. (my/sanitize value "opensmtpd-filter-chain" "filter-names" (list list-of-strings?))))))
  540. (define-record-type* <opensmtpd-filter-phase>
  541. opensmtpd-filter-phase make-opensmtpd-filter-phase
  542. opensmtpd-filter-phase?
  543. (name opensmtpd-filter-phase-name ;; string chain-name
  544. (default "filter-chain")
  545. (sanitize (lambda (value)
  546. (my/sanitize value "opensmtpd-filter-phase" "name" (list string?)))))
  547. (phase-name opensmtpd-filter-phase-phase-name ;; string
  548. (default #f)
  549. (sanitize (lambda (value)
  550. (my/sanitize value "opensmtpd-filter-phase" "phase-name" (list string?)))))
  551. (conditions opensmtpd-filter-phase-conditions
  552. (default #f)
  553. (sanitize (lambda (value)
  554. (my/sanitize value "opensmtpd-filter-phase" "conditions-name" (list list-of-strings?)))))
  555. (decision opensmtpd-filter-phase-decision
  556. (default #f)
  557. (sanitize (lambda (value)
  558. (my/sanitize value "opensmtpd-filter-phase" "decision"
  559. (list
  560. (lambda (value)
  561. (if (or (string=? "bypass" value)
  562. (string=? "disconnect" value)
  563. (string=? "reject" value)
  564. (string=? "rewrite" value)
  565. (string=? "junk" value))
  566. value
  567. #f)))))))
  568. (message opensmtpd-filter-phase-message
  569. (default #f)
  570. (sanitize (lambda (value)
  571. (my/sanitize value "opensmtpd-filter-phase" "message" (list false? string?)))))
  572. (value opensmtpd-filter-phase-value
  573. (default #f)
  574. (sanitize (lambda (value)
  575. (my/sanitize value "opensmtpd-filter-phase" "value" (list false? integer?))))))
  576. (define-record-type* <opensmtpd-filter-proc>
  577. opensmtpd-filter-proc make-opensmtpd-filter-proc
  578. opensmtpd-filter-proc?
  579. (name opensmtpd-filter-proc-name
  580. (default #f)
  581. (sanitize (lambda (value)
  582. (my/sanitize value "opensmtpd-filter-proc" "name" (list? string?)))))
  583. (proc-name opensmtpd-proc-proc-name
  584. (default #f)
  585. (sanitize (lambda (value)
  586. (my/sanitize value "opensmtpd-filter-proc" "name" (list? string?))))))
  587. (define-record-type* <opensmtpd-filter-proc-exec>
  588. opensmtpd-filter-proc-exec make-opensmtpd-filter-proc-exec
  589. opensmtpd-filter-proc-exec?
  590. (name opensmtpd-filter-proc-exec-name
  591. (default #f)
  592. (sanitize (lambda (value)
  593. (my/sanitize value "opensmtpd-filter-proc-exec" "name" (list string?)))))
  594. ;; FIXME/TODO how do I let this accept a list of file-like objects and strings?
  595. ;; how do I get opensmtpd-configuration-fieldname->string to accept a list of gexp and strings?
  596. (command opensmtpd-filter-proc-exec-command
  597. (default #f)
  598. (sanitize (lambda (value)
  599. (my/sanitize value "opensmtpd-filter-proc-exec" "command" (list string?))))))
  600. (define-record-type* <opensmtpd-listen-on>
  601. opensmtpd-listen-on make-opensmtpd-listen-on
  602. opensmtpd-listen-on?
  603. ;; interface may be an IP address, interface group, or domain name
  604. (interface opensmtpd-listen-on-interface
  605. (default "lo"))
  606. ;;FIXME/TODO? should I convert this lambda into a (my/sanitize) invocation?
  607. ;; NO. It's not worth the effort, and the code is cleaner/easier to understand without it.
  608. ;; I would have to make a string-in-strings? procedure, which I would use thrice...
  609. ;; register that function with list-of-procedures->string
  610. ;; and modify my/sanitize to accept a procedure which accepts an argument.
  611. ;; eg: (my/sanitize value "opensmtpd-match" "decision" (list (cons string-in-strings? (list "reject" "accept"))
  612. (family opensmtpd-listen-on-family
  613. (default #f)
  614. (sanitize (lambda (value)
  615. (cond
  616. [(eq? #f value) ;; value == #f
  617. value]
  618. [(and (string? value)
  619. (or (string=? "inet4" value)
  620. (string=? "inet6" value)))
  621. value]
  622. [else
  623. (begin
  624. (display "<opensmtpd-listen-on> fieldname 'family' must be string \"inet4\" or \"inet6\".\n")
  625. (throw 'bad! value))]))))
  626. ;; this is a string of <authtable>
  627. ;; FIXME/TODO sanitize this value so the string has to begin with "<" and end with ">".
  628. (auth opensmtpd-listen-on-auth
  629. (default #f)
  630. (sanitize (lambda (value)
  631. (my/sanitize value "opensmtpd-listen-on" "auth" (list boolean? string?)))))
  632. ;; FIXME/TODO sanitize this value so the string has to begin with "<" and end with ">".
  633. (auth-optional opensmtpd-listen-on-auth-optional
  634. (default #f)
  635. (sanitize (lambda (value)
  636. (my/sanitize value "opensmtpd-listen-on" "auth-optional" (list boolean? string?)))))
  637. ;; do I need a ca entry?
  638. ;; string
  639. (filter opensmtpd-listen-on-filter
  640. (default #f))
  641. ;; string
  642. (hostname opensmtpd-listen-on-hostname
  643. (default #f)
  644. (sanitize (lambda (value)
  645. (my/sanitize value "opensmtpd-listen-on" "hostname" (list false? string?)))))
  646. ;; string of type <table>
  647. (hostnames opensmtpd-listen-on-hostnames
  648. (default #f)
  649. (sanitize (lambda (value)
  650. (my/sanitize value "opensmtpd-listen-on" "hostnames" (list false? string?)))))
  651. (mask-src opensmtpd-listen-on-mask-src
  652. (default #f)
  653. (sanitize (lambda (value)
  654. (my/sanitize value "opensmtpd-listen-on" "mask-src" (list boolean?)))))
  655. (no-dsn opensmtpd-listen-on-no-dsn
  656. (default #f))
  657. ;; pki record
  658. (pki opensmtpd-listen-on-pki
  659. (default #f)
  660. (sanitize (lambda (value)
  661. (my/sanitize value "opensmtpd-listen-on" "pki" (list false? opensmtpd-pki?)))))
  662. (port opensmtpd-listen-on-port
  663. (default #f)
  664. (sanitize (lambda (value)
  665. (my/sanitize value "opensmtpd-listen-on" "port" (list false? integer?)))))
  666. (proxy-v2 opensmtpd-listen-on-proxy-k2
  667. (default #f))
  668. (received-auth opensmtpd-listen-on-received-auth
  669. (default #f))
  670. ;; string or <opensmtpd-senders> record
  671. (senders opensmtpd-listen-on-senders
  672. (default #f))
  673. (secure-connection opensmtpd-listen-on-secure-connection
  674. (default #f)
  675. (sanitize (lambda (value)
  676. (cond [(boolean? value)
  677. value]
  678. [(and (string? value)
  679. (or (string=? "smtps" value)
  680. (string=? "tls" value)
  681. (string=? "tls-require" value)
  682. (string=? "tls-require-verify" value)))
  683. value]
  684. [else
  685. (begin
  686. (display (string-append "<opensmtd-listen-on> fieldname 'secure-connection' can be "
  687. "one of the following strings: \n'smtps', 'tls', 'tls-require', "
  688. "or 'tls-require-verify'.\n"))
  689. (throw 'bad! value))]))))
  690. ;; string
  691. (tag opensmtpd-listen-on-tag
  692. (default #f))
  693. (protocols opensmtpd-listen-on-protocols
  694. (default #f))
  695. (ciphers opensmtpd-listen-on-ciphers
  696. (default #f)))
  697. (define-record-type* <opensmtpd-listen-on-socket-configuration>
  698. opensmtpd-listen-on-socket-configuration make-opensmtpd-listen-on-socket-configuration
  699. opensmtpd-listen-on-socket-configuration?
  700. ;; string or <opensmtpd-filter> record or false
  701. (filter opensmtpd-listen-on-socket-configuration-filter
  702. (default #f))
  703. (mask-src opensmtpd-listen-on-socket-configuration-mask-src
  704. (default #f))
  705. ;; string
  706. (tag opensmtpd-listen-on-socket-configuration-tag
  707. (default #f)))
  708. (define-record-type* <opensmtpd-match>
  709. opensmtpd-match make-opensmtpd-match
  710. opensmtpd-match?
  711. (name opensmtpd-match-name ;; name is a string OR it is (quote reject), in which case the match
  712. (default #f) ;; rejects the incoming connection/envelope.
  713. (sanitize (lambda (value)
  714. (if (or (string? value)
  715. (eq? (quote reject) value))
  716. value
  717. (begin
  718. (display
  719. (string-append "<opensmtpd-match> fieldname 'name' is of type string or '(quote reject).\n"
  720. "If its value is '(quote reject), then the match rejects the incoming message\n"
  721. "during the SMTP dialogue.\n"))
  722. (throw 'bad! value))))))
  723. ;;FIXME/TODO? Perhaps I should add in a reject fieldname. If reject
  724. ;;is #t, then the match record will be a reject match record. That
  725. ;;way it's less confusing for users to type in
  726. ;; (opensmtpd-match (name 'reject)).
  727. ;; BUT then I have to make fieldnames 'name' and 'reject' mutually exclusive.
  728. ;; #f (empty) or a string
  729. ;; eg: "for any" "! for local" "for domain gnucode.me" "! for domain <domains>"
  730. ;; FIXME/TODO should I properly sanitize this? any string works now...
  731. (for opensmtpd-match-for
  732. (default #f)
  733. (sanitize (lambda (value)
  734. (my/sanitize value "opensmtpd-match" "for" (list false? string?)))))
  735. ;; #f (empty) or a string
  736. ;; eg: "from any" "from auth" "from auth user" "! from auth <users>" "from local" "! from rdns"
  737. (from opensmtpd-match-from
  738. (default #f)
  739. (sanitize (lambda (value)
  740. (my/sanitize value "opensmtpd-match" "from" (list false? string?)))))
  741. (auth opensmtpd-match-auth
  742. (default #f)
  743. (sanitize (lambda (value)
  744. (my/sanitize value "opensmtpd-match" "auth" (list boolean? string?)))))
  745. (helo opensmtpd-match-helo
  746. (default #f)
  747. (sanitize (lambda (value)
  748. (my/sanitize value "opensmtpd-match" "helo" (list false? string?)))))
  749. (mail-from opensmtpd-match-mail-from
  750. (default #f)
  751. (sanitize (lambda (value)
  752. (my/sanitize value "opensmtpd-match" "mail-from" (list false? string?)))))
  753. (rcpt-to opensmtpd-match-rcpt-to
  754. (default #f)
  755. (sanitize (lambda (value)
  756. (my/sanitize value "opensmtpd-match" "rcpt-to" (list false? string?)))))
  757. (tag opensmtpd-match-tag
  758. (default #f)
  759. (sanitize (lambda (value)
  760. (my/sanitize value "opensmtpd-match" "tag" (list false? string?)))))
  761. ;; hmmm. How should I handle this?
  762. ;; #f would mean "! tls", #nil would mean nothing, and #t would mean "tls"
  763. ;; FIXME/TODO, this default value should be #nil or '(), but the last time I did that,
  764. ;; reconfiguring failed...
  765. (tls opensmtpd-match-tls
  766. (default '())
  767. (sanitize (lambda (value)
  768. (my/sanitize value "opensmtpd-match" "tls" (list boolean? null?))))))
  769. ;; this is for registering an already running process for opensmtpd
  770. (define-record-type* <opensmtpd-proc>
  771. opensmtpd-proc make-opensmtpd-proc
  772. opensmtpd-proc?
  773. (name opensmtpd-proc-name
  774. (default #f)
  775. (sanitize (lambda (value)
  776. (my/sanitize value "opensmtpd-proc" "name" (list false? string?)))))
  777. (command opensmtpd-proc-command
  778. (default #f)
  779. (sanitize (lambda (value)
  780. (my/sanitize value "opensmtpd-proc" "command" (list false? string?))))))
  781. (define-record-type* <opensmtpd-smtp-configuration>
  782. opensmtpd-smtp-configuration make-opensmtpd-smtp-configuration
  783. opensmtpd-smtp-configuration?
  784. (ciphers opensmtpd-smtp-configuration-ciphers
  785. (default #f)
  786. (sanitize (lambda (value)
  787. (my/sanitize value "opensmtpd-smtp-configuration" "ciphers" (list false? string?)))))
  788. (limit-max-mails opensmtpd-smtp-configuration-limit-max-mails
  789. (default #f)
  790. (sanitize (lambda (value)
  791. (my/sanitize value "opensmtpd-smtp-configuration" "limit-max-mails" (list false? integer?)))))
  792. (limit-max-rcpt opensmtpd-smtp-configuration-limit-max-rcpt
  793. (default #f)
  794. (sanitize (lambda (value)
  795. (my/sanitize value "opensmtpd-smtp-configuration" "limit-max-rcpt" (list false? integer?)))))
  796. (max-message-size opensmtpd-smtp-configuration-max-message-size
  797. (default #f)
  798. (sanitize (lambda (value)
  799. (my/sanitize value "opensmtpd-smtp-configuration" "max-message-size"
  800. (list false? integer? string?)))))
  801. ;; FIXME/TODO the sanitize function of sub-addr-delim should accept a string of length one not string?
  802. (sub-addr-delim opensmtpd-smtp-configuration-sub-addr-delim
  803. (default #f)
  804. (sanitize (lambda (value)
  805. (my/sanitize value "opensmtpd-smtp-configuration" "sub-addr-delim"
  806. (list false? integer? string?))))))
  807. (define-record-type* <opensmtpd-srs-configuration>
  808. opensmtpd-srs-configuration make-opensmtpd-srs-configuration
  809. opensmtpd-srs-configuration?
  810. (key opensmtpd-srs-configuration-key
  811. (default #f)
  812. (sanitize (lambda (value)
  813. (my/sanitize value "opensmtpd-srs-configuration" "key" (list false? boolean? string?)))))
  814. (backup-key opensmtpd-srs-configuration-backup-key
  815. (default #f)
  816. (sanitize (lambda (value)
  817. (my/sanitize value "opensmtpd-srs-configuration" "backup-key" (list false? integer?)))))
  818. (ttl-delay opensmtpd-srs-configuration-ttl-delay
  819. (default #f)
  820. (sanitize (lambda (value)
  821. (my/sanitize value "opensmtpd-srs-configuration" "ttl-delay" (list false? string?))))))
  822. (define-record-type* <opensmtpd-queue-configuration>
  823. opensmtpd-queue-configuration make-opensmtpd-queue-configuration
  824. opensmtpd-queue-configuration?
  825. (compression opensmtpd-queue-configuration-compression
  826. (default #f)
  827. (sanitize (lambda (value)
  828. (my/sanitize value "opensmtpd-queue-configuration" "compression" (list boolean?)))))
  829. (encryption opensmtpd-queue-configuration-encryption
  830. (default #f)
  831. (sanitize (lambda (value)
  832. (my/sanitize value "opensmtpd-queue-configuration" "encryption" (list boolean? file-exists? string?)))))
  833. (ttl-delay opensmtpd-queue-configuration-ttl-delay
  834. (default #f)
  835. (sanitize (lambda (value)
  836. (my/sanitize value "opensmtpd-queue-configuration" "ttl-delay" (list false? string?))))))
  837. (define-record-type* <opensmtpd-configuration>
  838. opensmtpd-configuration make-opensmtpd-configuration
  839. opensmtpd-configuration?
  840. (package opensmtpd-configuration-package
  841. (default opensmtpd))
  842. (config-file opensmtpd-configuration-config-file
  843. ;; TODO if you change this next line to
  844. ;; (default (opensmtpd-configuration)
  845. ;; you will create an infinite recursive list of <opensmtpd-configuration>.
  846. ;; bug-guix doesn't really consider this a bug, but a noobie footgun.
  847. ;; Guile will eventually refuse to compile if you use (opensmtpd-configuration)
  848. ;; BUT that error message is quite lame.
  849. (default #f))
  850. (actions opensmtpd-configuration-actions
  851. (default (list (opensmtpd-action
  852. (name "local")
  853. (method (opensmtpd-local-delivery-configuration
  854. (method "mbox"))))
  855. (opensmtpd-action
  856. (name "outbound")
  857. (method (opensmtpd-relay-configuration))))))
  858. ;; FIXME/TODO should I include a admd authservid entry?
  859. ;; string
  860. (bounce opensmtpd-configuration-bounce
  861. (default #f)
  862. (sanitize (lambda (value)
  863. (my/sanitize value "opensmtpd-configuration" "bounce"
  864. (list false? string?)))))
  865. (cas opensmtpd-configuration-cas
  866. (default #f)
  867. (sanitize (lambda (value)
  868. (my/sanitize value "opensmtpd-configuration" "cas" (list false? list-of-opensmtpd-ca?)))))
  869. ;;list of many records of type opensmtpd-filter-chain
  870. ;; FIXME/TODO? Perhaps I could make filter-chains accept a list like this:
  871. ;; (list (filter-chain (name "chain-filter")
  872. ;; (filters (list (filter-phase
  873. ;; (name "phase")
  874. ;; (conditions (list "rdns")))
  875. ;; (filter-proc-exec
  876. ;; (name "process")
  877. ;; (command "this command"))))))
  878. ;; then I could get rid of fieldnames filter-phases, filter-procs, and filter-proc-execs from
  879. ;; opensmtpd-configuration
  880. (filter-chains opensmtpd-configuration-filter-chains
  881. (sanitize (lambda (value)
  882. (my/sanitize value "opensmtpd-configuration" "filter-chain"
  883. (list false? list-of-opensmtpd-filter-chain?))))
  884. (default #f))
  885. ;; list of many records of type opensmtpd-filter-phase
  886. (filter-phases opensmtpd-configuration-filter-phases
  887. (default #f)
  888. (sanitize (lambda (value)
  889. (my/sanitize value "opensmtpd-configuration" "filter-phases"
  890. (list false? list-of-opensmtpd-filter-phase?)))))
  891. ;; list of many records of type opensmtpd-filter-proc
  892. (filter-procs opensmtpd-configuration-filter-procs
  893. (default #f))
  894. ;; list of many records of type opensmtpd-filter-proc-exec
  895. (filter-proc-execs opensmtpd-configuration-filter-proc-execs
  896. (default #f)
  897. (sanitize (lambda (value)
  898. (my/sanitize value "opensmtpd-configuration" "filter-proc-execs"
  899. (list false? list-of-opensmtpd-filter-proc-exec?)))))
  900. ;; should I have an "include" entry?
  901. ;; list of many records of type opensmtpd-listen-on
  902. (listen-ons opensmtpd-configuration-listen-ons
  903. (default (list (opensmtpd-listen-on)))
  904. (sanitize (lambda (value)
  905. (if (list-of-opensmtpd-listen-on? value)
  906. value
  907. (begin
  908. (display "<opensmtpd-configuration> fieldname 'listen-ons' expects a list of records ")
  909. (display "of one or more <opensmtpd-listen-on> records.\n")
  910. (throw 'bad! value))))))
  911. ;; accepts type <opensmtpd-listen-on-socket-configuration>
  912. (listen-on-socket opensmtpd-configuration-listen-on-socket
  913. (default (opensmtpd-listen-on-socket-configuration)))
  914. (includes opensmtpd-configuration-includes ;; list of strings of absolute path names
  915. (default #f)
  916. (sanitize (lambda (value)
  917. (my/sanitize value "opensmtpd-configuration" "includes" (list false? list-of-strings?)))))
  918. (matches opensmtpd-configuration-matches
  919. (default (list (opensmtpd-match
  920. (name "local")
  921. (for "for local"))
  922. (opensmtpd-match
  923. (name "outbound")
  924. (from "from local")
  925. (for "for any")))))
  926. ;; list of many records of type mda-wrapper
  927. ;; TODO/FIXME support using gexps here
  928. ;; eg (list "name" gexp)
  929. (mda-wrappers opensmtpd-configuration-mda-wrappers
  930. (default #f)
  931. (sanitize (lambda (value)
  932. (my/sanitize value "opensmtpd-configuration" "mda-wrappers" (list false? string?)))))
  933. (mta-max-deferred opensmtpd-configuration-mta-max-deferred
  934. (default 100)
  935. (sanitize (lambda (value)
  936. (my/sanitize value "opensmtpd-configuration" "mta-max-deferred" (list number?)))))
  937. ;; list of many records of type proc
  938. ;; FIXME/TODO remove this fieldname. Just use filter-proc
  939. ;; eg: (opensmtpd-configuration (listen-on (filters (list (filter-proc (name "name")
  940. ;; (command "command")))))
  941. (procs opensmtpd-configuration-procs
  942. (default #f)
  943. (sanitize (lambda (value)
  944. (my/sanitize value "opensmtpd-configuration" "procs" (list false? list-of-opensmtpd-proc?)))))
  945. ;; FIXME/TODO add queue, smtp, srs directives
  946. (queue opensmtpd-configuration-queue
  947. (default #f)
  948. (sanitize (lambda (value)
  949. (my/sanitize value "opensmtpd-configuration" "queue" (list false? opensmtpd-queue-configuration?)))))
  950. (smtp opensmtpd-configuration-smtp
  951. (default #f)
  952. (sanitize (lambda (value)
  953. (my/sanitize value "opensmtpd-configuration" "smtp" (list false? opensmtpd-smtp-configuration?)))))
  954. (srs opensmtpd-configuration-srs
  955. (default #f)
  956. (sanitize (lambda (value)
  957. (my/sanitize value "opensmtpd-configuration" "srs" (list false? opensmtpd-srs-configuration?)))))
  958. ;; list of many records of type opensmtpd-table
  959. (tables opensmtpd-configuration-tables
  960. (default #f)
  961. (sanitize (lambda (value)
  962. (my/sanitize value "opensmtpd-configuration" "procs" (list false? list-of-opensmtpd-table?))))))
  963. ;; given a list and procedure, this tests that each element of list is of type
  964. ;; ie: (list-of-type? list string?) tests each list is of type string.
  965. (define (list-of-type? list proc?)
  966. (if (and (list? list)
  967. (not (null? list)))
  968. (let loop ([list list])
  969. (if (null? list)
  970. #t
  971. (if (proc? (car list))
  972. (loop (cdr list))
  973. #f)))
  974. #f))
  975. (define (list-of-strings? list)
  976. (list-of-type? list string?))
  977. ;; FIXME/TODO? make all of this end like list-of-strings? It has an "s" in it.
  978. ;; list-of-opensmtpd-ca -> <opensmtpd-ca>s? or opensmtpd-cas?
  979. (define (list-of-opensmtpd-ca? list)
  980. (list-of-type? list opensmtpd-ca?))
  981. (define (list-of-opensmtpd-pki? list)
  982. (list-of-type? list opensmtpd-pki?))
  983. (define (list-of-opensmtpd-filter-phase? list)
  984. (list-of-type? list opensmtpd-filter-phase?))
  985. (define (list-of-opensmtpd-filter-chain? list)
  986. (list-of-type? list opensmtpd-filter-chain?))
  987. (define (list-of-opensmtpd-filter-proc-exec? list)
  988. (list-of-type? list opensmtpd-filter-proc-exec?))
  989. (define (list-of-opensmtpd-listen-on? list)
  990. (list-of-type? list opensmtpd-listen-on?))
  991. (define (list-of-opensmtpd-table? list)
  992. (list-of-type? list opensmtpd-table?))
  993. (define (list-of-opensmtpd-proc? list)
  994. (list-of-type? list opensmtpd-proc?))
  995. (define* (list-of-strings->string list
  996. #:key
  997. (string-delimiter ", ")
  998. (postpend "")
  999. (append "")
  1000. (drop-right-number 2))
  1001. (string-drop-right
  1002. (string-append (let loop ([list list])
  1003. (if (null? list)
  1004. ""
  1005. (string-append append (car list) postpend
  1006. string-delimiter
  1007. (loop (cdr list)))))
  1008. append)
  1009. drop-right-number))
  1010. ;; these next few functions help me to turn <table>s
  1011. ;; into strings suitable to fit into "opensmtpd.conf".
  1012. ;; at the moment I cannot define this by using list-of-type?
  1013. ;; the first (not (null? assoc-list)) prevents that.
  1014. (define (assoc-list? assoc-list)
  1015. (list-of-type? assoc-list (lambda (pair)
  1016. (if (and (pair? pair)
  1017. (string? (car pair))
  1018. (string? (cdr pair)))
  1019. #t
  1020. #f))))
  1021. (define (assoc-list->string assoc-list)
  1022. (string-drop-right
  1023. (let loop ([assoc-list assoc-list])
  1024. (if (null? assoc-list)
  1025. ""
  1026. ;; pair is (cons "hello" "world") -> ("hello" . "world")
  1027. (let ([pair (car assoc-list)])
  1028. (string-append
  1029. "\"" (car pair) "\""
  1030. " = "
  1031. "\"" (cdr pair) "\""
  1032. ", "
  1033. (loop (cdr assoc-list))))))
  1034. 2))
  1035. ;; The following functions convert various records into strings.
  1036. (define* (variable->string var #:key (append "") (postpend " "))
  1037. (if var
  1038. (string-append append var postpend)
  1039. ""))
  1040. (define (list-of-variables->string list)
  1041. (let loop [(list list)]
  1042. (if (null? list)
  1043. ""
  1044. (string-append (variable->string (car list))
  1045. (list-of-variables->string (cdr list))))))
  1046. ;; can be of type: (quote list-of-strings) or (quote assoc-list)
  1047. (define (opensmtpd-table->string table)
  1048. (string-append "table " (opensmtpd-table-name table) " "
  1049. (let ([type (opensmtpd-table-type table)])
  1050. (cond [(eq? type (quote list-of-strings))
  1051. (string-append "{ " (list-of-strings->string (opensmtpd-table-values table)
  1052. #:append "\""
  1053. #:drop-right-number 3
  1054. #:postpend "\"") " }")]
  1055. [(eq? type (quote assoc-list))
  1056. (string-append "{ " (assoc-list->string (opensmtpd-table-values table)) " }")]
  1057. [(eq? type (quote db))
  1058. (string-append "db:" (opensmtpd-table-file table))]
  1059. [(eq? type (quote file))
  1060. (string-append "file:" (opensmtpd-table-file table))]
  1061. [else (throw 'youMessedUp table)]))
  1062. " \n"))
  1063. (define (opensmtpd-listen-on->string record)
  1064. (string-append "listen on "
  1065. (opensmtpd-listen-on-interface record) " "
  1066. (let ([hostname (opensmtpd-listen-on-hostname record)]
  1067. [hostnames (opensmtpd-listen-on-hostnames record)]
  1068. [filter (opensmtpd-listen-on-filter record)]
  1069. [mask-src (opensmtpd-listen-on-mask-src record)]
  1070. [tag (opensmtpd-listen-on-tag record)]
  1071. [secure-connection (opensmtpd-listen-on-secure-connection record)]
  1072. [port (opensmtpd-listen-on-port record)]
  1073. [pki (opensmtpd-listen-on-pki record)]
  1074. [auth (opensmtpd-listen-on-auth record)]
  1075. [auth-optional (opensmtpd-listen-on-auth-optional record)])
  1076. (string-append
  1077. (if mask-src
  1078. (string-append "mask-src ")
  1079. "")
  1080. (variable->string hostname #:append "hostname ")
  1081. (variable->string hostnames #:append "hostnames <" #:postpend "> ")
  1082. (variable->string filter #:append "filter \"" #:postpend "\" ")
  1083. (variable->string tag #:append "tag \"" #:postpend "\" ")
  1084. (if secure-connection
  1085. (cond [(string=? "smtps" secure-connection)
  1086. "smtps "]
  1087. [(string=? "tls" secure-connection)
  1088. "tls "]
  1089. [(string=? "tls-require" secure-connection)
  1090. "tls-require "]
  1091. [(string=? "tls-require-verify" secure-connection)
  1092. "tls-require verify "])
  1093. "")
  1094. (if port
  1095. (string-append "port " (number->string port) " ")
  1096. "")
  1097. (variable->string (opensmtpd-pki-domain pki) #:append "pki ")
  1098. (if auth
  1099. (string-append "auth "
  1100. (if (string? auth)
  1101. (string-append "<" auth "> ")
  1102. ""))
  1103. "")
  1104. (if auth-optional
  1105. (string-append "auth-optional "
  1106. (if (string? auth-optional)
  1107. (string-append "<" auth-optional "> ")
  1108. ""))
  1109. "")
  1110. "\n"))))
  1111. (define (opensmtpd-relay-configuration->string record)
  1112. (let ([backup (opensmtpd-relay-configuration-backup record)]
  1113. [backup-mx (opensmtpd-relay-configuration-backup-mx record)]
  1114. [helo (opensmtpd-relay-configuration-helo record)]
  1115. [domain (opensmtpd-relay-configuration-domain record)]
  1116. [host (opensmtpd-relay-configuration-host record)]
  1117. [pki (opensmtpd-relay-configuration-pki record)]
  1118. [srs (opensmtpd-relay-configuration-srs record)]
  1119. [tls (opensmtpd-relay-configuration-tls record)]
  1120. [protocols (opensmtpd-relay-configuration-protocols record)]
  1121. [ciphers (opensmtpd-relay-configuration-ciphers record)]
  1122. [auth (opensmtpd-relay-configuration-auth record)]
  1123. [mail-from (opensmtpd-relay-configuration-mail-from record)]
  1124. [src (opensmtpd-relay-configuration-src record)])
  1125. (string-append "relay "
  1126. ;;FIXME should I always quote the host fieldname? do I need to quote localhost via "localhost" ?
  1127. (variable->string host #:append "host \"" #:postpend "\" ")
  1128. (variable->string backup)
  1129. (variable->string backup-mx #:append "backup mx ")
  1130. (variable->string helo #:append "helo ")
  1131. ;; TODO/FIXME add helo-src
  1132. (variable->string domain #:append "domain <" #:postpend "> ")
  1133. (variable->string host #:append "host ")
  1134. ;;(variable->string (opensmtpd-pki-domain pki) #:append "pki ")
  1135. (variable->string srs)
  1136. (variable->string tls #:append "tls ")
  1137. (variable->string protocols #:append "protocols ")
  1138. (variable->string ciphers #:append "ciphers ")
  1139. (variable->string auth #:append "auth <" #:postpend "> ")
  1140. (variable->string mail-from #:append "mail-from ")
  1141. (variable->string src #:append "src ")
  1142. "\n")))
  1143. (define (opensmtpd-lmtp-configuration->string record)
  1144. (string-append "lmtp "
  1145. (opensmtpd-lmtp-configuration-destination record)
  1146. (if (opensmtpd-lmtp-configuration-rcpt-to record)
  1147. (begin
  1148. " " (opensmtpd-lmtp-configuration-rcpt-to record))
  1149. "")))
  1150. (define (opensmtpd-mda-configuration->string record)
  1151. (string-append "mda "
  1152. (opensmtpd-mda-configuration-command record) " "))
  1153. (define (opensmtpd-maildir-configuration->string record)
  1154. (string-append "maildir "
  1155. "\"" (opensmtpd-maildir-configuration-pathname record) "\""
  1156. (if (opensmtpd-maildir-configuration-junk record)
  1157. " junk "
  1158. " ")))
  1159. (define (opensmtpd-proc->string record)
  1160. (string-append "proc "
  1161. (opensmtpd-proc-name record) " "
  1162. "\"" (opensmtpd-proc-command record) "\"\n"))
  1163. (define (opensmtpd-local-delivery-configuration->string record)
  1164. (let ([method (opensmtpd-local-delivery-configuration-method record)]
  1165. [alias (opensmtpd-local-delivery-configuration-alias record)]
  1166. [ttl (opensmtpd-local-delivery-configuration-ttl record)]
  1167. [user (opensmtpd-local-delivery-configuration-user record)]
  1168. [userbase (opensmtpd-local-delivery-configuration-userbase record)]
  1169. [virtual (opensmtpd-local-delivery-configuration-virtual record)]
  1170. [wrapper (opensmtpd-local-delivery-configuration-wrapper record)])
  1171. (string-append
  1172. (cond [(string? method)
  1173. (string-append method " ")]
  1174. [(opensmtpd-mda-configuration? method)
  1175. (opensmtpd-mda-configuration->string method)]
  1176. [(opensmtpd-lmtp-configuration? method)
  1177. (opensmtpd-lmtp-configuration->string method)]
  1178. [(opensmtpd-maildir-configuration? method)
  1179. (opensmtpd-maildir-configuration->string method)])
  1180. ;; FIXME/TODO support specifying alias file:/path/to/alias-file ?
  1181. ;; I do not think that is something that I can do...
  1182. (variable->string alias #:append "alias <" #:postpend "> ")
  1183. (variable->string ttl #:append "ttl ")
  1184. (variable->string user #:append "user ")
  1185. (variable->string userbase #:append "userbase <" #:postpend "> ")
  1186. (variable->string virtual #:append "virtual <" #:postpend "> ")
  1187. (variable->string wrapper #:append "wrapper "))))
  1188. (define (opensmtpd-action->string record)
  1189. (string-append "action "
  1190. "\"" (opensmtpd-action-name record) "\" "
  1191. (cond [(opensmtpd-local-delivery-configuration? (opensmtpd-action-method record))
  1192. (opensmtpd-local-delivery-configuration->string (opensmtpd-action-method record))]
  1193. [(opensmtpd-relay-configuration? (opensmtpd-action-method record))
  1194. (opensmtpd-relay-configuration->string (opensmtpd-action-method record))])
  1195. " \n"))
  1196. (define (opensmtpd-match->string record)
  1197. (string-append "match "
  1198. (let ([for (opensmtpd-match-for record)]
  1199. [name (opensmtpd-match-name record)]
  1200. [from (opensmtpd-match-from record)]
  1201. [auth (opensmtpd-match-auth record)]
  1202. [helo (opensmtpd-match-helo record)]
  1203. [mail-from (opensmtpd-match-mail-from record)]
  1204. [rcpt-to (opensmtpd-match-rcpt-to record)]
  1205. [tag (opensmtpd-match-tag record)]
  1206. [tls (opensmtpd-match-tls record)])
  1207. (string-append
  1208. (list-of-variables->string (list from for helo mail-from rcpt-to tag))
  1209. (if auth
  1210. (string-append "auth "
  1211. (if (string? auth)
  1212. (string-append auth " ")
  1213. ""
  1214. ))
  1215. "")
  1216. (cond [tls "tls "]
  1217. [(not tls) "! tls "]
  1218. [(null? tls) ""])
  1219. (if (string? name)
  1220. (string-append "action " "\"" name "\" ")
  1221. "reject ")
  1222. "\n"))))
  1223. (define (opensmtpd-ca->string record)
  1224. (string-append "ca " (opensmtpd-ca-name record) " "
  1225. "cert \"" (opensmtpd-ca-file record) "\"\n"))
  1226. (define (opensmtpd-pki->string record)
  1227. (let ([domain (opensmtpd-pki-domain record)]
  1228. [cert (opensmtpd-pki-cert record)]
  1229. [key (opensmtpd-pki-key record)]
  1230. [dhe (opensmtpd-pki-dhe record)])
  1231. (string-append "pki " domain " " "cert \"" cert "\" \n"
  1232. "pki " domain " " "key \"" key "\" \n"
  1233. (if dhe
  1234. (string-append
  1235. "pki " domain " " "dhe " dhe "\n")
  1236. ""))))
  1237. (define (opensmtpd-filter-chain->string record)
  1238. (string-append "filter "
  1239. "\"" (opensmtpd-filter-chain-name record) "\" "
  1240. "chain {"
  1241. (list-of-strings->string (opensmtpd-filter-chain-filter-names record))
  1242. "}\n"))
  1243. (define (opensmtpd-filter-phase->string record)
  1244. (let ([name (opensmtpd-filter-phase-name record)]
  1245. [phase-name (opensmtpd-filter-phase-phase-name record)]
  1246. [decision (opensmtpd-filter-phase-decision record)]
  1247. [conditions (opensmtpd-filter-phase-conditions record)]
  1248. [message (opensmtpd-filter-phase-message record)]
  1249. [value (opensmtpd-filter-phase-value record)])
  1250. (string-append "filter "
  1251. "\"" name "\" "
  1252. "phase " phase-name " "
  1253. "match "
  1254. (list-of-strings->string
  1255. conditions #:string-delimiter " " #:drop-right-number 1)
  1256. " "
  1257. decision " "
  1258. (if (or (string=? "reject" decision)
  1259. (string=? "disconnect" decision))
  1260. (string-append "\"" message "\"")
  1261. "")
  1262. (if (string=? "rewrite" decision)
  1263. (string-append "rewrite " (number->string value))
  1264. "")
  1265. "\n")))
  1266. ;; FIXME/TODO should I use format here srfi-28 ?
  1267. ;; web.scm nginx does a (format #f "string" "another string")
  1268. ;; this could be a list like (list (file-append opensmtpd-dkimsign "/libexec/filter") "-d gnucode.me -s /path/to/selector.cert")
  1269. ;; Then opensmtpd-configuration->mixed-text-file could be rewritten to be something like
  1270. ;; (mixed-text-file (eval `(string-append (opensmtpd-configuration-fieldname->string ...)) (gnu services mail)))
  1271. ;; and of course (opensmtpd-filter-proc-exec->string would have to be rewritten to
  1272. ;; (string-append "filter " \"name\" proc-exec \" ,@(command) \"
  1273. (define (opensmtpd-filter-proc-exec->string record)
  1274. (string-append "filter "
  1275. "\"" (opensmtpd-filter-proc-exec-name record) "\" "
  1276. "proc-exec " "\"" (opensmtpd-filter-proc-exec-command record) "\""
  1277. "\n"))
  1278. ;; FIXME/TODO do I need this? or want this? I am using a lambda function
  1279. ;; that works fairly well below in the ";; write out the includes" line
  1280. ;; or could I just use the variable->string function?
  1281. ;; something like (variable->string string #:append "include \"" #:append "\"\n")
  1282. (define (opensmtpd-configuration-listen->string string)
  1283. (string-append
  1284. "include \"" string "\"\n"))
  1285. ;; this is basically a opensmtpd-srs->string
  1286. (define (opensmtpd-configuration-srs->string record)
  1287. (let ([key (opensmtpd-srs-configuration-key record)]
  1288. [backup-key (opensmtpd-srs-configuration-backup-key record)]
  1289. [ttl-delay (opensmtpd-srs-configuration-ttl-delay record)])
  1290. (string-append
  1291. (variable->string key #:append "srs key " #:postpend "\n")
  1292. (variable->string backup-key #:append "srs key backup " #:postpend "\n")
  1293. (variable->string ttl-delay #:append "srs ttl " #:postpend "\n")
  1294. "\n")))
  1295. ;; this is basically the same thing as <opensmtpd-smtp-configuration>->string
  1296. (define (opensmtpd-configuration-smtp->string record)
  1297. (let ([ciphers (opensmtpd-smtp-configuration-ciphers record)]
  1298. [limit-max-mails (opensmtpd-smtp-configuration-limit-max-mails record)]
  1299. [limit-max-rcpt (opensmtpd-smtp-configuration-limit-max-rcpt record)]
  1300. [max-message-size (opensmtpd-smtp-configuration-max-message-size record)]
  1301. [sub-addr-delim (opensmtpd-smtp-configuration-sub-addr-delim record)])
  1302. (string-append
  1303. (variable->string ciphers #:append "smtp ciphers " #:postpend "\n")
  1304. (variable->string limit-max-mails #:append "smtp limit max-mails " #:postpend "\n")
  1305. (variable->string limit-max-rcpt #:append "smtp limit max-rcpt " #:postpend "\n")
  1306. (variable->string max-message-size #:append "smtp max-message-size " #:postpend "\n")
  1307. (variable->string sub-addr-delim #:append "smtp sub-addr-delim " #:postpend "\n")
  1308. "\n")))
  1309. (define (opensmtpd-configuration-queue->string record)
  1310. (let ([compression (opensmtpd-queue-configuration-compression record)]
  1311. [encryption (opensmtpd-queue-configuration-encryption record)]
  1312. [ttl-delay (opensmtpd-queue-configuration-ttl-delay record)])
  1313. (string-append
  1314. (if compression
  1315. "queue compression\n"
  1316. "")
  1317. (if encryption
  1318. (string-append
  1319. "queue encryption "
  1320. (if (not (boolean? encryption))
  1321. encryption
  1322. "")
  1323. "\n")
  1324. "")
  1325. (if ttl-delay
  1326. (string-append "queue ttl" ttl-delay "\n")
  1327. ""
  1328. ))))
  1329. ;; build a list of opensmtpd-pkis from
  1330. ;; opensmtpd-configuration-listen-ons and
  1331. ;; opensmtpd-configuration-actions
  1332. (define (opensmtpd-configuration-pkis record)
  1333. ;; TODO/FIXME could opensmtpd-configuration-actions ever be #f? aka left blank by the user? -> no? I think.
  1334. ;; TODO/FIXME could opensmtpd-configuration-actions -> NOT have an opensmtpd-relay-configuration?
  1335. ;; I think so. And if it did NOT have a relay configuration, then action-pkis would be '() when
  1336. ;; it needs to be #f. because if the opensmtpd-configuration has NO pkis, then this function will
  1337. ;; return '(), when it should return #f. If it returns '(), then opensmtpd-configuration-fieldname->string will
  1338. ;; have an error.
  1339. (define action-pkis
  1340. (let loop1 ([list (opensmtpd-configuration-actions record)])
  1341. (if (null? list)
  1342. '()
  1343. (if (and (opensmtpd-relay-configuration? (opensmtpd-action-method (car list)))
  1344. (opensmtpd-relay-configuration-pki (opensmtpd-action-method (car list))))
  1345. (cons (opensmtpd-relay-configuration-pki (opensmtpd-action-method (car list)))
  1346. (loop1 (cdr list)))
  1347. (loop1 (cdr list))))))
  1348. ;; FIXME/TODO
  1349. ;; this could be #f aka left blank. aka there are no listen-ons records with pkis.
  1350. ;; aka there are no lins in the configuration like:
  1351. ;; listen on eth0 tls pki smtp.gnucode.me
  1352. (define listen-on-pkis
  1353. (let loop2 ([list (opensmtpd-configuration-listen-ons record)])
  1354. (if (null? list)
  1355. '()
  1356. (if (opensmtpd-listen-on-pki (car list))
  1357. (cons (opensmtpd-listen-on-pki (car list))
  1358. (loop2 (cdr list)))
  1359. (loop2 (cdr list))))))
  1360. (delete-duplicates (append action-pkis listen-on-pkis)))
  1361. (define (opensmtpd-configuration-fieldname->string record fieldname-accessor record->string)
  1362. (if (fieldname-accessor record)
  1363. (begin
  1364. (string-append
  1365. (list-of-records->string (fieldname-accessor record) record->string) "\n"))
  1366. ""))
  1367. (define (list-of-records->string list-of-records record->string)
  1368. (string-append
  1369. (cond [(not (list? list-of-records))
  1370. (record->string list-of-records)]
  1371. [else
  1372. (let loop ([list list-of-records])
  1373. (if (null? list)
  1374. ""
  1375. (string-append
  1376. (record->string (car list))
  1377. (loop (cdr list)))))])))
  1378. (define (opensmtpd-configuration->mixed-text-file record)
  1379. ;; should I use this named let instead? or should I give this a name...
  1380. ;; (write-all-fieldnames (list (cons fieldname fieldname->string) (cons fieldname2 fieldname->string)))
  1381. ;; (let loop ([list (list (cons opensmtpd-configuration-includes (lambda (string)
  1382. ;; (string-append
  1383. ;; "include \"" string "\"\n")))
  1384. ;; (cons opensmtpd-configuration-tables opensmtpd-table->string)
  1385. ;; (cons opensmtpd-configuration-pkis opensmtpd-pki->string))])
  1386. ;; (if (null? list)
  1387. ;; ""
  1388. ;; (string-append (opensmtpd-configuration-fieldname->string record
  1389. ;; (caar list)
  1390. ;; (cdar list))
  1391. ;; (loop (cadr list)))))
  1392. (mixed-text-file
  1393. "opensmtpd.conf"
  1394. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-smtp
  1395. opensmtpd-configuration-smtp->string)
  1396. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-srs
  1397. opensmtpd-configuration-srs->string)
  1398. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-queue
  1399. opensmtpd-configuration-queue->string)
  1400. ;; write out the mta-max-deferred
  1401. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-mta-max-deferred
  1402. (lambda (value)
  1403. (string-append "mta max-deferred " (number->string value) "\n")))
  1404. ;; write out the includes
  1405. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-includes
  1406. opensmtpd-configuration-listen->string)
  1407. ;;write out all the tables
  1408. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-tables opensmtpd-table->string)
  1409. ;; write out all the cas
  1410. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-cas opensmtpd-ca->string)
  1411. ;; write out all the pkis
  1412. ;; opensmtpd-configuration-pkis is a function that I defined. "pkis" is not a fieldname of
  1413. ;; <opensmtpd-configuration>
  1414. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-pkis opensmtpd-pki->string)
  1415. ;; write out all the procs
  1416. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-procs opensmtpd-proc->string)
  1417. ;; write all of the opensmtpd-configuration-filter-phases
  1418. (opensmtpd-configuration-fieldname->string record
  1419. opensmtpd-configuration-filter-phases opensmtpd-filter-phase->string)
  1420. ;; write all of the opensmtpd-filter-proc-exec
  1421. (opensmtpd-configuration-fieldname->string record
  1422. opensmtpd-configuration-filter-proc-execs opensmtpd-filter-proc-exec->string)
  1423. ;; write all of the opensmtpd-configuration-filter-chains
  1424. (opensmtpd-configuration-fieldname->string record
  1425. opensmtpd-configuration-filter-chains opensmtpd-filter-chain->string)
  1426. ;; write out all of the opensmtpd-configuration-procs
  1427. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-procs opensmtpd-proc->string)
  1428. ;; write all of the listen-on-records
  1429. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-listen-ons
  1430. opensmtpd-listen-on->string)
  1431. ;; write all the actions
  1432. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-actions
  1433. opensmtpd-action->string)
  1434. ;; write all of the matches
  1435. (opensmtpd-configuration-fieldname->string record opensmtpd-configuration-matches opensmtpd-match->string))
  1436. )
  1437. (define (opensmtpd-shepherd-service config)
  1438. (list (shepherd-service
  1439. (provision '(smtpd ;;config
  1440. ))
  1441. (requirement '(loopback))
  1442. (documentation "Run the OpenSMTPD daemon.")
  1443. ;; FIXME/TODO add a config option to show the current smtpd.conf file.
  1444. ;; (config #~(display (or #$(opensmtpd-configuration-config-file config)
  1445. ;; #$(opensmtpd-configuration->mixed-text-file config))))
  1446. (start (let ((smtpd (file-append (opensmtpd-configuration-package config) "/sbin/smtpd")))
  1447. #~(make-forkexec-constructor
  1448. (list #$smtpd "-f" (or #$(opensmtpd-configuration-config-file config)
  1449. #$(opensmtpd-configuration->mixed-text-file config)))
  1450. #:pid-file "/var/run/smtpd.pid")))
  1451. (stop #~(make-kill-destructor)))))
  1452. (define %opensmtpd-accounts
  1453. (list (user-group
  1454. (name "smtpq")
  1455. (system? #t))
  1456. (user-account
  1457. (name "smtpd")
  1458. (group "nogroup")
  1459. (system? #t)
  1460. (comment "SMTP Daemon")
  1461. (home-directory "/var/empty")
  1462. (shell (file-append shadow "/sbin/nologin")))
  1463. (user-account
  1464. (name "smtpq")
  1465. (group "smtpq")
  1466. (system? #t)
  1467. (comment "SMTPD Queue")
  1468. (home-directory "/var/empty")
  1469. (shell (file-append shadow "/sbin/nologin")))))
  1470. (define (opensmtpd-activation config)
  1471. (let ((smtpd (file-append (opensmtpd-configuration-package config) "/sbin/smtpd"))
  1472. (config-file (opensmtpd-configuration-config-file config))
  1473. (configuration (opensmtpd-configuration->mixed-text-file config)))
  1474. #~(begin
  1475. (use-modules (guix build utils))
  1476. ;; Create mbox and spool directories.
  1477. (mkdir-p "/var/mail")
  1478. (mkdir-p "/var/spool/smtpd")
  1479. (chmod "/var/spool/smtpd" #o711)
  1480. (mkdir-p "/var/spool/mail")
  1481. (chmod "/var/spool/mail" #o711)
  1482. (display (string-append "smtpd: checking syntax of "
  1483. (or
  1484. #$config-file
  1485. #$configuration)
  1486. "\n"))
  1487. (system* #$smtpd "-nf"
  1488. (or
  1489. #$config-file
  1490. #$configuration)))))
  1491. (define %opensmtpd-pam-services
  1492. (list (unix-pam-service "smtpd")))
  1493. (define opensmtpd-service-type
  1494. (service-type
  1495. (name 'opensmtpd)
  1496. (extensions
  1497. (list (service-extension account-service-type
  1498. (const %opensmtpd-accounts))
  1499. (service-extension activation-service-type
  1500. opensmtpd-activation)
  1501. (service-extension pam-root-service-type
  1502. (const %opensmtpd-pam-services))
  1503. (service-extension profile-service-type
  1504. (compose list opensmtpd-configuration-package))
  1505. (service-extension shepherd-root-service-type
  1506. opensmtpd-shepherd-service)))
  1507. (default-value (opensmtpd-configuration))
  1508. (description "Run the Opensmtpd email server.")))