1 /**
2  * This file is part of the Detox package.
3  *
4  * Copyright (c) Doug Harple <detox.dharple@gmail.com>
5  *
6  * For the full copyright and license information, please view the LICENSE
7  * file that was distributed with this source code.
8  */
9 
10 #include "config.h"
11 
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 
20 #include "detox.h"
21 #include "clean_string.h"
22 
23 #include "file.h"
24 
25 #include "config_file.h"
26 #include "config_file_spoof.h"
27 #include "config_file_dump.h"
28 #include "parse_table.h"
29 #include "parse_options.h"
30 
31 #define MAX_PATH_LEN 256
32 
main(int argc,char ** argv)33 int main(int argc, char **argv)
34 {
35 	struct stat stat_info;
36 	int err;
37 
38 	struct detox_parse_results *parse_results = NULL;
39 	struct detox_sequence_list *list_work = NULL;
40 	struct detox_sequence_entry *which_sequence = NULL;
41 	struct detox_sequence_entry *work = NULL;
42 	struct detox_options *main_options;
43 
44 	char *check_config_file = NULL;
45 	char *file_work = NULL;
46 	char **file_walk;
47 
48 	main_options = parse_options_getopt(argc, argv);
49 
50 	if (main_options == NULL) {
51 		fprintf(stderr, "detox: an error occurred while parsing command line arguments\n");
52 		exit(EXIT_FAILURE);
53 	}
54 
55 	if (main_options->check_config_file) {
56 		check_config_file = strdup(main_options->check_config_file);
57 	}
58 
59 	if (check_config_file != NULL) {
60 		parse_results = parse_config_file(check_config_file, NULL, main_options);
61 		if (parse_results == NULL) {
62 			fprintf(stderr, "detox: unable to open: %s\n", check_config_file);
63 			exit(EXIT_FAILURE);
64 		}
65 	}
66 	else {
67 		check_config_file = malloc(MAX_PATH_LEN);
68 		if (check_config_file == NULL) {
69 			fprintf(stderr, "out of memory: %s\n", strerror(errno));
70 			exit(EXIT_FAILURE);
71 		}
72 
73 #ifdef SYSCONFDIR
74 		err = snprintf(check_config_file, MAX_PATH_LEN, "%s/detoxrc", SYSCONFDIR);
75 		if (err < MAX_PATH_LEN)
76 			parse_results = parse_config_file(check_config_file, NULL, main_options);
77 #endif
78 
79 		if (parse_results == NULL) {
80 			parse_results = parse_config_file("/etc/detoxrc", NULL, main_options);
81 		}
82 
83 		if (parse_results == NULL) {
84 			parse_results = parse_config_file("/usr/local/etc/detoxrc", NULL, main_options);
85 		}
86 
87 		file_work = getenv("HOME");
88 		if (file_work != NULL) {
89 			err = snprintf(check_config_file, MAX_PATH_LEN, "%s/.detoxrc", file_work);
90 			if (err < MAX_PATH_LEN)
91 				parse_results = parse_config_file(check_config_file, parse_results, main_options);
92 
93 			file_work = NULL;
94 		}
95 
96 		if (parse_results == NULL) {
97 			parse_results = spoof_config_file(main_options);
98 		}
99 
100 		free(check_config_file);
101 	}
102 
103 	if (parse_results == NULL) {
104 		fprintf(stderr, "detox: no config file to work with\n");
105 		exit(EXIT_FAILURE);
106 	}
107 
108 	/*
109 	 * Store the files_to_ignore array in the main_options struct for use in
110 	 * parse_dir/file/special
111 	 */
112 
113 	main_options->files_to_ignore = parse_results->files_to_ignore;
114 
115 	/*
116 	 * Determine which sequence to use
117 	 */
118 
119 	which_sequence = NULL;
120 
121 	list_work = parse_results->sequences;
122 
123 	while (list_work != NULL) {
124 		if (strcmp(list_work->name, (main_options->sequence_name == NULL) ? "default" : main_options->sequence_name) == 0) {
125 			which_sequence = list_work->head;
126 			break;
127 		}
128 
129 		list_work = list_work->next;
130 	}
131 
132 	/*
133 	 * If no sequence was found, and the user didn't specify a sequence
134 	 * to use, just use the first sequence.
135 	 */
136 
137 	if (which_sequence == NULL && main_options->sequence_name == NULL) {
138 		if (parse_results->sequences != NULL) {
139 			which_sequence = parse_results->sequences->head;
140 		}
141 	}
142 
143 	main_options->sequence_to_use = which_sequence;
144 
145 	/*
146 	 * List sequences
147 	 */
148 	if (main_options->list_sequences) {
149 		dump_config_file(parse_results, main_options);
150 		exit(EXIT_SUCCESS);
151 	}
152 
153 	/*
154 	 * Fail if no sequence is available
155 	 */
156 	if (main_options->sequence_to_use == NULL) {
157 		/*
158 		 * XXX - Explain this better
159 		 */
160 		fprintf(stderr, "detox: no sequence to work with\n");
161 		exit(EXIT_FAILURE);
162 	}
163 
164 	/*
165 	 * Check translation tables
166 	 */
167 
168 	work = main_options->sequence_to_use;
169 	while (work != NULL) {
170 		char *check_filename = NULL;
171 		int do_search = 0;
172 
173 		struct translation_table *table = NULL;
174 		struct clean_string_options *opts;
175 
176 		if (work->cleaner == &clean_iso8859_1) {
177 			if (work->options != NULL) {
178 				opts = work->options;
179 				if (opts->filename != NULL) {
180 					check_filename = opts->filename;
181 				}
182 			}
183 
184 			if (!check_filename) {
185 				check_filename = "iso8859_1.tbl";
186 				do_search = 1;
187 			}
188 		}
189 		else if (work->cleaner == &clean_utf_8) {
190 			if (work->options != NULL) {
191 				opts = work->options;
192 				if (opts->filename != NULL) {
193 					check_filename = opts->filename;
194 				}
195 			}
196 
197 			if (!check_filename) {
198 				check_filename = "unicode.tbl";
199 				do_search = 1;
200 			}
201 		}
202 		else if (work->cleaner == &clean_safe) {
203 			if (work->options != NULL) {
204 				opts = work->options;
205 				if (opts->filename != NULL) {
206 					check_filename = opts->filename;
207 				}
208 			}
209 
210 			if (!check_filename) {
211 				check_filename = "safe.tbl";
212 				do_search = 1;
213 			}
214 		}
215 
216 		if (check_filename || do_search) {
217 
218 			table = NULL;
219 
220 			if (do_search) {
221 				check_config_file = malloc(MAX_PATH_LEN);
222 				if (check_config_file == NULL) {
223 					fprintf(stderr, "out of memory: %s\n", strerror(errno));
224 					exit(EXIT_FAILURE);
225 				}
226 
227 #ifdef DATADIR
228 				err = snprintf(check_config_file, MAX_PATH_LEN, "%s/detox/%s", DATADIR, check_filename);
229 				if (err < MAX_PATH_LEN)
230 					table = parse_table(check_config_file);
231 #endif
232 
233 				if (table == NULL) {
234 					err = snprintf(check_config_file, MAX_PATH_LEN, "/usr/share/detox/%s", check_filename);
235 					if (err < MAX_PATH_LEN)
236 						table = parse_table(check_config_file);
237 				}
238 
239 				if (table == NULL) {
240 					err = snprintf(check_config_file, MAX_PATH_LEN, "/usr/local/share/detox/%s", check_filename);
241 					if (err < MAX_PATH_LEN)
242 						table = parse_table(check_config_file);
243 				}
244 
245 				if (table == NULL) {
246 
247 					/*
248 					 * Fall back to the non-file based
249 					 * cleaner
250 					 */
251 					if (work->cleaner == &clean_iso8859_1) {
252 						work->cleaner = &clean_iso8859_1_basic;
253 					}
254 					else if (work->cleaner == &clean_utf_8) {
255 						work->cleaner = &clean_utf_8_basic;
256 					}
257 					else if (work->cleaner == &clean_safe) {
258 						work->cleaner = &clean_safe_basic;
259 					}
260 					else {
261 						fprintf(stderr, "detox: unable to locate translation table or fall back\n");
262 						exit(EXIT_FAILURE);
263 					}
264 				}
265 				else {
266 
267 					/*
268 					 * Allocate an options
269 					 */
270 					opts = malloc(sizeof(struct clean_string_options));
271 					if (opts == NULL) {
272 						fprintf(stderr, "out of memory: %s\n", strerror(errno));
273 						exit(EXIT_FAILURE);
274 					}
275 					memset(opts, 0, sizeof(struct clean_string_options));
276 
277 					opts->translation_table = table;
278 					work->options = opts;
279 				}
280 
281 				free(check_config_file);
282 			}
283 			else {
284 				table = parse_table(check_filename);
285 				if (table == NULL) {
286 					fprintf(stderr, "detox: unable to parse file: %s\n", check_filename);
287 					exit(EXIT_FAILURE);
288 				}
289 
290 				opts = work->options;
291 				opts->translation_table = table;
292 			}
293 		}
294 
295 
296 		work = work->next;
297 	}
298 
299 	/*
300 	 * Do some actual work
301 	 */
302 
303 	if (!main_options->is_inline_mode) {
304 		file_walk = main_options->files;
305 		while (*file_walk) {
306 			if (main_options->verbose) {
307 				printf("Scanning: %s\n", *file_walk);
308 			}
309 
310 			err = lstat(*file_walk, &stat_info);
311 			if (err == -1) {
312 				fprintf(stderr, "%s: %s\n", *file_walk, strerror(errno));
313 			}
314 			else {
315 				if (S_ISDIR(stat_info.st_mode)) {
316 					file_work = parse_file(*file_walk, main_options);
317 					parse_dir(file_work, main_options);
318 					free(file_work);
319 				}
320 				else if (S_ISREG(stat_info.st_mode)) {
321 					parse_file(*file_walk, main_options);
322 				}
323 				else if (main_options->special) {
324 					parse_special(*file_walk, main_options);
325 				}
326 			}
327 
328 			file_walk++;
329 		}
330 	} else {
331 		if (main_options->files[0] != NULL) {
332 			file_walk = main_options->files;
333 			while (*file_walk) {
334 				err = lstat(*file_walk, &stat_info);
335 				if (err == -1) {
336 					fprintf(stderr, "%s: %s\n", *file_walk, strerror(errno));
337 				}
338 				else {
339 					if (S_ISDIR(stat_info.st_mode)) {
340 						fprintf(stderr, "%s: is a directory\n", *file_walk);
341 					}
342 					else {
343 						parse_inline(*file_walk, main_options);
344 					}
345 				}
346 
347 				file_walk++;
348 			}
349 		}
350 		else {
351 			parse_inline(NULL, main_options);
352 		}
353 	}
354 
355 	return 0;
356 }
357