/*
 *  Copyright (C) 2007-2008 CMM Sigma Andrzej Chmielowiec <cmmsigma@cmmsigma.eu>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  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 General Public License for more details.
 */

#include "MainWindow.h"
#include "FileDialogWPR.h"
#include "GenCertReqWidget.h"
#include "QDetailsMessageBox.h"
#include "AboutBox.h"

extern QString lang;

// MainWindow class
MainWindow::MainWindow()
{
	// OpenSSL command and configuretion.
	mOpensslCmd = QCoreApplication::applicationDirPath().append("/../bin/stoken_openssl");
#ifdef Q_WS_WIN
	mOpensslCmd.append(QString(".exe"));
#endif
	mOpensslConfFilePath = QDir::currentPath() + QDir::separator() + "openssl.cnf";
	
	if (!QFile::exists(mOpensslConfFilePath))
	{
		QFile srcFile(":openssl.cnf");
		srcFile.open(QIODevice::ReadOnly);
		QByteArray content = srcFile.readAll();
		srcFile.close();
		
		QFile dstFile(mOpensslConfFilePath);
		dstFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
		dstFile.write(content);
		dstFile.close();
	}
	
	// Window layout.
	QGridLayout *layout;
	
	// Logo.
	QLabel *logo = new QLabel;
	logo->setPixmap(QPixmap(QString(":img/stoken_logo_120_60.png")));
	logo->setAlignment(Qt::AlignRight);
	
	// Private key.
	mKeyPath = new QLineEdit;
	mSelectKey = new QPushButton(QIcon(QString(":img/keys_folder_blue.png")), tr("&Select key file"));
	connect(mSelectKey, SIGNAL(clicked()), this, SLOT(selectKeySlot()));
	mKeyAlgo = new QComboBox();
	mKeyAlgo->addItem(QString("ECDSA-256"), QVariant(QString("ec:prime256v1")));
	mKeyAlgo->addItem(QString("ECDSA-384"), QVariant(QString("ec:secp384r1")));
	mKeyAlgo->addItem(QString("RSA-1024"), QVariant(QString("rsa:1024")));
	mKeyAlgo->addItem(QString("RSA-2048"), QVariant(QString("rsa:2048")));
	mKeyAlgo->addItem(QString("RSA-3072"), QVariant(QString("rsa:3072")));
	mKeyAlgo->addItem(QString("RSA-4096"), QVariant(QString("rsa:4096")));
	mKeyAlgo->setCurrentIndex(0);
	mKeyMD = new QComboBox();
	mKeyMD->addItem(QString("SHA-256"), QVariant(QString("sha256")));
	mKeyMD->addItem(QString("SHA-224"), QVariant(QString("sha224")));
	mKeyMD->addItem(QString("SHA-1"), QVariant(QString("sha1")));
	mKeyMD->setCurrentIndex(0);
	
	layout = new QGridLayout;
	layout->addWidget(new QLabel(tr("Key file")), 0, 0);
	layout->addWidget(mKeyPath, 0, 1);
	layout->addWidget(mSelectKey, 0, 2);
	layout->addWidget(new QLabel(tr("Key algorithm")), 1, 0);
	layout->addWidget(mKeyAlgo, 1, 1, 1, 2);
	layout->addWidget(new QLabel(tr("Hash algorithm")), 2, 0);
	layout->addWidget(mKeyMD, 2, 1, 1, 2);
	layout->setColumnStretch(1, 1);
	layout->setRowStretch(3, 1);
	
	QWidget *keyBox = new QWidget;
	keyBox->setLayout(layout);
	
	// Request.
	mGenCertReq = new GenCertReqWidget(this);
	
	layout = new QGridLayout;
	layout->addWidget(mGenCertReq, 0, 0);
	
	QWidget *reqBox = new QWidget;
	reqBox->setLayout(layout);
	
	// Generation button.
	mGenerate = new QPushButton(QIcon(QString(":img/filetype_userreq.png")), 
		tr("Generate"));
	connect(mGenerate, SIGNAL(clicked()), this, SLOT(generateCertReqSlot()));
	
	// All together.
	QTabWidget *mainTab = new QTabWidget;
	mainTab->addTab(keyBox, tr("Private key"));
	mainTab->addTab(reqBox, tr("Request data"));
	
	layout = new QGridLayout;
	layout->addWidget(logo, 0, 0, 1, 2);
	layout->addWidget(mainTab, 1, 0, 1, 2);
	layout->addWidget(mGenerate, 2, 1);
	layout->setColumnStretch(0, 1);
	
	QWidget *mainWidget = new QWidget;
	mainWidget->setLayout(layout);
	setCentralWidget(mainWidget);
	
	createActions();
	createMenus();
	createAboutBox();
	
	setWindowIcon(QIcon(QString(":img/stoken_softadmin_032.png")));
	setWindowTitle(tr("sToken SoftAdmin"));
	setMinimumWidth(450);
	
}

