<commit_before>/*
 * Copyright 2012--2014 Red Hat Inc., Durham, North Carolina.
 * All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h> // for P_tmpdir macro
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <ftw.h>
#endif
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <curl/curl.h>
#include <curl/easy.h>

#include "oscap_acquire.h"
#include "common/util.h"
#include "common/oscap_buffer.h"
#include "common/_error.h"
#include "oscap_string.h"

#ifndef P_tmpdir
#define P_tmpdir "/tmp"
#endif

#define TEMP_DIR_TEMPLATE P_tmpdir "/oscap.XXXXXX"
#define TEMP_URL_TEMPLATE "downloaded.XXXXXX"

char *
oscap_acquire_temp_dir()
{
#ifdef _WIN32
	DWORD dwRetVal = 0;
	UINT uRetVal = 0;
	TCHAR lpTempPathBuffer[PATH_MAX];
	TCHAR szTempFileName[PATH_MAX];

	dwRetVal = GetTempPath(PATH_MAX, lpTempPathBuffer);
	if (dwRetVal > PATH_MAX || dwRetVal == 0) {
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not retrieve the path of the directory for temporary files.");
		return NULL;
	}
	uRetVal = GetTempFileName(lpTempPathBuffer, TEXT("oscap"), 0, szTempFileName);
	if (uRetVal == 0) {
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not get a name for new temporary directory.");
		return NULL;
	}
	if (!CreateDirectory(szTempFileName, NULL)) {
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not create temp directory '%s'.", szTempFileName);
		return NULL;
	}
	return oscap_strdup(szTempFileName);
#else
	char *temp_dir = oscap_strdup(TEMP_DIR_TEMPLATE);
	if (mkdtemp(temp_dir) == NULL) {
		free(temp_dir);
		oscap_seterr(OSCAP_EFAMILY_GLIBC, "Could not create temp directory " TEMP_DIR_TEMPLATE ". %s", strerror(errno));
		return NULL;
	}
	return temp_dir;
#endif
}

#ifndef _WIN32
static int
__unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
	int rv = remove(fpath);

	if (rv)
		oscap_seterr(OSCAP_EFAMILY_GLIBC, "Could not remove %s. %s", fpath, strerror(errno));

	return rv;
}
#endif

void
oscap_acquire_cleanup_dir(char **dir_path)
{
#ifndef _WIN32
	if (*dir_path != NULL)
	{
		nftw(*dir_path, __unlink_cb, 64, FTW_DEPTH | FTW_PHYS | FTW_MOUNT);
		free(*dir_path);
		*dir_path = NULL;
	}
#else
	// TODO
#endif
}

int
oscap_acquire_temp_file(const char *dir, const char *template, char **filename)
{
#ifdef _WIN32
	int old_mode;
#else
	mode_t old_mode;
#endif
	int fd;

	if (dir == NULL || template == NULL || filename == NULL)
		return -1;

	*filename = malloc(PATH_MAX * sizeof(char));
	old_mode = umask(077); /* Override unusual umask. Ensure 0700 permissions. */
#ifdef _WIN32
	char *base_name = oscap_strdup(template);
	_mktemp_s(base_name, strlen(base_name) + 1); // +1 for terminator
	snprintf(*filename, PATH_MAX, "%s/%s", dir, base_name);
	free(base_name);
	fd = open(*filename, _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE);
#else
	snprintf(*filename, PATH_MAX, "%s/%s", dir, template);<commit_msg>Create and remove temporary directory on Windows<commit_after>/*
 * Copyright 2012--2014 Red Hat Inc., Durham, North Carolina.
 * All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h> // for P_tmpdir macro
#include <string.h>
#include <errno.h>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
/* The rand_s function requires constant _CRT_RAND_S to be defined before including <stdlib.h>.
 * See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s
 */
#define _CRT_RAND_S
#else
#include <unistd.h>
#include <ftw.h>
#endif
#include <stdlib.h>
#include <limits.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <curl/curl.h>
#include <curl/easy.h>

#include "oscap_acquire.h"
#include "common/util.h"
#include "common/oscap_buffer.h"
#include "common/_error.h"
#include "oscap_string.h"

#ifndef P_tmpdir
#define P_tmpdir "/tmp"
#endif

#define TEMP_DIR_TEMPLATE P_tmpdir "/oscap.XXXXXX"
#define TEMP_URL_TEMPLATE "downloaded.XXXXXX"

