From f12b482d97cc7ce213edb6defd4764016df675d2 Mon Sep 17 00:00:00 2001 From: Jessica Reid Date: Tue, 12 Nov 2024 19:37:20 -0500 Subject: [PATCH 1/3] seems to be in a working state on linux --- src/downloadextractthread.cpp | 42 ++++++ src/imagewriter.cpp | 169 +++++++++++++++++++++- src/imagewriter.h | 14 +- src/qmlcomponents/regex_validator_qt6.qml | 2 +- 4 files changed, 218 insertions(+), 9 deletions(-) diff --git a/src/downloadextractthread.cpp b/src/downloadextractthread.cpp index a968ddecc..45511c9f5 100644 --- a/src/downloadextractthread.cpp +++ b/src/downloadextractthread.cpp @@ -398,6 +398,48 @@ void DownloadExtractThread::extractMultiFileRun() } } + if(_imgWriterSettings.contains("UNRAID_LANG_JSON")) { + // remove this tmp file now that we're definitely done with it + QFile langJson(_imgWriterSettings["UNRAID_LANG_JSON"].toByteArray()); + langJson.remove(); + } + + if(_imgWriterSettings.contains("UNRAID_LANG_CODE")) + { + QString unraidLangCode(_imgWriterSettings["UNRAID_LANG_CODE"].toString()); + if(_imgWriterSettings.contains("UNRAID_LANG_XML")) { + QFile langXml(_imgWriterSettings["UNRAID_LANG_XML"].toByteArray()); + QFile::rename(langXml.fileName(), folder + "/config/plugins/lang-" + unraidLangCode + ".xml"); + } + + if(_imgWriterSettings.contains("UNRAID_LANG_ZIP")) { + QFile langZip(_imgWriterSettings["UNRAID_LANG_ZIP"].toByteArray()); + QFile::rename(langZip.fileName(), folder + "/config/plugins/dynamix/lang-" + unraidLangCode + ".zip"); + } + QFile dynamixCfg(folder + "/config/plugins/dynamix/dynamix.cfg"); + if (dynamixCfg.exists()) + { + dynamixCfg.open(QIODevice::ReadOnly); + QString dataText = dynamixCfg.readAll(); + dynamixCfg.close(); + + QString oldData(dataText); + dataText.replace("locale=\"\"", "locale=\"" + unraidLangCode + "\"", Qt::CaseInsensitive); + if(oldData == dataText) + { + // if this string wasn't found for replacement, just add it + dataText += "locale=\"" + unraidLangCode + "\""; + } + + if (dynamixCfg.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&dynamixCfg); + out << dataText; + } + dynamixCfg.close(); + } + } + // restore make bootable scripts and/or syslinux, if necessary QDir dirTarget(folder); if (dirTarget.mkdir("syslinux")) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 5281b1f42..df49ddc8e 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #ifndef QT_NO_WIDGETS #include #include @@ -188,6 +189,7 @@ ImageWriter::ImageWriter(QObject *parent) { _currentLang = langname; _currentLangcode = currentlangcode; + _unraidLangcode = _generateUnraidLangcode(currentlangcode, langname); } } //_currentKeyboard = "us"; @@ -354,15 +356,24 @@ void ImageWriter::startWrite() if (_multipleFilesInZip) { static_cast(_thread)->enableMultipleFileExtraction(); - QString label{""}; if(_initFormat == "UNRAID") { - // if this is an unraid zip, the volume label needs to be UNRAID for the make bootable script to work as intended - label = "UNRAID"; + // in order to properly propagate language selection to the unraid usb files themselves, + // we need to kick of a chain of events that starts with formatting the drive, + // which then kicks off a download of the available languages json, + // which then kicks off a download of the select language xml, + // which, if there is a zip file specified in that xml, then kicks off a download of that zip file, + // which then kicks of the download/extract of the actual unraid files + QString label{"UNRAID"}; // since this is an unraid zip, the volume label needs to be UNRAID for the make bootable script to work as intended + DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), label, this); + connect(dft, SIGNAL(error(QString)), SLOT(onError(QString))); + connect(dft, SIGNAL(success()), SLOT(onUnraidFormatComplete())); + dft->start(); + } else { + DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), "", this); + connect(dft, SIGNAL(success()), _thread, SLOT(start())); + connect(dft, SIGNAL(error(QString)), SLOT(onError(QString))); + dft->start(); } - DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), label, this); - connect(dft, SIGNAL(success()), _thread, SLOT(start())); - connect(dft, SIGNAL(error(QString)), SLOT(onError(QString))); - dft->start(); } else { @@ -1355,6 +1366,7 @@ void ImageWriter::changeLanguage(const QString &newLanguageName) replaceTranslator(trans); _currentLang = newLanguageName; _currentLangcode = langcode; + _unraidLangcode = _generateUnraidLangcode(_currentLangcode, _currentLang); } else { @@ -1520,3 +1532,146 @@ bool ImageWriter::windowsBuild() { return false; #endif } + +QString ImageWriter::_generateUnraidLangcode(const QString& langcode, const QString& languageName) +{ + QString unraidLangcode = langcode + "_"; + + if (langcode == "en") { + unraidLangcode.append("US"); + } + else if (langcode == "pt") { + if (languageName.contains("Brasil")) { + unraidLangcode.append("BR"); + } + else { + unraidLangcode.append("PT"); + } + } + else if (langcode == "sv") { + unraidLangcode.append("SE"); + } + else if (langcode == "uk") { + unraidLangcode.append("UA"); + } + else if (langcode == "zh") { + unraidLangcode.append("CN"); + } + else { + unraidLangcode.append(langcode.toUpper()); + } + return unraidLangcode; +} + +QByteArray ImageWriter::_parseUnraidLangJson(const QByteArray& jsonFilePath, const QString& unraidLangCode) +{ + QByteArray jsonData = _readFileContents(jsonFilePath); + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); + + qDebug() << parseError.errorString(); + if (parseError.error != QJsonParseError::NoError) { + onError(parseError.errorString()); + } + + QJsonObject jsonObj = jsonDoc.object(); + + QJsonValue langVal = jsonObj.value(unraidLangCode); + QJsonObject langObj = langVal.toObject(); + + QJsonValue url = langObj.value("URL"); + return url.toString().toUtf8(); +} + +QString ImageWriter::_parseUnraidLangXml(const QByteArray& xmlFilePath) +{ + QByteArray xmlData = _readFileContents(xmlFilePath); + QXmlStreamReader xml(xmlData); + int count = 0; + QString found; + + while (!xml.atEnd() && !xml.hasError()) { + QXmlStreamReader::TokenType token = xml.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (xml.name() == "LanguageURL") { + xml.readNext(); + if (xml.atEnd()) { + break; + } + + if (xml.isCharacters() && (xml.text() != "\n")) { + found.append(xml.text().toString()); + } + } + } + } + + return found; +} + +QByteArray ImageWriter::_readFileContents(const QByteArray& filePath) +{ + QFile fileIn(filePath); + if(!fileIn.open(QIODevice::ReadOnly)) { + onError("Error opening " + filePath); + } + // remove excess null characters (they cause the qt json parser to fail) + QByteArray data = fileIn.readAll().replace('\0', ""); + fileIn.close(); + if(!fileIn.open(QIODevice::WriteOnly)) { + onError("Error opening " + filePath); + } + fileIn.write(data); + return data; +} + +void ImageWriter::onUnraidFormatComplete() +{ + QTemporaryFile tmp; + tmp.open(); // need this to actually generate a unique file path + _unraidLangJsonTmpPath = tmp.fileName().toUtf8(); + setSetting("imagecustomization/UNRAID_LANG_JSON", _unraidLangJsonTmpPath); + DownloadThread * dl_thread = new DownloadThread(_unraidLangJsonUrl, _unraidLangJsonTmpPath, "", this); + connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); + connect(dl_thread, SIGNAL(success()), SLOT(onUnraidJsonDownloadComplete())); + dl_thread->start(); +} + +void ImageWriter::onUnraidJsonDownloadComplete() +{ + QByteArray xmlUrl = _parseUnraidLangJson(_unraidLangJsonTmpPath, _unraidLangcode); + QTemporaryFile tmp; + tmp.open(); // need this to actually generate a unique file path + _unraidLangXmlTmpPath = tmp.fileName().toUtf8(); + setSetting("imagecustomization/UNRAID_LANG_XML", _unraidLangXmlTmpPath); + DownloadThread * dl_thread = new DownloadThread(xmlUrl, _unraidLangXmlTmpPath, "", this); + connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); + connect(dl_thread, SIGNAL(success()), SLOT(onUnraidXmlDownloadComplete())); + dl_thread->start(); +} + +void ImageWriter::onUnraidXmlDownloadComplete() +{ + QString zipUrl = _parseUnraidLangXml(_unraidLangXmlTmpPath); + setSetting("imagecustomization/UNRAID_LANG_CODE", _unraidLangcode); + if(zipUrl.isEmpty()) + { + // this has to be called again so getSavedCustomizationSettings returns the updated qvariantmap + _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, getSavedCustomizationSettings()); + _thread->start(); + } + else + { + QTemporaryFile tmp; + tmp.open(); // need this to actually generate a unique file path + _unraidLangZipTmpPath = tmp.fileName().toUtf8(); + setSetting("imagecustomization/UNRAID_LANG_ZIP", _unraidLangZipTmpPath); + // this has to be called again so getSavedCustomizationSettings returns the updated qvariantmap + _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, getSavedCustomizationSettings()); + DownloadThread * dl_thread = new DownloadThread(zipUrl.toUtf8(), _unraidLangZipTmpPath, "", this); + connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); + connect(dl_thread, SIGNAL(success()), _thread, SLOT(start())); + dl_thread->start(); + } +} + diff --git a/src/imagewriter.h b/src/imagewriter.h index c2e5c5c00..273eef96b 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "config.h" #include "powersaveblocker.h" #include "drivelistmodel.h" @@ -183,6 +184,9 @@ protected slots: void onPreparationStatusUpdate(QString msg); void handleNetworkRequestFinished(QNetworkReply *data); void onSTPdetected(); + void onUnraidFormatComplete(); + void onUnraidJsonDownloadComplete(); + void onUnraidXmlDownloadComplete(); private: // Recursively walk all the entries with subitems and, for any which @@ -195,7 +199,7 @@ protected slots: protected: QUrl _src, _repo; - QString _dst, _cacheFileName, _parentCategory, _osName, _currentLang, _currentLangcode, _currentKeyboard; + QString _dst, _cacheFileName, _parentCategory, _osName, _currentLang, _currentLangcode, _currentKeyboard, _unraidLangcode; QByteArray _expectedHash, _cachedFileHash, _cmdline, _config, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat; quint64 _downloadLen, _extrLen, _devLen, _dlnow, _verifynow; DriveListModel _drivelist; @@ -212,6 +216,10 @@ protected slots: QWinTaskbarButton *_taskbarButton; #endif bool _guidValid; + const QByteArray _unraidLangJsonUrl{"https://assets.ca.unraid.net/feed/languageSelection.json"}; + QByteArray _unraidLangJsonTmpPath; + QByteArray _unraidLangXmlTmpPath; + QByteArray _unraidLangZipTmpPath; void _parseCompressedFile(); void _parseXZFile(); @@ -219,6 +227,10 @@ protected slots: QString _privKeyFileName(); QString _sshKeyDir(); QString _sshKeyGen(); + QString _generateUnraidLangcode(const QString& langcode, const QString& languageName); + QByteArray _readFileContents(const QByteArray& filePath); + QString _parseUnraidLangXml(const QByteArray& xmlFilePath); + QByteArray _parseUnraidLangJson(const QByteArray& jsonFilePath, const QString& unraidLangCode); }; #endif // IMAGEWRITER_H diff --git a/src/qmlcomponents/regex_validator_qt6.qml b/src/qmlcomponents/regex_validator_qt6.qml index db2c280fd..d3830c9db 100644 --- a/src/qmlcomponents/regex_validator_qt6.qml +++ b/src/qmlcomponents/regex_validator_qt6.qml @@ -1,4 +1,4 @@ -import QtQuick +import QtQuick 2.9 RegularExpressionValidator { regularExpression: /^[A-Za-z0-9]([A-Za-z0-9\-\.]{0,13}[A-Za-z0-9])?$/ From 26e3f0b1ec5da74ebcf3943147c767797f911834 Mon Sep 17 00:00:00 2001 From: Sarah Cattigan Date: Wed, 13 Nov 2024 14:41:53 -0500 Subject: [PATCH 2/3] Mac fixes for invalid operands and permissions issues. --- src/imagewriter.cpp | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index df49ddc8e..2be51a135 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -1593,13 +1593,13 @@ QString ImageWriter::_parseUnraidLangXml(const QByteArray& xmlFilePath) while (!xml.atEnd() && !xml.hasError()) { QXmlStreamReader::TokenType token = xml.readNext(); if (token == QXmlStreamReader::StartElement) { - if (xml.name() == "LanguageURL") { + if (QString::compare(xml.name(), "LanguageURL") == 0) { xml.readNext(); if (xml.atEnd()) { break; } - if (xml.isCharacters() && (xml.text() != "\n")) { + if (xml.isCharacters() && (QString::compare(xml.text(), "\n") != 0)) { found.append(xml.text().toString()); } } @@ -1627,9 +1627,12 @@ QByteArray ImageWriter::_readFileContents(const QByteArray& filePath) void ImageWriter::onUnraidFormatComplete() { - QTemporaryFile tmp; - tmp.open(); // need this to actually generate a unique file path - _unraidLangJsonTmpPath = tmp.fileName().toUtf8(); + QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); + QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + _unraidLangJsonTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); + QFile langFile(_unraidLangJsonTmpPath); + langFile.open(QIODevice::ReadWrite); + setSetting("imagecustomization/UNRAID_LANG_JSON", _unraidLangJsonTmpPath); DownloadThread * dl_thread = new DownloadThread(_unraidLangJsonUrl, _unraidLangJsonTmpPath, "", this); connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); @@ -1640,9 +1643,13 @@ void ImageWriter::onUnraidFormatComplete() void ImageWriter::onUnraidJsonDownloadComplete() { QByteArray xmlUrl = _parseUnraidLangJson(_unraidLangJsonTmpPath, _unraidLangcode); - QTemporaryFile tmp; - tmp.open(); // need this to actually generate a unique file path - _unraidLangXmlTmpPath = tmp.fileName().toUtf8(); + + QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); + QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + _unraidLangXmlTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); + QFile langFile(_unraidLangXmlTmpPath); + langFile.open(QIODevice::ReadWrite); + setSetting("imagecustomization/UNRAID_LANG_XML", _unraidLangXmlTmpPath); DownloadThread * dl_thread = new DownloadThread(xmlUrl, _unraidLangXmlTmpPath, "", this); connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); @@ -1662,9 +1669,13 @@ void ImageWriter::onUnraidXmlDownloadComplete() } else { - QTemporaryFile tmp; - tmp.open(); // need this to actually generate a unique file path - _unraidLangZipTmpPath = tmp.fileName().toUtf8(); + QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); + QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + _unraidLangZipTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); + QFile langFile(_unraidLangZipTmpPath); + langFile.open(QIODevice::ReadWrite); + + setSetting("imagecustomization/UNRAID_LANG_ZIP", _unraidLangZipTmpPath); // this has to be called again so getSavedCustomizationSettings returns the updated qvariantmap _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, getSavedCustomizationSettings()); From 72b2d620cbd10fffb4754aea0d91ab13525a996d Mon Sep 17 00:00:00 2001 From: Jessica Reid Date: Fri, 15 Nov 2024 15:58:38 -0500 Subject: [PATCH 3/3] a little light refactoring --- src/downloadextractthread.cpp | 304 +++++++++++++++++++++------------- src/downloadextractthread.h | 7 + src/imagewriter.cpp | 77 ++++----- src/imagewriter.h | 1 + 4 files changed, 233 insertions(+), 156 deletions(-) diff --git a/src/downloadextractthread.cpp b/src/downloadextractthread.cpp index 45511c9f5..72a048348 100644 --- a/src/downloadextractthread.cpp +++ b/src/downloadextractthread.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using namespace std; @@ -355,125 +356,11 @@ void DownloadExtractThread::extractMultiFileRun() } if(_initFormat == "UNRAID") { - if(_allNetworkSettingsPresent() && _imgWriterSettings["static"].toBool()) - { - QFile fileNetwork(folder + "/config/network.cfg"); - if (fileNetwork.exists()) - { - fileNetwork.open(QIODevice::ReadOnly); - QString dataText = fileNetwork.readAll(); - fileNetwork.close(); - - dataText.replace("USE_DHCP=\"yes\"", "USE_DHCP=\"no\""); - dataText.replace("IPADDR=", "IPADDR=\"" + _imgWriterSettings["ipaddr"].toString() + "\""); - dataText.replace("NETMASK=", "NETMASK=\"" + _imgWriterSettings["netmask"].toString() + "\""); - dataText.replace("GATEWAY=", "GATEWAY=\"" + _imgWriterSettings["gateway"].toString() + "\""); - dataText.append("DNS_SERVER1=\"" + _imgWriterSettings["dns"].toString() + "\"\r\n"); - - if (fileNetwork.open(QFile::WriteOnly | QFile::Truncate)) - { - QTextStream out(&fileNetwork); - out << dataText; - } - fileNetwork.close(); - } - - } - if(_imgWriterSettings.contains("servername")) { - QFile fileIdent(folder + "/config/ident.cfg"); - if (fileIdent.exists()) - { - fileIdent.open(QIODevice::ReadOnly); - QString dataText = fileIdent.readAll(); - fileIdent.close(); - - dataText.replace("NAME=\"Tower\"", "NAME=\"" + _imgWriterSettings["servername"].toString() + "\""); - - if (fileIdent.open(QFile::WriteOnly | QFile::Truncate)) - { - QTextStream out(&fileIdent); - out << dataText; - } - fileIdent.close(); - } - } - - if(_imgWriterSettings.contains("UNRAID_LANG_JSON")) { - // remove this tmp file now that we're definitely done with it - QFile langJson(_imgWriterSettings["UNRAID_LANG_JSON"].toByteArray()); - langJson.remove(); - } - - if(_imgWriterSettings.contains("UNRAID_LANG_CODE")) - { - QString unraidLangCode(_imgWriterSettings["UNRAID_LANG_CODE"].toString()); - if(_imgWriterSettings.contains("UNRAID_LANG_XML")) { - QFile langXml(_imgWriterSettings["UNRAID_LANG_XML"].toByteArray()); - QFile::rename(langXml.fileName(), folder + "/config/plugins/lang-" + unraidLangCode + ".xml"); - } - - if(_imgWriterSettings.contains("UNRAID_LANG_ZIP")) { - QFile langZip(_imgWriterSettings["UNRAID_LANG_ZIP"].toByteArray()); - QFile::rename(langZip.fileName(), folder + "/config/plugins/dynamix/lang-" + unraidLangCode + ".zip"); - } - QFile dynamixCfg(folder + "/config/plugins/dynamix/dynamix.cfg"); - if (dynamixCfg.exists()) - { - dynamixCfg.open(QIODevice::ReadOnly); - QString dataText = dynamixCfg.readAll(); - dynamixCfg.close(); - - QString oldData(dataText); - dataText.replace("locale=\"\"", "locale=\"" + unraidLangCode + "\"", Qt::CaseInsensitive); - if(oldData == dataText) - { - // if this string wasn't found for replacement, just add it - dataText += "locale=\"" + unraidLangCode + "\""; - } - - if (dynamixCfg.open(QFile::WriteOnly | QFile::Truncate)) - { - QTextStream out(&dynamixCfg); - out << dataText; - } - dynamixCfg.close(); - } - } - - // restore make bootable scripts and/or syslinux, if necessary - QDir dirTarget(folder); - if (dirTarget.mkdir("syslinux")) - { - QFile::copy(":/unraid/syslinux/ldlinux.c32", folder + "/syslinux/ldlinux.c32"); - QFile::copy(":/unraid/syslinux/libcom32.c32", folder + "/syslinux/libcom32.c32"); - QFile::copy(":/unraid/syslinux/libutil.c32", folder + "/syslinux/libutil.c32"); - QFile::copy(":/unraid/syslinux/make_bootable_linux.sh", folder + "/syslinux/make_bootable_linux.sh"); - QFile::copy(":/unraid/syslinux/make_bootable_mac.sh", folder + "/syslinux/make_bootable_mac.sh"); - QFile::copy(":/unraid/syslinux/mboot.c32", folder + "/syslinux/mboot.c32"); - QFile::copy(":/unraid/syslinux/mbr.bin", folder + "/syslinux/mbr.bin"); - QFile::copy(":/unraid/syslinux/menu.c32", folder + "/syslinux/menu.c32"); - QFile::copy(":/unraid/syslinux/syslinux", folder + "/syslinux/syslinux"); - QFile::copy(":/unraid/syslinux/syslinux_linux", folder + "/syslinux/syslinux_linux"); - QFile::copy(":/unraid/syslinux/syslinux.cfg", folder + "/syslinux/syslinux.cfg"); - QFile::copy(":/unraid/syslinux/syslinux.cfg-", folder + "/syslinux/syslinux.cfg-"); - QFile::copy(":/unraid/syslinux/syslinux.exe", folder + "/syslinux/syslinux.exe"); - QFile::copy(":/unraid/make_bootable_linux", folder + "/make_bootable_linux"); - QFile::copy(":/unraid/make_bootable_mac", folder + "/make_bootable_mac"); - QFile::copy(":/unraid/make_bootable.bat", folder + "/make_bootable.bat"); - } - -#ifdef Q_OS_WIN - QString program{"cmd.exe"}; - QStringList args; - args << "/C" << "echo Y | make_bootable.bat"; - - int retcode = QProcess::execute(program, args); - - if (retcode) - { - throw runtime_error("Error running make_bootable script"); - } -#endif + _writeUnraidNetworkSettings(); + _writeUnraidServerSettings(); + _writeUnraidLanguageSettings(); + _writeUnraidSyslinuxFiles(); + _runUnraidMakeBootableScript(); } emit success(); } @@ -595,3 +482,182 @@ void DownloadExtractThread::_pushQueue(const char *data, size_t len) _cv.notify_one(); } } + +void DownloadExtractThread::_writeUnraidNetworkSettings(const QString& dstDir) +{ + if(_allNetworkSettingsPresent() && _imgWriterSettings["static"].toBool()) + { + QFile fileNetwork(dstDir + "/config/network.cfg"); + if (fileNetwork.exists()) + { + fileNetwork.open(QIODevice::ReadOnly); + QString dataText = fileNetwork.readAll(); + fileNetwork.close(); + + dataText.replace("USE_DHCP=\"yes\"", "USE_DHCP=\"no\""); + dataText.replace("IPADDR=", "IPADDR=\"" + _imgWriterSettings["ipaddr"].toString() + "\""); + dataText.replace("NETMASK=", "NETMASK=\"" + _imgWriterSettings["netmask"].toString() + "\""); + dataText.replace("GATEWAY=", "GATEWAY=\"" + _imgWriterSettings["gateway"].toString() + "\""); + dataText.append("DNS_SERVER1=\"" + _imgWriterSettings["dns"].toString() + "\"\r\n"); + + if (fileNetwork.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&fileNetwork); + out << dataText; + } + fileNetwork.close(); + } + + } +} + +void DownloadExtractThread::_writeUnraidServerSettings(const QString& dstDir) +{ + if(_imgWriterSettings.contains("servername")) { + QFile fileIdent(dstDir + "/config/ident.cfg"); + if (fileIdent.exists()) + { + fileIdent.open(QIODevice::ReadOnly); + QString dataText = fileIdent.readAll(); + fileIdent.close(); + + dataText.replace("NAME=\"Tower\"", "NAME=\"" + _imgWriterSettings["servername"].toString() + "\""); + + if (fileIdent.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&fileIdent); + out << dataText; + } + fileIdent.close(); + } + } +} + +void DownloadExtractThread::_writeUnraidLanguageSettings(const QString& dstDir) +{ + if(_imgWriterSettings.contains("UNRAID_LANG_JSON")) { + // remove this tmp file now that we're definitely done with it + QFile langJson(_imgWriterSettings["UNRAID_LANG_JSON"].toByteArray()); + langJson.remove(); + } + + if(_imgWriterSettings.contains("UNRAID_LANG_CODE")) + { + QString unraidLangCode(_imgWriterSettings["UNRAID_LANG_CODE"].toString()); + if(_imgWriterSettings.contains("UNRAID_LANG_XML")) { + QFile langXml(_imgWriterSettings["UNRAID_LANG_XML"].toByteArray()); + QFile::rename(langXml.fileName(), dstDir + "/config/plugins/lang-" + unraidLangCode + ".xml"); + } + + if(_imgWriterSettings.contains("UNRAID_LANG_ZIP")) { + QFile langZip(_imgWriterSettings["UNRAID_LANG_ZIP"].toByteArray()); + QFile::rename(langZip.fileName(), dstDir + "/config/plugins/dynamix/lang-" + unraidLangCode + ".zip"); + } + QFile dynamixCfg(dstDir + "/config/plugins/dynamix/dynamix.cfg"); + if (dynamixCfg.exists()) + { + dynamixCfg.open(QIODevice::ReadOnly); + QString dataText = dynamixCfg.readAll(); + dynamixCfg.close(); + + _addLocaleToUnraidDynamixConfig(dataText, unraidLangCode); + + if (dynamixCfg.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&dynamixCfg); + out << dataText; + } + dynamixCfg.close(); + } + } +} + +void DownloadExtractThread::_writeUnraidSyslinuxFiles(const QString& dstDir) +{ + // restore make bootable scripts and/or syslinux, if necessary + QDir dirTarget(dstDir); + if (dirTarget.mkdir("syslinux")) + { + QFile::copy(":/unraid/syslinux/ldlinux.c32", dstDir + "/syslinux/ldlinux.c32"); + QFile::copy(":/unraid/syslinux/libcom32.c32", dstDir + "/syslinux/libcom32.c32"); + QFile::copy(":/unraid/syslinux/libutil.c32", dstDir + "/syslinux/libutil.c32"); + QFile::copy(":/unraid/syslinux/make_bootable_linux.sh", dstDir + "/syslinux/make_bootable_linux.sh"); + QFile::copy(":/unraid/syslinux/make_bootable_mac.sh", dstDir + "/syslinux/make_bootable_mac.sh"); + QFile::copy(":/unraid/syslinux/mboot.c32", dstDir + "/syslinux/mboot.c32"); + QFile::copy(":/unraid/syslinux/mbr.bin", dstDir + "/syslinux/mbr.bin"); + QFile::copy(":/unraid/syslinux/menu.c32", dstDir + "/syslinux/menu.c32"); + QFile::copy(":/unraid/syslinux/syslinux", dstDir + "/syslinux/syslinux"); + QFile::copy(":/unraid/syslinux/syslinux_linux", dstDir + "/syslinux/syslinux_linux"); + QFile::copy(":/unraid/syslinux/syslinux.cfg", dstDir + "/syslinux/syslinux.cfg"); + QFile::copy(":/unraid/syslinux/syslinux.cfg-", dstDir + "/syslinux/syslinux.cfg-"); + QFile::copy(":/unraid/syslinux/syslinux.exe", dstDir + "/syslinux/syslinux.exe"); + QFile::copy(":/unraid/make_bootable_linux", dstDir + "/make_bootable_linux"); + QFile::copy(":/unraid/make_bootable_mac", dstDir + "/make_bootable_mac"); + QFile::copy(":/unraid/make_bootable.bat", dstDir + "/make_bootable.bat"); + } +} + +void DownloadExtractThread::_runUnraidMakeBootableScript() +{ +#ifdef Q_OS_WIN + QString program{"cmd.exe"}; + QStringList args; + args << "/C" << "echo Y | make_bootable.bat"; + + int retcode = QProcess::execute(program, args); + + if (retcode) + { + throw runtime_error("Error running make_bootable script"); + } +#endif +} + +void DownloadExtractThread::_addLocaleToUnraidDynamixConfig(QString& dataText, const QString& unraidLangCode) +{ + QDomDocument doc; + QString errorMsg; + int errorLine, errorColumn; + + // Load the dataText as an XML document + if (!doc.setContent(dataText, &errorMsg, &errorLine, &errorColumn)) + { + throw runtime_error("Error parsing XML at line" + errorLine + "column" + errorColumn + ":" + errorMsg); + } + + // Find the section + QDomElement root = doc.documentElement(); // Get the root element + QDomNodeList displaySections = root.elementsByTagName("display"); + + if (!displaySections.isEmpty()) + { + QDomElement display = displaySections.at(0).toElement(); // Assuming only one section + + if (!display.isNull()) + { + // Check if "locale" attribute exists + if (display.hasAttribute("locale")) + { + display.setAttribute("locale", unraidLangCode); + } + else + { + // Add "locale" attribute if not present + display.setAttribute("locale", unraidLangCode); + } + } + } + else + { + // Add a new [display] section with the locale attribute + QDomElement newDisplay = doc.createElement("display"); + newDisplay.setAttribute("locale", unraidLangCode); + root.appendChild(newDisplay); + } + + // Convert the modified XML back to a string + QString newXml; + QTextStream stream(&newXml); + doc.save(stream, 4); // Indent with 4 spaces + dataText = newXml; +} diff --git a/src/downloadextractthread.h b/src/downloadextractthread.h index 002b9bf94..cb0dae80c 100644 --- a/src/downloadextractthread.h +++ b/src/downloadextractthread.h @@ -58,6 +58,13 @@ class DownloadExtractThread : public DownloadThread static ssize_t _archive_read(struct archive *a, void *client_data, const void **buff); static int _archive_close(struct archive *a, void *client_data); + + void _writeUnraidNetworkSettings(const QString& dstDir); + void _writeUnraidServerSettings(const QString& dstDir); + void _writeUnraidLanguageSettings(const QString& dstDir); + void _writeUnraidSyslinuxFiles(const QString& dstDir); + void _runUnraidMakeBootableScript(); + void _addLocaleToUnraidDynamixConfig(QString& dataText, const QString& unraidLangCode); }; #endif // DOWNLOADEXTRACTTHREAD_H diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 2be51a135..8c97addaf 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -1569,8 +1569,8 @@ QByteArray ImageWriter::_parseUnraidLangJson(const QByteArray& jsonFilePath, con QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); - qDebug() << parseError.errorString(); - if (parseError.error != QJsonParseError::NoError) { + if (parseError.error != QJsonParseError::NoError) + { onError(parseError.errorString()); } @@ -1587,22 +1587,27 @@ QString ImageWriter::_parseUnraidLangXml(const QByteArray& xmlFilePath) { QByteArray xmlData = _readFileContents(xmlFilePath); QXmlStreamReader xml(xmlData); - int count = 0; QString found; - while (!xml.atEnd() && !xml.hasError()) { + while (!xml.atEnd() && !xml.hasError()) + { QXmlStreamReader::TokenType token = xml.readNext(); - if (token == QXmlStreamReader::StartElement) { - if (QString::compare(xml.name(), "LanguageURL") == 0) { - xml.readNext(); - if (xml.atEnd()) { - break; - } - - if (xml.isCharacters() && (QString::compare(xml.text(), "\n") != 0)) { - found.append(xml.text().toString()); - } - } + if (token != QXmlStreamReader::StartElement) + { + continue; + } + if (xml.name().compare("LanguageURL") != 0) + { + continue; + } + xml.readNext(); + if (xml.atEnd()) + { + break; + } + if (xml.isCharacters() && (xml.text().compare("\n") != 0)) + { + found.append(xml.text().toString()); } } @@ -1612,13 +1617,15 @@ QString ImageWriter::_parseUnraidLangXml(const QByteArray& xmlFilePath) QByteArray ImageWriter::_readFileContents(const QByteArray& filePath) { QFile fileIn(filePath); - if(!fileIn.open(QIODevice::ReadOnly)) { + if(!fileIn.open(QIODevice::ReadOnly)) + { onError("Error opening " + filePath); } // remove excess null characters (they cause the qt json parser to fail) QByteArray data = fileIn.readAll().replace('\0', ""); fileIn.close(); - if(!fileIn.open(QIODevice::WriteOnly)) { + if(!fileIn.open(QIODevice::WriteOnly)) + { onError("Error opening " + filePath); } fileIn.write(data); @@ -1627,12 +1634,7 @@ QByteArray ImageWriter::_readFileContents(const QByteArray& filePath) void ImageWriter::onUnraidFormatComplete() { - QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); - QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - _unraidLangJsonTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); - QFile langFile(_unraidLangJsonTmpPath); - langFile.open(QIODevice::ReadWrite); - + _unraidLangJsonTmpPath = _generateTempFilePath(); setSetting("imagecustomization/UNRAID_LANG_JSON", _unraidLangJsonTmpPath); DownloadThread * dl_thread = new DownloadThread(_unraidLangJsonUrl, _unraidLangJsonTmpPath, "", this); connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); @@ -1643,13 +1645,7 @@ void ImageWriter::onUnraidFormatComplete() void ImageWriter::onUnraidJsonDownloadComplete() { QByteArray xmlUrl = _parseUnraidLangJson(_unraidLangJsonTmpPath, _unraidLangcode); - - QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); - QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - _unraidLangXmlTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); - QFile langFile(_unraidLangXmlTmpPath); - langFile.open(QIODevice::ReadWrite); - + _unraidLangXmlTmpPath = _generateTempFilePath(); setSetting("imagecustomization/UNRAID_LANG_XML", _unraidLangXmlTmpPath); DownloadThread * dl_thread = new DownloadThread(xmlUrl, _unraidLangXmlTmpPath, "", this); connect(dl_thread, SIGNAL(error(QString)), SLOT(onError(QString))); @@ -1669,13 +1665,7 @@ void ImageWriter::onUnraidXmlDownloadComplete() } else { - QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); - QString download_dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); - _unraidLangZipTmpPath = (download_dir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); - QFile langFile(_unraidLangZipTmpPath); - langFile.open(QIODevice::ReadWrite); - - + _unraidLangZipTmpPath = _generateTempFilePath(); setSetting("imagecustomization/UNRAID_LANG_ZIP", _unraidLangZipTmpPath); // this has to be called again so getSavedCustomizationSettings returns the updated qvariantmap _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, getSavedCustomizationSettings()); @@ -1686,3 +1676,16 @@ void ImageWriter::onUnraidXmlDownloadComplete() } } +QByteArray ImageWriter::_generateTempFilePath(bool create) +{ + QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces); + QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); + QByteArray tmpFilePath = (downloadDir + "/unraid-usb-creator-" + uuid + ".tmp").toUtf8(); + if(create) + { + QFile tmpFile(tmpFilePath); + tmpFile.open(QIODevice::ReadWrite); + } + return tmpFilePath; +} + diff --git a/src/imagewriter.h b/src/imagewriter.h index 273eef96b..466ddc5b4 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -231,6 +231,7 @@ protected slots: QByteArray _readFileContents(const QByteArray& filePath); QString _parseUnraidLangXml(const QByteArray& xmlFilePath); QByteArray _parseUnraidLangJson(const QByteArray& jsonFilePath, const QString& unraidLangCode); + QByteArray _generateTempFilePath(bool create = true); }; #endif // IMAGEWRITER_H