]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/services/mail/mlmmj.nix
nix: update inputs
[sourcephile-nix.git] / nixos / modules / services / mail / mlmmj.nix
1 { config, lib, pkgs, ... }:
2
3 with lib;
4
5 let
6
7 concatMapLines = f: l: lib.concatStringsSep "\n" (map f l);
8
9 cfg = config.services.mlmmj;
10 stateDir = "/var/lib/mlmmj";
11 spoolDir = "/var/spool/mlmmj";
12 listDir = domain: list: "${spoolDir}/${domain}/${list}";
13 listCtl = domain: list: "${listDir domain list}/control";
14 transport = domain: list: "${domain}--${list}@local.list.mlmmj mlmmj:${domain}/${list}";
15 virtual = domain: list: "${list}@${domain} ${domain}--${list}@local.list.mlmmj";
16 alias = domain: list: "${list}: \"|${pkgs.mlmmj}/bin/mlmmj-receive -L ${listDir domain list}/\"";
17 subjectPrefix = list: "[${list}]";
18 listAddress = domain: list: "${list}@${domain}";
19 customHeaders = domain: list: [ "List-Id: ${list}" "Reply-To: ${list}@${domain}" ];
20 footer = domain: list: "-- \nTo unsubscribe send a mail to ${list}+unsubscribe@${domain}";
21 createList = d: l:
22 let ctlDir = listCtl d l; in
23 ''
24 for DIR in incoming queue queue/discarded archive text subconf unsubconf \
25 bounce control moderation subscribers.d digesters.d requeue \
26 nomailsubs.d
27 do
28 mkdir -p '${listDir d l}'/"$DIR"
29 done
30 ${pkgs.coreutils}/bin/mkdir -p ${ctlDir}
31 echo ${listAddress d l} > '${ctlDir}/listaddress'
32 [ ! -e ${ctlDir}/customheaders ] && \
33 echo "${lib.concatStringsSep "\n" (customHeaders d l)}" > '${ctlDir}/customheaders'
34 [ ! -e ${ctlDir}/footer ] && \
35 echo ${footer d l} > '${ctlDir}/footer'
36 [ ! -e ${ctlDir}/prefix ] && \
37 echo ${subjectPrefix l} > '${ctlDir}/prefix'
38 '';
39 customHeaderOptions =
40 {
41 name = lib.mkOption {
42 type = types.str;
43 };
44 value = lib.mkOption {
45 type = types.str;
46 };
47 }
48
49 mailingListConfig = config: {
50 settings = {
51 use_template = lib.mkDefault config.useTemplate;
52 recursive = lib.mkDefault config.recursive;
53 process_children_only = lib.mkDefault config.processChildrenOnly;
54 };
55 };
56
57 in
58
59 {
60
61 ###### interface
62
63 options = {
64
65 services.mlmmj = {
66
67 enable = mkOption {
68 type = types.bool;
69 default = false;
70 description = "Enable mlmmj";
71 };
72
73 user = mkOption {
74 type = types.str;
75 default = "mlmmj";
76 description = "mailinglist local user";
77 };
78
79 group = mkOption {
80 type = types.str;
81 default = "mlmmj";
82 description = "mailinglist local group";
83 };
84
85 listDomain = mkOption {
86 type = types.str;
87 default = "localhost";
88 description = "Set the mailing list domain";
89 };
90
91 mailLists = mkOption {
92 type = types.listOf types.str;
93 default = [ ];
94 description = "The collection of hosted maillists";
95 };
96
97 maillists = lib.mkOption {
98 type = types.attrsOf (types.submodule (domain: {
99 options = {
100 type = types.attrsOf
101 (types.submodule (list: {
102 options = {
103 customHeaders = lib.mkOption {
104 type = types.listOf customHeaderOptions;
105 default = [
106 "List-Id: ${list.name}"
107 "Reply-To: ${list.name}@${domain.name}"
108 ];
109 description = "Custom headers.";
110 };
111 footer = lib.mkOption {
112 type = types.lines;
113 default = "-- \nTo unsubscribe send a mail to ${list.name}+unsubscribe@${domain.name}";
114 description = "Text to be added as a footer to the mails' body.";
115 };
116 lang = lib.mkOption {
117 type = types.enum [ "ast" "cs" "de" "en" "fi" "fr" "gr" "it" "pt" "sk" "zh-cn" ];
118 default = "en";
119 description = "Language of the templates.";
120 };
121 subjectPrefix = lib.mkOption {
122 type = types.str;
123 default = "[${list.name}]";
124 description = "String added as a prefix to the Subject header.";
125 };
126 };
127 config = mailingListConfig config;
128 }))
129 }
130 }));
131 default = { };
132 description = "Mailing-lists.";
133 };
134
135 maintInterval = mkOption {
136 type = types.str;
137 default = "20min";
138 description = ''
139 Time interval between mlmmj-maintd runs, see
140 <citerefentry><refentrytitle>systemd.time</refentrytitle>
141 <manvolnum>7</manvolnum></citerefentry> for format information.
142 '';
143 };
144
145 postfix = {
146 enable = mkOption {
147 type = types.bool;
148 default = false;
149 description = "Configure postfix to use mlmmj as a transport";
150 };
151 };
152
153 };
154
155 };
156
157 ###### implementation
158
159 config = mkIf cfg.enable {
160
161 users.users.${cfg.user} = {
162 description = "mlmmj user";
163 home = stateDir;
164 createHome = true;
165 uid = config.ids.uids.mlmmj;
166 group = cfg.group;
167 useDefaultShell = true;
168 };
169
170 users.groups.${cfg.group} = {
171 gid = config.ids.gids.mlmmj;
172 };
173
174 services.postfix = mkIf cfg.postfix.enable {
175 enable = true;
176 recipientDelimiter = "+";
177 masterConfig.mlmmj = {
178 type = "unix";
179 private = true;
180 privileged = true;
181 chroot = false;
182 wakeup = 0;
183 command = "pipe";
184 args = [
185 "flags=ORhu"
186 "user=${cfg.user}:${cfg.group}"
187 "argv=${pkgs.mlmmj}/bin/mlmmj-receive"
188 "-F"
189 "-L"
190 "${spoolDir}/$nexthop"
191 ];
192 };
193
194 extraAliases = lib.concatMapStringsSep "\n" (alias cfg.listDomain) cfg.mailLists;
195
196 config = {
197 propagate_unmatched_extensions = [ "virtual" ];
198 mlmmj_destination_recipient_limit = "1";
199 };
200
201 virtual = lib.concatMapStringsSep "\n" (virtual cfg.listDomain) cfg.mailLists;
202 transport = lib.concatMapStringsSep "\n" (transport cfg.listDomain) cfg.mailLists;
203 };
204
205 environment.systemPackages = [ pkgs.mlmmj ];
206
207 system.activationScripts.mlmmj = ''
208 ${pkgs.coreutils}/bin/mkdir -p ${stateDir} ${spoolDir}/${cfg.listDomain}
209 ${lib.concatMapStringsSep "\n" (createList cfg.listDomain) cfg.mailLists}
210 ${pkgs.coreutils}/bin/chown -R ${cfg.user}:${cfg.group} ${spoolDir}
211 '';
212
213 systemd.services.mlmmj-maintd = {
214 description = "mlmmj maintenance daemon";
215 serviceConfig = {
216 User = cfg.user;
217 Group = cfg.group;
218 ExecStart = "${pkgs.mlmmj}/bin/mlmmj-maintd -F -d ${spoolDir}/${cfg.listDomain}";
219 };
220 };
221
222 systemd.timers.mlmmj-maintd = {
223 description = "mlmmj maintenance timer";
224 timerConfig.OnUnitActiveSec = cfg.maintInterval;
225 wantedBy = [ "timers.target" ];
226 };
227 };
228
229 }