Commit 1c2596eb 1c2596eba7c55f40e5b31e7c310ce3e0777c4ca5 by Sergey Poznyakoff

Implement forwarding functionality in Maidag.

* maidag/forward.c: New file.
* maidag/Makefile.am (maidag_SOURCES): Add forward.c.
* maidag/deliver.c (deliver_url): Call maidag_forward for local
users.
* maidag/maidag.c (forward_file, forward_file_checks): New
variables.
(maidag_cfg_param): New keywords forward-file and
forward-file-checks.
* maidag/maidag.h (FWD_IWGRP, FWD_IWOTH, FWD_LINK, FWD_DIR_IWGRP)
(FWD_DIR_IWOTH, FWD_ALL): New defines.
(forward_file, forward_file_checks): New variables.
(enum maidag_forward_result): New data type.
(maidag_forward): New prototype.
1 parent 2ba12864
1 2008-11-05 Sergey Poznyakoff <gray@gnu.org.ua> 1 2008-11-05 Sergey Poznyakoff <gray@gnu.org.ua>
2 2
3 Implement forwarding functionality in Maidag.
4
5 * maidag/forward.c: New file.
6 * maidag/Makefile.am (maidag_SOURCES): Add forward.c.
7 * maidag/deliver.c (deliver_url): Call maidag_forward for local
8 users.
9 * maidag/maidag.c (forward_file, forward_file_checks): New
10 variables.
11 (maidag_cfg_param): New keywords forward-file and
12 forward-file-checks.
13 * maidag/maidag.h (FWD_IWGRP, FWD_IWOTH, FWD_LINK, FWD_DIR_IWGRP)
14 (FWD_DIR_IWOTH, FWD_ALL): New defines.
15 (forward_file, forward_file_checks): New variables.
16 (enum maidag_forward_result): New data type.
17 (maidag_forward): New prototype.
18
3 Simplify calls to final delivery functions. 19 Simplify calls to final delivery functions.
4 20
5 * maidag/deliver.c (mda): Pass msg as a 1st argument to deliver. 21 * maidag/deliver.c (mda): Pass msg as a 1st argument to deliver.
......
...@@ -20,6 +20,7 @@ INCLUDES = -I${top_srcdir} @MU_COMMON_INCLUDES@ @GUILE_INCLUDES@ ...@@ -20,6 +20,7 @@ INCLUDES = -I${top_srcdir} @MU_COMMON_INCLUDES@ @GUILE_INCLUDES@
20 sbin_PROGRAMS=maidag 20 sbin_PROGRAMS=maidag
21 maidag_SOURCES=\ 21 maidag_SOURCES=\
22 deliver.c\ 22 deliver.c\
23 forward.c\
23 lmtp.c\ 24 lmtp.c\
24 maidag.c\ 25 maidag.c\
25 maidag.h\ 26 maidag.h\
......
...@@ -329,6 +329,22 @@ deliver_url (mu_url_t url, mu_message_t msg, const char *name, char **errp) ...@@ -329,6 +329,22 @@ deliver_url (mu_url_t url, mu_message_t msg, const char *name, char **errp)
329 mu_auth_data_free (auth); 329 mu_auth_data_free (auth);
330 return 0; 330 return 0;
331 } 331 }
332
333 if (forward_file)
334 switch (maidag_forward (msg, auth, forward_file))
335 {
336 case maidag_forward_none:
337 case maidag_forward_metoo:
338 break;
339
340 case maidag_forward_ok:
341 mu_auth_data_free (auth);
342 return 0;
343
344 case maidag_forward_error:
345 mu_auth_data_free (auth);
346 return exit_code = EX_TEMPFAIL;
347 }
332 } 348 }
333 349
334 if (!url) 350 if (!url)
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2002, 2005,
3 2007, 2008 Free Software Foundation, Inc.
4
5 GNU Mailutils is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3, or (at your option)
8 any later version.
9
10 GNU Mailutils is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNU Mailutils; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 MA 02110-1301 USA */
19
20 /* ".forward" support for GNU Maidag */
21
22 #include "maidag.h"
23
24 /* Functions for checking file mode of .forward and its directory.
25 Each of these checks certain bits and returns 0 if they are OK
26 and non-0 otherwise. */
27
28 static int
29 check_iwgrp (struct stat *filest, struct stat *dirst)
30 {
31 return filest->st_mode & S_IWGRP;
32 }
33
34 static int
35 check_iwoth (struct stat *filest, struct stat *dirst)
36 {
37 return filest->st_mode & S_IWOTH;
38 }
39
40 static int
41 check_linked_wrdir (struct stat *filest, struct stat *dirst)
42 {
43 return (filest->st_mode & S_IFLNK) && (dirst->st_mode & (S_IWGRP | S_IWOTH));
44 }
45
46 static int
47 check_dir_iwgrp (struct stat *filest, struct stat *dirst)
48 {
49 return dirst->st_mode & S_IWGRP;
50 }
51
52 static int
53 check_dir_iwoth (struct stat *filest, struct stat *dirst)
54 {
55 return dirst->st_mode & S_IWOTH;
56 }
57
58 /* The table of permission checkers below has this type: */
59 struct perm_checker
60 {
61 int flag; /* FWD_ flag that enables this entry */
62 char *descr; /* Textual description to use if FUN returns !0 */
63 int (*fun) (struct stat *filest, struct stat *dirst); /* Checker function */
64 };
65
66 static struct perm_checker perm_check_tab[] = {
67 { FWD_IWGRP, N_("group writable forward file"), check_iwgrp },
68 { FWD_IWOTH, N_("world writable forward file"), check_iwoth },
69 { FWD_LINK, N_("linked forward file in writable dir"), check_linked_wrdir },
70 { FWD_DIR_IWGRP, N_("forward file in group writable directory"),
71 check_dir_iwgrp },
72 { FWD_DIR_IWOTH, N_("forward file in world writable directory"),
73 check_dir_iwoth },
74 { 0 }
75 };
76
77 /* Check if the forwrd file FILENAME has right permissions and file mode.
78 DIRST describes the directory holding the file, AUTH gives current user
79 authority. */
80 int
81 check_forward_permissions (const char *filename, struct stat *dirst,
82 struct mu_auth_data *auth)
83 {
84 struct stat st;
85
86 if (stat (filename, &st) == 0)
87 {
88 int i;
89
90 if (auth->uid != st.st_uid)
91 {
92 mu_error (_("%s not owned by %s"), filename, auth->name);
93 return 1;
94 }
95 for (i = 0; perm_check_tab[i].flag; i++)
96 if ((forward_file_checks & perm_check_tab[i].flag)
97 && perm_check_tab[i].fun (&st, dirst))
98 {
99 mu_error ("%s: %s", filename, gettext (perm_check_tab[i].descr));
100 return 1;
101 }
102 return 0;
103 }
104 else if (errno != ENOENT)
105 mu_error (_("%s: cannot stat file: %s"),
106 filename, mu_strerror (errno));
107 return 1;
108 }
109
110
111 /* Auxiliary functions */
112
113 /* Forward message MSG to given EMAIL, using MAILER and sender address FROM */
114 static int
115 forward_to_email (mu_message_t msg, mu_address_t from,
116 mu_mailer_t mailer, const char *email)
117 {
118 mu_address_t to;
119 int rc;
120
121 rc = mu_address_create (&to, email);
122 if (rc)
123 {
124 mu_error (_("%s: cannot create email: %s"), email, mu_strerror (rc));
125 return 1;
126 }
127
128 rc = mu_mailer_send_message (mailer, msg, from, to);
129 if (rc)
130 mu_error (_("Sending message to `%s' failed: %s"),
131 email, mu_strerror (rc));
132 mu_address_destroy (&to);
133 return rc;
134 }
135
136 /* Create a mailer if it does not already exist.*/
137 int
138 forward_mailer_create (mu_mailer_t *pmailer)
139 {
140 int rc;
141
142 if (*pmailer == NULL)
143 {
144 rc = mu_mailer_create (pmailer, NULL);
145 if (rc)
146 {
147 const char *url = NULL;
148 mu_mailer_get_url_default (&url);
149 mu_error (_("Creating mailer `%s' failed: %s"),
150 url, mu_strerror (rc));
151 return 1;
152 }
153
154
155 rc = mu_mailer_open (*pmailer, 0);
156 if (rc)
157 {
158 const char *url = NULL;
159 mu_mailer_get_url_default (&url);
160 mu_error (_("Opening mailer `%s' failed: %s"),
161 url, mu_strerror (rc));
162 mu_mailer_destroy (pmailer);
163 return 1;
164 }
165 }
166 return 0;
167 }
168
169 /* Create *PFROM (if it is NULL), from the envelope sender address of MSG. */
170 static int
171 create_from_address (mu_message_t msg, mu_address_t *pfrom)
172 {
173 if (!*pfrom)
174 {
175 mu_envelope_t envelope;
176 const char *str;
177 int status = mu_message_get_envelope (msg, &envelope);
178 if (status)
179 {
180 mu_error (_("cannot get envelope: %s"), mu_strerror (status));
181 return 1;
182 }
183 status = mu_envelope_sget_sender (envelope, &str);
184 if (status)
185 {
186 mu_error (_("cannot get envelope sender: %s"), mu_strerror (status));
187 return 1;
188 }
189 status = mu_address_create (pfrom, str);
190 if (status)
191 {
192 mu_error (_("%s: cannot create email: %s"), str,
193 mu_strerror (status));
194 return 1;
195 }
196 }
197 return 0;
198 }
199
200
201 /* Forward message MSG as requested by file FILENAME.
202 MYNAME gives local user name. */
203 enum maidag_forward_result
204 process_forward (mu_message_t msg, char *filename, const char *myname)
205 {
206 FILE *fp;
207 size_t size = 0;
208 char *buf = NULL;
209 enum maidag_forward_result result = maidag_forward_ok;
210 mu_mailer_t mailer = NULL;
211 mu_address_t from = NULL;
212
213 fp = fopen (filename, "r");
214 if (!fp)
215 {
216 mu_error (_("%s: cannot open forward file: %s"),
217 filename, mu_strerror (errno));
218 return maidag_forward_error;
219 }
220
221 while (getline (&buf, &size, fp) > 0)
222 {
223 char *p, *q;
224 for (p = buf; *p && isascii (*p) && isspace (*p); p++)
225 ;
226 q = p + strlen (p);
227 if (q > p)
228 {
229 if (*--q == '\n')
230 q--;
231 for (; q > p && isascii (*q) && isspace (*q); q--)
232 ;
233 q[1] = 0;
234 }
235
236 if (*p && *p != '#')
237 {
238 if (strchr (p, '@'))
239 {
240 if (create_from_address (msg, &from)
241 || forward_mailer_create (&mailer)
242 || forward_to_email (msg, from, mailer, p))
243 result = maidag_forward_error;
244 }
245 else
246 {
247 if (*p == '\\')
248 p++;
249 if (strcmp (p, myname) == 0)
250 {
251 if (result == maidag_forward_ok)
252 result = maidag_forward_metoo;
253 }
254 else if (deliver (msg, p, NULL))
255 result = maidag_forward_error;
256 }
257 }
258 }
259
260 mu_address_destroy (&from);
261 if (mailer)
262 {
263 mu_mailer_close (mailer);
264 mu_mailer_destroy (&mailer);
265 }
266 free (buf);
267 fclose (fp);
268 return result;
269 }
270
271 /* Check if the forward file FWFILE for user given by AUTH exists, and if
272 so, use to to forward message MSG. */
273 enum maidag_forward_result
274 maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
275 {
276 struct stat st;
277 char *filename;
278 enum maidag_forward_result result = maidag_forward_none;
279
280 if (stat (auth->dir, &st))
281 {
282 if (errno == ENOENT)
283 /* FIXME: a warning, maybe? */;
284 else if (!S_ISDIR (st.st_mode))
285 mu_error (_("%s: not a directory"), auth->dir);
286 else
287 mu_error (_("%s: cannot stat directory: %s"),
288 auth->dir, mu_strerror (errno));
289 return maidag_forward_none;
290 }
291 asprintf (&filename, "%s/%s", auth->dir, fwfile);
292 if (!filename)
293 {
294 mu_error ("%s", mu_strerror (errno));
295 return maidag_forward_error;
296 }
297
298 if (check_forward_permissions (filename, &st, auth) == 0)
299 result = process_forward (msg, filename, auth->name);
300
301 free (filename);
302 return result;
303 }
304
...@@ -33,6 +33,9 @@ char *sender_address = NULL; ...@@ -33,6 +33,9 @@ char *sender_address = NULL;
33 char *progfile_pattern = NULL; 33 char *progfile_pattern = NULL;
34 char *sieve_pattern = NULL; 34 char *sieve_pattern = NULL;
35 35
36 char *forward_file = NULL;
37 int forward_file_checks = FWD_ALL;
38
36 int log_to_stderr = -1; 39 int log_to_stderr = -1;
37 40
38 /* Debuggig options */ 41 /* Debuggig options */
...@@ -277,6 +280,55 @@ cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg) ...@@ -277,6 +280,55 @@ cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg)
277 return mu_cfg_string_value_cb (debug, arg, cb2_group, data); 280 return mu_cfg_string_value_cb (debug, arg, cb2_group, data);
278 } 281 }
279 282
283 static struct mu_kwd forward_checks[] = {
284 { "all", FWD_ALL },
285 { "groupwritablefile", FWD_IWGRP },
286 { "file_iwgrp", FWD_IWGRP },
287 { "worldwritablefile", FWD_IWOTH },
288 { "file_iwoth", FWD_IWOTH },
289 { "linkedfileinwritabledir", FWD_LINK },
290 { "link", FWD_LINK },
291 { "fileingroupwritabledir", FWD_DIR_IWGRP },
292 { "dir_iwgrp", FWD_DIR_IWGRP },
293 { "fileinworldwritabledir", FWD_DIR_IWOTH },
294 { "dir_iwoth", FWD_DIR_IWOTH },
295 { NULL }
296 };
297
298 static int
299 cb2_forward_file_checks (mu_debug_t debug, const char *name, void *data)
300 {
301 int negate = 0;
302 const char *str;
303 int val;
304
305 if (strlen (name) > 2 && strncasecmp (name, "no", 2) == 0)
306 {
307 negate = 1;
308 str = name + 2;
309 }
310 else
311 str = name;
312
313 if (mu_kwd_xlat_name_ci (forward_checks, str, &val))
314 mu_cfg_format_error (debug, MU_DEBUG_ERROR, _("Unknown keyword: %s"),
315 name);
316 else
317 {
318 if (negate)
319 forward_file_checks &= ~val;
320 else
321 forward_file_checks |= val;
322 }
323 return 0;
324 }
325
326 static int
327 cb_forward_file_checks (mu_debug_t debug, void *data, mu_config_value_t *arg)
328 {
329 return mu_cfg_string_value_cb (debug, arg, cb2_forward_file_checks, data);
330 }
331
280 struct mu_cfg_param maidag_cfg_param[] = { 332 struct mu_cfg_param maidag_cfg_param[] = {
281 { "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL, 333 { "exit-multiple-delivery-success", mu_cfg_bool, &multiple_delivery, 0, NULL,
282 N_("In case of multiple delivery, exit with code 0 if at least one " 334 N_("In case of multiple delivery, exit with code 0 if at least one "
...@@ -316,6 +368,11 @@ struct mu_cfg_param maidag_cfg_param[] = { ...@@ -316,6 +368,11 @@ struct mu_cfg_param maidag_cfg_param[] = {
316 " l - sieve action logs\n") }, 368 " l - sieve action logs\n") },
317 { "stderr", mu_cfg_bool, &log_to_stderr, 0, NULL, 369 { "stderr", mu_cfg_bool, &log_to_stderr, 0, NULL,
318 N_("Log to stderr instead of syslog.") }, 370 N_("Log to stderr instead of syslog.") },
371 { "forward-file", mu_cfg_string, &forward_file, 0, NULL,
372 N_("Process forward file.") },
373 { "forward-file-checks", mu_cfg_callback, NULL, 0, cb_forward_file_checks,
374 N_("Configure safety checks for the forward file."),
375 N_("arg: list") },
319 /* LMTP support */ 376 /* LMTP support */
320 { "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL, 377 { "lmtp", mu_cfg_bool, &lmtp_mode, 0, NULL,
321 N_("Run in LMTP mode.") }, 378 N_("Run in LMTP mode.") },
......
...@@ -109,6 +109,14 @@ extern int debug_level; ...@@ -109,6 +109,14 @@ extern int debug_level;
109 #define MAXFD 64 109 #define MAXFD 64
110 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE) 110 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE)
111 111
112 /* .forward file checks */
113 #define FWD_IWGRP 0x0001 /* group writable forward file */
114 #define FWD_IWOTH 0x0002 /* world writable forward file */
115 #define FWD_LINK 0x0004 /* linked forward file in writable dir */
116 #define FWD_DIR_IWGRP 0x0008 /* forward file in group writable directory */
117 #define FWD_DIR_IWOTH 0x0010 /* forward file in world writable directory */
118 #define FWD_ALL (FWD_IWGRP|FWD_IWOTH|FWD_LINK|FWD_DIR_IWOTH|FWD_DIR_IWGRP)
119
112 extern int exit_code; 120 extern int exit_code;
113 extern int log_to_stderr; 121 extern int log_to_stderr;
114 extern int multiple_delivery; 122 extern int multiple_delivery;
...@@ -117,6 +125,9 @@ extern uid_t current_uid; ...@@ -117,6 +125,9 @@ extern uid_t current_uid;
117 extern char *quotadbname; 125 extern char *quotadbname;
118 extern char *quota_query; 126 extern char *quota_query;
119 127
128 extern char *forward_file;
129 extern int forward_file_checks;
130
120 extern char *sender_address; 131 extern char *sender_address;
121 extern char *progfile_pattern; 132 extern char *progfile_pattern;
122 extern char *sieve_pattern; 133 extern char *sieve_pattern;
...@@ -165,3 +176,15 @@ int mail_tmp_begin (struct mail_tmp **pmtmp, const char *from); ...@@ -165,3 +176,15 @@ int mail_tmp_begin (struct mail_tmp **pmtmp, const char *from);
165 int mail_tmp_add_line (struct mail_tmp *mtmp, char *buf, size_t buflen); 176 int mail_tmp_add_line (struct mail_tmp *mtmp, char *buf, size_t buflen);
166 int mail_tmp_finish (struct mail_tmp *mtmp, mu_mailbox_t *mbox); 177 int mail_tmp_finish (struct mail_tmp *mtmp, mu_mailbox_t *mbox);
167 void mail_tmp_destroy (struct mail_tmp **pmtmp); 178 void mail_tmp_destroy (struct mail_tmp **pmtmp);
179
180 enum maidag_forward_result
181 {
182 maidag_forward_none,
183 maidag_forward_ok,
184 maidag_forward_metoo,
185 maidag_forward_error
186 };
187
188 enum maidag_forward_result maidag_forward (mu_message_t msg,
189 struct mu_auth_data *auth,
190 char *fwfile);
......