/* Pi-hole: A black hole for Internet advertisements
*  (c) 2017 Pi-hole, LLC (https://pi-hole.net)
*  Network-wide ad blocking via your own hardware.
*
*  FTL Engine
*  File operation routines
*
*  This file is copyright under the latest version of the EUPL.
*  Please see LICENSE file for your rights under this license. */

#include "FTL.h"
#include "files.h"
#include "memory.h"
#include "config.h"
#include "setupVars.h"
#include "log.h"

// opendir(), readdir()
#include <dirent.h>
// getpwuid()
#include <pwd.h>
// getgrgid()
#include <grp.h>
// NAME_MAX
#include <limits.h>

// chmod_file() changes the file mode bits of a given file (relative
// to the directory file descriptor) according to mode. mode is an
// octal number representing the bit pattern for the new mode bits
bool chmod_file(const char *filename, const mode_t mode)
{
	if(chmod(filename, mode) < 0)
	{
		logg("WARNING: chmod(%s, %d): chmod() failed: %s (%d)", filename, mode, strerror(errno), errno);
		return false;
	}

	struct stat st;
	if(stat(filename, &st) < 0)
	{
		logg("WARNING: chmod(%s, %d): stat() failed: %s (%d)", filename, mode, strerror(errno), errno);
		return false;
	}

	// We need to apply a bitmask on st.st_mode as the upper bits may contain random data
	// 0x1FF = 0b111_111_111 corresponding to the three-digit octal mode number
	if((st.st_mode & 0x1FF) != mode)
	{
		logg("WARNING: chmod(%s, %d): Verification failed, %d != %d", filename, mode, st.st_mode, mode);
		return false;
	}

	return true;
}

bool file_exists(const char *filename)
{
	struct stat st;
	return stat(filename, &st) == 0;
}

unsigned long long get_FTL_db_filesize(void)
{
	struct stat st;
	if(stat(FTLfiles.FTL_db, &st) != 0)
	{
		// stat() failed (maybe the DB file does not exist?)
		return 0;
	}
	return st.st_size;
}

void ls_dir(const char* path)
{
	// Open directory stream
	DIR* dirp = opendir(path);
	if(dirp == NULL)
	{
		logg("opendir(\"%s\") failed with %s (%d)", path, strerror(errno), errno);
		return;
	}

	// Stack space for full path (directory + "/" + filename + terminating \0)
	char full_path[strlen(path)+NAME_MAX+2];

	logg("------ Listing content of directory %s ------", path);
	logg("File Mode User:Group  Filesize Filename");

	struct dirent *dircontent = NULL;
	// Walk directory file by file
	while((dircontent = readdir(dirp)) != NULL)
	{
		// Get filename
		const char *filename = dircontent->d_name;

		// Construct full path
		snprintf(full_path, sizeof(full_path), "%s/%s", path, filename);

		struct stat st;
		// Use stat to get file size, permissions, and ownership
		if(stat(full_path, &st) < 0)
		{
			logg("%s failed with %s (%d)", filename, strerror(errno), errno);
			continue;
		}

		// Get owner's name
		struct passwd *pwd;
		char user[256];
		if ((pwd = getpwuid(st.st_uid)) != NULL)
			snprintf(user, sizeof(user), "%s", pwd->pw_name);
		else
			snprintf(user, sizeof(user), "%d", st.st_uid);

		struct group *grp;
		char group[256];
		// Get out group name
		if ((grp = getgrgid(st.st_gid)) != NULL)
			snprintf(group, sizeof(group), "%s", grp->gr_name);
		else
			snprintf(group, sizeof(group), "%d", st.st_gid);

		char permissions[10];
		// Get human-readable format of permissions as known from ls
		snprintf(permissions, sizeof(permissions),
		         "%s%s%s%s%s%s%s%s%s",
		         st.st_mode & S_IRUSR ? "r":"-",
		         st.st_mode & S_IWUSR ? "w":"-",
		         st.st_mode & S_IXUSR ? "x":"-",
		         st.st_mode & S_IRGRP ? "r":"-",
		         st.st_mode & S_IWGRP ? "w":"-",
		         st.st_mode & S_IXGRP ? "x":"-",
		         st.st_mode & S_IROTH ? "r":"-",
		         st.st_mode & S_IWOTH ? "w":"-",
		         st.st_mode & S_IXOTH ? "x":"-");

		char prefix[2] = " ";
		double formated = 0.0;
		format_memory_size(prefix, (unsigned long long)st.st_size, &formated);

		// Log output for this file
		logg("%s %s:%s %.0f%s %s", permissions, user, group, formated, prefix, filename);
	}

	logg("---------------------------------------------------");

	// Close directory stream
	closedir(dirp);
}
