/*
 * Copyright (C) 2003-2008  Justin Karneges <justin@affinix.com>
 * Copyright (C) 2014  Ivan Romanov <drizt@land.ru>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "utils.h"
#include "mykeystorelist.h"
#include <QFileInfo>
#include <QStringList>
#include <QCoreApplication>
#ifdef Q_OS_WIN
#include <windows.h>
#endif

using namespace QCA;

namespace gpgQCAPlugin
{

void gpg_waitForFinished(GpgOp *gpg)
{
	while(1)
	{
		GpgOp::Event e = gpg->waitForEvent(-1);
		if(e.type == GpgOp::Event::Finished)
			break;
	}
}

void gpg_keyStoreLog(const QString &str)
{
	MyKeyStoreList *ksl = MyKeyStoreList::instance();
	if(ksl)
		ksl->ext_keyStoreLog(str);
}

inline bool check_bin(const QString &bin)
{
	QFileInfo fi(bin);
	return fi.exists();
}

#ifdef Q_OS_WIN
static bool get_reg_key(HKEY root, const char *path, QString &value)
{
	HKEY hkey = 0;

	char szValue[256];
	DWORD dwLen = 256;

	bool res = false;

	if(RegOpenKeyExA(root, path, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS)
	{
		if (RegQueryValueExA(hkey, "Install Directory", NULL, NULL, (LPBYTE)szValue, &dwLen) == ERROR_SUCCESS)
		{
			value = QString::fromLocal8Bit(szValue);
			res = true;
		}
		RegCloseKey(hkey);
	}
	return res;
}


static QString find_reg_gpgProgram()
{
	QStringList bins;
	bins << "gpg.exe" << "gpg2.exe";

	HKEY root;
	root = HKEY_CURRENT_USER;

	const char *path = "Software\\GNU\\GnuPG";
	const char *path2 = "Software\\Wow6432Node\\GNU\\GnuPG";

	QString dir;
	// check list of possible places in registry
	get_reg_key(HKEY_CURRENT_USER, path, dir)  ||
	get_reg_key(HKEY_CURRENT_USER, path2, dir) ||
	get_reg_key(HKEY_LOCAL_MACHINE, path, dir) ||
	get_reg_key(HKEY_LOCAL_MACHINE, path2, dir);

	if (!dir.isEmpty())
	{
		foreach (const QString &bin, bins)
		{
			if (check_bin(dir + "\\" + bin))
			{
				return dir + "\\" + bin;
			}
		}
	}
	return QString();
}
#endif

QString find_bin()
{
	// gpg and gpg2 has identical semantics
	// so any from them can be used
	QStringList bins;
#ifdef Q_OS_WIN
	bins << "gpg.exe" << "gpg2.exe";
#else
	bins << "gpg" << "gpg2";
#endif

	// Prefer bundled gpg
	foreach (const QString &bin, bins)
	{
		if (check_bin(QCoreApplication::applicationDirPath() + "/" + bin))
		{
			return QCoreApplication::applicationDirPath() + "/" + bin;
		}
	}

#ifdef Q_OS_WIN
	// On Windows look up at registry
	QString bin = find_reg_gpgProgram();
	if (!bin.isEmpty())
		return bin;
#endif

	// Look up at PATH environment
#ifdef Q_OS_WIN
	QString pathSep = ";";
#else
	QString pathSep = ":";
#endif

	QStringList paths = QString::fromLocal8Bit(qgetenv("PATH")).split(pathSep, QString::SkipEmptyParts);

#ifdef Q_OS_MAC
	// On Mac OS bundled always uses system default PATH
	// so it need explicity add extra paths which can
	// contain gpg
	// Mac GPG and brew use /usr/local/bin
	// MacPorts uses /opt/local/bin
	paths << "/usr/local/bin" << "/opt/local/bin";
#endif
	paths.removeDuplicates();

	foreach (const QString &path, paths)
	{
		foreach (const QString &bin, bins)
		{
			if (check_bin(path + "/" + bin))
			{
				return path + "/" + bin;
			}
		}
	}

	// Return nothing if gpg not found
	return QString();
}

QString escape_string(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
			out += "\\\\";
		else if(in[n] == ':')
			out += "\\c";
		else
			out += in[n];
	}
	return out;
}

QString unescape_string(const QString &in)
{
	QString out;
	for(int n = 0; n < in.length(); ++n)
	{
		if(in[n] == '\\')
		{
			if(n + 1 < in.length())
			{
				if(in[n + 1] == '\\')
					out += '\\';
				else if(in[n + 1] == 'c')
					out += ':';
				++n;
			}
		}
		else
			out += in[n];
	}
	return out;
}

PGPKey publicKeyFromId(const QString &id)
{
	MyKeyStoreList *ksl = MyKeyStoreList::instance();
	if(!ksl)
		return PGPKey();

	return ksl->publicKeyFromId(id);
}

PGPKey secretKeyFromId(const QString &id)
{
	MyKeyStoreList *ksl = MyKeyStoreList::instance();
	if(!ksl)
		return PGPKey();

	return ksl->secretKeyFromId(id);
}

} // end namespace gpgQCAPlugin
