Can't extract exe or dll files with quazip (Qt) - c++

I am building a qt framework to download and install application updates (like sparkle for obj-c). The download works, the downloaded zip file is valid and i can extract the contents manually but when I let my framework unzip the contents via quazip the files (dll and exe) contains this and only this string: "MZ" and a special char which is wrong encoded (some kind of square on windows and "ê" on mac), so exactly 3 bytes. When I include a text file (or xml) in the zip file, it will be unzipped correctly, manually and with quazip, so I assume that the library was compiled correctly. Where is my error?
I think this can be part of the solution http://en.wikipedia.org/wiki/DOS_MZ_executable?
Here is my method to install the update:
QuaZip archiveWrapper(filename); // The downloaded zip file
if (archiveWrapper.open(QuaZip::mdUnzip)) {
QuaZipFile archive(&archiveWrapper);
qDebug() << "Extracting files" << archiveWrapper.getFileNameList();
for (bool more = archiveWrapper.goToFirstFile(); more; more = archiveWrapper.goToNextFile()) {
QString filePath = archiveWrapper.getCurrentFileName();
QString destinationPath = QDir::cleanPath(QDir::currentPath() + QDir::separator() + filePath);
QString destinationBackup = destinationPath + "_backup";
qDebug() << "Extract" << filePath << "to" << destinationPath;
QuaZipFile zip(archive.getZipName(), filePath);
zip.open(QIODevice::ReadOnly);
QByteArray data = zip.readAll();
zip.close();
QFile oldFile(destinationPath);
if (oldFile.exists()) {
qDebug() << "Rename" << destinationPath << "to" << destinationBackup;
if (!oldFile.rename(destinationBackup)) {
qWarning("Could not rename %s to %s!", destinationPath.toUtf8().constData(), destinationBackup.toUtf8().constData());
}
}
QFile destination(destinationPath);
destination.open(QIODevice::WriteOnly);
destination.write(data.data());
destination.close();
if (oldFile.exists()) {
qDebug() << "Deleting backup of" << destinationPath;
if (!oldFile.remove()) {
qWarning("Could not delete %s!", destinationPath.toUtf8().constData());
}
}
}
if (archive.getZipError() == UNZ_OK) {
qDebug() << "All files extracted successfully";
qDebug() << "Restarting application...";
archiveWrapper.close();
qApp->quit();
QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
} else {
qWarning("Error while extracting files (Error %d)", archive.getZipError());
archiveWrapper.close();
}
} else {
qWarning("Could not open archive to extract contents");
}
Edit:
I found out that data (QByteArray) has the expected size, so I think the problem is that QFile does not write the contents of QByteArray into the exe/dll files the way it should be?
Edit 2:
I've found one error, the file size to write:
destination.write(data.data(), data.size());
instead of
destination.write(data.data());
but still, the exe does not have an icon or is executable (but with the correct file size). For a short time a dos window opens and closes. There is a antivirus software running but there is no alert (and because this is a corporate notebook i am not able to shut it down and the update framework should also be running whether there is a antivirus software running or not).
Edit 3:
Although I thought writing exe files is complicated, it was a mixture of stupid bugs I "implemented" for testing purposes. So the simple
QFile destination(destinationPath);
destination.open(QIODevice::WriteOnly);
destination.write(data.data(), data.size())
is sufficient.

Related

Qt: QuaZip to extract file and show its progress in QProgressDialog

