From 5ce92c078692bb7fb5020d9ddec7ade6dacac1e9 Mon Sep 17 00:00:00 2001 From: Benjamin Dobell Date: Fri, 8 Jul 2011 05:02:18 +1000 Subject: Version 1.3 beta. --- heimdall-frontend/Source/FirmwareInfo.cpp | 188 +++++++++-- heimdall-frontend/Source/FirmwareInfo.h | 16 + heimdall-frontend/Source/Packaging.cpp | 496 +++++++++++++++++++++++------- heimdall-frontend/Source/Packaging.h | 16 +- heimdall-frontend/Source/mainwindow.cpp | 88 +++--- heimdall-frontend/Source/mainwindow.h | 2 + 6 files changed, 624 insertions(+), 182 deletions(-) (limited to 'heimdall-frontend/Source') diff --git a/heimdall-frontend/Source/FirmwareInfo.cpp b/heimdall-frontend/Source/FirmwareInfo.cpp index 7d11605..39ec242 100755 --- a/heimdall-frontend/Source/FirmwareInfo.cpp +++ b/heimdall-frontend/Source/FirmwareInfo.cpp @@ -50,7 +50,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml) { if (foundManufacturer) { - // TODO: "found multiple device manufacturers." + // TODO: "Found multiple device manufacturers." return (false); } @@ -62,7 +62,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml) { if (foundProduct) { - // TODO: "found multiple device product identifiers." + // TODO: "Found multiple device product identifiers." return (false); } @@ -74,7 +74,7 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml) { if (foundName) { - // TODO: "found multiple device names.")); + // TODO: "Found multiple device names.")); return (false); } @@ -101,6 +101,25 @@ bool DeviceInfo::ParseXml(QXmlStreamReader& xml) return (false); } +void DeviceInfo::WriteXml(QXmlStreamWriter& xml) const +{ + xml.writeStartElement("device"); + + xml.writeStartElement("manufacturer"); + xml.writeCharacters(manufacturer); + xml.writeEndElement(); + + xml.writeStartElement("product"); + xml.writeCharacters(product); + xml.writeEndElement(); + + xml.writeStartElement("name"); + xml.writeCharacters(name); + xml.writeEndElement(); + + xml.writeEndElement(); +} + PlatformInfo::PlatformInfo() @@ -135,7 +154,7 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml) { if (foundName) { - // TODO: "found multiple platform names." + // TODO: "Found multiple platform names." return (false); } @@ -147,7 +166,7 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml) { if (foundVersion) { - // TODO: "found multiple platform versions." + // TODO: "Found multiple platform versions." return (false); } @@ -179,6 +198,21 @@ bool PlatformInfo::ParseXml(QXmlStreamReader& xml) return (false); } +void PlatformInfo::WriteXml(QXmlStreamWriter& xml) const +{ + xml.writeStartElement("platform"); + + xml.writeStartElement("name"); + xml.writeCharacters(name); + xml.writeEndElement(); + + xml.writeStartElement("version"); + xml.writeCharacters(version); + xml.writeEndElement(); + + xml.writeEndElement(); +} + FileInfo::FileInfo() @@ -206,7 +240,7 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml) { if (foundId) { - // TODO: "found multiple file IDs." + // TODO: "Found multiple file IDs." return (false); } @@ -218,7 +252,7 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml) { if (foundFilename) { - // TODO: "found multiple file filenames." + // TODO: "Found multiple file filenames." return (false); } @@ -245,11 +279,34 @@ bool FileInfo::ParseXml(QXmlStreamReader& xml) return (false); } +void FileInfo::WriteXml(QXmlStreamWriter& xml) const +{ + xml.writeStartElement("file"); + + xml.writeStartElement("id"); + xml.writeCharacters(QString::number(partitionId)); + xml.writeEndElement(); + + xml.writeStartElement("filename"); + + int lastSlash = filename.lastIndexOf('/'); + + if (lastSlash < 0) + lastSlash = filename.lastIndexOf('\\'); + + xml.writeCharacters(filename.mid(lastSlash + 1)); + + xml.writeEndElement(); + + xml.writeEndElement(); +} + FirmwareInfo::FirmwareInfo() { repartition = false; + noReboot = false; } void FirmwareInfo::Clear(void) @@ -267,13 +324,15 @@ void FirmwareInfo::Clear(void) pitFilename.clear(); repartition = false; + noReboot = false; + fileInfos.clear(); } bool FirmwareInfo::IsCleared(void) const { return (name.isEmpty() && version.isEmpty() && platformInfo.IsCleared() && developers.isEmpty() && url.isEmpty() && url.isEmpty() && donateUrl.isEmpty() - && deviceInfos.isEmpty() && pitFilename.isEmpty() && !repartition && fileInfos.isEmpty()); + && deviceInfos.isEmpty() && pitFilename.isEmpty() && !repartition && !noReboot && fileInfos.isEmpty()); } bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) @@ -289,6 +348,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) bool foundDevices = false; bool foundPit = false; bool foundRepartition = false; + bool foundNoReboot = false; bool foundFiles = false; if (!xml.readNextStartElement()) @@ -337,7 +397,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundName) { - // TODO: "found multiple firmware names." + // TODO: "Found multiple firmware names." return (false); } @@ -348,7 +408,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundVersion) { - // TODO: "found multiple firmware versions." + // TODO: "Found multiple firmware versions." return (false); } @@ -359,7 +419,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundPlatform) { - // TODO: "found multiple firmware platforms." + // TODO: "Found multiple firmware platforms." return (false); } @@ -372,7 +432,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundDevelopers) { - // TODO: "found multiple sets of firmware developers." + // TODO: "Found multiple sets of firmware developers." return (false); } @@ -406,7 +466,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundUrl) { - // TODO: "found multiple firmware URLs." + // TODO: "Found multiple firmware URLs." return (false); } @@ -418,7 +478,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundDonateUrl) { - // TODO: "found multiple firmware donate URLs." + // TODO: "Found multiple firmware donate URLs." return (false); } @@ -430,7 +490,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundDevices) { - // TODO: "found multiple sets of firmware devices." + // TODO: "Found multiple sets of firmware devices." return (false); } @@ -471,7 +531,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundPit) { - // TODO: "found multiple firmware PIT files." + // TODO: "Found multiple firmware PIT files." return (false); } @@ -483,7 +543,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (foundRepartition) { - // TODO: "found multiple firmware repartition values." + // TODO: "Found multiple firmware repartition values." return (false); } @@ -491,11 +551,23 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) repartition = (xml.readElementText().toInt() != 0); } + else if (xml.name() == "noreboot") + { + if (foundNoReboot) + { + // TODO: "Found multiple firmware noreboot values." + return (false); + } + + foundNoReboot = true; + + noReboot = (xml.readElementText().toInt() != 0); + } else if (xml.name() == "files") { if (foundFiles) { - // TODO: "found multiple sets of firmware files." + // TODO: "Found multiple sets of firmware files." return (false); } @@ -542,7 +614,7 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) { if (xml.name() == "firmware") { - if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundFiles)) + if (!(foundName && foundVersion && foundPlatform && foundDevelopers && foundDevices && foundPit && foundRepartition && foundNoReboot && foundFiles)) return (false); else break; @@ -569,3 +641,81 @@ bool FirmwareInfo::ParseXml(QXmlStreamReader& xml) return (true); } + +void FirmwareInfo::WriteXml(QXmlStreamWriter& xml) const +{ + xml.writeStartDocument(); + xml.writeStartElement("firmware"); + xml.writeAttribute("version", QString::number(FirmwareInfo::kVersion)); + + xml.writeStartElement("name"); + xml.writeCharacters(name); + xml.writeEndElement(); + + xml.writeStartElement("version"); + xml.writeCharacters(version); + xml.writeEndElement(); + + platformInfo.WriteXml(xml); + + xml.writeStartElement("developers"); + + for (int i = 0; i < developers.length(); i++) + { + xml.writeStartElement("name"); + xml.writeCharacters(developers[i]); + xml.writeEndElement(); + } + + xml.writeEndElement(); + + if (!url.isEmpty()) + { + xml.writeStartElement("url"); + xml.writeCharacters(url); + xml.writeEndElement(); + } + + if (!donateUrl.isEmpty()) + { + xml.writeStartElement("donateurl"); + xml.writeCharacters(donateUrl); + xml.writeEndElement(); + } + + xml.writeStartElement("devices"); + + for (int i = 0; i < deviceInfos.length(); i++) + deviceInfos[i].WriteXml(xml); + + xml.writeEndElement(); + + xml.writeStartElement("pit"); + + int lastSlash = pitFilename.lastIndexOf('/'); + + if (lastSlash < 0) + lastSlash = pitFilename.lastIndexOf('\\'); + + xml.writeCharacters(pitFilename.mid(lastSlash + 1)); + + xml.writeEndElement(); + + xml.writeStartElement("repartition"); + xml.writeCharacters((repartition) ? "1" : "0"); + xml.writeEndElement(); + + xml.writeStartElement("noreboot"); + xml.writeCharacters((noReboot) ? "1" : "0"); + xml.writeEndElement(); + + xml.writeStartElement("files"); + + for (int i = 0; i < fileInfos.length(); i++) + fileInfos[i].WriteXml(xml); + + xml.writeEndElement(); + + xml.writeEndElement(); + xml.writeEndDocument(); +} diff --git a/heimdall-frontend/Source/FirmwareInfo.h b/heimdall-frontend/Source/FirmwareInfo.h index a72dab1..3fd6341 100755 --- a/heimdall-frontend/Source/FirmwareInfo.h +++ b/heimdall-frontend/Source/FirmwareInfo.h @@ -42,6 +42,7 @@ namespace HeimdallFrontend DeviceInfo(const QString& manufacturer, const QString& product, const QString& name); bool ParseXml(QXmlStreamReader& xml); + void WriteXml(QXmlStreamWriter& xml) const; const QString& GetManufacturer(void) const { @@ -89,6 +90,7 @@ namespace HeimdallFrontend bool IsCleared(void) const; bool ParseXml(QXmlStreamReader& xml); + void WriteXml(QXmlStreamWriter& xml) const; const QString& GetName(void) const { @@ -124,6 +126,7 @@ namespace HeimdallFrontend FileInfo(unsigned int partitionId, const QString& filename); bool ParseXml(QXmlStreamReader& xml); + void WriteXml(QXmlStreamWriter& xml) const; unsigned int GetPartitionId(void) const { @@ -170,6 +173,8 @@ namespace HeimdallFrontend QString pitFilename; bool repartition; + bool noReboot; + QList fileInfos; public: @@ -180,6 +185,7 @@ namespace HeimdallFrontend bool IsCleared(void) const; bool ParseXml(QXmlStreamReader& xml); + void WriteXml(QXmlStreamWriter& xml) const; const QString& GetName(void) const { @@ -271,6 +277,16 @@ namespace HeimdallFrontend this->repartition = repartition; } + bool GetNoReboot(void) const + { + return (noReboot); + } + + void SetNoReboot(bool noReboot) + { + this->noReboot = noReboot; + } + const QList& GetFileInfos(void) const { return (fileInfos); diff --git a/heimdall-frontend/Source/Packaging.cpp b/heimdall-frontend/Source/Packaging.cpp index cbd03a4..1034539 100755 --- a/heimdall-frontend/Source/Packaging.cpp +++ b/heimdall-frontend/Source/Packaging.cpp @@ -18,6 +18,10 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/ +#ifdef WIN32 +#pragma warning(disable : 4996) +#endif + // C/C++ Standard Library #include @@ -27,6 +31,7 @@ // Qt #include #include +#include // Heimdall Frontend #include "Packaging.h" @@ -35,20 +40,44 @@ using namespace HeimdallFrontend; const char *Packaging::ustarMagic = "ustar"; -bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageData) +bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *packageData) { TarHeader tarHeader; - tarFile.reset(); + if (!tarFile.open()) + { + // TODO: "Error opening temporary TAR archive." + return (false); + } bool previousEmpty = false; + QProgressDialog progressDialog("Extracting files...", "Cancel", 0, tarFile.size()); + progressDialog.setWindowModality(Qt::ApplicationModal); + progressDialog.setWindowTitle("Heimdall Frontend"); + while (!tarFile.atEnd()) { qint64 dataRead = tarFile.read(tarHeader.buffer, TarHeader::kBlockLength); if (dataRead != TarHeader::kBlockLength) + { + // TODO: "Package's TAR archive is malformed." + tarFile.close(); + progressDialog.close(); + return (false); + } + + progressDialog.setValue(tarFile.pos()); + + if (progressDialog.wasCanceled()) + { + tarFile.close(); + progressDialog.close(); + + return (false); + } bool ustarFormat = strcmp(tarHeader.fields.magic, ustarMagic) == 0; bool empty = true; @@ -73,7 +102,20 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa } else { - // TODO: Check checksum + int checksum = 0; + + for (char *bufferIndex = tarHeader.buffer; bufferIndex < tarHeader.fields.checksum; bufferIndex++) + checksum += static_cast(*bufferIndex); + + checksum += 8 * ' '; + checksum += static_cast(tarHeader.fields.typeFlag); + + // Both the TAR and USTAR formats have terrible documentation, it's not clear if the following code is required. + /*if (ustarFormat) + { + for (char *bufferIndex = tarHeader.fields.linkName; bufferIndex < tarHeader.fields.prefix + 155; bufferIndex++) + checksum += static_cast(*bufferIndex); + }*/ bool parsed = false; @@ -87,6 +129,7 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa if (!parsed) { // TODO: Error message? + tarFile.close(); return (false); } @@ -95,14 +138,15 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa // We're working with a file. QString filename = QString::fromUtf8(tarHeader.fields.name); - // This is slightly pointless as we don't support directories... - if (ustarFormat) - filename.prepend(tarHeader.fields.prefix); - QTemporaryFile *outputFile = new QTemporaryFile("XXXXXX-" + filename); - outputFile->open(); + packageData->GetFiles().append(outputFile); - outputPackageData->GetFiles().append(outputFile); + if (!outputFile->open()) + { + // TODO: "Failed to open output file \"%s\"" + tarFile.close(); + return (false); + } qulonglong dataRemaining = fileSize; char readBuffer[TarHeader::kBlockReadCount * TarHeader::kBlockLength]; @@ -116,18 +160,41 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa qint64 dataRead = tarFile.read(readBuffer, fileDataToRead + (TarHeader::kBlockLength - fileDataToRead % TarHeader::kBlockLength) % TarHeader::kBlockLength); if (dataRead < fileDataToRead || dataRead % TarHeader::kBlockLength != 0) + { + // TODO: "Unexpected error extracting package files." + tarFile.close(); + outputFile->close(); + + remove(outputFile->fileName().toStdString().c_str()); + return (false); + } outputFile->write(readBuffer, fileDataToRead); dataRemaining -= fileDataToRead; + + progressDialog.setValue(tarFile.pos()); + + if (progressDialog.wasCanceled()) + { + tarFile.close(); + outputFile->close(); + + remove(outputFile->fileName().toStdString().c_str()); + + progressDialog.close(); + + return (false); + } } outputFile->close(); } else { - // We don't support links/directories. + // TODO: "Heimdall packages shouldn't contain links or directories." + tarFile.close(); return (false); } } @@ -135,195 +202,320 @@ bool Packaging::ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageDa previousEmpty = empty; } + progressDialog.close(); + tarFile.close(); + return (true); } -bool Packaging::CreateTar(const PackageData& packageData, QTemporaryFile *outputTarFile) +bool Packaging::WriteTarEntry(const QString& filename, QTemporaryFile *tarFile, bool firmwareXml) { - const QList& fileInfos = packageData.GetFirmwareInfo().GetFileInfos(); + TarHeader tarHeader; + memset(tarHeader.buffer, 0, TarHeader::kBlockLength); + + QFile file(filename); - if (!outputTarFile->open()) + if (!file.open(QFile::ReadOnly)) { // TODO: "Failed to open \"%s\"" return (false); } - bool failure = false; - - TarHeader tarHeader; - - for (int i = 0; i < fileInfos.length(); i++) + if (file.size() > TarHeader::kMaxFileSize) { - memset(tarHeader.buffer, 0, TarHeader::kBlockLength); + // TODO: "File is too large to packaged" + return (false); + } - QFile file(fileInfos[i].GetFilename()); + QFileInfo qtFileInfo(file); + QByteArray utfFilename; - if (!file.open(QFile::ReadOnly)) - { - // TODO: "Failed to open \"%s\"" - failure = true; - break; - } + if (firmwareXml) + { + utfFilename = QString("firmware.xml").toUtf8(); + } + else + { + utfFilename = qtFileInfo.fileName().toUtf8(); - if (file.size() > TarHeader::kMaxFileSize) + if (utfFilename.length() > 100) { - // TODO: "File is too large to packaged" - failure = true; - break; + // TODO: "Filename is too long" + return (false); } + } - QFileInfo qtFileInfo(file); - strcpy(tarHeader.fields.name, qtFileInfo.fileName().toUtf8().constData()); + strcpy(tarHeader.fields.name, utfFilename.constData()); - unsigned int mode = 0; - - QFile::Permissions permissions = file.permissions(); - - // Other - if (permissions.testFlag(QFile::ExeOther)) - mode |= TarHeader::kModeOtherExecute; - if (permissions.testFlag(QFile::WriteOther)) - mode |= TarHeader::kModeOtherWrite; - if (permissions.testFlag(QFile::ReadOther)) - mode |= TarHeader::kModeOtherRead; - - // Group - if (permissions.testFlag(QFile::ExeGroup)) - mode |= TarHeader::kModeGroupExecute; - if (permissions.testFlag(QFile::WriteGroup)) - mode |= TarHeader::kModeGroupWrite; - if (permissions.testFlag(QFile::ReadGroup)) - mode |= TarHeader::kModeGroupRead; - - // Owner - if (permissions.testFlag(QFile::ExeOwner)) - mode |= TarHeader::kModeOwnerExecute; - if (permissions.testFlag(QFile::WriteOwner)) - mode |= TarHeader::kModeOwnerWrite; - if (permissions.testFlag(QFile::ReadOwner)) - mode |= TarHeader::kModeOwnerRead; - - sprintf(tarHeader.fields.mode, "%o", mode); - - sprintf(tarHeader.fields.userId, "%o", qtFileInfo.ownerId()); - sprintf(tarHeader.fields.groupId, "%o", qtFileInfo.groupId()); - - // Note: We don't support base-256 encoding. Support could be added in future. - sprintf(tarHeader.fields.size, "%o", file.size()); - - sprintf(tarHeader.fields.modifiedTime, "%o", qtFileInfo.lastModified().toMSecsSinceEpoch()); + unsigned int mode = 0; + + QFile::Permissions permissions = file.permissions(); + + // Other + if (permissions.testFlag(QFile::ExeOther)) + mode |= TarHeader::kModeOtherExecute; + if (permissions.testFlag(QFile::WriteOther)) + mode |= TarHeader::kModeOtherWrite; + if (permissions.testFlag(QFile::ReadOther)) + mode |= TarHeader::kModeOtherRead; + + // Group + if (permissions.testFlag(QFile::ExeGroup)) + mode |= TarHeader::kModeGroupExecute; + if (permissions.testFlag(QFile::WriteGroup)) + mode |= TarHeader::kModeGroupWrite; + if (permissions.testFlag(QFile::ReadGroup)) + mode |= TarHeader::kModeGroupRead; + + // Owner + if (permissions.testFlag(QFile::ExeOwner)) + mode |= TarHeader::kModeOwnerExecute; + if (permissions.testFlag(QFile::WriteOwner)) + mode |= TarHeader::kModeOwnerWrite; + if (permissions.testFlag(QFile::ReadOwner)) + mode |= TarHeader::kModeOwnerRead; + + sprintf(tarHeader.fields.mode, "%07o", mode); + + // Owner id + uint id = qtFileInfo.ownerId(); + + if (id < 2097151) + sprintf(tarHeader.fields.userId, "%07o", id); + else + sprintf(tarHeader.fields.userId, "%07o", 0); + + // Group id + id = qtFileInfo.groupId(); + + if (id < 2097151) + sprintf(tarHeader.fields.groupId, "%07o", id); + else + sprintf(tarHeader.fields.groupId, "%07o", 0); + + // Note: We don't support base-256 encoding. Support could be added later. + sprintf(tarHeader.fields.size, "%011o", file.size()); + sprintf(tarHeader.fields.modifiedTime, "%011o", qtFileInfo.lastModified().toMSecsSinceEpoch() / 1000); - // Regular File - tarHeader.fields.typeFlag = '0'; + // Regular File + tarHeader.fields.typeFlag = '0'; - // Calculate checksum - int checksum = 0; + // Calculate checksum + int checksum = 0; + memset(tarHeader.fields.checksum, ' ', 8); + + for (int i = 0; i < TarHeader::kTarHeaderLength; i++) + checksum += static_cast(tarHeader.buffer[i]); - for (int i = 0; i < TarHeader::kTarHeaderLength; i++) - checksum += tarHeader.buffer[i]; + sprintf(tarHeader.fields.checksum, "%07o", checksum); - sprintf(tarHeader.fields.checksum, "%o", checksum); + // Write the header to the TAR file. + tarFile->write(tarHeader.buffer, TarHeader::kBlockLength); - // Write the header to the TAR file. - outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength); + char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength]; + qint64 offset = 0; - char buffer[TarHeader::kBlockWriteCount * TarHeader::kBlockLength]; + while (offset < file.size()) + { + qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength); + + if (tarFile->write(buffer, dataRead) != dataRead) + { + // TODO: "Failed to write data to the temporary TAR file." + return (false); + } - for (qint64 i = 0; i < file.size(); i++) + if (dataRead % TarHeader::kBlockLength != 0) { - qint64 dataRead = file.read(buffer, TarHeader::kBlockWriteCount * TarHeader::kBlockLength); + int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength; + memset(buffer, 0, remainingBlockLength); - if (outputTarFile->write(buffer, dataRead) != dataRead) + if (tarFile->write(buffer, remainingBlockLength) != remainingBlockLength) { // TODO: "Failed to write data to the temporary TAR file." - failure = true; - break; + return (false); } + } - if (dataRead % TarHeader::kBlockLength != 0) - { - int remainingBlockLength = TarHeader::kBlockLength - dataRead % TarHeader::kBlockLength; - memset(buffer, 0, remainingBlockLength); + offset += dataRead; + } - if (outputTarFile->write(buffer, remainingBlockLength) != remainingBlockLength) - { - // TODO: "Failed to write data to the temporary TAR file." - failure = true; - break; - } - } + return (true); +} + +bool Packaging::CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile) +{ + const QList& fileInfos = firmwareInfo.GetFileInfos(); + + QProgressDialog progressDialog("Packaging files...", "Cancel", 0, fileInfos.length() + 2); + progressDialog.setWindowModality(Qt::ApplicationModal); + progressDialog.setWindowTitle("Heimdall Frontend"); + + QTemporaryFile firmwareXmlFile("XXXXXX-firmware.xml"); + + if (!firmwareXmlFile.open()) + { + // TODO: "Failed to create temporary file "%s" + return (false); + } + + firmwareInfo.WriteXml(QXmlStreamWriter(&firmwareXmlFile)); + + firmwareXmlFile.close(); + + if (!tarFile->open()) + { + // TODO: "Failed to open \"%s\"" + return (false); + } + + for (int i = 0; i < fileInfos.length(); i++) + { + if (!WriteTarEntry(fileInfos[i].GetFilename(), tarFile)) + { + tarFile->resize(0); + tarFile->close(); + + progressDialog.close(); - i += dataRead; + return (false); } - if (failure) - break; + progressDialog.setValue(i); + + if (progressDialog.wasCanceled()) + { + tarFile->resize(0); + tarFile->close(); + + progressDialog.close(); + + return (false); + } } - if (failure) + if (!WriteTarEntry(firmwareInfo.GetPitFilename(), tarFile)) { - outputTarFile->resize(0); - outputTarFile->close(); + tarFile->resize(0); + tarFile->close(); + return (false); } + progressDialog.setValue(progressDialog.value() + 1); + + if (progressDialog.wasCanceled()) + { + tarFile->resize(0); + tarFile->close(); + + progressDialog.close(); + + return (false); + } + + if (!WriteTarEntry(firmwareXmlFile.fileName(), tarFile, true)) + { + tarFile->resize(0); + tarFile->close(); + + return (false); + } + + progressDialog.setValue(progressDialog.value() + 1); + progressDialog.close(); + // Write two empty blocks to signify the end of the archive. - memset(tarHeader.buffer, 0, TarHeader::kBlockLength); - outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength); - outputTarFile->write(tarHeader.buffer, TarHeader::kBlockLength); + char emptyEntry[TarHeader::kBlockLength]; + memset(emptyEntry, 0, TarHeader::kBlockLength); + + tarFile->write(emptyEntry, TarHeader::kBlockLength); + tarFile->write(emptyEntry, TarHeader::kBlockLength); - outputTarFile->close(); + tarFile->close(); return (true); } -bool Packaging::ExtractPackage(const QString& packagePath, PackageData *outputPackageData) +bool Packaging::ExtractPackage(const QString& packagePath, PackageData *packageData) { FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "rb"); + + if (fopen == NULL) + { + // TODO: "Failed to open package \"%s\"." + return (false); + } + + fseek(compressedPackageFile, 0, SEEK_END); + quint64 compressedFileSize = ftell(compressedPackageFile); + rewind(compressedPackageFile); + gzFile packageFile = gzdopen(fileno(compressedPackageFile), "rb"); QTemporaryFile outputTar("XXXXXX.tar"); if (!outputTar.open()) { + // TODO: "Error opening temporary TAR archive." gzclose(packageFile); return (false); } - char buffer[32768]; - + char buffer[kExtractBufferLength]; int bytesRead; + quint64 totalBytesRead = 0; + + QProgressDialog progressDialog("Decompressing package...", "Cancel", 0, compressedFileSize); + progressDialog.setWindowModality(Qt::ApplicationModal); + progressDialog.setWindowTitle("Heimdall Frontend"); do { - bytesRead = gzread(packageFile, buffer, 32768); + bytesRead = gzread(packageFile, buffer, kExtractBufferLength); if (bytesRead == -1) { + // TODO: "Error decompressing archive." gzclose(packageFile); + progressDialog.close(); return (false); } outputTar.write(buffer, bytesRead); + + totalBytesRead += bytesRead; + progressDialog.setValue(totalBytesRead); + + if (progressDialog.wasCanceled()) + { + gzclose(packageFile); + progressDialog.close(); + + return (false); + } } while (bytesRead > 0); + progressDialog.close(); + + outputTar.close(); gzclose(packageFile); // Closes packageFile and compressedPackageFile - if (!ExtractTar(outputTar, outputPackageData)) + if (!ExtractTar(outputTar, packageData)) return (false); // Find and read firmware.xml - for (int i = 0; i < outputPackageData->GetFiles().length(); i++) + for (int i = 0; i < packageData->GetFiles().length(); i++) { - QTemporaryFile *file = outputPackageData->GetFiles()[i]; + QTemporaryFile *file = packageData->GetFiles()[i]; if (file->fileTemplate() == "XXXXXX-firmware.xml") { - if (!outputPackageData->ReadFirmwareInfo(file)) + if (!packageData->ReadFirmwareInfo(file)) { - outputPackageData->Clear(); + packageData->Clear(); return (false); } @@ -334,12 +526,82 @@ bool Packaging::ExtractPackage(const QString& packagePath, PackageData *outputPa return (false); } -bool Packaging::BuildPackage(const QString& packagePath, const PackageData& packageData) +bool Packaging::BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo) { - QTemporaryFile temporaryFile("XXXXXX.tar"); + FILE *compressedPackageFile = fopen(packagePath.toStdString().c_str(), "wb"); - if (!CreateTar(packageData, &temporaryFile)) + if (fopen == NULL) + { + // TODO: "Failed to open package "%s" return (false); + } + + QTemporaryFile tar("XXXXXX.tar"); + + if (!CreateTar(firmwareInfo, &tar)) + return (false); + + if (!tar.open()) + { + // TODO: "Failed to open temporary file "%s" + fclose(compressedPackageFile); + remove(packagePath.toStdString().c_str()); + return (false); + } + + gzFile packageFile = gzdopen(fileno(compressedPackageFile), "wb"); + + char buffer[kCompressBufferLength]; + qint64 totalBytesRead = 0; + int bytesRead; + + QProgressDialog progressDialog("Compressing package...", "Cancel", 0, tar.size()); + progressDialog.setWindowModality(Qt::ApplicationModal); + progressDialog.setWindowTitle("Heimdall Frontend"); + + do + { + bytesRead = tar.read(buffer, kCompressBufferLength); + + if (bytesRead == -1) + { + // TODO: "Error reading temporary TAR file." + gzclose(packageFile); + remove(packagePath.toStdString().c_str()); + + progressDialog.close(); + + return (false); + } + + if (gzwrite(packageFile, buffer, bytesRead) != bytesRead) + { + // TODO: "Error compressing package." + gzclose(packageFile); + remove(packagePath.toStdString().c_str()); + + progressDialog.close(); + + return (false); + } + + totalBytesRead += bytesRead; + progressDialog.setValue(totalBytesRead); + + if (progressDialog.wasCanceled()) + { + gzclose(packageFile); + remove(packagePath.toStdString().c_str()); + + progressDialog.close(); + + return (false); + } + } while (bytesRead > 0); + + progressDialog.close(); + + gzclose(packageFile); // Closes packageFile and compressedPackageFile return (true); } diff --git a/heimdall-frontend/Source/Packaging.h b/heimdall-frontend/Source/Packaging.h index c341f0f..97637de 100755 --- a/heimdall-frontend/Source/Packaging.h +++ b/heimdall-frontend/Source/Packaging.h @@ -93,17 +93,25 @@ namespace HeimdallFrontend class Packaging { private: + + enum + { + kExtractBufferLength = 262144, + kCompressBufferLength = 262144 + }; // TODO: Add support for sparse files to both methods. - static bool ExtractTar(QTemporaryFile& tarFile, PackageData *outputPackageData); - static bool CreateTar(const PackageData& packageData, QTemporaryFile *outputTarFile); // Uses original TAR format. + static bool ExtractTar(QTemporaryFile& tarFile, PackageData *packageData); + + static bool WriteTarEntry(const QString& filename, QTemporaryFile *tarFile, bool firmwareXml = false); + static bool CreateTar(const FirmwareInfo& firmwareInfo, QTemporaryFile *tarFile); // Uses original TAR format. public: static const char *ustarMagic; - static bool ExtractPackage(const QString& packagePath, PackageData *outputPackageData); - static bool BuildPackage(const QString& packagePath, const PackageData& packageData); + static bool ExtractPackage(const QString& packagePath, PackageData *packageData); + static bool BuildPackage(const QString& packagePath, const FirmwareInfo& firmwareInfo); }; } diff --git a/heimdall-frontend/Source/mainwindow.cpp b/heimdall-frontend/Source/mainwindow.cpp index 8043599..b44c3de 100644 --- a/heimdall-frontend/Source/mainwindow.cpp +++ b/heimdall-frontend/Source/mainwindow.cpp @@ -91,6 +91,7 @@ void MainWindow::UpdatePackageUserInterface(void) developerDonateButton->setEnabled(false); repartitionRadioButton->setChecked(false); + noRebootRadioButton->setChecked(false); loadFirmwareButton->setEnabled(false); } @@ -126,7 +127,7 @@ void MainWindow::UpdatePackageUserInterface(void) for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetDeviceInfos().length(); i++) { const DeviceInfo& deviceInfo = loadedPackageData.GetFirmwareInfo().GetDeviceInfos()[i]; - supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + " (" + deviceInfo.GetProduct() + ")"); + supportedDevicesListWidget->addItem(deviceInfo.GetManufacturer() + " " + deviceInfo.GetName() + ": " + deviceInfo.GetProduct()); } for (int i = 0; i < loadedPackageData.GetFirmwareInfo().GetFileInfos().length(); i++) @@ -136,6 +137,7 @@ void MainWindow::UpdatePackageUserInterface(void) } repartitionRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetRepartition()); + noRebootRadioButton->setChecked(loadedPackageData.GetFirmwareInfo().GetNoReboot()); loadFirmwareButton->setEnabled(true); } @@ -143,7 +145,7 @@ void MainWindow::UpdatePackageUserInterface(void) bool MainWindow::IsArchive(QString path) { - // Not a real check but hopefully it gets the message across, don't flash archives! + // Not a real check but hopefully it gets the message across, don't directly flash archives! return (path.endsWith(".tar", Qt::CaseInsensitive) || path.endsWith(".gz", Qt::CaseInsensitive) || path.endsWith(".zip", Qt::CaseInsensitive) || path.endsWith(".bz2", Qt::CaseInsensitive) || path.endsWith(".7z", Qt::CaseInsensitive) || path.endsWith(".rar", Qt::CaseInsensitive)); } @@ -266,7 +268,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) QObject::connect(partitionFileBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPartitionFile())); QObject::connect(pitBrowseButton, SIGNAL(clicked()), this, SLOT(SelectPit())); + QObject::connect(repartitionCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetRepartition(int))); + QObject::connect(noRebootCheckBox, SIGNAL(stateChanged(int)), this, SLOT(SetNoReboot(int))); QObject::connect(startFlashButton, SIGNAL(clicked()), this, SLOT(StartFlash())); @@ -394,6 +398,7 @@ void MainWindow::LoadFirmwarePackage(void) UpdateUnusedPartitionIds(); workingPackageData.GetFirmwareInfo().SetRepartition(loadedPackageData.GetFirmwareInfo().GetRepartition()); + workingPackageData.GetFirmwareInfo().SetNoReboot(loadedPackageData.GetFirmwareInfo().GetNoReboot()); loadedPackageData.Clear(); UpdatePackageUserInterface(); @@ -431,6 +436,9 @@ void MainWindow::LoadFirmwarePackage(void) repartitionCheckBox->setEnabled(true); repartitionCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetRepartition()); + noRebootCheckBox->setEnabled(true); + noRebootCheckBox->setChecked(workingPackageData.GetFirmwareInfo().GetNoReboot()); + partitionsListWidget->setEnabled(true); addPartitionButton->setEnabled(true); removePartitionButton->setEnabled(true && partitionsListWidget->currentRow() >= 0); @@ -466,7 +474,7 @@ void MainWindow::SelectPartitionFile(void) { QString path = PromptFileSelection(); - if (path != "") + if (path != "" && !IsArchive(path)) { workingPackageData.GetFirmwareInfo().GetFileInfos()[partitionsListWidget->currentRow()].SetFilename(path); partitionFileLineEdit->setText(path); @@ -608,6 +616,7 @@ void MainWindow::SelectPit(void) pitLineEdit->setText(workingPackageData.GetFirmwareInfo().GetPitFilename()); repartitionCheckBox->setEnabled(validPit); + noRebootCheckBox->setEnabled(validPit); partitionsListWidget->setEnabled(validPit); addPartitionButton->setEnabled(validPit); @@ -620,24 +629,41 @@ void MainWindow::SetRepartition(int enabled) { workingPackageData.GetFirmwareInfo().SetRepartition(enabled); } +void MainWindow::SetNoReboot(int enabled) +{ + workingPackageData.GetFirmwareInfo().SetNoReboot(enabled); +} void MainWindow::StartFlash(void) { + outputPlainTextEdit->clear(); + heimdallRunning = true; heimdallFailed = false; + + const FirmwareInfo& firmwareInfo = workingPackageData.GetFirmwareInfo(); + const QList& fileInfos = firmwareInfo.GetFileInfos(); QStringList arguments; arguments.append("flash"); - if (repartitionCheckBox->isChecked()) - { + if (firmwareInfo.GetRepartition()) arguments.append("--repartition"); - arguments.append("--pit"); - arguments.append(pitLineEdit->text()); + arguments.append("--pit"); + arguments.append(firmwareInfo.GetPitFilename()); + + for (int i = 0; i < fileInfos.length(); i++) + { + QString flag; + flag.sprintf("--%u", fileInfos[i].GetPartitionId()); + + arguments.append(flag); + arguments.append(fileInfos[i].GetFilename()); } - // TODO: Loop through partitions and append them. + if (firmwareInfo.GetNoReboot()) + arguments.append("--no-reboot"); flashProgressBar->setEnabled(true); UpdateStartButton(); @@ -785,10 +811,10 @@ void MainWindow::SelectDevice(int row) void MainWindow::AddDevice(void) { - workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceNameLineEdit->text(), - deviceProductCodeLineEdit->text())); + workingPackageData.GetFirmwareInfo().GetDeviceInfos().append(DeviceInfo(deviceManufacturerLineEdit->text(), deviceProductCodeLineEdit->text(), + deviceNameLineEdit->text())); - createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + " (" + deviceProductCodeLineEdit->text() + ")"); + createDevicesListWidget->addItem(deviceManufacturerLineEdit->text() + " " + deviceNameLineEdit->text() + ": " + deviceProductCodeLineEdit->text()); deviceManufacturerLineEdit->clear(); deviceNameLineEdit->clear(); deviceProductCodeLineEdit->clear(); @@ -825,12 +851,12 @@ void MainWindow::BuildPackage(void) packagePath.append(".tar.gz"); } - Packaging::BuildPackage(packagePath, workingPackageData); + Packaging::BuildPackage(packagePath, workingPackageData.GetFirmwareInfo()); } void MainWindow::HandleHeimdallStdout(void) { - QString output = process.read(1024); + QString output = process.readAll(); // We often receive multiple lots of output from Heimdall at one time. So we use regular expressions // to ensure we don't miss out on any important information. @@ -845,35 +871,10 @@ void MainWindow::HandleHeimdallStdout(void) flashProgressBar->setValue(percentString.mid(1, percentString.length() - 2).toInt()); } - /*// Handle other information - - int endOfLastLine = output.length() - 1; - for (; endOfLastLine > -1; endOfLastLine--) - { - if (output[endOfLastLine] != '\n') - break; - } - - if (endOfLastLine < 0) - return; // Output was blank or just a bunch of new line characters. - - int startOfLastLine = endOfLastLine - 1; - for (; startOfLastLine > -1; startOfLastLine--) - { - if (output[startOfLastLine] == '\n') - break; - } - - startOfLastLine++; - - // Just look at the last line of the output - output = output.mid(startOfLastLine, endOfLastLine - startOfLastLine + 1); // Work with the last line only - - percentExp.setPattern("[0-9]+%"); - - // If the last line wasn't a uploading message or a percentage transferred then display it. - if (output.lastIndexOf(uploadingExp) < 0 && output.lastIndexOf(percentExp) < 0) - flashLabel->setText(output);*/ + output.remove(QChar('\b')); + output.replace(QChar('%'), QString("%\n")); + outputPlainTextEdit->insertPlainText(output); + outputPlainTextEdit->ensureCursorVisible(); } void MainWindow::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitStatus) @@ -894,6 +895,9 @@ void MainWindow::HandleHeimdallReturned(int exitCode, QProcess::ExitStatus exitS { QString error = process.readAllStandardError(); + outputPlainTextEdit->insertPlainText(error); + outputPlainTextEdit->ensureCursorVisible(); + int firstNewLineChar = error.indexOf('\n'); if (firstNewLineChar == 0) diff --git a/heimdall-frontend/Source/mainwindow.h b/heimdall-frontend/Source/mainwindow.h index 7525d67..6e16596 100644 --- a/heimdall-frontend/Source/mainwindow.h +++ b/heimdall-frontend/Source/mainwindow.h @@ -99,7 +99,9 @@ namespace HeimdallFrontend void RemovePartition(void); void SelectPit(void); + void SetRepartition(int enabled); + void SetNoReboot(int enabled); void StartFlash(void); -- cgit v1.2.3