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.
Showing
6 changed files
with
417 additions
and
0 deletions
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) | ... | ... |
maidag/forward.c
0 → 100644
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); | ... | ... |
-
Please register or sign in to post a comment