Commit db1ee5e0 db1ee5e0af1f25405ffb35876149eb25cf7763f2 by Sergey Poznyakoff

maidag: improve forwarding capability, add a testcase for it.

* maidag/forward.c (check_forward_permissions): Keep track of the
processed files.  Refuse to process a file twice.
(process_forward): Rewrite using streams instead of FILE.
(maidag_forward): Accept absolute file names (mainly for testing).
* maidag/maidag.c (forward_checks): New keyword "owner".
(cb2_forward_file_checks): New keyword "none".
(main): Require at least one recipient in MDA mode.
* maidag/maidag.h (FWD_OWNER): New mask.
* maidag/tests/forward.at: New file.
* maidag/tests/Makefile.am (TESTSUITE_AT): Add forward.at
* maidag/tests/testsuite.at: Include forward.at.
1 parent 0bf8123e
...@@ -74,6 +74,52 @@ static struct perm_checker perm_check_tab[] = { ...@@ -74,6 +74,52 @@ static struct perm_checker perm_check_tab[] = {
74 { 0 } 74 { 0 }
75 }; 75 };
76 76
77 static mu_list_t idlist;
78
79 struct file_id
80 {
81 dev_t dev;
82 ino_t inode;
83 };
84
85 static int
86 file_id_cmp (const void *item, const void *data)
87 {
88 const struct file_id *a = item;
89 const struct file_id *b = data;
90
91 if (a->dev != b->dev)
92 return 1;
93 if (a->inode != b->inode)
94 return 1;
95 return 0;
96 }
97
98 static int
99 file_id_lookup (dev_t dev, ino_t ino)
100 {
101 struct file_id id;
102
103 id.dev = dev;
104 id.inode = ino;
105 return mu_list_locate (idlist, &id, NULL);
106 }
107
108 static int
109 file_id_remember (dev_t dev, ino_t ino)
110 {
111 struct file_id *id = malloc (sizeof (*id));
112 if (!id)
113 {
114 mu_error ("%s", mu_strerror (errno));
115 return 1;
116 }
117 id->dev = dev;
118 id->inode = ino;
119 return mu_list_append (idlist, id);
120 }
121
122
77 /* Check if the forwrd file FILENAME has right permissions and file mode. 123 /* Check if the forwrd file FILENAME has right permissions and file mode.
78 DIRST describes the directory holding the file, AUTH gives current user 124 DIRST describes the directory holding the file, AUTH gives current user
79 authority. */ 125 authority. */
...@@ -87,7 +133,21 @@ check_forward_permissions (const char *filename, struct stat *dirst, ...@@ -87,7 +133,21 @@ check_forward_permissions (const char *filename, struct stat *dirst,
87 { 133 {
88 int i; 134 int i;
89 135
90 if (auth->uid != st.st_uid) 136 if (!idlist)
137 {
138 mu_list_create (&idlist);
139 mu_list_set_comparator (idlist, file_id_cmp);
140 }
141 else if (file_id_lookup (st.st_dev, st.st_ino) == 0)
142 {
143 mu_diag_output (MU_DIAG_NOTICE,
144 _("skipping forward file %s: already processed"),
145 filename);
146 return 1;
147 }
148
149 if ((forward_file_checks & FWD_OWNER) &&
150 auth->uid != st.st_uid)
91 { 151 {
92 mu_error (_("%s not owned by %s"), filename, auth->name); 152 mu_error (_("%s not owned by %s"), filename, auth->name);
93 return 1; 153 return 1;
...@@ -99,6 +159,7 @@ check_forward_permissions (const char *filename, struct stat *dirst, ...@@ -99,6 +159,7 @@ check_forward_permissions (const char *filename, struct stat *dirst,
99 mu_error ("%s: %s", filename, gettext (perm_check_tab[i].descr)); 159 mu_error ("%s: %s", filename, gettext (perm_check_tab[i].descr));
100 return 1; 160 return 1;
101 } 161 }
162 file_id_remember (st.st_dev, st.st_ino);
102 return 0; 163 return 0;
103 } 164 }
104 else if (errno != ENOENT) 165 else if (errno != ENOENT)
...@@ -203,22 +264,23 @@ create_from_address (mu_message_t msg, mu_address_t *pfrom) ...@@ -203,22 +264,23 @@ create_from_address (mu_message_t msg, mu_address_t *pfrom)
203 enum maidag_forward_result 264 enum maidag_forward_result
204 process_forward (mu_message_t msg, char *filename, const char *myname) 265 process_forward (mu_message_t msg, char *filename, const char *myname)
205 { 266 {
206 FILE *fp; 267 int rc;
207 size_t size = 0; 268 mu_stream_t file;
269 size_t size = 0, n;
208 char *buf = NULL; 270 char *buf = NULL;
209 enum maidag_forward_result result = maidag_forward_ok; 271 enum maidag_forward_result result = maidag_forward_ok;
210 mu_mailer_t mailer = NULL; 272 mu_mailer_t mailer = NULL;
211 mu_address_t from = NULL; 273 mu_address_t from = NULL;
212 274
213 fp = fopen (filename, "r"); 275 rc = mu_file_stream_create (&file, filename, MU_STREAM_READ);
214 if (!fp) 276 if (rc)
215 { 277 {
216 mu_error (_("%s: cannot open forward file: %s"), 278 mu_error (_("%s: cannot open forward file: %s"),
217 filename, mu_strerror (errno)); 279 filename, mu_strerror (rc));
218 return maidag_forward_error; 280 return maidag_forward_error;
219 } 281 }
220 282
221 while (getline (&buf, &size, fp) > 0) 283 while (mu_stream_getline (file, &buf, &size, &n) == 0 && n > 0)
222 { 284 {
223 char *p; 285 char *p;
224 286
...@@ -256,7 +318,7 @@ process_forward (mu_message_t msg, char *filename, const char *myname) ...@@ -256,7 +318,7 @@ process_forward (mu_message_t msg, char *filename, const char *myname)
256 mu_mailer_destroy (&mailer); 318 mu_mailer_destroy (&mailer);
257 } 319 }
258 free (buf); 320 free (buf);
259 fclose (fp); 321 mu_stream_destroy (&file);
260 return result; 322 return result;
261 } 323 }
262 324
...@@ -269,6 +331,8 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile) ...@@ -269,6 +331,8 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
269 char *filename; 331 char *filename;
270 enum maidag_forward_result result = maidag_forward_none; 332 enum maidag_forward_result result = maidag_forward_none;
271 333
334 if (fwfile[0] != '/')
335 {
272 if (stat (auth->dir, &st)) 336 if (stat (auth->dir, &st))
273 { 337 {
274 if (errno == ENOENT) 338 if (errno == ENOENT)
...@@ -281,6 +345,10 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile) ...@@ -281,6 +345,10 @@ maidag_forward (mu_message_t msg, struct mu_auth_data *auth, char *fwfile)
281 return maidag_forward_none; 345 return maidag_forward_none;
282 } 346 }
283 asprintf (&filename, "%s/%s", auth->dir, fwfile); 347 asprintf (&filename, "%s/%s", auth->dir, fwfile);
348 }
349 else
350 filename = strdup (fwfile);
351
284 if (!filename) 352 if (!filename)
285 { 353 {
286 mu_error ("%s", mu_strerror (errno)); 354 mu_error ("%s", mu_strerror (errno));
......
...@@ -321,6 +321,7 @@ cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg) ...@@ -321,6 +321,7 @@ cb_group (mu_debug_t debug, void *data, mu_config_value_t *arg)
321 321
322 static struct mu_kwd forward_checks[] = { 322 static struct mu_kwd forward_checks[] = {
323 { "all", FWD_ALL }, 323 { "all", FWD_ALL },
324 { "owner", FWD_OWNER },
324 { "groupwritablefile", FWD_IWGRP }, 325 { "groupwritablefile", FWD_IWGRP },
325 { "file_iwgrp", FWD_IWGRP }, 326 { "file_iwgrp", FWD_IWGRP },
326 { "worldwritablefile", FWD_IWOTH }, 327 { "worldwritablefile", FWD_IWOTH },
...@@ -341,6 +342,12 @@ cb2_forward_file_checks (mu_debug_t debug, const char *name, void *data) ...@@ -341,6 +342,12 @@ cb2_forward_file_checks (mu_debug_t debug, const char *name, void *data)
341 const char *str; 342 const char *str;
342 int val; 343 int val;
343 344
345 if (strcmp (name, "none") == 0)
346 {
347 forward_file_checks = 0;
348 return 0;
349 }
350
344 if (strlen (name) > 2 && mu_c_strncasecmp (name, "no", 2) == 0) 351 if (strlen (name) > 2 && mu_c_strncasecmp (name, "no", 2) == 0)
345 { 352 {
346 negate = 1; 353 negate = 1;
...@@ -609,6 +616,11 @@ main (int argc, char *argv[]) ...@@ -609,6 +616,11 @@ main (int argc, char *argv[])
609 break; 616 break;
610 617
611 case mode_mda: 618 case mode_mda:
619 if (argc == 0)
620 {
621 mu_error (_("recipients not given"));
622 return EX_USAGE;
623 }
612 delivery_fun = deliver_to_user; 624 delivery_fun = deliver_to_user;
613 break; 625 break;
614 } 626 }
......
...@@ -111,12 +111,13 @@ extern int debug_level; ...@@ -111,12 +111,13 @@ extern int debug_level;
111 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE) 111 #define EX_QUOTA() (ex_quota_tempfail ? EX_TEMPFAIL : EX_UNAVAILABLE)
112 112
113 /* .forward file checks */ 113 /* .forward file checks */
114 #define FWD_IWGRP 0x0001 /* group writable forward file */ 114 #define FWD_OWNER 0x0001 /* file ownership */
115 #define FWD_IWOTH 0x0002 /* world writable forward file */ 115 #define FWD_IWGRP 0x0002 /* group writable forward file */
116 #define FWD_LINK 0x0004 /* linked forward file in writable dir */ 116 #define FWD_IWOTH 0x0004 /* world writable forward file */
117 #define FWD_DIR_IWGRP 0x0008 /* forward file in group writable directory */ 117 #define FWD_LINK 0x0008 /* linked forward file in writable dir */
118 #define FWD_DIR_IWOTH 0x0010 /* forward file in world writable directory */ 118 #define FWD_DIR_IWGRP 0x0010 /* forward file in group writable directory */
119 #define FWD_ALL (FWD_IWGRP|FWD_IWOTH|FWD_LINK|FWD_DIR_IWOTH|FWD_DIR_IWGRP) 119 #define FWD_DIR_IWOTH 0x0020 /* forward file in world writable directory */
120 #define FWD_ALL (FWD_OWNER|FWD_IWGRP|FWD_IWOTH|FWD_LINK|FWD_DIR_IWOTH|FWD_DIR_IWGRP)
120 121
121 enum maidag_mode 122 enum maidag_mode
122 { 123 {
......
...@@ -39,6 +39,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac ...@@ -39,6 +39,7 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
39 ## ------------ ## 39 ## ------------ ##
40 40
41 TESTSUITE_AT = \ 41 TESTSUITE_AT = \
42 forward.at\
42 lmtp.at\ 43 lmtp.at\
43 mda.at\ 44 mda.at\
44 testsuite.at\ 45 testsuite.at\
......
...@@ -29,4 +29,5 @@ MUT_VERSION(maidag) ...@@ -29,4 +29,5 @@ MUT_VERSION(maidag)
29 m4_include([mda.at]) 29 m4_include([mda.at])
30 m4_include([lmtp.at]) 30 m4_include([lmtp.at])
31 m4_include([url-mbox.at]) 31 m4_include([url-mbox.at])
32 m4_include([forward.at])
32 33
......
...@@ -95,7 +95,6 @@ if ![mu_check_capability ENABLE_SENDMAIL] { ...@@ -95,7 +95,6 @@ if ![mu_check_capability ENABLE_SENDMAIL] {
95 " 48: > All mimsy were the borogoves,"\ 95 " 48: > All mimsy were the borogoves,"\
96 " 49: > And the mome raths outgrabe."\ 96 " 49: > And the mome raths outgrabe."\
97 " 50: > "\ 97 " 50: > "\
98 " 51: "\
99 "END OF MESSAGE" 98 "END OF MESSAGE"
100 } 99 }
101 100
......