How could I use QuaZip to extract a .zip file and show its extraction progress in a QProgressDialog?
I've tried an example from this question1 (and also this question2) without success. Because I need to unzip .zip files (not .gz files) and that code shows % of progress but not unzip the files.
And even that, I don't know how to show that % in a QProgressDialog.
I've been able to extract .zip file using:
JlCompress::extractDir("C:/test/test.zip", "C:/test/");
However, that is not enought for my goal, because I need to show that extraction progress in a QProgressDialog in real time...
This is my code:
QString fileName = "C:/test/firefox-29.0.1.gz"; //I need to unzip .zip files
qDebug() << "Opened";
progressUnzip->setWindowTitle(tr("Unzip Manager"));
progressUnzip->setLabelText(tr("Unzipping %1. \nThis can take a long time to complete").arg(fileName));
progressUnzip->setAttribute(Qt::WA_DeleteOnClose);
QFile file(fileName);
file.open(QFile::ReadOnly);
QuaGzipFile gzip;
gzip.open(file.handle(), QuaGzipFile::ReadOnly);
progressUnzip->show();
while(true) {
QByteArray buf = gzip.read(1000);
//process buf
if (buf.isEmpty()) { break; }
QFile temp_file_object;
temp_file_object.open(file.handle(), QFile::ReadOnly);
double progress = 100.0 * temp_file_object.pos() / file.size();
updateUnzipProgress(temp_file_object.pos(), file.size());
qDebug() << qRound(progress) << "%";
}
unzipFinished();
qDebug() << "Finish";
Where:
void MainWindow::updateUnzipProgress(qint64 bytesRead, qint64 totalBytes)
{
qDebug() << bytesRead << "/" << totalBytes;
qint64 th = 1;
if (totalBytes >= 100000000)
{
th = 1000;
}
progressUnzip->setMaximum(totalBytes/th);
progressUnzip->setValue(bytesRead/th);
}
// When unzip finished or canceled, this will be called
void MainWindow::unzipFinished()
{
qDebug() << "Unzip finished.";
progressUnzip->hide();
}
In this code, QProgressDialog doesn't show any progress bar and at the end the file is not unzipped.
Any idea?
Thanks for your help,
I'm not going to write the whole code for you, but I can give you some hints to help you solve the problem.
QuaZIP library does not provide any Qt signals about the extraction progress, and that means that you should implement it yourself.
First, take a look at JlCompress::extractDir function implementation to see how it works. The source code can be found here. The function creates a QuaZip object which describes the zip archive you want to extract. Then it iterates over the files in the archive. On every iteration it creates a QuaZipFile object which describes a compressed file in the archive and then writes (=extracts) its data to a new file.
This is the copy function:
static bool copyData(QIODevice &inFile, QIODevice &outFile)
{
while (!inFile.atEnd()) {
char buf[4096];
qint64 readLen = inFile.read(buf, 4096);
if (readLen <= 0)
return false;
if (outFile.write(buf, readLen) != readLen)
return false;
}
return true;
}
inFile is the compressed (QuaZipFile) file and outFile is a new file where the compressed data is extracted to.
Now you should understand the underlying logic.
To add signals about extraction progress, you can can copy the JlCompress::extractDir source code to some wrapper class and make some changes.
While iterating over the files in the archive, you can get information about them using QuaZipFile::getFileInfo. That information contains uncompressed file size. Now go back to the copyData function. You know how many bytes you have written and you know the expected file size. That means you can calculate the single file extraction progress. Also you can get total files count and their uncompressed sizes using QuaZip::getFileInfoList64. That will let you calculate the whole extraction progress too.

Duplicate events received from POCO DirectoryWatcher

I'm trying to use the DirectoryWatcher class from POCO's file system library to monitor a specific folder for changes. The code is pretty simple and looks like this:
Monitor::Monitor() {
pattern = new Glob("/path/to/dir/*.dat",
Glob::GLOB_DOT_SPECIAL);
watcher = new DirectoryWatcher(std::string("/path/to/dir"));
watcher->itemAdded += delegate(this, &Monitor::onFileAdded);
watcher->itemModified += delegate(this, &Monitor::onFileChanged);
}
void Monitor::onFileAdded(const DirectoryWatcher::DirectoryEvent& addEvent) {
if (pattern->match(addEvent.item.path())) {
std::cout << "File added: " << addEvent.item.path() << std::endl;
}
}
void Monitor::onFileChanged(const DirectoryWatcher::DirectoryEvent& changeEvent) {
if (pattern->match(changeEvent.item.path())) {
std::cout << "File changed: " << changeEvent.item.path() << std::endl;
}
}
I'm observing some odd behavior. If I copy a new version of a file over a file that's already in the watched folder, I receive the 'item changed' notification twice. If I open a file that's already in the watched folder, edit its contents and save it, I receive an 'item added' notification followed by an 'item changed' notification.
This is on Ubuntu Linux 14.04 and ext4 file system with POCO 1.4.6p2.
Has anyone else observed similar behavior? Could this be related to some specific characteristic of my machine and the OS/file system combo? Is it possible to filter the unwanted events somehow?
Thanks in advance.

File retrieving in MongoDB using C++ Drivers

I am doing one project to make a database application in which i need to store some files in the database and later i need to retrieve those files back to the local system. I am using C++ drivers of the mongoDB..Storing the files on the server is working perfectly and when i am trying to retrieve it back only text files are received as it is but when i am doing it with images, .pdf files or other files format the file is corrupt.Can anyone tell me how i can save the files on local system without any corruption. Thanks
Code:
std::fstream out;
const char* gridfilename="Penguins.jpg";
const char* filename="Temp.jpg";
out.open(filename, ios::out);
DBClientConnection c;
c.connect("localhost");
cout << "connected ok" <<endl;
GridFS gfs = GridFS(c, "Test", "DB");
GridFile gf = gfs.findFile(gridfilename);
if (true != gf.exists()) {
cerr << "There is no file like " << gridfilename << endl;
}
gf.write(out);
out.close();

Qt how to get application to get md5 checksum of itself

