<commit_before>/**
 * @file
 *
 * @brief module for calling the GPG binary
 *
 * @copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 *
 */

#include "gpg.h"
#include <assert.h>
#include <errno.h>
#include <kdberrors.h>
#include <kdbhelper.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static inline void closePipe (int * pipe)
{
	close (pipe[0]);
	close (pipe[1]);
}

/**
 * @brief lookup if the test mode for unit testing is enabled.
 * @param conf KeySet holding the plugin configuration.
 * @retval 0 test mode is not enabled
 * @retval 1 test mode is enabled
 */
static int inTestMode (KeySet * conf)
{
	Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_UNIT_TEST, 0);
	if (k && !strcmp (keyString (k), "1"))
	{
		return 1;
	}
	return 0;
}

/**
 * @brief concatenates dir and file.
 * @param errorKey holds an error description in case of failure.
 * @param dir contains the path to a directory
 * @param file contains a file name
 * @returns an allocated string containing "dir/file" which must be freed by the caller or NULL in case of error.
 */
static char * genGpgCandidate (Key * errorKey, char * dir, const char * file)
{
	const size_t resultLen = strlen (dir) + strlen (file) + 2;
	char * result = elektraMalloc (resultLen);
	if (!result)
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return NULL;
	}
	snprintf (result, resultLen, "%s/%s", dir, file);
	return result;
}

/**
 * @brief lookup binary file bin in the PATH environment variable.
 * @param errorKey holds an error description in case of failure.
 * @param bin the binary file to look for
 * @param result holds an allocated string containing the full path to the binary file or NULL in case of error. Must be freed by the caller.
 * @retval -1 if an error occured. See errorKey for a description.
 * @retval 0 if the binary could not be found within PATH.
 * @retval 1 if the binary was found and the full path was stored in result.
 */
static int searchPathForBin (Key * errorKey, const char * bin, char ** result)
{
	*result = NULL;

	const char * envPath = getenv ("PATH");
	if (envPath)
	{
		const size_t envPathLen = strlen (envPath) + 1;
		char * dir;

		char * path = elektraMalloc (envPathLen);
		if (!path)
		{
			ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
			return -1;
		}
		memcpy (path, envPath, envPathLen);
		// save start of path as strsep() modifies path while splitting it up
		char * pathBegin = path;
		while ((dir = strsep (&path, ":")) != NULL)
		{
			char * candidate = genGpgCandidate (errorKey, dir, bin);
			if (!candidate)
			{
				elektraFree (pathBegin);
				return -1;
			}
			if (access (candidate, X_OK) == 0)
			{
				*result = candidate;
				elektraFree (pathBegin);
				return 1;
			}
			elektraFree (candidate);
		}
		elektraFree (pathBegin);
	}
	return 0;
}

/**
 * @brief lookup the path to the gpg binary in conf.
 * @param gpgBin holds allocated path to the gpg binary to be used or NULL in case of an error. Must bee freed by the caller.
 * @param conf KeySet holding the plugin configuration.
 * @param errorKey holds an error description if something goes wrong.
 * @retval 1 on success.
 * @retval -1 on error. In this case errorkey holds an error description.
 */
static int getGpgBinary (char ** gpgBin, KeySet * conf, Key * errorKey)
{
	*gpgBin = NULL;

	// plugin configuration has highest priority
	Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_BIN, 0);
	if (k)
	{
		const char * configPath = keyString (k);
		const size_t configPathLen = strlen (configPath);
		if (configPathLen > 0)
		{
			*gpgBin = elektraMalloc (configPathLen + 1);
			if (!(*gpgBin))
			{
				ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
				return -1;
			}
			strncpy (*gpgBin, configPath, configPathLen);
			return 1;
		}
	}

	// search PATH for gpg and gpg2 binaries
	switch (searchPathForBin (errorKey, "gpg2", gpgBin))
	{
	case 1: // success
		return 1;

	case -1: // error
		return -1;

	default: // not found
		break;
	}

	switch (searchPathForBin (errorKey, "gpg", gpgBin))
	{
	case 1: // success
		return 1;

	case -1: // error
		return -1;

	default: // not found
		break;
	}


	// last resort
	const size_t defaultLen = strlen (ELEKTRA_CRYPTO_DEFAULT_GPG_BIN);
	*gpgBin = elektraMalloc (defaultLen + 1);
	if (!(*gpgBin))
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return -1;
	}
	strncpy (*gpgBin, ELEKTRA_CRYPTO_DEFAULT_GPG_BIN, defaultLen);
	return 1;<commit_msg>crypto-plugin: fix misuse of strncpy()<commit_after>/**
 * @file
 *
 * @brief module for calling the GPG binary
 *
 * @copyright BSD License (see doc/COPYING or http://www.libelektra.org)
 *
 */

#include "gpg.h"
#include <assert.h>
#include <errno.h>
#include <kdberrors.h>
#include <kdbhelper.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

