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