#ifdef _WIN32
char *oscap_acquire_temp_dir()
{
	WCHAR temp_path[PATH_MAX];
	WCHAR temp_dir[PATH_MAX];

	DWORD ret = GetTempPathW(PATH_MAX, temp_path);
	if (ret > PATH_MAX || ret == 0) {
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not retrieve the path of the directory for temporary files.");
		return NULL;
	}

	unsigned int unique;
	rand_s(&unique);
	ret = GetTempFileNameW(temp_path, L"oscap", unique, temp_dir);
	if (ret == 0) {
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not get a name for new temporary directory.");
		return NULL;
	}
	char *temp_dir_str = oscap_windows_wstr_to_str(temp_dir);

	WCHAR *path_prefix = temp_dir;
	do {
		/* Use wide characters with L modifier because we work with a wide string. */
		WCHAR *delimiter = wcschr(path_prefix, L'\\');
		if (delimiter == NULL) {
			break;
		}
		*delimiter = L'\0';
		if (!CreateDirectoryW(temp_dir, NULL)) {
			ret = GetLastError();
			if (ret != ERROR_ALREADY_EXISTS) {
				char *error_message = oscap_windows_error_message(ret);
				oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not create temp directory '%s': %s.", temp_dir_str, error_message);
				free(error_message);
				free(temp_dir_str);
				return NULL;
			}
		}
		*delimiter = L'\\';
		path_prefix = ++delimiter;
	} while (*path_prefix != L'\0');

	if (!CreateDirectoryW(temp_dir, NULL)) {
		ret = GetLastError();
		char *error_message = oscap_windows_error_message(ret);
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "Could not create temp directory '%s': %s.", temp_dir_str, error_message);
		free(error_message);
		free(temp_dir_str);
		return NULL;
	}

	return temp_dir_str;
}
#else
char *oscap_acquire_temp_dir()
{
	char *temp_dir = oscap_strdup(TEMP_DIR_TEMPLATE);
	if (mkdtemp(temp_dir) == NULL) {
		free(temp_dir);
		oscap_seterr(OSCAP_EFAMILY_GLIBC, "Could not create temp directory " TEMP_DIR_TEMPLATE ". %s", strerror(errno));
		return NULL;
	}
	return temp_dir;
}
#endif

#ifdef _WIN32
static bool _recursive_delete_directory(WCHAR *directory)
{
	if (directory == NULL) {
		return false;
	}
	WCHAR find_pattern[MAX_PATH];
	wcsncpy(find_pattern, directory, MAX_PATH);
	wcsncat(find_pattern, L"\\*", MAX_PATH);
	WCHAR dir_path[MAX_PATH];
	wcsncpy(dir_path, directory, MAX_PATH);
	wcsncat(dir_path, L"\\", MAX_PATH);
	WCHAR file_path[MAX_PATH];
	wcsncpy(file_path, dir_path, MAX_PATH);
	DWORD err;

	WIN32_FIND_DATAW find_data;
	HANDLE find_handle = FindFirstFileW(find_pattern, &find_data);
	if (find_handle == INVALID_HANDLE_VALUE) {
		err = GetLastError();
		char *error_message = oscap_windows_error_message(err);
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "FindFirstFileW error: %s", error_message);
		free(error_message);
		return false;
	}

	do {
		if (wcscmp(find_data.cFileName, L".") != 0 && wcscmp(find_data.cFileName, L"..") != 0) {
			wcsncat(file_path, find_data.cFileName, MAX_PATH);
			if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
				if (!_recursive_delete_directory(file_path)) {
					FindClose(find_handle);
					return false;
				}
			} else {
				if (!DeleteFileW(file_path)) {
					err = GetLastError();
					char *error_message = oscap_windows_error_message(err);
					oscap_seterr(OSCAP_EFAMILY_WINDOWS, "DeleteFileW Error: %s", error_message);
					free(error_message);
					FindClose(find_handle);
					return false;
				}
			}
			wcsncpy(file_path, dir_path, MAX_PATH);
		}
	} while (FindNextFileW(find_handle, &find_data) != 0);
	FindClose(find_handle);

	if (!RemoveDirectoryW(dir_path)) {
		err = GetLastError();
		char *error_message = oscap_windows_error_message(err);
		oscap_seterr(OSCAP_EFAMILY_WINDOWS, "RemoveDirectoryW error: %s", error_message);
		free(error_message);
		return false;
	}

	return true;
}

void oscap_acquire_cleanup_dir(char **dir_path)
{
	if (*dir_path != NULL) {
		WCHAR *dir_path_wstr = oscap_windows_str_to_wstr(*dir_path);
		_recursive_delete_directory(dir_path_wstr);
		free(dir_path_wstr);
		free(*dir_path);
		*dir_path = NULL;
	}
}
#else
static int __unlink_cb(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
	int rv = remove(fpath);

	if (rv)
		oscap_seterr(OSCAP_EFAMILY_GLIBC, "Could not remove %s. %s", fpath, strerror(errno));

	return rv;
}

void oscap_acquire_cleanup_dir(char **dir_path)
{
	if (*dir_path != NULL)
	{
		nftw(*dir_path, __unlink_cb, 64, FTW_DEPTH | FTW_PHYS | FTW_MOUNT);
		free(*dir_path);
		*dir_path = NULL;
	}
}
#endif

int
oscap_acquire_temp_file(const char *dir, const char *template, char **filename)
{
#ifdef _WIN32
	int old_mode;
#else
	mode_t old_mode;
#endif
	int fd;

	if (dir == NULL || template == NULL || filename == NULL)
		return -1;

	*filename = malloc(PATH_MAX * sizeof(char));
	old_mode = umask(077); /* Override unusual umask. Ensure 0700 permissions. */
#ifdef _WIN32
	char *base_name = oscap_strdup(template);
	_mktemp_s(base_name, strlen(base_name) + 1); // +1 for terminator
	snprintf(*filename, PATH_MAX, "%s/%s", dir, base_name);
	free(base_name);
	fd = open(*filename, _O_RDWR | _O_CREAT, _S_IREAD | _S_IWRITE);
#else
	snprintf(*filename, PATH_MAX, "%s/%s", dir, template);
{'fraction_non_alphanumeric': 0.1216549629910799, 'fraction_numerical': 0.010248624027329664, 'mean_word_length': 4.891000558971492, 'pattern_counts': {'":': 0, '<': 32, '<?xml version=': 0, '>': 34, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '634172', 'n_tokens_mistral': 4150, 'n_tokens_neox': 3875, 'n_words': 1022}