Working on a Qt application. I'm trying to get the exe file to return an md5 checksum of itself while it's running. How can I do this?
I tried this:
QFile theFile("file.exe");
QByteArray thisFile;
if (theFile.open(QIODevice::ReadOnly))
{
thisFile = theFile.readAll();
}
else
{
qDebug() << "Can't open";
}
qDebug() << QString("%1").arg(thisFile.length());
fileMd5 = QString(QCryptographicHash::hash((thisFile), QCryptographicHash::Md5).toHex().toUpper());
qDebug() << fileMd5;
This does not return the correct value, however.
Update:
I got it working with other files. The problem seems to be that I am unable to read the exe while it's running.
Final update:
This is the solution:
QFile theFile(QCoreApplication::applicationFilePath());
QByteArray thisFile;
if (theFile.open(QIODevice::ReadOnly))
{
thisFile = theFile.readAll();
}
else
{
qDebug() << "Can't open file.";
}
QString fileMd5 = QString(QCryptographicHash::hash((thisFile), QCryptographicHash::Md5).toHex());
qDebug() << fileMd5;
You forgot to call open on theFile.
if (!theFile.open(QIODevice::ReadOnly))
// Handle error here
Also, you should be using QCoreApplication::applicationFilePath() to get the path to the executable.
You have to create an independent application (let's call it myApp) which check the MD5sum and compare it with your PHP script and ask for an update if needed or load directly the application.
Like so : myApp=> need update ? (update) : (TheRealApp)
Ok, looks like it just wasn't finding the file. I tried an absolute path instead of a relative and it worked. I'll have to figure out what's going wrong, but it looks like it can read itself while running.

ofstream wont create files in documents folder

I'm trying to make a function for a game I'm making that saves data from the game to a text file in a folder, both with a name provided by the user. I was able to do this in my project folder, and wanted it to be in a more universal place, so I am trying the documents folder.
When I switched the locations, however, the code stopped producing the desired result, and started creating the file and the folder in my program's main directory. (the file is not in the folder, fyi)
void Player::save()
{
system("mkdir \"C:\\Users\\Default\\Documents\\Ice Road\"");
//Make "Ice Road" folder in documents
std::string filename((name + ".txt"));
//make a name (inputed by the user earlier) to later be used to name a text file
std::string command("mkdir ");
//string to be combined with name to make folder
std::string commandString((command + name));
//combine to make string command that creates folder
std::string newDir = ("C:\\Users\\Default\\Documents\\Ice Road\\" + name);
//string to set directory to newly created folder
std::ofstream saveStream;
//open output stream for the saving process
SetCurrentDirectory("C:\\Users\\Default\\Documents\\Ice Road\\");
//set the directory to the Ice Road documents folder (DOES NOT WORK)
system((commandString.c_str()));
//create named folder for the save files.
SetCurrentDirectory(newDir.c_str());
//set the directory to the newly created folder
saveStream.open(filename.c_str());
//Create/open a text file that holds the data being saved
system("echo on");
//turn on echo for debugging
saveStream << name << std::endl
<< difficulty << std::endl
<< health << std::endl
<< warmth << std::endl
<< hunger << std::endl
<< packSpace << std::endl
<< packUsed << std::endl;
saveStream.close();
//input data to save file
system("dir");
//show folder for debugging
system("PAUSE");
//wait for input
}
How could I get this code to create a folder in documents called Ice Road, with the named folder inside and the named text file inside that?
(Documents\Ice Road\yourname\yourname.txt)
I solved the problem. My biggest problem was that I had no access to this folder (UAC), but because I wasn't receiving any errors I didn't think of it. Between that and some other tweaking I got it to work as shown below, running as administrator.
void Player::save()
{
std::string iceroad("C:\\Users\\Default\\Documents\\Ice Road");
//Ice road directory, made into a string variable for easy usage, as recommended by Ben
system("mkdir \"C:\\Users\\Default\\Documents\\Ice Road\"");
//Make Ice Road folder in documents
std::string filename(iceroad + "\\" + name + "\\" + name + ".txt");
//make a name (inputed by the user earlier) to later be used to name a text file, now using full address
std::string command("mkdir ");
//string to be combined with name to make folder
std::string commandString((command + name));
//combine to make string command that creates folder
std::string newDir = (iceroad + "\\" + name);
//string to set directory to newly created folder, simplified
std::cout << filename;
//debugging, as reccommended by Dietmar
std::ofstream saveStream;
//open output stream for the saving process
SetCurrentDirectory(iceroad.c_str());
//set the directory to the Ice Road documents folder
system("dir");
//debugging, as recommended by Dietmar
system((commandString.c_str()));
//create named folder for the save files.
SetCurrentDirectory(newDir.c_str());
//set the directory to the newly created folder
saveStream.open(filename.c_str());
//Create/open a text file that holds the data being saved
system("echo on");
//turn on echo for debugging
saveStream << name << std::endl
<< difficulty << std::endl
<< health << std::endl
<< warmth << std::endl
<< hunger << std::endl
<< packSpace << std::endl
<< packUsed << std::endl;
saveStream.close();
//inputs data to save file
system("dir");
//show folder for debugging
system("PAUSE");
//wait for input
}
Thank you for your constructive critiques, I did take them to heart and implement them.