void
MainWindow::selectKeySlot()
{
	QString keyPath = FileDialogWPR::getSaveFileName(this, tr("Save private key as"),
		QString(), tr("Key files (*.key);;All files (*)"));
	
	if (!keyPath.isEmpty())
	{
		mKeyPath->setText(keyPath);
	}
}

void
MainWindow::generateCertReqSlot()
{
	// Check key path.
	if (mKeyPath->text().isEmpty())
	{
		QMessageBox::information(this, tr("Information"), tr("Key file cannot be empty."));
		return;
	}
	
	// Check request.
	if (!mGenCertReq->isReady())
	{
		QMessageBox::information(this, tr("Information"), tr("Common name field cannot "
			"be empty."));
		return;
	}
	
	// Get and check password.
	QString pass = getPass(true);
	
	if (pass.isEmpty())
		return;
	
	// Request file path.
	QString reqPath = mKeyPath->text();
	reqPath.remove(QString(".key"));
	reqPath.append(QString(".req"));
	reqPath = FileDialogWPR::getSaveFileName(this, tr("Save request as"),
		reqPath, tr("Requests (*.req);;All files (*)"));
	
	// Key signature and digest algorithm.
	QString algo = mKeyAlgo->itemData(mKeyAlgo->currentIndex()).toString();
	QString md = mKeyMD->itemData(mKeyMD->currentIndex()).toString();
	
	if (algo.startsWith(QString("ec:")))
	{
		md = QString("ecdsa-with-") + md;
	}
	else
	{
		md = md + QString("WithRSAEncryption");
	}
	
	// Generate ECDSA params set I.
	mOpensslArgs.clear();
	mOpensslArgs << "ecparam" << "-name" << "prime256v1" << "-out" << "prime256v1";
	
	if (!runOpenssl())
	{
		QDetailsMessageBox::critical(this, QObject::tr("Error"), 
			QObject::tr("Cannot create domain file."), mOpensslError);
		return;
	}
	
	// Generate ECDSA params set II.
	mOpensslArgs.clear();
	mOpensslArgs << "ecparam" << "-name" << "secp384r1" << "-out" << "secp384r1";
	
	if (!runOpenssl())
	{
		QDetailsMessageBox::critical(this, QObject::tr("Error"), 
			QObject::tr("Cannot create domain file."), mOpensslError);
		return;
	}
	
	// Run OpenSSL and create request.
	mOpensslArgs.clear();
	mOpensslArgs << "req" << "-new" << "-newkey" << algo << "-keyout" <<
		mKeyPath->text() << "-md" << md << "-utf8" << "-batch" <<
		"-config" << mOpensslConfFilePath << "-passout" <<
		QString("pass:") + pass << "-subj" << mGenCertReq->subject() <<
		"-out" << reqPath;
	
	if (!runOpenssl())
	{
		if(mOpensslError.contains("error:0906A065"))
		{
			QDetailsMessageBox::critical(this, tr("Error"), tr("Incorrect password."),
				mOpensslError);
		}
		else
		{
			QDetailsMessageBox::critical(this, tr("Error"), tr("Cannot create request."), 
				mOpensslError);
		}
	}
	else
	{
		QMessageBox::information(this, tr("Information"), tr("Request was created successfully."));
	}
}

void
MainWindow::helpSlot()
{
	mAboutBox->setActiveHelp();
	mAboutBox->exec();
}

void
MainWindow::aboutSlot()
{
	mAboutBox->setActiveAbout();
	mAboutBox->exec();
}

void
MainWindow::langEnSlot()
{
	lang = "";
	langExclamation();
}

void
MainWindow::langPlSlot()
{
	lang = "pl";
	langExclamation();
}

QString
MainWindow::getPass(bool confirm)
{
	bool ok;
	QString pass;
	QString repass;
	
	do
	{
		pass = QInputDialog::getText(this, tr("Enter private key password"),
			tr("Password:"), QLineEdit::Password, QString(), &ok);
		
		if (!ok)
			return QString();
		
		if (pass.isEmpty())
			QMessageBox::warning(this, tr("Information"), tr("Key password cannot be empty."));
	}
	while (pass.isEmpty());
	
	if (confirm)
	{
		repass = QInputDialog::getText(this, tr("Re-enter password"),
			tr("Re-entered password:"), QLineEdit::Password, QString(), &ok);
		
		if (!ok)
			return QString();
		
		if(pass != repass)
		{
			QMessageBox::information(this, tr("Information"), tr("Entered passwords are not equal."));
			return QString();
		}
		else
			return pass;
	}
	else
		return pass;
}

void
MainWindow::langExclamation()
{
	QMessageBox::information(this, tr("Information"), tr("Application language was "
		"changed. Please restart application to apply changes."));
}

