1 /* This file is part of Mailfromd.
2 Copyright (C) 2005-2021 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include "mailfromd.h"
23 #include "prog.h"
24
25 extern int yy_flex_debug;
26
27 static mu_list_t include_path;
28 static mu_list_t std_include_path;
29
30 struct file_data {
31 const char *name;
32 size_t namelen;
33 char *buf;
34 size_t buflen;
35 int found;
36 };
37
38 static int
find_include_file(void * item,void * data)39 find_include_file(void *item, void *data)
40 {
41 char *dir = item;
42 struct file_data *dptr = data;
43 size_t size = strlen(dir) + 1 + dptr->namelen + 1;
44 if (size > dptr->buflen) {
45 dptr->buflen = size;
46 dptr->buf = mu_realloc(dptr->buf, dptr->buflen);
47 }
48 strcpy(dptr->buf, dir);
49 strcat(dptr->buf, "/");
50 strcat(dptr->buf, dptr->name);
51 return dptr->found = access(dptr->buf, F_OK) == 0;
52 }
53
54 void
include_path_setup()55 include_path_setup()
56 {
57 mu_list_create(&include_path);
58 mu_list_create(&std_include_path);
59 mu_list_append(std_include_path, DEFAULT_VERSION_INCLUDE_DIR);
60 mu_list_append(std_include_path, DEFAULT_INCLUDE_DIR);
61 mu_list_append(std_include_path, "/usr/share/mailfromd/include");
62 mu_list_append(std_include_path, "/usr/local/share/mailfromd/include");
63 }
64
65 void
add_include_dir(const char * dir)66 add_include_dir(const char *dir)
67 {
68 int rc;
69
70 if (!include_path) {
71 rc = mu_list_create(&include_path);
72 if (rc) {
73 mu_error(_("cannot create include path list: %s"),
74 mu_strerror(rc));
75 return;
76 }
77 }
78 rc = mu_list_append(include_path, mu_strdup(dir));
79 if (rc)
80 mu_error(_("cannot append to the include path list: %s"),
81 mu_strerror(rc));
82 }
83
84
85 #define ID_EQ(a,b) \
86 ((a).i_node == (b).i_node && (a).device == (a).device)
87
88 static int
input_file_ident_cmp(const void * item,const void * data)89 input_file_ident_cmp(const void *item, const void *data)
90 {
91 const struct input_file_ident *id1 = item;
92 const struct input_file_ident *id2 = data;
93 return !ID_EQ(*id1, *id2);
94 }
95
96 static void
input_file_ident_free(void * item)97 input_file_ident_free(void *item)
98 {
99 free(item);
100 }
101
102 int
source_lookup(struct input_file_ident * idptr)103 source_lookup(struct input_file_ident *idptr)
104 {
105 int rc;
106 if (!top_module->incl_sources) {
107 mu_list_create(&top_module->incl_sources);
108 mu_list_set_comparator(top_module->incl_sources,
109 input_file_ident_cmp);
110 mu_list_set_destroy_item(top_module->incl_sources,
111 input_file_ident_free);
112 }
113 rc = mu_list_locate (top_module->incl_sources, idptr, NULL);
114 if (rc == MU_ERR_NOENT) {
115 struct input_file_ident *new_id = mu_alloc(sizeof *new_id);
116 *new_id = *idptr;
117 mu_list_append(top_module->incl_sources, new_id);
118 }
119 return rc == 0;
120 }
121
122 static int
try_file(const char * name,int allow_cwd,int err_not_found,char ** newp)123 try_file(const char *name, int allow_cwd, int err_not_found, char **newp)
124 {
125 static char *cwd = ".";
126 struct file_data fd;
127
128 fd.name = name;
129 fd.namelen = strlen(name);
130 fd.buf = NULL;
131 fd.buflen = 0;
132 fd.found = 0;
133
134 if (allow_cwd) {
135 mu_list_prepend(include_path, cwd);
136 mu_list_foreach(include_path, find_include_file, &fd);
137 mu_list_remove(include_path, cwd);
138 } else
139 mu_list_foreach(include_path, find_include_file, &fd);
140
141 if (!fd.found) {
142 mu_list_foreach(std_include_path, find_include_file, &fd);
143
144 if (!fd.found && err_not_found) {
145 parse_error(_("%s: no such file or directory"), name);
146 *newp = NULL;
147 }
148 }
149 if (fd.found)
150 *newp = fd.buf;
151 return fd.found;
152 }
153
154 int
begin_module(const char * modname,const char * filename,struct import_rule * import_rules)155 begin_module(const char *modname, const char *filename,
156 struct import_rule *import_rules)
157 {
158 int rc;
159
160 if (access(filename, R_OK)) {
161 parse_error(_("input file %s is not readable: %s"),
162 filename,
163 mu_strerror(errno));
164 return 1;
165 }
166 if (set_top_module(modname, filename, import_rules, get_locus()) == 0)
167 rc = lex_new_source(filename, LEX_MODULE);
168 else
169 rc = 0;
170 return rc;
171 }
172
173 int
parse_include(const char * text,int once)174 parse_include(const char *text, int once)
175 {
176 struct mu_wordsplit ws;
177 char *tmp = NULL;
178 char *p = NULL;
179 int rc = 1;
180
181 if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) {
182 parse_error(_("cannot parse include line: %s"),
183 mu_wordsplit_strerror (&ws));
184 return errno;
185 } else if (ws.ws_wordc != 2)
186 mu_wordsplit_free (&ws);
187 else {
188 size_t len;
189 int allow_cwd;
190
191 p = ws.ws_wordv[1];
192 len = strlen(p);
193
194 if (p[0] == '<' && p[len - 1] == '>') {
195 allow_cwd = 0;
196 p[len - 1] = 0;
197 p++;
198 } else
199 allow_cwd = 1;
200
201 if (p[0] != '/' && try_file(p, allow_cwd, 1, &tmp))
202 p = tmp;
203 }
204
205 if (p)
206 rc = lex_new_source(p, once ? LEX_ONCE : LEX_NONE);
207 free(tmp);
208 mu_wordsplit_free (&ws);
209 return rc;
210 }
211
212 #define DEFAULT_SUFFIX ".mf"
213
214 void
require_module(const char * modname,struct import_rule * import_rules)215 require_module(const char *modname, struct import_rule *import_rules)
216 {
217 size_t len;
218 char *fname, *pathname;
219
220 len = strlen(modname);
221
222 fname = mu_alloc(len + sizeof DEFAULT_SUFFIX);
223 strcpy(fname, modname);
224 strcat(fname, DEFAULT_SUFFIX);
225
226 if (try_file(fname, 0, 1, &pathname))
227 begin_module(modname, pathname, import_rules);
228 free(fname);
229 }
230
231 static int
assign_locus(struct mu_locus_point * ploc,const char * name,const char * linestr)232 assign_locus(struct mu_locus_point *ploc, const char *name,
233 const char *linestr)
234 {
235 char *p;
236 unsigned long linenum;
237 errno = 0;
238 linenum = strtoul(linestr, &p, 10);
239 if (errno)
240 return errno;
241 if (*p)
242 return MU_ERR_PARSE;
243 if (linenum > UINT_MAX)
244 return ERANGE;
245 mu_locus_point_init(ploc);
246 mu_locus_point_set_file(ploc, name);
247 ploc->mu_line = (unsigned) linenum;
248 return 0;
249 }
250
251 int
parse_line(char * text,struct mu_locus_point * ploc)252 parse_line(char *text, struct mu_locus_point *ploc)
253 {
254 int rc;
255 struct mu_wordsplit ws;
256
257 while (*text && mu_isspace (*text))
258 text++;
259 text++;
260
261 if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) {
262 parse_error(_("cannot parse #line line: %s"),
263 mu_wordsplit_strerror (&ws));
264 return 1;
265 } else {
266 if (ws.ws_wordc == 2)
267 rc = assign_locus(ploc, NULL, ws.ws_wordv[1]);
268 else if (ws.ws_wordc == 3)
269 rc = assign_locus(ploc, ws.ws_wordv[2],
270 ws.ws_wordv[1]);
271 else
272 rc = 1;
273
274 if (rc)
275 parse_error(_("malformed #line statement"));
276 }
277 mu_wordsplit_free(&ws);
278 return rc;
279 }
280
281 int
parse_line_cpp(char * text,struct mu_locus_point * ploc)282 parse_line_cpp(char *text, struct mu_locus_point *ploc)
283 {
284 struct mu_wordsplit ws;
285 int rc;
286
287 if (mu_wordsplit(text, &ws, MU_WRDSF_DEFFLAGS)) {
288 parse_error(_("cannot parse #line line: %s"),
289 mu_wordsplit_strerror (&ws));
290 rc = 1;
291 } else if (ws.ws_wordc < 3) {
292 parse_error(_("invalid #line statement"));
293 rc = 1;
294 } else if (assign_locus(ploc, ws.ws_wordv[2], ws.ws_wordv[1])) {
295 parse_error(_("malformed #line statement"));
296 rc = 1;
297 } else
298 rc = 0;
299 mu_wordsplit_free(&ws);
300 return rc;
301 }
302
303 static void
stderr_redirector(char * ppcmd,char ** argv)304 stderr_redirector(char *ppcmd, char **argv)
305 {
306 if (!logger_flags(LOGF_STDERR)) {
307 int p[2];
308 char buf[1024];
309 FILE *fp;
310 pid_t pid;
311
312 signal(SIGCHLD, SIG_DFL);
313 if (pipe(p)) {
314 mu_diag_funcall(MU_DIAG_ERROR, "pipe", "p", errno);
315 exit(127);
316 }
317
318 switch (pid = fork()) {
319 /* Grandchild */
320 case 0:
321 if (p[1] != 2) {
322 close(2);
323 dup2(p[1], 2);
324 }
325 close(p[0]);
326
327 execvp(argv[0], argv);
328 exit(127);
329
330 case -1:
331 /* Fork failed */
332 mf_server_log_setup();
333 mu_error("Cannot run `%s': %s",
334 ppcmd, mu_strerror(errno));
335 exit(127);
336
337 default:
338 /* Sub-master */
339 close(p[1]);
340 fp = fdopen(p[0], "r");
341 mf_server_log_setup();
342 while (fgets(buf, sizeof(buf), fp))
343 mu_error("%s", buf);
344 exit(0);
345 }
346 } else {
347 execvp(argv[0], argv);
348 mf_server_log_setup();
349 mu_error("Cannot run `%s': %s",
350 ppcmd, mu_strerror(errno));
351 exit(127);
352 }
353 }
354
355 FILE *
pp_extrn_start(const char * name,pid_t * ppid)356 pp_extrn_start(const char *name, pid_t *ppid)
357 {
358 int pout[2];
359 pid_t pid;
360 size_t size;
361 char *ppcmd;
362 struct mu_wordsplit ws;
363 FILE *fp = NULL;
364
365 size = strlen(ext_pp) + strlen(name) + 2;
366 ppcmd = mu_alloc(size);
367 strcpy(ppcmd, ext_pp);
368 strcat(ppcmd, " ");
369 strcat(ppcmd, name);
370
371 mu_debug(MF_SOURCE_PP, MU_DEBUG_TRACE1,
372 ("Running preprocessor: `%s'", ppcmd));
373
374 if (mu_wordsplit(ppcmd, &ws, MU_WRDSF_DEFFLAGS)) {
375 parse_error(_("cannot parse line: %s"),
376 mu_wordsplit_strerror (&ws));
377 exit(EX_UNAVAILABLE);
378 }
379
380 if (pipe(pout)) {
381 mu_diag_funcall(MU_DIAG_ERROR, "pipe", "pout", errno);
382 exit(EX_UNAVAILABLE);
383 }
384 switch (pid = fork()) {
385 /* The child branch. */
386 case 0:
387 if (pout[1] != 1) {
388 close(1);
389 dup2(pout[1], 1);
390 }
391
392 /* Close unneded descripitors */
393 close_fds_above(1);
394 stderr_redirector(ppcmd, ws.ws_wordv);
395 break;
396
397 case -1:
398 /* Fork failed */
399 mu_error("Cannot run `%s': %s",
400 ppcmd, mu_strerror(errno));
401 break;
402
403 default:
404 close(pout[1]);
405 fp = fdopen(pout[0], "r");
406 break;
407 }
408 mu_wordsplit_free(&ws);
409 free(ppcmd);
410 *ppid = pid;
411 return fp;
412 }
413
414 void
pp_extrn_shutdown(FILE * file,pid_t pid)415 pp_extrn_shutdown(FILE *file, pid_t pid)
416 {
417 int status;
418 fclose(file);
419 waitpid(pid, &status, 0);
420 if (WIFEXITED(status)) {
421 status = WEXITSTATUS(status);
422 if (status) {
423 mu_error(_("preprocessor exited with status %d"),
424 status);
425 }
426 } else if (WIFSIGNALED(status)) {
427 mu_error(_("preprocessor terminated on signal %d"),
428 WTERMSIG(status));
429 } else if (WIFSTOPPED(status)) {
430 mu_error("%s", _("preprocessor stopped"));
431 } else {
432 mu_error("%s", _("preprocessor terminated abnormally"));
433 }
434 }
435
436 static int
_count_include_size(void * item,void * data)437 _count_include_size(void *item, void *data)
438 {
439 size_t *psize = data;
440 *psize += 3 + strlen((char*)item);
441 return 0;
442 }
443
444 static int
_append_includes(void * item,void * data)445 _append_includes(void *item, void *data)
446 {
447 char *str = data;
448 strcat(str, " -I");
449 strcat(str, (char*) item);
450 return 0;
451 }
452
453 static char *setup_file;
454
455 void
alloc_ext_pp()456 alloc_ext_pp()
457 {
458 size_t size;
459
460 if (ext_pp_options_given && !ext_pp) {
461 if (!DEF_EXT_PP) {
462 mu_error(_("default preprocessor is not set; "
463 "use --preprocessor option"));
464 exit(EX_USAGE);
465 }
466 ext_pp = DEF_EXT_PP;
467 }
468
469 if (!ext_pp)
470 return;
471 ext_pp = mu_strdup(ext_pp);
472
473 if (ext_pp_options) {
474 size_t len;
475 char *p;
476
477 len = strlen(ext_pp) + strlen(ext_pp_options);
478 p = mu_alloc(len + 1);
479 strcpy(p, ext_pp);
480 strcat(p, ext_pp_options);
481 ext_pp = p;
482 }
483
484 size = 0;
485 mu_list_foreach(include_path, _count_include_size, &size);
486 if (size) {
487 ext_pp = mu_realloc(ext_pp, strlen(ext_pp) + size + 1);
488 mu_list_foreach(include_path, _append_includes, ext_pp);
489 }
490
491 if (try_file("pp-setup", 1, 0, &setup_file)) {
492 ext_pp = mu_realloc(ext_pp, strlen(ext_pp) +
493 strlen(setup_file) + 2);
494 strcat(ext_pp, " ");
495 strcat(ext_pp, setup_file);
496 }
497 }
498
499 int
preprocess_input()500 preprocess_input()
501 {
502 char buffer[512];
503 FILE *file;
504 pid_t pid;
505 ssize_t n;
506
507 file = pp_extrn_start(script_file, &pid);
508 if (!file)
509 return EX_NOINPUT;
510 while ((n = fread(buffer, 1, sizeof buffer, file)) > 0)
511 fwrite(buffer, 1, n, stdout);
512 pp_extrn_shutdown(file, pid);
513 return 0;
514 }
515
516