static inline void closePipe (int * pipe)
{
	close (pipe[0]);
	close (pipe[1]);
}

/**
 * @brief lookup if the test mode for unit testing is enabled.
 * @param conf KeySet holding the plugin configuration.
 * @retval 0 test mode is not enabled
 * @retval 1 test mode is enabled
 */
static int inTestMode (KeySet * conf)
{
	Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_UNIT_TEST, 0);
	if (k && !strcmp (keyString (k), "1"))
	{
		return 1;
	}
	return 0;
}

/**
 * @brief concatenates dir and file.
 * @param errorKey holds an error description in case of failure.
 * @param dir contains the path to a directory
 * @param file contains a file name
 * @returns an allocated string containing "dir/file" which must be freed by the caller or NULL in case of error.
 */
static char * genGpgCandidate (Key * errorKey, char * dir, const char * file)
{
	const size_t resultLen = strlen (dir) + strlen (file) + 2;
	char * result = elektraMalloc (resultLen);
	if (!result)
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return NULL;
	}
	snprintf (result, resultLen, "%s/%s", dir, file);
	return result;
}

/**
 * @brief lookup binary file bin in the PATH environment variable.
 * @param errorKey holds an error description in case of failure.
 * @param bin the binary file to look for
 * @param result holds an allocated string containing the full path to the binary file or NULL in case of error. Must be freed by the caller.
 * @retval -1 if an error occured. See errorKey for a description.
 * @retval 0 if the binary could not be found within PATH.
 * @retval 1 if the binary was found and the full path was stored in result.
 */
static int searchPathForBin (Key * errorKey, const char * bin, char ** result)
{
	*result = NULL;

	const char * envPath = getenv ("PATH");
	if (envPath)
	{
		const size_t envPathLen = strlen (envPath) + 1;
		char * dir;

		char * path = elektraMalloc (envPathLen);
		if (!path)
		{
			ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
			return -1;
		}
		memcpy (path, envPath, envPathLen);
		// save start of path as strsep() modifies path while splitting it up
		char * pathBegin = path;
		while ((dir = strsep (&path, ":")) != NULL)
		{
			char * candidate = genGpgCandidate (errorKey, dir, bin);
			if (!candidate)
			{
				elektraFree (pathBegin);
				return -1;
			}
			if (access (candidate, X_OK) == 0)
			{
				*result = candidate;
				elektraFree (pathBegin);
				return 1;
			}
			elektraFree (candidate);
		}
		elektraFree (pathBegin);
	}
	return 0;
}

/**
 * @brief lookup the path to the gpg binary in conf.
 * @param gpgBin holds allocated path to the gpg binary to be used or NULL in case of an error. Must bee freed by the caller.
 * @param conf KeySet holding the plugin configuration.
 * @param errorKey holds an error description if something goes wrong.
 * @retval 1 on success.
 * @retval -1 on error. In this case errorkey holds an error description.
 */
static int getGpgBinary (char ** gpgBin, KeySet * conf, Key * errorKey)
{
	*gpgBin = NULL;

	// plugin configuration has highest priority
	Key * k = ksLookupByName (conf, ELEKTRA_CRYPTO_PARAM_GPG_BIN, 0);
	if (k)
	{
		const char * configPath = keyString (k);
		const size_t configPathLen = strlen (configPath);
		if (configPathLen > 0)
		{
			*gpgBin = elektraMalloc (configPathLen + 1);
			if (!(*gpgBin))
			{
				ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
				return -1;
			}
			strncpy (*gpgBin, configPath, configPathLen);
			return 1;
		}
	}

	// search PATH for gpg and gpg2 binaries
	switch (searchPathForBin (errorKey, "gpg2", gpgBin))
	{
	case 1: // success
		return 1;

	case -1: // error
		return -1;

	default: // not found
		break;
	}

	switch (searchPathForBin (errorKey, "gpg", gpgBin))
	{
	case 1: // success
		return 1;

	case -1: // error
		return -1;

	default: // not found
		break;
	}


	// last resort
	const size_t defaultLen = strlen (ELEKTRA_CRYPTO_DEFAULT_GPG_BIN) + 1;
	*gpgBin = elektraMalloc (defaultLen);
	if (!(*gpgBin))
	{
		ELEKTRA_SET_ERROR (87, errorKey, "Memory allocation failed");
		return -1;
	}
	strncpy (*gpgBin, ELEKTRA_CRYPTO_DEFAULT_GPG_BIN, defaultLen);
	return 1;
{'fraction_non_alphanumeric': 0.11460501807822943, 'fraction_numerical': 0.010079982469595706, 'mean_word_length': 3.5143422354104845, 'pattern_counts': {'":': 2, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1518645', 'n_tokens_mistral': 3369, 'n_tokens_neox': 3096, 'n_words': 1139}