/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2008 OMC Denmark ApS.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "fileops.h"
#include "globals.h"
#include "logging.h"

#include <windows.h>
#include <aclapi.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ace/OS_NS_sys_stat.h>
#include <ace/OS_NS_fcntl.h>
#include <ace/OS_NS_unistd.h>

bool
create_file(const bool directory,
	    const char * const name)
{
	bool retv = false;
	DWORD dwRes;
	PSID pEveryoneSID = NULL;
	PACL pACL = NULL;
	PSECURITY_DESCRIPTOR pSD = NULL;
	EXPLICIT_ACCESS ea[1];
	SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
	SECURITY_ATTRIBUTES sa;

	// SID for "Everyone"
	if (!AllocateAndInitializeSid(&SIDAuthWorld, 1,
				      SECURITY_WORLD_RID,
				      0, 0, 0, 0, 0, 0, 0,
				      &pEveryoneSID)) {
		ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - AllocateAndInitializeSid() failed, error code: %u\n"), GetLastError()));
		goto exit;
	}

	// Initialize an EXPLICIT_ACCESS structure for an ACE.
	// The ACE will allow Everyone read access to the key.
	ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
	ea[0].grfAccessPermissions = GENERIC_ALL;
	ea[0].grfAccessMode = SET_ACCESS;
	ea[0].grfInheritance = CONTAINER_INHERIT_ACE; // NO_INHERITANCE; 
	ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
	ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
	ea[0].Trustee.ptstrName  = (LPTSTR)pEveryoneSID;


	// Create a new ACL that contains the new ACEs.
	dwRes = SetEntriesInAcl(1, ea, NULL, &pACL);
	if (ERROR_SUCCESS != dwRes) {
		ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - SetEntriesInAcl() failed, error code: %u\n"), GetLastError()));
		goto exit;
	}

	// Initialize a security descriptor.  
	pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, 
					       SECURITY_DESCRIPTOR_MIN_LENGTH); 
	if (NULL == pSD) { 
		ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - LocalAlloc() failed, error code: %u\n"), GetLastError()));
		goto exit; 
	} 
 
	if (!InitializeSecurityDescriptor(pSD,
					  SECURITY_DESCRIPTOR_REVISION)) {  
		ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - InitializeSecurityDescriptor() failed, error code: %u\n"), GetLastError()));
		goto exit; 
	} 
 
	// Add the ACL to the security descriptor. 
	if (!SetSecurityDescriptorDacl(pSD, 
				       TRUE, 
				       pACL, 
				       FALSE)) {  
		ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - SetSecurityDescriptorDacl() failed, error code: %u\n"), GetLastError()));
		goto exit; 
	} 

	// Initialize a security attributes structure.
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = pSD;
	sa.bInheritHandle = FALSE;

	if (directory) {
		retv = CreateDirectory(name, &sa);
		dwRes = GetLastError();
		if (!retv && (ERROR_ALREADY_EXISTS == dwRes))
			retv = true;
		else if (!retv)
			ACE_DEBUG((LM_CRITICAL, ACE_TEXT("%N:%l - CreateDirectory() failed, error code: %u\n"), dwRes));
	} else {
		ACE_HANDLE file_fd = ACE_INVALID_HANDLE;
		FILE *file = NULL;

		file_fd = ACE_OS::open(name, 
				       O_CREAT | O_RDWR | O_TRUNC,
				       ACE_DEFAULT_OPEN_PERMS,
				       &sa);
		if (ACE_INVALID_HANDLE == file_fd) {
			int err = 0;
			_get_errno(&err); 
			ACE_ERROR((LM_ERROR, "(%P|%t) %N:%l - could not ACE_OS::fdopen %s - error = %s\n", name, strerror(err)));
		} else {
			ACE_OS::close(file_fd);
			retv = true;
		}
	}

exit:
	if (pEveryoneSID) 
		FreeSid(pEveryoneSID);
	if (pACL) 
		LocalFree(pACL);
	if (pSD) 
		LocalFree(pSD);

	return retv;
}

bool
file_exist(const char * const name)
{
	ACE_stat st;
	return (0 == ACE_OS::stat(name, &st)) ? true : false;
}

bool
file_name_valid(const char * const pre,
		const char * const name,
		const char * const post)
{
	const char *pre_ = pre ? pre : ""; 
	const char *name_ = name ? name : ""; 
	const char *post_ = post ? post : ""; 

	size_t len = strlen(pre_) + strlen(name_) + strlen(post_);
	char *path = (char*)malloc(len + 1);
	if (!path)
		return false;

	bool retv = false;
	int w = sprintf_s(path, len + 1, "%s%s%s", pre_, name_, post_);
	if (w != len)
		goto out;

	retv = create_file(false, path);
	remove(path);

out:
	free(path);
	return retv;
}

char*
make_log_file_name(const char * const basedir,
		   const char * const basename)
{
	char cwd[_MAX_PATH] = { '\0' };

	strcat_s(cwd, sizeof(cwd), basedir);
	strcat_s(cwd, sizeof(cwd), "\\");
	strcat_s(cwd, sizeof(cwd), LOG_DIR);
	cwd[strlen(cwd) - 1] = '\0';
	strcat_s(cwd, sizeof(cwd), "\\");
	strcat_s(cwd, sizeof(cwd), basename);
	strcat_s(cwd, sizeof(cwd), ".log");

	return strdup(cwd);
}

char*
make_ior_file_name(const char * const basedir,
		   const char * const basename)
{
	char cwd[_MAX_PATH] = { '\0' };

	strcat_s(cwd, sizeof(cwd), basedir);
	strcat_s(cwd, sizeof(cwd), "\\");
	strcat_s(cwd, sizeof(cwd), IOR_DIR);
	cwd[strlen(cwd) - 1] = '\0'; // remove trailing '/'
	strcat_s(cwd, sizeof(cwd), "\\");
	strcat_s(cwd, sizeof(cwd), basename);
	strcat_s(cwd, sizeof(cwd), ".ior");

	return strdup(cwd);
}

char*
make_ior_file_ref(const char * const filename)
{
	size_t len = strlen("file://") + strlen(filename) + 1;
	char *retv = (char*)malloc(len);
	if (!retv)
		return NULL;
		
	sprintf_s(retv, len, "%s%s", "file://", filename);
	return retv;
}

