/*
* BSD 3-Clause License
*
* Copyright (c) 2018-2019, Antony jr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @filename : appimageupdateinformation_p.cc
* @description : This is where the extraction of embeded update information
* from AppImages is implemented.
*/
#include <QBuffer>
#include <QProcessEnvironment>
#include "appimageupdateinformation_p.hpp"
#include "qappimageupdateenums.hpp"
/*
* An efficient logging system.
* Warning: Hard coded to work only with this class.
*/
#ifndef LOGGING_DISABLED
#define LOGS *(p_Logger.data()) <<
#define LOGR <<
#define LOGE ; \
emit(logger(s_LogBuffer , s_AppImagePath)); \
s_LogBuffer.clear();
#else
#define LOGS (void)
#define LOGR ;(void)
#define LOGE ;
#endif // LOGGING_DISABLED
#define INFO_START LOGS " INFO: " LOGR
#define INFO_END LOGE
#define WARNING_START LOGS "WARNING: " LOGR
#define WARNING_END LOGE
#define FATAL_START LOGS " FATAL: " LOGR
#define FATAL_END LOGE
/*
* Sets the offset and length of the need section header
* from a elf file.
*
* Example:
* long unsigned offset = 0 , length = 0;
* ElfXX_Ehdr *elfXX = (ElfXX_Ehdr *) data;
* ElfXX_Shdr *shdrXX = (ElfXX_Shdr *) (data + elfXX->e_shoff);
* strTab = (char *)(data + shdrXX[elfXX->e_shstrndx].sh_offset);
* lookupSectionHeaders(strTab , shdr , elf , ".section_header_name" , offset , length , progress);
*
* Note:
* progress must be a Qt signal which takes int as a parameter.
*/
#define lookupSectionHeaders(strTab , shdr , elf , section , offset , length , progsig) \
{ \
for(int i = 0; i < elf->e_shnum; i++) { \
emit(progsig((int)((i * 100)/elf->e_shnum))); \
QCoreApplication::processEvents(); \
if(!strcmp(&strTab[shdr[i].sh_name] , section)){ \
offset = shdr[i].sh_offset; \
length = shdr[i].sh_size; \
emit(progsig(80)); \
break;\
} \
}\
}
/*
* AppImage update information positions and magic values.
* See https://github.com/AppImage/AppImageSpec/blob/master/draft.md
*/
static constexpr auto AppimageType1UpdateInfoPos = 0x8373;
static constexpr auto AppimageType1UpdateInfoLen = 0x200;
static constexpr auto AppimageType2UpdateInfoShdr = (char*)".upd_info";
static constexpr char AppimageUpdateInfoDelimiter = 0x7c;
static constexpr auto ElfMagicPos = 0x1;
static constexpr auto IsoMagicPos = 0x8001;
static constexpr auto ElfMagicValueSize= 0x4;
static constexpr auto IsoMagicValueSize= 0x6;
static const QByteArray ElfMagicValue = "ELF";
static const QByteArray IsoMagicValue = "CD001";
/*
* e_ident[] identification indexes
* See http://www.sco.com/developers/gabi/latest/ch4.eheader.html
*/
static constexpr auto EI_CLASS = 0x4; /* file class */
static constexpr auto EI_NIDENT = 0x10; /* Size of e_ident[] */
/* e_ident[] file class */
static constexpr auto ELFCLASS32 = 0x1; /* 32-bit objs */
static constexpr auto ELFCLASS64 = 0x2; /* 64-bit objs */
typedef quint8 Elf_Byte;
typedef quint32 Elf32_Addr; /* Unsigned program address */
typedef quint32 Elf32_Off; /* Unsigned file offset */
typedef quint32 Elf32_Sword; /* Signed large integer */
typedef quint32 Elf32_Word; /* Unsigned large integer */
typedef quint16 Elf32_Half; /* Unsigned medium integer */
typedef quint64 Elf64_Addr;
typedef quint64 Elf64_Off;
typedef qint32 Elf64_Shalf;
typedef qint64 Elf64_Sword;
typedef quint64 Elf64_Word;
typedef qint64 Elf64_Sxword;
typedef quint64 Elf64_Xword;
typedef quint32 Elf64_Half;
typedef quint16 Elf64_Quarter;
/* ELF Header */
typedef struct elfhdr {
unsigned char e_ident[EI_NIDENT]; /* ELF Identification */
Elf32_Half e_type; /* object file type */
Elf32_Half e_machine; /* machine */
Elf32_Word e_version; /* object file version */
Elf32_Addr e_entry; /* virtual entry point */
Elf32_Off e_phoff; /* program header table offset */
Elf32_Off e_shoff; /* section header table offset */
Elf32_Word e_flags; /* processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size */
Elf32_Half e_phentsize; /* program header entry size */
Elf32_Half e_phnum; /* number of program header entries */
Elf32_Half e_shentsize; /* section header entry size */
Elf32_Half e_shnum; /* number of section header entries */
Elf32_Half e_shstrndx; /* section header table's "section
header string table" entry offset */
} Elf32_Ehdr;
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Id bytes */
Elf64_Quarter e_type; /* file type */
Elf64_Quarter e_machine; /* machine type */
Elf64_Half e_version; /* version number */
Elf64_Addr e_entry; /* entry point */
Elf64_Off e_phoff; /* Program hdr offset */
Elf64_Off e_shoff; /* Section hdr offset */
Elf64_Half e_flags; /* Processor flags */
Elf64_Quarter e_ehsize; /* sizeof ehdr */
Elf64_Quarter e_phentsize; /* Program header entry size */
Elf64_Quarter e_phnum; /* Number of program headers */
Elf64_Quarter e_shentsize; /* Section header entry size */
Elf64_Quarter e_shnum; /* Number of section headers */
Elf64_Quarter e_shstrndx; /* String table index */
} Elf64_Ehdr;
/* Section Header */
typedef struct {
Elf32_Word sh_name; /* name - index into section header
string table section */
Elf32_Word sh_type; /* type */
Elf32_Word sh_flags; /* flags */
Elf32_Addr sh_addr; /* address */
Elf32_Off sh_offset; /* file offset */
Elf32_Word sh_size; /* section size */
Elf32_Word sh_link; /* section header table index link */
Elf32_Word sh_info; /* extra information */
Elf32_Word sh_addralign; /* address alignment */
Elf32_Word sh_entsize; /* section entry size */
} Elf32_Shdr;
typedef struct {
Elf64_Half sh_name; /* section name */
Elf64_Half sh_type; /* section type */
Elf64_Xword sh_flags; /* section flags */
Elf64_Addr sh_addr; /* virtual address */
Elf64_Off sh_offset; /* file offset */
Elf64_Xword sh_size; /* section size */
Elf64_Half sh_link; /* link to another */
Elf64_Half sh_info; /* misc info */
Elf64_Xword sh_addralign; /* memory alignment */
Elf64_Xword sh_entsize; /* table entry size */
} Elf64_Shdr;
struct AutoBoolCounter {
explicit AutoBoolCounter(bool *p)
: p_Bool(p) {
*p_Bool = true;
}
~AutoBoolCounter() {
*p_Bool = false;
}
void lock() {
*p_Bool = true;
}
void unlock() {
*p_Bool = false;
}
private:
bool *p_Bool = nullptr;
};
/*
* Returns a new QByteArray which contains the contents from the given QFile from the given offset to the given
* max count. This function does not change the position of the QFile.
*
* Example:
* QFile file("Some.AppImage")
* file.open(QIODevice::ReadOnly);
* QByteArray data = read(&file , 512 , 1024);
*/
static QByteArray read(QFile *IO, qint64 offset, qint64 max) {
QByteArray ret;
qint64 before = IO->pos();
IO->seek(offset);
ret = IO->read(max);
IO->seek(before);
return ret;
}
static QByteArray readLine(QFile *IO) {
QByteArray ret;
char c = 0;
while(IO->getChar(&c) && c != '\n') {
ret.append(c);
}
return ret;
}
// TODO: Evaluate this using Regex instead.
static QByteArray getExecPathFromDesktopFile(QFile *file) {
QByteArray line;
qint64 prevPos = file->pos();
file->seek(0);
while(!(line = readLine(file)).isEmpty()) {
if(line.contains("Exec")) {
for(auto i = 0; i < line.size() ; ++i) {
if(line[i] == '=') {
line = line.mid(i+1);
break;
}
}
break;
}
}
file->seek(prevPos);
return line;
}
/*
* AppImageUpdateInformationPrivate is the worker class that provides the ability to easily get the update
* information from an AppImage. This class can be constructed in two ways. The default construct sets the
* QObject parent to be null and creates an empty AppImageUpdateInformationPrivate Object.
*
* Example:
* QObject parent;
* AppImageUpdateInformationPrivate AppImageInfoWithParent(&parent);
* AppImageUpdateInformationPrivate AppImageInfoWithoutParent;
*/
AppImageUpdateInformationPrivate::AppImageUpdateInformationPrivate(QObject *parent)
: QObject(parent) {
#ifndef LOGGING_DISABLED
try {
p_Logger.reset(new QDebug(&s_LogBuffer));
} catch ( ... ) {
emit(error(QAppImageUpdateEnums::Error::NotEnoughMemory));
throw;
}
#endif // LOGGING_DISABLED
return;
}
/*
* Destructs the AppImageUpdateInformationPrivate , When the user provides the AppImage as a QFile ,
* QFile is not closed , the user is fully responsible to deallocate or close the QFile.
*/
AppImageUpdateInformationPrivate::~AppImageUpdateInformationPrivate() {
return;
}
void AppImageUpdateInformationPrivate::setLoggerName(const QString &name) {
if(b_Busy) {
return;
}
#ifndef LOGGING_DISABLED
s_LoggerName = QString(name);
#else
(void)name;
#endif
return;
}
/*
* This method returns nothing and sets the AppImage referenced by the given QString , The QString is
* expected to be a valid path either an absolute or a relative one. if the path is empty then this
* exits doing nothing.
*
* Example:
* AppImageUpdateInformationPrivate AppImageInfo;
* AppImageInfo.setAppImage("PathTo.AppImage");
*
*/
void AppImageUpdateInformationPrivate::setAppImage(const QString &AppImagePath) {
if(b_Busy) {
return;
}
clear(); /* clear old data */
if(AppImagePath.isEmpty()) {
WARNING_START " setAppImage : AppImagePath is empty , operation ignored." WARNING_END;
return;
}
INFO_START " setAppImage : " LOGR AppImagePath LOGR "." INFO_END;
s_AppImagePath = AppImagePath;
s_AppImageName = QFileInfo(AppImagePath).fileName();
return;
}
/*
* This is a overloaded method , Sets the AppImage with reference to the given QFile pointer ,
* The given QFile has to be opened and must be readable.
*
* Example:
* AppImageUpdateInformationPrivate AppImageInfo;
* QFile file("PathTo.AppImage");
* file.open(QIODevice::ReadOnly);
* AppImageInfo.setAppImage(&file);
* file.close();
*/
void AppImageUpdateInformationPrivate::setAppImage(QFile *AppImage) {
if(b_Busy) {
return;
}
clear(); /* clear old data. */
if(!AppImage) {
WARNING_START " setAppImage : given AppImage QFile is nullptr , operation ignored. " WARNING_END
return;
}
INFO_START " setAppImage : " LOGR AppImage LOGR "." INFO_END;
p_AppImage = AppImage;
s_AppImagePath = QFileInfo(AppImage->fileName()).canonicalFilePath();
s_AppImageName = QFileInfo(s_AppImagePath).fileName();
return;
}
/*
* If the given bool is true then it connects the logger signal to the
* logPrinter slot to enable debugging messages. On false this disconnects the logPrinter.
*
* Example:
* AppImageUpdateInformationPrivate AppImageInfo("PathTo.AppImage");
* AppImageInfo.setShowLog(true);
*/
void AppImageUpdateInformationPrivate::setShowLog(bool logNeeded) {
if(b_Busy) {
return;
}
#ifndef LOGGING_DISABLED
if(logNeeded) {
connect(this, &AppImageUpdateInformationPrivate::logger,
this, &AppImageUpdateInformationPrivate::handleLogMessage,
Qt::UniqueConnection);
return;
}
disconnect(this, &AppImageUpdateInformationPrivate::logger,
this, &AppImageUpdateInformationPrivate::handleLogMessage);
#else
(void)logNeeded;
#endif // LOGGING_DISABLED
}
void AppImageUpdateInformationPrivate::getInfo(void) {
if(b_Busy) {
return;
}
AutoBoolCounter bc(&b_Busy);
/*
* Check if the user called this twice , If so , We don't need to waste our time on calculating the obvious.
* Note: m_Info will always will be empty for a new AppImage , And so if it is not empty then that implies
* that the user called getInfo() twice or more.
*/
if(!m_Info.isEmpty()) {
QJsonObject fileInfo = m_Info.value("FileInformation").toObject();
emit(operatingAppImagePath(fileInfo.value("AppImageFilePath").toString()));
emit(info(m_Info));
return;
}
/* If this class is constructed without an AppImage to operate on ,
* Then lets guess it. */
if(!p_AppImage && s_AppImagePath.isEmpty()) {
/* Do not check the QCoreApplication first. First check if the environmental variable
* $APPIMAGE has something , if not then use the arguments given by QCoreApplication. */
bc.unlock();
setAppImage(QProcessEnvironment::systemEnvironment().value("APPIMAGE"));
bc.lock();
// Check if it's a AppImageLauncher's path, if so then use the map file to
// get the actual appimage path.
bool tryUsingProgramArguments = false;
bool checkForAIL = true;
QRegExp rx(QString::fromUtf8("/run/user/*/appimagelauncherfs/*.AppImage"));
rx.setPatternSyntax(QRegExp::Wildcard);
QString desktopIntegration;
if(QProcessEnvironment::systemEnvironment().contains("DESKTOPINTEGRATION")) {
desktopIntegration = QProcessEnvironment::systemEnvironment().value("DESKTOPINTEGRATION");
INFO_START " getInfo: Desktop integration detected" INFO_END;
if(desktopIntegration == QString::fromStdString("AppImageLauncher")) {
INFO_START " getInfo: Desktop integration seems to be AppImageLauncher." INFO_END;
QString progPath = QProcessEnvironment::systemEnvironment().value("ARGV0");
INFO_START " getInfo: ARGV0 = " LOGR progPath INFO_END;
if(!progPath.isEmpty()) {
bc.unlock();
setAppImage(progPath);
bc.lock();
checkForAIL = false;
}
}
}
if(checkForAIL && rx.exactMatch(s_AppImagePath)) {
INFO_START " getInfo: Reading AppImageLauncher internal Map file to get the actual AppImage" INFO_END;
QFileInfo pathInfo(s_AppImagePath);
QString mapPath = pathInfo.absolutePath();
mapPath += QString::fromUtf8("/map");
QString fileID = pathInfo.fileName();
QFile mapFile(mapPath);
if(mapFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
while (!mapFile.atEnd()) {
QByteArray line = mapFile.readLine();
QString str(line);
auto parts = str.split(QString::fromUtf8(" -> "));
QString partID = parts.at(0).left(fileID.size());
if(fileID == partID) {
// Remove trailling new line
QString parsedPath = parts.at(1).left(parts.at(1).size() - 1);
bc.unlock();
setAppImage(parsedPath);
bc.lock();
break;
}
QCoreApplication::processEvents();
}
mapFile.close();
} else {
INFO_START " getInfo: Failed to get the actual AppImage path, trying to use program arguments." INFO_END;
tryUsingProgramArguments = true; // Try to parse from QCoreApplication.
}
}
/*
* Lets try getting it from QCoreApplication arguments. */
if(s_AppImagePath.isEmpty() || tryUsingProgramArguments) {
INFO_START " getInfo: getting the AppImage path from program arguments, This might not work inside firejail." INFO_END;
auto arguments = QCoreApplication::arguments();
if(!arguments.isEmpty()) {
bc.unlock();
setAppImage(QFileInfo(arguments.at(0)).absolutePath() +
QString::fromUtf8("/") +
QFileInfo(arguments.at(0)).fileName());
bc.lock();
}
if(s_AppImagePath.isEmpty() || !QFileInfo::exists(s_AppImagePath)) {
emit(error(QAppImageUpdateEnums::Error::NoAppimagePathGiven));
return;
}
}
}
emit(operatingAppImagePath(s_AppImagePath));
if(!p_AppImage) {
/* Open appimage if the user only given the path. */
try {
p_AppImage = new QFile(this);
} catch (...) {
emit(error(QAppImageUpdateEnums::Error::NotEnoughMemory));
return;
}
/*
* Check if its really a file and not a folder.
*/
if(!QFileInfo(s_AppImagePath).isFile()) {
p_AppImage->deleteLater();
p_AppImage = nullptr;
FATAL_START " setAppImage : cannot use a directory as a file." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::AppimageNotFound));
return;
}
p_AppImage->setFileName(s_AppImagePath);
QCoreApplication::processEvents();
/* Check if the file actually exists. */
if(!p_AppImage->exists()) {
p_AppImage->deleteLater();
p_AppImage = nullptr;
FATAL_START " setAppImage : cannot find the AppImage in the given path , file not found." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::AppimageNotFound));
return;
}
/* Check if we have the permission to read it. */
auto perm = p_AppImage->permissions();
if(
!(perm & QFileDevice::ReadUser) &&
!(perm & QFileDevice::ReadGroup) &&
!(perm & QFileDevice::ReadOther)
) {
p_AppImage->deleteLater();
p_AppImage = nullptr;
FATAL_START " setAppImage : no permission(" LOGR perm LOGR ") for reading the given AppImage." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::NoReadPermission));
return;
}
/*
* Finally open the file.
*/
if(!p_AppImage->open(QIODevice::ReadOnly)) {
p_AppImage->deleteLater();
p_AppImage = nullptr;
FATAL_START " setAppImage : cannot open AppImage for reading." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::CannotOpenAppimage));
return;
}
QCoreApplication::processEvents();
} else {
QCoreApplication::processEvents();
/* Check if exists */
if(!p_AppImage->exists()) {
FATAL_START " setAppImage : cannot find the AppImage from given QFile , file does not exists." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::AppimageNotFound));
return;
}
/* Check if readable. */
if(!p_AppImage->isReadable()) {
FATAL_START " setAppImage : invalid QFile given, not readable." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::AppimageNotReadable));
return;
}
/* Check if opened. */
if(!p_AppImage->isOpen()) {
FATAL_START " setAppImage : invalid QFile given, not opened." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::CannotOpenAppimage));
return;
}
QCoreApplication::processEvents();
}
QString AppImageSHA1;
QString updateString;
QStringList data;
/*
* Read the magic byte , i.e the AI stamp on the given binary. The characters 'AI'
* are hardcoded at the offset 8 with a maximum of 3 characters.
* The 3rd character decides the type of the AppImage.
*/
QCoreApplication::processEvents();
auto magicBytes = read(p_AppImage, /*offset=*/8,/*maxchars=*/ 3);
if (magicBytes[0] != 'A' || magicBytes[1] != 'I') {
/*
* If its not an AppImage then lets check if its a linux desktop file,
* If so then parse the 'Exec' to find the actual AppImage.
*/
magicBytes = read(p_AppImage, 0, 15);
if(magicBytes == "[Desktop Entry]") {
auto path = QString(getExecPathFromDesktopFile(p_AppImage));
if(!path.isEmpty()) {
if(QFileInfo(path).isRelative()) {
path = QFileInfo(p_AppImage->fileName()).path() + QString::fromUtf8("/") + QString(path);
}
bc.unlock(); /* unlock the bool counter. */
setAppImage(path);
getInfo();
return;
}
}
FATAL_START " getInfo : invalid magic bytes("
LOGR (unsigned)magicBytes[0] LOGR ","
LOGR (unsigned)magicBytes[1] LOGR ")." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::InvalidMagicBytes));
return;
}
/*
* Calculate the AppImages SHA1 Hash which will be used later to find if we need to update the
* AppImage.
*/
QCoreApplication::processEvents();
{
qint64 bufferSize = 0;
if(p_AppImage->size() >= 1073741824) { // 1 GiB and more.
bufferSize = 104857600; // copy per 100 MiB.
} else if(p_AppImage->size() >= 1048576 ) { // 1 MiB and more.
bufferSize = 1048576; // copy per 1 MiB.
} else if(p_AppImage->size() >= 1024) { // 1 KiB and more.
bufferSize = 4096; // copy per 4 KiB.
} else { // less than 1 KiB
bufferSize = 1024; // copy per 1 KiB.
}
QCryptographicHash *SHA1Hasher = new QCryptographicHash(QCryptographicHash::Sha1);
while(!p_AppImage->atEnd()) {
SHA1Hasher->addData(p_AppImage->read(bufferSize));
QCoreApplication::processEvents();
}
p_AppImage->seek(0); // rewind file to the top for later use.
AppImageSHA1 = QString(SHA1Hasher->result().toHex().toUpper());
delete SHA1Hasher;
}
QCoreApplication::processEvents();
/*
* 0x1H -> Type 1 AppImage.
* 0x2H -> Type 2 AppImage. (Latest Version)
*/
int type = (int)magicBytes[2];
if(type == 0x1) {
INFO_START " getInfo : AppImage is confirmed to be type 1." INFO_END;
progress(/*percentage=*/80); /*Signal progress.*/
QCoreApplication::processEvents();
updateString = QString::fromUtf8(read(p_AppImage, AppimageType1UpdateInfoPos, AppimageType1UpdateInfoLen));
} else if(type == 0x2) {
INFO_START " getInfo : AppImage is confirmed to be type 2." INFO_END;
INFO_START " getInfo : mapping AppImage to memory." INFO_END;
{
uint8_t *data = NULL;
char *strTab = NULL;
unsigned long offset = 0, length = 0;
uchar *mapped = p_AppImage->map(/*offset=*/0, /*max=*/p_AppImage->size());
if(mapped == NULL) {
FATAL_START " getInfo : not enough memory to map AppImage to memory." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::NotEnoughMemory));
return;
}
QCoreApplication::processEvents();
data = (uint8_t*) mapped;
if((((Elf32_Ehdr*)data)->e_ident[EI_CLASS] == ELFCLASS32)) {
INFO_START " getInfo : AppImage architecture is x86 (32 bits)." INFO_END;
Elf32_Ehdr *elf32 = (Elf32_Ehdr *) data;
Elf32_Shdr *shdr32 = (Elf32_Shdr *) (data + elf32->e_shoff);
strTab = (char *)(data + shdr32[elf32->e_shstrndx].sh_offset);
lookupSectionHeaders(strTab, shdr32, elf32, AppimageType2UpdateInfoShdr,
/*variable to set offset=*/offset, /*length of the header=*/length,
/*Progress signal to use=*/progress);
} else if((((Elf64_Ehdr*)data)->e_ident[EI_CLASS] == ELFCLASS64)) {
INFO_START " getInfo : AppImage architecture is x86_64 (64 bits)." INFO_END;
Elf64_Ehdr *elf64 = (Elf64_Ehdr *) data;
Elf64_Shdr *shdr64 = (Elf64_Shdr *) (data + elf64->e_shoff);
strTab = (char *)(data + shdr64[elf64->e_shstrndx].sh_offset);
lookupSectionHeaders(strTab, shdr64, elf64, AppimageType2UpdateInfoShdr,
offset, length, progress);
} else {
p_AppImage->unmap(mapped);
FATAL_START " getInfo : Unsupported elf format." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::UnsupportedElfFormat));
return;
}
p_AppImage->unmap(mapped);
if(offset == 0 || length == 0) {
FATAL_START " getInfo : cannot find '"
LOGR AppimageType2UpdateInfoShdr LOGR "' section header." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::SectionHeaderNotFound));
} else {
updateString = QString::fromUtf8(read(p_AppImage, offset, length));
}
}
} else {
WARNING_START " getInfo : unable to confirm AppImage type." WARNING_END;
if(
(read(p_AppImage, ElfMagicPos, ElfMagicValueSize) == ElfMagicValue) &&
(read(p_AppImage, IsoMagicPos, IsoMagicValueSize) == IsoMagicValue)
) {
WARNING_START " getInfo : guessing AppImage type to be 1." WARNING_END;
emit(progress(80));
updateString = QString::fromUtf8(read(p_AppImage, AppimageType1UpdateInfoPos, AppimageType1UpdateInfoLen));
} else {
FATAL_START " getInfo : invalid AppImage type(" LOGR type LOGR ")." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::InvalidAppimageType));
return;
}
}
QCoreApplication::processEvents();
if(updateString.isEmpty()) {
FATAL_START " getInfo : update information is empty." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::EmptyUpdateInformation));
return;
}
INFO_START " getInfo : updateString(" LOGR updateString LOGR ")." INFO_END;
/*
* Split the raw update information with the specified
* delimiter.
*/
data = updateString.split(AppimageUpdateInfoDelimiter);
// This will be sent along the update information.
QJsonObject fileInformation {
{ "AppImageFilePath", s_AppImagePath },
{ "AppImageSHA1Hash", AppImageSHA1 }
};
QJsonObject updateInformation; // will be filled up later on.
if(data.size() < 2) {
FATAL_START " getInfo : update information has invalid delimiters." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::InvalidAppimageType));
return;
} else if(data.size() == 2) {
{
QJsonObject buffer {
{ "transport", data.at(0) },
{ "zsyncUrl", data.at(1) }
};
updateInformation = buffer;
}
} else if(data.size() == 5) {
if(data.at(0) == "gh-releases-zsync") {
{
QJsonObject buffer {
{"transport", data.at(0) },
{"username", data.at(1) },
{"repo", data.at(2) },
{"tag", data.at(3) },
{"filename", data.at(4) }
};
updateInformation = buffer;
}
} else if(data.at(0) == "bintray-zsync") {
{
QJsonObject buffer {
{"transport", data.at(0) },
{"username", data.at(1) },
{"repo", data.at(2) },
{"packageName", data.at(3) },
{"filename", data.at(4) }
};
updateInformation = buffer;
}
} else {
FATAL_START " getInfo : unsupported transport mechanism given." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::UnsupportedTransport));
return;
}
} else {
FATAL_START " getInfo : update information has invalid number of entries(" LOGR data.size() LOGR ")." FATAL_END;
emit(error(QAppImageUpdateEnums::Error::InvalidAppimageType));
return;
}
{
QJsonObject buffer {
{ "IsEmpty", updateInformation.isEmpty() },
{ "FileInformation", fileInformation },
{ "UpdateInformation", updateInformation }
};
m_Info = buffer;
}
emit(progress(100)); /*Signal progress.*/
emit(info(m_Info));
INFO_START " getInfo : finished." INFO_END;
return;
}
/*
* This clears all the data held in the current object , making it
* reusable.
*
* Example:
* AppImageInfo.clear();
*/
void AppImageUpdateInformationPrivate::clear(void) {
if(b_Busy) {
return;
}
m_Info = QJsonObject(); /* TODO: if QJsonObject has a clear in future , use it instead. */
#ifndef LOGGING_DISABLED
s_LogBuffer.clear();
#endif
s_AppImagePath.clear();
s_AppImageName.clear();
p_AppImage = nullptr; /* Drop all responsibilities. */
return;
}
#ifndef LOGGING_DISABLED
/* This private slot proxies the log messages from the logger signal to qInfo(). */
void AppImageUpdateInformationPrivate::handleLogMessage(QString msg, QString path) {
(void)path;
qInfo().noquote() << "["
<< QDateTime::currentDateTime().toString(Qt::ISODate)
<< "] "
<< s_LoggerName
<< "("
<< s_AppImageName << ")::" << msg;
return;
}
#endif // LOGGING_DISABLED
↑ V575 The null pointer is passed into 'read' function. Inspect the second argument.