bool
MainWindow::runOpenssl()
{
	QProcess openssl;
	
	openssl.setWorkingDirectory(QDir::homePath() + QDir::separator() +
		QString(".stoken_softadmin"));
	openssl.start(mOpensslCmd, mOpensslArgs);
	
	QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
	
	while (openssl.state() != QProcess::NotRunning)
		QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
	
	QApplication::restoreOverrideCursor();
	
	mOpensslOut.clear();
	mOpensslError.clear();
	
	mOpensslOut = QString(openssl.readAllStandardOutput());
	mOpensslError = QString(openssl.readAllStandardError());
	
	if (openssl.exitCode() != 0)
		return false;
	
	return true;
}

void
MainWindow::createActions()
{
	mSelectKeyAct = new QAction(QIcon(QString(":img/keys_folder_blue.png")), tr("Select key file"), this);
	connect(mSelectKeyAct, SIGNAL(triggered()), this, SLOT(selectKeySlot()));
	
	mHelpAct = new QAction(QIcon(QString(":img/contents_ii.png")), tr("Manual"), this);
	mHelpAct->setShortcut(tr("Ctrl+H"));
	connect(mHelpAct, SIGNAL(triggered()), this, SLOT(helpSlot()));
	
	mAboutAct = new QAction(QIcon(QString(":img/info.png")), tr("About"), this);
	connect(mAboutAct, SIGNAL(triggered()), this, SLOT(aboutSlot()));
	
	mExitAct = new QAction(QIcon(QString(":img/exit.png")), tr("Exit"), this);
	mExitAct->setShortcut(tr("Ctrl+Q"));
	connect(mExitAct, SIGNAL(triggered()), this, SLOT(close()));
	
	mLangEn = new QAction(tr("English"), this);
	mLangEn->setCheckable(true);
	connect(mLangEn, SIGNAL(triggered()), this, SLOT(langEnSlot()));
	mLangPl = new QAction(tr("Polish"), this);
	mLangPl->setCheckable(true);
	connect(mLangPl, SIGNAL(triggered()), this, SLOT(langPlSlot()));
	
	mLang = new QActionGroup(this);
	mLang->addAction(mLangEn);
	mLang->addAction(mLangPl);
}

void
MainWindow::createMenus()
{
	QMenu *menu, *language;
	
	menu = menuBar()->addMenu(tr("&General"));
	
	language = menu->addMenu(tr("&Language"));
	language->addAction(mLangEn);
	language->addAction(mLangPl);
	
	if (lang == "pl")
		mLangPl->setChecked(true);
	else
		mLangEn->setChecked(true);
	
	menu->addSeparator();
	
	menu->addAction(mExitAct);
	
	menu = menuBar()->addMenu(tr("&Key"));
	menu->addAction(mSelectKeyAct);
	
	menu = menuBar()->addMenu(tr("&Help"));
	menu->addAction(mHelpAct);
	menu->addSeparator();
	menu->addAction(mAboutAct);
}

void
MainWindow::createToolBars()
{
	QToolBar *toolBar;
	toolBar = addToolBar(tr("Key"));
	toolBar->addAction(mSelectKeyAct);
	
	toolBar = addToolBar(tr("Help"));
	toolBar->addAction(mHelpAct);
	toolBar->addAction(mAboutAct);
}

void
MainWindow::createAboutBox()
{
	mAboutBox = new AboutBox(this);
	
	// About informations.
	mAboutBox->setAboutPixmap(QPixmap(QString(":img/stoken_about_softadmin_384.png")));
	mAboutBox->setAboutInfo(tr("<h2>sToken SoftAdmin v1.2</h2>"
	"<br>This program is free software: you can redistribute it and/or modify "
	"it under the terms of the GNU General Public License as published by the Free "
	"Software Foundation, either version 3 of the License, or (at your option) any "
	"later version.<br><br>"
	"<a href=\"http://www.stoken.eu/soft/index.en.html\">http://www.stoken.eu/soft</a>"));
	
	// Help informations.
	mAboutBox->setHelpPixmap(QPixmap(QString(":img/stoken_about_softadmin_384.png")));
	mAboutBox->setHelpInfo(tr("<h2>sToken SoftAdmin v1.2</h2>"
	"<br>Online instructions to this application are avaliable on the following "
	"website: <a href=\"http://www.stoken.eu/products/man_softadmin/index.en.html\">"
	"http://www.stoken.eu</a>"));
	
	// License.
	QFile file(QString(":LICENSE"));
	file.open(QIODevice::ReadOnly);
	QByteArray license = file.readAll();
	file.close();
	mAboutBox->setLicense(QString(license.data()));
	
	// Thanks.
	mAboutBox->setThanksPixmap(QPixmap(QString(":img/stoken_about_softadmin_384.png")));
	mAboutBox->setThanksInfo(tr("<h4>Thanks to</h4>"
		"Andrzej Chmielowiec, Wiktor Kazana"
		"<h4>Special thanks</h4>"
		"Special thanks to all people who participate in development process of "
		"<a href=\"www.openssl.org\">OpenSSL</a> library and "
		"<a href=\"www.openvpn.net\">OpenVPN</a> software. Both of this projects "
		"are crucial for this application."
		));
}
