/* 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 #include #include #include #include #include #include #ifdef Q_OS_MACOS #include #define I18N_DIR "" #else #define I18N_DIR "i18n/" #endif #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN32) #include #include #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x04 #endif #else #include #include #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; 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()) return; 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 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) { Settings["workingdir"] = QFileInfo(file).absolutePath(); }