/* Copyright (c) Pigeonhole authors, see top-level COPYING file */

#include "lib.h"
#include "str.h"

#include "sieve.h"
#include "sieve-script.h"

#include "sieve-file-storage.h"
#include "settings-consts.h"

#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>

int sieve_file_storage_quota_havespace(struct sieve_storage *storage,
				       const char *scriptname, size_t size,
				       enum sieve_storage_quota *quota_r,
				       uint64_t *limit_r)
{
	struct sieve_file_storage *fstorage =
		container_of(storage, struct sieve_file_storage, storage);
	struct dirent *dp;
	DIR *dirp;
	uint64_t script_count = 1;
	uint64_t script_storage = size;
	int result = 1;

	/* Open the directory */
	dirp = opendir(fstorage->path);
	if (dirp == NULL) {
		sieve_storage_set_critical(
			storage, "quota: opendir(%s) failed: %m",
			fstorage->path);
		return -1;
	}

	/* Scan all files */
	for (;;) {
		const char *name;
		bool replaced = FALSE;

		/* Read next entry */
		errno = 0;
		dp = readdir(dirp);
		if (dp == NULL) {
			if (errno != 0) {
				sieve_storage_set_critical(
					storage,
					"quota: readdir(%s) failed: %m",
					fstorage->path);
				result = -1;
			}
			break;
		}

		/* Parse filename */
		name = sieve_script_file_get_scriptname(dp->d_name);

		/* Ignore non-script files */
		if (name == NULL)
			continue;

		/* Don't list our active sieve script link if the link resides
		   in the script dir (generally a bad idea).
		 */
		i_assert( fstorage->link_path != NULL );
		if (*(fstorage->link_path) == '\0' &&
		    strcmp(fstorage->active_fname, dp->d_name) == 0)
			continue;

		if (strcmp(name, scriptname) == 0)
			replaced = TRUE;

		/* Check count quota if necessary */
		if (storage->max_scripts != SET_UINT_UNLIMITED) {
			if (!replaced) {
				script_count++;

				if (script_count > storage->max_scripts) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSCRIPTS;
					*limit_r = storage->max_scripts;
					result = 0;
					break;
				}
			}
		}

		/* Check storage quota if necessary */
		if (storage->max_storage != SET_SIZE_UNLIMITED) {
			const char *path;
			struct stat st;

			path = t_strconcat(fstorage->path, "/", dp->d_name,
					   NULL);
			if (stat(path, &st) < 0) {
				e_warning(storage->event,
					  "quota: stat(%s) failed: %m", path);
				continue;
			}

			if (!replaced) {
				script_storage += st.st_size;

				if (script_storage > storage->max_storage) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSTORAGE;
					*limit_r = storage->max_storage;
					result = 0;
					break;
				}
			}
		}
	}

	/* Close directory */
	if (closedir(dirp) < 0) {
		sieve_storage_set_critical(storage,
					   "quota: closedir(%s) failed: %m",
					   fstorage->path);
	}
	return result;
}
