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