/*
 * Copyright (C) 2014-2019 Firejail Authors
 *
 * This file is part of firejail project
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "fbuilder.h"

static FileDB *db_skip = NULL;
static FileDB *db_out = NULL;

static void load_whitelist_common(void) {
	FILE *fp = fopen("/etc/firejail/whitelist-common.inc", "r");
	if (!fp) {
		fprintf(stderr, "Error: cannot open whitelist-common.inc\n");
		exit(1);
	}

	char buf[MAX_BUF];
	while (fgets(buf, MAX_BUF, fp)) {
		if (strncmp(buf, "whitelist ~/", 12) != 0)
			continue;
		char *fn = buf + 12;
		char *ptr = strchr(buf, '\n');
		if (!ptr)
			continue;
		*ptr = '\0';

		// add the file to skip list
		db_skip = filedb_add(db_skip, fn);
	}

	fclose(fp);
}

void process_home(const char *fname, char *home, int home_len) {
	assert(fname);
	assert(home);
	assert(home_len);

	// process trace file
	FILE *fp = fopen(fname, "r");
	if (!fp) {
		fprintf(stderr, "Error: cannot open %s\n", fname);
		exit(1);
	}

	char buf[MAX_BUF];
	while (fgets(buf, MAX_BUF, fp)) {
		// remove \n
		char *ptr = strchr(buf, '\n');
		if (ptr)
			*ptr = '\0';

		// parse line: 4:galculator:access /etc/fonts/conf.d:0
		// number followed by :
		ptr = buf;
		if (!isdigit(*ptr))
			continue;
		while (isdigit(*ptr))
			ptr++;
		if (*ptr != ':')
			continue;
		ptr++;

		// next :
		ptr = strchr(ptr, ':');
		if (!ptr)
			continue;
		ptr++;
		if (strncmp(ptr, "access /home", 12) == 0)
			ptr +=  7;
		else if (strncmp(ptr, "fopen /home", 11) == 0)
			ptr += 6;
		else if (strncmp(ptr, "fopen64 /home", 13) == 0)
			ptr += 8;
		else if (strncmp(ptr, "open64 /home", 12) == 0)
			ptr += 7;
		else if (strncmp(ptr, "open /home", 10) == 0)
			ptr += 5;
		else
			continue;

		// end of filename
		char *ptr2 = strchr(ptr, ':');
		if (!ptr2)
			continue;
		*ptr2 = '\0';

		// check home directory
		if (strncmp(ptr, home, home_len) != 0)
			continue;
		if (strcmp(ptr, home) == 0)
			continue;
		ptr += home_len + 1;

		// skip files handled automatically by firejail
		if (strcmp(ptr, ".Xauthority") == 0 ||
		    strcmp(ptr, ".Xdefaults-debian") == 0 ||
		    strncmp(ptr, ".config/pulse/", 14) == 0 ||
		    strncmp(ptr, ".pulse/", 7) == 0 ||
		    strncmp(ptr, ".bash_hist", 10) == 0 ||
		    strcmp(ptr, ".bashrc") == 0)
			continue;


		// try to find the relevant directory for this file
		char *dir = extract_dir(ptr);
		char *toadd = (dir)? dir: ptr;

		// skip some dot directories
		if (strcmp(toadd, ".config") == 0 ||
		    strcmp(toadd, ".local") == 0 ||
		    strcmp(toadd, ".local/share") == 0 ||
		    strcmp(toadd, ".cache") == 0) {
			if (dir)
				free(dir);
		    	continue;
		}

		// clean .cache entries
		if (strncmp(toadd, ".cache/", 7) == 0) {
			char *ptr2 = toadd + 7;
			ptr2 = strchr(ptr2, '/');
			if (ptr2)
				*ptr2 = '\0';
		}

		// skip files and directories in whitelist-common.inc
		if (filedb_find(db_skip, toadd)) {
			if (dir)
				free(dir);
			continue;
		}

		// add the file to out list
		db_out = filedb_add(db_out, toadd);
		if (dir)
			free(dir);

	}
	fclose(fp);
}


// process fname, fname.1, fname.2, fname.3, fname.4, fname.5
void build_home(const char *fname, FILE *fp) {
	assert(fname);

	// load whitelist common
	load_whitelist_common();

	// find user home directory
	struct passwd *pw = getpwuid(getuid());
	if (!pw)
		errExit("getpwuid");
	char *home = pw->pw_dir;
	if (!home)
		errExit("getpwuid");
	int home_len = strlen(home);

	// run fname
	process_home(fname, home, home_len);

	// run all the rest
	struct stat s;
	int i;
	for (i = 1; i <= 5; i++) {
		char *newname;
		if (asprintf(&newname, "%s.%d", fname, i) == -1)
			errExit("asprintf");
		if (stat(newname, &s) == 0)
			process_home(newname, home, home_len);
		free(newname);
	}

	// print the out list if any
	if (db_out) {
		filedb_print(db_out, "whitelist ~/", fp);
		fprintf(fp, "include /etc/firejail/whitelist-common.inc\n");
	}
	else
		fprintf(fp, "private\n");

}
