mirror of
https://github.com/chris2511/xca.git
synced 2026-09-12 19:51:05 +05:00
441 lines
9.8 KiB
C++
441 lines
9.8 KiB
C++
/* vi: set sw=4 ts=4:
|
|
*
|
|
* Copyright (C) 2001 - 2014 Christian Hohnstaedt.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "func.h"
|
|
#include "exception.h"
|
|
#include "asn1time.h"
|
|
#include "settings.h"
|
|
#include "XcaWarningCore.h"
|
|
#include <openssl/evp.h>
|
|
#include <openssl/objects.h>
|
|
#include <openssl/sha.h>
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/buffer.h>
|
|
|
|
#ifdef Q_OS_MACOS
|
|
#include <IOKit/IOKitLib.h>
|
|
#define I18N_DIR ""
|
|
#else
|
|
#define I18N_DIR "i18n/"
|
|
#endif
|
|
#include <QStandardPaths>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QFileInfo>
|
|
#include <QStringList>
|
|
#include <QCoreApplication>
|
|
#include <QDebug>
|
|
#include <QRegularExpression>
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
#include <shlobj.h>
|
|
#include <conio.h>
|
|
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x04
|
|
#endif
|
|
#else
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#define getch() getchar()
|
|
#endif
|
|
|
|
int console_write(FILE *fp, const QByteArray &ba)
|
|
{
|
|
if (ba.size() == 0)
|
|
return 0;
|
|
#if defined(Q_OS_WIN32)
|
|
HANDLE con = GetStdHandle(fp == stderr ? STD_ERROR_HANDLE :
|
|
STD_OUTPUT_HANDLE);
|
|
if (con != INVALID_HANDLE_VALUE) {
|
|
QString string = QString::fromUtf8(ba);
|
|
WriteConsoleW(con, string.utf16(), string.size(), NULL, NULL);
|
|
//return 0;
|
|
}
|
|
#endif
|
|
fputs(ba.constData(), fp);
|
|
fflush(fp);
|
|
return 0;
|
|
}
|
|
|
|
Passwd readPass()
|
|
{
|
|
Passwd pw;
|
|
#if !defined(Q_OS_WIN32)
|
|
struct termios t, back;
|
|
if (tcgetattr(0, &t))
|
|
throw errorEx(strerror(errno));
|
|
back = t;
|
|
t.c_lflag &= ~(ECHO | ICANON);
|
|
if (tcsetattr(0, TCSAFLUSH, &t))
|
|
throw errorEx(strerror(errno));
|
|
#else
|
|
qFatal("Password input not supported");
|
|
#endif
|
|
while(1) {
|
|
char p = getch();
|
|
if (p == '\n' || p == '\r')
|
|
break;
|
|
if (p == 0x7f)
|
|
pw.chop(1);
|
|
else
|
|
pw += p;
|
|
}
|
|
fputc('\n', stdout);
|
|
#if !defined(Q_OS_WIN32)
|
|
if (tcsetattr(0, TCSAFLUSH, &back))
|
|
throw errorEx(strerror(errno));
|
|
#endif
|
|
return pw;
|
|
}
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
static QString registryInstallDir()
|
|
{
|
|
QString dir;
|
|
wchar_t inst_dir[2048] = L"";
|
|
ULONG len = sizeof inst_dir;
|
|
|
|
if (RegGetValueW(HKEY_LOCAL_MACHINE, L"Software\\xca",
|
|
L"Install_Dir64", RRF_RT_REG_SZ, NULL,
|
|
inst_dir, &len) != ERROR_SUCCESS)
|
|
return dir;
|
|
|
|
/* "len" is in octets */
|
|
len /= sizeof inst_dir[0];
|
|
/* "len" includes the trailing \0\0 */
|
|
dir = QString::fromWCharArray(inst_dir, len -1);
|
|
return QFileInfo(dir).canonicalFilePath();
|
|
}
|
|
#endif
|
|
|
|
int portable_app()
|
|
{
|
|
static int portable = -1;
|
|
qDebug() << "portable" << portable;
|
|
QString f1, f2;
|
|
if (portable == -1) {
|
|
#if defined(Q_OS_WIN32)
|
|
f1 = registryInstallDir();
|
|
f2 = QCoreApplication::applicationDirPath();
|
|
/* f1 == f2 Registry entry of install dir exists and matches
|
|
* path of this xca.exe -> Installed. Not the portable app
|
|
*/
|
|
portable = f1 == f2 ? 0 : 1;
|
|
qDebug() << "Portable:" << f1 << " != " << f2;
|
|
#else
|
|
const char *p = getenv("XCA_PORTABLE");
|
|
portable = p && *p;
|
|
#endif
|
|
}
|
|
return portable;
|
|
}
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
static QString specialFolder(int csidl)
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
wchar_t buf[MAX_PATH] = L"";
|
|
|
|
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, csidl, &pidl)))
|
|
SHGetPathFromIDListW(pidl, buf);
|
|
|
|
QString f = QString::fromWCharArray(buf);
|
|
qDebug() << "Special Folder" << csidl << f;
|
|
return QFileInfo(f).canonicalFilePath();
|
|
}
|
|
#endif
|
|
|
|
const QString getHomeDir()
|
|
{
|
|
return portable_app() ? QCoreApplication::applicationDirPath() :
|
|
QStandardPaths::writableLocation(
|
|
QStandardPaths::DocumentsLocation);
|
|
}
|
|
|
|
/* For portable APP remove leading file name if it is
|
|
* the app directory.
|
|
*/
|
|
QString relativePath(QString path)
|
|
{
|
|
QFileInfo fi_path(path);
|
|
QFileInfo fi_home(getHomeDir());
|
|
|
|
QString prefix = fi_home.absoluteFilePath();
|
|
path = fi_path.absoluteFilePath();
|
|
|
|
if (portable_app()) {
|
|
if (path.startsWith(prefix))
|
|
path = path.mid(prefix.length()+1);
|
|
}
|
|
return path;
|
|
}
|
|
|
|
const QString getLibDir()
|
|
{
|
|
#if defined(Q_OS_WIN32)
|
|
return specialFolder(CSIDL_SYSTEM);
|
|
#else
|
|
QString ulib = "/usr/lib/";
|
|
QString lib = "/lib/";
|
|
QString multi;
|
|
QString hd = ulib;
|
|
|
|
QFile f(ulib + "pkg-config.multiarch");
|
|
if (f.open(QIODevice::ReadOnly)) {
|
|
QTextStream in(&f);
|
|
multi = in.readLine();
|
|
if (!multi.isEmpty())
|
|
multi += "/";
|
|
}
|
|
QStringList dirs; dirs
|
|
<< ulib + multi + "pkcs11/"
|
|
<< lib + multi + "pkcs11/"
|
|
<< ulib + "pkcs11/"
|
|
<< lib + "pkcs11/"
|
|
<< ulib + multi
|
|
<< lib + multi
|
|
<< ulib
|
|
<< lib;
|
|
foreach(QString dir, dirs) {
|
|
if (QDir(dir).exists()) {
|
|
hd = dir;
|
|
break;
|
|
}
|
|
}
|
|
return QFileInfo(hd).canonicalFilePath();
|
|
#endif
|
|
}
|
|
|
|
const QString getDocDir()
|
|
{
|
|
static QString docdir;
|
|
|
|
if (!docdir.isEmpty())
|
|
return docdir;
|
|
|
|
QStringList docs;
|
|
#ifdef DOCDIR
|
|
docs << QString(DOCDIR);
|
|
#endif
|
|
#ifdef INSTALL_DATA_PREFIX
|
|
docs << QString(INSTALL_DATA_PREFIX);
|
|
#endif
|
|
docs += QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
|
|
foreach (docdir, docs) {
|
|
#ifndef Q_OS_MACOS
|
|
docdir += "/html";
|
|
#endif
|
|
if (QFileInfo::exists(docdir + "/xca.qhc")) {
|
|
qDebug() << "Detected" << docdir + "/xca.qhc";
|
|
return docdir;
|
|
}
|
|
}
|
|
docdir = QString();
|
|
return docdir;
|
|
}
|
|
|
|
// The intent of this function is to return the proper location for
|
|
// user-controlled settings on the current platform
|
|
const QString getUserSettingsDir()
|
|
{
|
|
static QString dir;
|
|
|
|
if (!dir.isEmpty())
|
|
return dir;
|
|
|
|
dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
if (portable_app())
|
|
dir = QCoreApplication::applicationDirPath() + "/settings";
|
|
|
|
#endif
|
|
if (!QDir().mkpath(dir))
|
|
qCritical("Failed to create Path: '%s'", CCHAR(dir));
|
|
|
|
return dir;
|
|
}
|
|
|
|
const QString getI18nDir()
|
|
{
|
|
static QString qm;
|
|
if (!qm.isEmpty())
|
|
return qm;
|
|
#ifdef INSTALL_DATA_PREFIX
|
|
if (QFileInfo::exists(INSTALL_DATA_PREFIX "/xca_de.qm"))
|
|
qm = INSTALL_DATA_PREFIX "/xca_de.qm";
|
|
#endif
|
|
if (qm.isEmpty())
|
|
qm = QStandardPaths::locate(QStandardPaths::AppDataLocation,
|
|
I18N_DIR "xca_de.qm");
|
|
if (qm.isEmpty())
|
|
qm = QCoreApplication::applicationDirPath() + "/xca_de.qm";
|
|
qm = QFileInfo(qm).path();
|
|
qDebug() << "QM" << qm;
|
|
return qm;
|
|
}
|
|
|
|
void migrateOldPaths()
|
|
{
|
|
QString old;
|
|
#if defined(Q_OS_UNIX)
|
|
old = QDir::homePath() + "/.xca";
|
|
|
|
#elif defined(Q_OS_MACOS)
|
|
old = QStandardPaths::writableLocation(
|
|
QStandardPaths::GenericDataLocation) + "/data/" +
|
|
QCoreApplication::applicationName();
|
|
#endif
|
|
QDir old_dir(old);
|
|
if (!old.isEmpty() && old_dir.exists()) {
|
|
qDebug() << "Old XCA directory exists" << old;
|
|
QString new_dir = getUserSettingsDir() + "/";
|
|
foreach(QString n, QStringList({"dbhistory", "defaultdb",
|
|
"defaultlang", ".rnd"}))
|
|
{
|
|
old_dir.rename(n, new_dir + n);
|
|
qDebug() << "Move file" << old + "/" + n << new_dir + n;
|
|
}
|
|
old_dir.rmdir(old);
|
|
}
|
|
|
|
QString usd = getUserSettingsDir();
|
|
qDebug() << "User settings dir" << usd;
|
|
QFile file(usd + "/defaultlang");
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QLocale lang = QLocale(QString(file.read(128)));
|
|
GlobalSettings().setValue("language", lang);
|
|
file.close();
|
|
file.remove();
|
|
}
|
|
qDebug() << "REMOVE" << file.fileName();
|
|
file.setFileName(usd + "/defaultdb");
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QString db = QString(file.read(128));
|
|
if (!db.isEmpty()) {
|
|
db = db.trimmed();
|
|
GlobalSettings().setValue("defaultdb", db);
|
|
file.close();
|
|
file.remove();
|
|
}
|
|
}
|
|
file.setFileName(usd + "/dbhistory");
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QStringList history;
|
|
QSettings s = GlobalSettings();
|
|
int idx = 0;
|
|
s.beginWriteArray("history");
|
|
while (!file.atEnd()) {
|
|
QByteArray ba(file.readLine(1024));
|
|
if (ba.size() == 0)
|
|
break;
|
|
QString name = QString::fromUtf8(ba).trimmed();
|
|
if (name.size() == 0 || history.contains(name))
|
|
continue;
|
|
history << name;
|
|
s.setArrayIndex(idx++);
|
|
s.setValue("name", name);
|
|
}
|
|
s.endArray();
|
|
file.close();
|
|
file.remove();
|
|
}
|
|
}
|
|
|
|
QString hostId()
|
|
{
|
|
static QString id;
|
|
unsigned char guid[100] = "", md[SHA_DIGEST_LENGTH];
|
|
|
|
if (!id.isEmpty())
|
|
return id;
|
|
|
|
#if defined(Q_OS_WIN32)
|
|
#define REG_CRYPTO "SOFTWARE\\Microsoft\\Cryptography"
|
|
#define REG_GUID "MachineGuid"
|
|
ULONG dwGuid = sizeof guid;
|
|
HKEY hKey;
|
|
|
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, REG_CRYPTO, 0,
|
|
KEY_READ, &hKey) != ERROR_SUCCESS) {
|
|
XCA_WARN("Registry Key: '" REG_CRYPTO "' not found");
|
|
} else {
|
|
if (RegQueryValueExA(hKey, REG_GUID, NULL, NULL,
|
|
guid, &dwGuid) != ERROR_SUCCESS) {
|
|
XCA_WARN("Registry Key: '" REG_CRYPTO "\\" REG_GUID
|
|
"' not found");
|
|
}
|
|
}
|
|
RegCloseKey(hKey);
|
|
|
|
#elif defined(Q_OS_MACOS)
|
|
io_registry_entry_t ioRegistryRoot = IORegistryEntryFromPath(
|
|
kIOMasterPortDefault, "IOService:/");
|
|
CFStringRef uuidCf = (CFStringRef)IORegistryEntryCreateCFProperty(
|
|
ioRegistryRoot, CFSTR(kIOPlatformUUIDKey),
|
|
kCFAllocatorDefault, 0);
|
|
|
|
|
|
CFStringGetCString(uuidCf, (char*)guid, sizeof guid,
|
|
kCFStringEncodingMacRoman);
|
|
|
|
qDebug() << QString::fromCFString(uuidCf) << (char*)guid;
|
|
|
|
IOObjectRelease(ioRegistryRoot);
|
|
CFRelease(uuidCf);
|
|
|
|
#else
|
|
QString mach_id;
|
|
const QStringList dirs = { "/etc", "/var/lib/dbus", "/var/db/dbus" };
|
|
for (const QString &dir : dirs) {
|
|
QFile file(dir + "/machine-id");
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QTextStream in(&file);
|
|
mach_id = in.readLine().trimmed();
|
|
file.close();
|
|
}
|
|
qDebug() << "ID:" << mach_id;
|
|
if (!mach_id.isEmpty()) {
|
|
snprintf((char*)guid, sizeof guid, "%s", CCHAR(mach_id));
|
|
break;
|
|
}
|
|
}
|
|
if (mach_id.isEmpty())
|
|
sprintf((char*)guid, "%ld", gethostid());
|
|
#endif
|
|
guid[sizeof guid -1] = 0;
|
|
SHA1(guid, strlen((char*)guid), md);
|
|
id = QByteArray((char*)md, (int)sizeof md).toBase64().mid(0, 8);
|
|
|
|
qDebug() << "GUID:" << guid << "ID:" << id;
|
|
|
|
return id;
|
|
}
|
|
|
|
QString fingerprint(const QByteArray &data, const EVP_MD *type)
|
|
{
|
|
return formatHash(Digest(data, type),
|
|
Settings["fp_separator"], Settings["fp_digits"]);
|
|
}
|
|
|
|
void update_workingdir(const QString &file)
|
|
{
|
|
QFileInfo fi(file);
|
|
if (fi.isDir())
|
|
Settings["workingdir"] = fi.absoluteFilePath();
|
|
else
|
|
Settings["workingdir"] = fi.absolutePath();
|
|
}
|
|
|
|
QSettings GlobalSettings()
|
|
{
|
|
return QSettings(getUserSettingsDir() + "/settings.ini",
|
|
QSettings::IniFormat);
|
|
}
|