Commit 437bd788 437bd78824ad255ed9013a9501cbb86aa8361874 by Sergey Poznyakoff

Make maidag hanlde mailer URLs.

This makes it possible to `deliver' mails to mailers without
explicitly specifying `remote+mailer' mailbox URLs.  In
particular, it is handy for implementing mailing lists using
`prog' mailers (as with Mailman).

* maidag/deliver.c (notify_action): Do nothing if biff_user_name
is empty.
(deliver_to_user): Remove `name' argument.  Allow for auth==NULL.
(is_remote_url): Use case-sensitive comparison.
(is_mailer_url): New function.
(deliver_url): Handle mailer URLs as if they were corresponding
`remote+' mailbox URLs.  This allows to handle mailing lists.
* maidag/lmtp.c (lmtp_groups): New global.
(lmtp_set_privs): New function. Sets primary and
supplementary group privileges.
(maidag_lmtp_server): Call lmtp_set_privs.
* maidag/maidag.c (lmtp_group): Remove.  It is superceded by
lmtp_groups.
(maidag_cfg_param): Allow to specify supplementary groups.
* maidag/maidag.h (lmtp_group): Replace with lmtp_groups.
1 parent 01dabb5c
1 2008-10-28 Sergey Poznyakoff <gray@gnu.org.ua> 1 2008-10-28 Sergey Poznyakoff <gray@gnu.org.ua>
2 2
3 Make maidag hanlde mailer URLs.
4
5 This makes it possible to `deliver' mails to mailers without
6 explicitly specifying `remote+mailer' mailbox URLs. In
7 particular, it is handy for implementing mailing lists using
8 `prog' mailers (as with Mailman).
9
10 * maidag/deliver.c (notify_action): Do nothing if biff_user_name
11 is empty.
12 (deliver_to_user): Remove `name' argument. Allow for auth==NULL.
13 (is_remote_url): Use case-sensitive comparison.
14 (is_mailer_url): New function.
15 (deliver_url): Handle mailer URLs as if they were corresponding
16 `remote+' mailbox URLs. This allows to handle mailing lists.
17 * maidag/lmtp.c (lmtp_groups): New global.
18 (lmtp_set_privs): New function. Sets primary and
19 supplementary group privileges.
20 (maidag_lmtp_server): Call lmtp_set_privs.
21 * maidag/maidag.c (lmtp_group): Remove. It is superceded by
22 lmtp_groups.
23 (maidag_cfg_param): Allow to specify supplementary groups.
24 * maidag/maidag.h (lmtp_group): Replace with lmtp_groups.
25
3 Change mailer creation mechanism. 26 Change mailer creation mechanism.
4 27
5 * include/mailutils/mailer.h (mu_mailer_create_from_url): New 28 * include/mailutils/mailer.h (mu_mailer_create_from_url): New
......
...@@ -87,7 +87,7 @@ static const char *biff_user_name; ...@@ -87,7 +87,7 @@ static const char *biff_user_name;
87 static int 87 static int
88 notify_action (mu_observer_t obs, size_t type, void *data, void *action_data) 88 notify_action (mu_observer_t obs, size_t type, void *data, void *action_data)
89 { 89 {
90 if (type == MU_EVT_MESSAGE_APPEND) 90 if (type == MU_EVT_MESSAGE_APPEND && biff_user_name)
91 { 91 {
92 mu_message_qid_t qid = data; 92 mu_message_qid_t qid = data;
93 mu_mailbox_t mbox = mu_observer_get_owner (obs); 93 mu_mailbox_t mbox = mu_observer_get_owner (obs);
...@@ -144,7 +144,7 @@ attach_notify (mu_mailbox_t mbox) ...@@ -144,7 +144,7 @@ attach_notify (mu_mailbox_t mbox)
144 144
145 int 145 int
146 deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg, 146 deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
147 struct mu_auth_data *auth, const char *name, 147 struct mu_auth_data *auth,
148 char **errp) 148 char **errp)
149 { 149 {
150 int status; 150 int status;
...@@ -184,59 +184,61 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg, ...@@ -184,59 +184,61 @@ deliver_to_user (mu_mailbox_t imbx, mu_mailbox_t mbox, mu_message_t msg,
184 } 184 }
185 185
186 #if defined(USE_MAILBOX_QUOTAS) 186 #if defined(USE_MAILBOX_QUOTAS)
187 { 187 if (auth)
188 mu_off_t n; 188 {
189 mu_off_t msg_size; 189 mu_off_t n;
190 mu_off_t mbsize; 190 mu_off_t msg_size;
191 191 mu_off_t mbsize;
192 if ((status = mu_mailbox_get_size (mbox, &mbsize))) 192
193 { 193 if ((status = mu_mailbox_get_size (mbox, &mbsize)))
194 maidag_error (_("Cannot get size of mailbox %s: %s"), 194 {
195 path, mu_strerror (status)); 195 maidag_error (_("Cannot get size of mailbox %s: %s"),
196 if (status == ENOSYS) 196 path, mu_strerror (status));
197 mbsize = 0; /* Try to continue anyway */ 197 if (status == ENOSYS)
198 else 198 mbsize = 0; /* Try to continue anyway */
199 return EX_TEMPFAIL; 199 else
200 } 200 return EX_TEMPFAIL;
201 }
201 202
202 switch (check_quota (auth, mbsize, &n)) 203 switch (check_quota (auth, mbsize, &n))
203 { 204 {
204 case MQUOTA_EXCEEDED: 205 case MQUOTA_EXCEEDED:
205 maidag_error (_("%s: mailbox quota exceeded for this recipient"), name); 206 maidag_error (_("%s: mailbox quota exceeded for this recipient"),
206 if (errp) 207 auth->name);
207 asprintf (errp, "%s: mailbox quota exceeded for this recipient", 208 if (errp)
208 name); 209 asprintf (errp, "%s: mailbox quota exceeded for this recipient",
209 exit_code = EX_QUOTA(); 210 auth->name);
210 failed++; 211 exit_code = EX_QUOTA();
211 break; 212 failed++;
212 213 break;
213 case MQUOTA_UNLIMITED: 214
214 break; 215 case MQUOTA_UNLIMITED:
215 216 break;
216 default: 217
217 if ((status = mu_mailbox_get_size (imbx, &msg_size))) 218 default:
218 { 219 if ((status = mu_mailbox_get_size (imbx, &msg_size)))
219 maidag_error (_("Cannot get message size (input message %s): %s"), 220 {
220 path, mu_strerror (status)); 221 maidag_error (_("Cannot get message size (input message %s): %s"),
221 exit_code = EX_UNAVAILABLE; 222 path, mu_strerror (status));
222 failed++; 223 exit_code = EX_UNAVAILABLE;
223 } 224 failed++;
224 else if (msg_size > n) 225 }
225 { 226 else if (msg_size > n)
226 maidag_error (_("%s: message would exceed maximum mailbox size for " 227 {
227 "this recipient"), 228 maidag_error (_("%s: message would exceed maximum mailbox size for "
228 name); 229 "this recipient"),
229 if (errp) 230 auth->name);
230 asprintf (errp, 231 if (errp)
231 "%s: message would exceed maximum mailbox size " 232 asprintf (errp,
232 "for this recipient", 233 "%s: message would exceed maximum mailbox size "
233 name); 234 "for this recipient",
234 exit_code = EX_QUOTA(); 235 auth->name);
235 failed++; 236 exit_code = EX_QUOTA();
236 } 237 failed++;
237 break; 238 }
238 } 239 break;
239 } 240 }
241 }
240 #endif 242 #endif
241 243
242 if (!failed && switch_user_id (auth, 1) == 0) 244 if (!failed && switch_user_id (auth, 1) == 0)
...@@ -271,9 +273,24 @@ is_remote_url (mu_url_t url) ...@@ -271,9 +273,24 @@ is_remote_url (mu_url_t url)
271 { 273 {
272 const char *scheme; 274 const char *scheme;
273 int rc = mu_url_sget_scheme (url, &scheme); 275 int rc = mu_url_sget_scheme (url, &scheme);
274 return rc == 0 && strncasecmp (scheme, "remote+", 7) == 0; 276 return rc == 0 && strncmp (scheme, "remote+", 7) == 0;
275 } 277 }
276 278
279 static int
280 is_mailer_url (mu_url_t url)
281 {
282 mu_record_t record = NULL;
283 int (*pfn) (mu_mailer_t) = NULL;
284
285 return mu_registrar_lookup_url (url, MU_FOLDER_ATTRIBUTE_FILE,
286 &record, NULL) == 0
287 && mu_record_get_mailer (record, &pfn) == 0
288 && pfn;
289 }
290
291 #define REMOTE_PREFIX "remote+"
292 #define REMOTE_PREFIX_LEN (sizeof(REMOTE_PREFIX)-1)
293
277 int 294 int
278 deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp) 295 deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
279 { 296 {
...@@ -282,9 +299,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp) ...@@ -282,9 +299,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
282 mu_message_t msg; 299 mu_message_t msg;
283 int status; 300 int status;
284 301
285 if (is_remote_url (url)) 302 if (name)
286 auth = NULL;
287 else
288 { 303 {
289 auth = mu_get_auth_by_name (name); 304 auth = mu_get_auth_by_name (name);
290 if (!auth) 305 if (!auth)
...@@ -292,9 +307,10 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp) ...@@ -292,9 +307,10 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
292 maidag_error (_("%s: no such user"), name); 307 maidag_error (_("%s: no such user"), name);
293 if (errp) 308 if (errp)
294 asprintf (errp, "%s: no such user", name); 309 asprintf (errp, "%s: no such user", name);
295 exit_code = EX_UNAVAILABLE; 310 exit_code = EX_NOUSER;
296 return EX_UNAVAILABLE; 311 return EX_NOUSER;
297 } 312 }
313
298 if (current_uid) 314 if (current_uid)
299 auth->change_uid = 0; 315 auth->change_uid = 0;
300 316
...@@ -314,16 +330,59 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp) ...@@ -314,16 +330,59 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
314 return EX_TEMPFAIL; 330 return EX_TEMPFAIL;
315 } 331 }
316 332
317 if (url) 333 if (!url)
318 status = mu_mailbox_create_from_url (&mbox, url); 334 {
319 else 335 status = mu_url_create (&url, auth->mailbox);
320 status = mu_mailbox_create (&mbox, auth->mailbox); 336 if (status)
337 {
338 maidag_error (_("Cannot create URL for %s: %s"),
339 auth->mailbox, mu_strerror (status));
340 return exit_code = EX_UNAVAILABLE;
341 }
342 status = mu_url_parse (url);
343 if (status)
344 {
345 maidag_error (_("Error parsing URL %s: %s"),
346 auth->mailbox, mu_strerror (status));
347 return exit_code = EX_UNAVAILABLE;
348 }
349 }
350
351 if (is_mailer_url (url))
352 {
353 const char *scheme;
354 size_t len;
355 char *new_scheme;
356
357 mu_url_sget_scheme (url, &scheme);
358 len = REMOTE_PREFIX_LEN + strlen (scheme);
359 new_scheme = malloc (len + 1);
360 if (!new_scheme)
361 {
362 mu_error (_("Not enough memory"));
363 exit (EX_SOFTWARE);
364 }
365 strcat (strcpy (new_scheme, REMOTE_PREFIX), scheme);
366 status = mu_url_set_scheme (url, new_scheme);
367 free (new_scheme);
368 if (status)
369 {
370 errno = status;
371 maidag_error (_("Cannot modify URL %s: %s"),
372 mu_url_to_string (url),
373 mu_strerror (status));
374 mu_auth_data_free (auth);
375 return EX_TEMPFAIL;
376 }
377 }
378 status = mu_mailbox_create_from_url (&mbox, url);
321 379
322 if (status) 380 if (status)
323 { 381 {
324 maidag_error (_("Cannot open mailbox %s: %s"), 382 maidag_error (_("Cannot open mailbox %s: %s"),
325 url ? mu_url_to_string (url): auth->mailbox, 383 mu_url_to_string (url),
326 mu_strerror (status)); 384 mu_strerror (status));
385 mu_url_destroy (&url);
327 mu_auth_data_free (auth); 386 mu_auth_data_free (auth);
328 return EX_TEMPFAIL; 387 return EX_TEMPFAIL;
329 } 388 }
...@@ -335,7 +394,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp) ...@@ -335,7 +394,7 @@ deliver_url (mu_url_t url, mu_mailbox_t imbx, const char *name, char **errp)
335 will be created */ 394 will be created */
336 if (switch_user_id (auth, 1)) 395 if (switch_user_id (auth, 1))
337 return EX_TEMPFAIL; 396 return EX_TEMPFAIL;
338 status = deliver_to_user (imbx, mbox, msg, auth, name, errp); 397 status = deliver_to_user (imbx, mbox, msg, auth, errp);
339 if (switch_user_id (auth, 0)) 398 if (switch_user_id (auth, 0))
340 return EX_TEMPFAIL; 399 return EX_TEMPFAIL;
341 400
...@@ -371,7 +430,18 @@ deliver (mu_mailbox_t imbx, char *dest_id, char **errp) ...@@ -371,7 +430,18 @@ deliver (mu_mailbox_t imbx, char *dest_id, char **errp)
371 } 430 }
372 status = mu_url_sget_user (url, &name); 431 status = mu_url_sget_user (url, &name);
373 if (status == MU_ERR_NOENT) 432 if (status == MU_ERR_NOENT)
374 name = NULL; 433 {
434 if (!is_mailer_url (url) && !is_remote_url (url))
435 {
436 maidag_error (_("no user name"));
437 if (errp)
438 asprintf (errp, "no user such");
439 exit_code = EX_NOUSER;
440 return EX_NOUSER;
441 }
442 else
443 name = NULL;
444 }
375 else if (status) 445 else if (status)
376 { 446 {
377 maidag_error (_("%s: cannot get user name from url: %s"), 447 maidag_error (_("%s: cannot get user name from url: %s"),
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
25 #include <signal.h> 25 #include <signal.h>
26 #include <mu_umaxtostr.h> 26 #include <mu_umaxtostr.h>
27 27
28 mu_list_t lmtp_groups;
28 static int lmtp_transcript; 29 static int lmtp_transcript;
29 30
30 void 31 void
...@@ -572,22 +573,67 @@ lmtp_connection (int fd, struct sockaddr *sa, int salen, void *data, ...@@ -572,22 +573,67 @@ lmtp_connection (int fd, struct sockaddr *sa, int salen, void *data,
572 return 0; 573 return 0;
573 } 574 }
574 575
575 int 576 static int
576 maidag_lmtp_server () 577 lmtp_set_privs ()
577 { 578 {
578 struct group *gr = getgrnam (lmtp_group); 579 gid_t gid;
579 580
580 if (gr == NULL) 581 if (lmtp_groups)
581 { 582 {
582 mu_error (_("Error getting mail group")); 583 gid_t *gidset = NULL;
583 return EX_UNAVAILABLE; 584 size_t size = 0;
585 size_t j = 0;
586 mu_iterator_t itr;
587 int rc;
588
589 mu_list_count (lmtp_groups, &size);
590 gidset = calloc (size, sizeof (gidset[0]));
591 if (!gidset)
592 {
593 mu_error (_("Not enough memory"));
594 return EX_UNAVAILABLE;
595 }
596 if (mu_list_get_iterator (lmtp_groups, &itr) == 0)
597 {
598 for (mu_iterator_first (itr);
599 !mu_iterator_is_done (itr); mu_iterator_next (itr))
600 mu_iterator_current (itr,
601 (void **)(gidset + j++));
602 mu_iterator_destroy (&itr);
603 }
604 gid = gidset[0];
605 rc = setgroups (j, gidset);
606 free (gidset);
607 if (rc)
608 {
609 mu_error(_("setgroups failed: %s"), mu_strerror (errno));
610 return EX_UNAVAILABLE;
611 }
584 } 612 }
585 613 else
586 if (setgid (gr->gr_gid) == -1) 614 {
615 struct group *gr = getgrnam ("mail");
616 if (gr == NULL)
617 {
618 mu_error (_("Error getting group `mail'"));
619 return EX_UNAVAILABLE;
620 }
621 gid = gr->gr_gid;
622 }
623 if (setgid (gid) == -1)
587 { 624 {
588 mu_error (_("Error setting mail group")); 625 mu_error (_("Error setting mail group"));
589 return EX_UNAVAILABLE; 626 return EX_UNAVAILABLE;
590 } 627 }
628 return 0;
629 }
630
631 int
632 maidag_lmtp_server ()
633 {
634 int rc = lmtp_set_privs ();
635 if (rc)
636 return rc;
591 637
592 if (mu_m_server_mode (server) == MODE_DAEMON) 638 if (mu_m_server_mode (server) == MODE_DAEMON)
593 { 639 {
......
...@@ -48,7 +48,6 @@ int lmtp_mode; ...@@ -48,7 +48,6 @@ int lmtp_mode;
48 int url_option; 48 int url_option;
49 char *lmtp_url_string; 49 char *lmtp_url_string;
50 int reuse_lmtp_address = 1; 50 int reuse_lmtp_address = 1;
51 char *lmtp_group = "mail";
52 51
53 const char *program_version = "maidag (" PACKAGE_STRING ")"; 52 const char *program_version = "maidag (" PACKAGE_STRING ")";
54 static char doc[] = 53 static char doc[] =
...@@ -256,6 +255,28 @@ cb_debug (mu_debug_t debug, void *data, mu_config_value_t *val) ...@@ -256,6 +255,28 @@ cb_debug (mu_debug_t debug, void *data, mu_config_value_t *val)
256 return 0; 255 return 0;
257 } 256 }
258 257
258 static int
259 cb2_group (mu_debug_t debug, const char *gname, void *data)
260 {
261 mu_list_t *plist = data;
262 struct group *group;
263
264 if (!*plist)
265 mu_list_create (plist);
266 group = getgrnam (gname);
267 if (!group)
268 mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Unknown group: %s"), gname);
269 else
270 mu_list_append (*plist, (void*)group->gr_gid);
271 return 0;
272 }
273
274 static int
275 cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg)
276 {
277 return mu_cfg_string_value_cb (debug, arg, cb2_group, data);
278 }
279
259 struct mu_cfg_param maidag_cfg_param[] = { 280 struct mu_cfg_param maidag_cfg_param[] = {
260 { "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL, 281 { "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL,
261 N_("In case of multiple delivery, exit with code 0 if at least one " 282 N_("In case of multiple delivery, exit with code 0 if at least one "
...@@ -298,8 +319,9 @@ struct mu_cfg_param maidag_cfg_param[] = { ...@@ -298,8 +319,9 @@ struct mu_cfg_param maidag_cfg_param[] = {
298 /* LMTP support */ 319 /* LMTP support */
299 { "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL, 320 { "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL,
300 N_("Run in LMTP mode.") }, 321 N_("Run in LMTP mode.") },
301 { "group", mu_cfg_string, &lmtp_group, 0, NULL, 322 { "group", mu_cfg_callback, &lmtp_groups, 0, cb_group,
302 N_("In LMTP mode, change to this group after startup.") }, 323 N_("In LMTP mode, retain these supplementary groups."),
324 N_("groups: list of string") },
303 { "listen", mu_cfg_string, &lmtp_url_string, 0, NULL, 325 { "listen", mu_cfg_string, &lmtp_url_string, 0, NULL,
304 N_("In LMTP mode, listen on the given URL. Valid URLs are:\n" 326 N_("In LMTP mode, listen on the given URL. Valid URLs are:\n"
305 " tcp://<address: string>:<port: number> (note that port is " 327 " tcp://<address: string>:<port: number> (note that port is "
......
...@@ -126,7 +126,7 @@ extern int lmtp_mode; ...@@ -126,7 +126,7 @@ extern int lmtp_mode;
126 extern int url_option; 126 extern int url_option;
127 extern char *lmtp_url_string; 127 extern char *lmtp_url_string;
128 extern int reuse_lmtp_address; 128 extern int reuse_lmtp_address;
129 extern char *lmtp_group; 129 extern mu_list_t lmtp_groups;
130 extern mu_acl_t maidag_acl; 130 extern mu_acl_t maidag_acl;
131 131
132 void close_fds (void); 132 void close_fds (void);
......