Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The encrypted zip file generated with QuaZip on Ubuntu cannot be decrypted on Windows. #201

Open
xiqixi opened this issue Sep 12, 2024 · 4 comments

Comments

@xiqixi
Copy link

xiqixi commented Sep 12, 2024

quazip-0.7.1 + qt-5.12.12
quazip-1.5 + qt-5.12.12

I generated a password-protected zip file (password is "a") using Qt + QuaZip on Ubuntu.
After copying the file to Windows, I tried to extract it using Qt + QuaZip (with the same password "a"), but the extraction failed.
Moreover, I couldn't extract the file using unzip on Ubuntu, nor using 7-Zip on Windows.
The only way to successfully extract it is by using Qt + QuaZip on Ubuntu.
Below is the code I used.
How can I make it extract successfully on Windows?
Looking forward to your answer.
Thank you!

bool ZipHandler::compressFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password)
{
    if (!zip)
    {
        return false;
    }
    if (zip->getMode()!=QuaZip::mdCreate &&
            zip->getMode()!=QuaZip::mdAppend &&
            zip->getMode()!=QuaZip::mdAdd)
    {
        return false;
    }

    QFile inFile;
    inFile.setFileName(fileName);
    if (!inFile.open(QIODevice::ReadOnly))
    {
        return false;
    }

    QuaZipFile outFile(zip);
    if (!password.isEmpty())
    {
        if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()), password.toUtf8().constData()))
        {
            return false;
        }
    } else
    {
        if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName())))
        {
            return false;
        }
    }

    if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK)
    {
        return false;
    }

    outFile.close();
    if (outFile.getZipError()!=UNZ_OK)
    {
        return false;
    }
    inFile.close();

    return true;
}

ZipError ZipHandler::extractFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password)
{
    QuaZipFile inFile(zip);
    QuaZipFileInfo64 info;
    QFile::Permissions srcPerm;
    ZipError ret = NoError;

    if (!fileName.isEmpty())
    {
        zip->setCurrentFile(fileName);
    }

    if (!zip->getCurrentFileInfo(&info))
    {
        ret = OtherError;
    }

    if (ret == NoError)
    {
        if (info.isEncrypted())
        {
            if (!inFile.open(QIODevice::ReadOnly, password.toUtf8().constData()) || inFile.getZipError()!=UNZ_OK)
            {
                ret = OtherError;
            }
        } else
        {
            if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError()!=UNZ_OK)
            {
                ret = OtherError;
            }
        }
    }

    if (ret == NoError)
    {
        ret = makepath(fileDest);
    }

    if (ret == NoError)
    {
        srcPerm = info.getPermissions();
        if (fileDest.endsWith(FILEPATH_SEP) && QFileInfo(fileDest).isDir()) {
            if (srcPerm != 0)
            {
                QFile(fileDest).setPermissions(srcPerm);
            }
        } else
        {
            ret = extractData(srcPerm, inFile, fileDest);
        }
    }

    return ret;
}

ZipError ZipHandler::makepath(const QString& fileDest)
{
    QDir curDir;
    ZipError ret = NoError;


    if (fileDest.endsWith(FILEPATH_SEP))
    {
        if (!curDir.mkpath(fileDest))
        {
            ret = NoPermission;
        }
    } else
    {
        if (!curDir.mkpath(QFileInfo(fileDest).absolutePath()))
        {
            ret = NoPermission;
        }
    }

    return ret;
}

ZipError ZipHandler::extractData(const QFile::Permissions& srcPerm, QuaZipFile& inFile, const QString& fileDest)
{
    QFile outFile;
    ZipError ret = NoError;

    outFile.setFileName(fileDest);
    if (!outFile.open(QIODevice::WriteOnly))
    {
        ret = NoPermission;
    }

    if (ret == NoError)
    {
        if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK)
        {
            outFile.close();
            removeFile(QStringList(fileDest));
            ret = NoSpace;
        }
    }

    if (ret == NoError)
    {
        outFile.close();
        inFile.close();

        if (inFile.getZipError() != UNZ_OK)
        {
            removeFile(QStringList(fileDest));
            ret = OtherError;
        }
    }

    if (ret == NoError)
    {
        if (srcPerm != 0) {
            outFile.setPermissions(srcPerm);
        }
    }

    return ret;
}

bool ZipHandler::copyData(QIODevice &inFile, QIODevice &outFile)
{
    char buf[writeBuff];
    qint64 readLen = 0;
    bool ret = true;
    qint64 writeBuffNum = 4096;

    while (!inFile.atEnd())
    {
        readLen = inFile.read(buf, writeBuffNum);
        if (readLen <= 0)
        {
            ret = false;
            break;
        }
        if (outFile.write(buf, readLen) != readLen)
        {
            ret = false;
            break;
        }
    }
    return ret;
}

bool ZipHandler::removeFile(const QStringList& listFile)
{
    bool ret = true;

    for (int i=0; i<listFile.count(); ++i)
    {
        ret = ret && QFile::remove(listFile.at(i));
    }
    return ret;
}
@cen1
Copy link
Collaborator

cen1 commented Sep 14, 2024

Your code seems to work fine for me.

QFile oldZip("../../xiqixi.zip");
if (oldZip.exists()) oldZip.remove();

QuaZip zip("../../xiqixi .zip");
zip.open(QuaZip::mdCreate);
compressFile(&zip, "../../file.txt", "file2.txt", "a");
zip.close();
if (zip.getZipError() != ZIP_OK) return 1;

I can decompress this on Linux, MacOS and Windows.

Perhaps you are sending full path as fileDest?

@xiqixi
Copy link
Author

xiqixi commented Sep 19, 2024

I can decompress this on Linux, MacOS and Windows.

Perhaps you are sending full path as fileDest?
Thank you for your help. The file name of the source file is set as fileDest.

2 comments:
1.The password-protected zip file, generated by Qt and QuaZip on Windows, can be extracted using Qt and QuaZip on Windows or 7-Zip.
2.If no password is set, the zip file generated by Qt and QuaZip on Ubuntu can be extracted with Qt and QuaZip on Windows or 7-Zip.

@cen1
Copy link
Collaborator

cen1 commented Sep 19, 2024

Can you send a code snippet just like mine to see how you call the functions?

@xiqixi
Copy link
Author

xiqixi commented Sep 20, 2024

Can you send a code snippet just like mine to see how you call the functions?

#include <ziphandler.h>

StringList fileList;
fileList.append("../9.txt");
ZipHandler::compressFiles("../9.zip", xxx, "1");

ziphandler.h

#ifndef ZIPHANDLER_H
#define ZIPHANDLER_H

#include <QString>
#include <quazip/quazip.h>
#include <quazip/quazipfile.h>

enum ZipError
{
    NoError,
    NoSpace,
    NoPermission,
    OtherError
};

class ZipHandler
{
public:
    ZipHandler();
    ~ZipHandler();

    static bool compressFiles(const QString& fileCompressed, const QStringList& files, const QString& password = QString());
    static bool compressDir(const QString& fileCompressed, const QString& dir = QString(), bool recursive = true, const QString& password = QString());
    static ZipError extractDir(const QString& fileCompressed, const QString& dir = QString(), const QString& password = QString());

private:

    static bool compressFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password);
    static bool compressSubDir(QuaZip* parentZip, const QString& dir, const QString& parentDir, bool recursive = true, const QString& password = QString());
    static ZipError extractFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password);
    static ZipError extractData(const QFile::Permissions& srcPerm, QuaZipFile& inFile, const QString& fileDest);
    static bool copyData(QIODevice &inFile, QIODevice &outFile);
    static ZipError makepath(const QString& fileDest);
    static bool removeFile(const QStringList& listFile);
    static const int writeBuff;
    static const QString FILEPATH_SEP;
};

#endif // ZIPHANDLER_H

ziphandler.cpp

#include <QDir>
#include <ziphandler.h>

const int ZipHandler::writeBuff = 4096;
const QString ZipHandler::FILEPATH_SEP = "/";

ZipHandler::ZipHandler()
{}

ZipHandler::~ZipHandler()
{}

bool ZipHandler::compressFiles(const QString& fileCompressed, const QStringList& files, const QString& password)
{
    QuaZip zip(fileCompressed);
    QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
    if (!zip.open(QuaZip::mdCreate))
    {
        QFile::remove(fileCompressed);
        return false;
    }

    QFileInfo info;
    foreach (QString file, files)
    {
        info.setFile(file);
        if (!info.exists() || !compressFile(&zip, file, info.fileName(), password)) {
            QFile::remove(fileCompressed);
            return false;
        }
    }

    zip.close();
    if (zip.getZipError() != 0)
    {
        QFile::remove(fileCompressed);
        return false;
    }

    return true;
}

bool ZipHandler::compressDir(const QString& fileCompressed, const QString& dir, bool recursive, const QString& password)
{
    QuaZip zip(fileCompressed);
    QDir().mkpath(QFileInfo(fileCompressed).absolutePath());
    if (!zip.open(QuaZip::mdCreate))
    {
        QFile::remove(fileCompressed);
        return false;
    }

    if (!compressSubDir(&zip, dir, dir, recursive, password))
    {
        QFile::remove(fileCompressed);
        return false;
    }

    zip.close();
    if (zip.getZipError()!=0)
    {
        QFile::remove(fileCompressed);
        return false;
    }

    return true;
}

ZipError ZipHandler::extractDir(const QString& fileCompressed, const QString& dir, const QString& password)
{
    QuaZip zip(fileCompressed);
    ZipError ret = NoError;
    QDir directory(dir);
    QStringList extracted;
    QString name;
    QString absFilePath;


    if (!zip.open(QuaZip::mdUnzip))
    {
        ret = OtherError;
    }

    if (ret == NoError)
    {
        if (!zip.goToFirstFile())
        {
            ret = OtherError;
        }
    }

    if (ret == NoError)
    {
        for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile())
        {
            name = zip.getCurrentFileName();
            absFilePath = directory.absoluteFilePath(name);
            ret = extractFile(&zip, "", absFilePath, password);
            if (ret != NoError)
            {
                removeFile(extracted);
                break;
            }
            extracted.append(absFilePath);
        }
    }

    if (ret == NoError)
    {
        zip.close();
    }

    if (ret == NoError)
    {
        if (zip.getZipError()!=0)
        {
            removeFile(extracted);
            ret = OtherError;
        }
    }

    return ret;
}

bool ZipHandler::compressFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password)
{
    if (!zip)
    {
        return false;
    }
    if (zip->getMode()!=QuaZip::mdCreate &&
            zip->getMode()!=QuaZip::mdAppend &&
            zip->getMode()!=QuaZip::mdAdd)
    {
        return false;
    }

    QFile inFile;
    inFile.setFileName(fileName);
    if (!inFile.open(QIODevice::ReadOnly))
    {
        return false;
    }

    QuaZipFile outFile(zip);
    if (!password.isEmpty())
    {
        if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName()), password.toUtf8().constData()))
        {
            return false;
        }
    } else
    {
        if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileDest, inFile.fileName())))
        {
            return false;
        }
    }

    if (!copyData(inFile, outFile) || outFile.getZipError()!=UNZ_OK)
    {
        return false;
    }

    outFile.close();
    if (outFile.getZipError()!=UNZ_OK)
    {
        return false;
    }
    inFile.close();

    return true;
}

bool ZipHandler::compressSubDir(QuaZip* parentZip, const QString& dir, const QString& parentDir, bool recursive, const QString& password)
{
    if (!parentZip)
    {
        return false;
    }
    if (parentZip->getMode()!=QuaZip::mdCreate &&
            parentZip->getMode()!=QuaZip::mdAppend &&
            parentZip->getMode()!=QuaZip::mdAdd)
    {
        return false;
    }

    QDir directory(dir);
    if (!directory.exists())
    {
        return false;
    }

    QDir origDirectory(parentDir);
    if (dir != parentDir)
    {
        QuaZipFile dirZipFile(parentZip);
        if (!dirZipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(origDirectory.relativeFilePath(dir) + "/", dir), 0, 0, 0))
        {
            return false;
        }
        dirZipFile.close();
    }

    if (recursive) {
        QFileInfoList files = directory.entryInfoList(QDir::AllDirs|QDir::NoDotAndDotDot);
        foreach (QFileInfo file, files)
        {
            if (!compressSubDir(parentZip, file.absoluteFilePath(), parentDir, recursive, password))
            {
                return false;
            }
        }
    }

    QFileInfoList files = directory.entryInfoList(QDir::Files);
    foreach (QFileInfo file, files)
    {
        if (!file.isFile() || file.absoluteFilePath() == parentZip->getZipName())
        {
            continue;
        }

        QString filename = origDirectory.relativeFilePath(file.absoluteFilePath());
        if (!compressFile(parentZip, file.absoluteFilePath(), filename, password))
        {
            return false;
        }
    }

    return true;
}

ZipError ZipHandler::extractFile(QuaZip* zip, const QString& fileName, const QString& fileDest, const QString& password)
{
    QuaZipFile inFile(zip);
    QuaZipFileInfo64 info;
    QFile::Permissions srcPerm;
    ZipError ret = NoError;

    if (!fileName.isEmpty())
    {
        zip->setCurrentFile(fileName);
    }

    if (!zip->getCurrentFileInfo(&info))
    {
        ret = OtherError;
    }

    if (ret == NoError)
    {
        if (info.isEncrypted())
        {
            if (!inFile.open(QIODevice::ReadOnly, password.toUtf8().constData()) || inFile.getZipError()!=UNZ_OK)
            {
                ret = OtherError;
            }
        } else
        {
            if (!inFile.open(QIODevice::ReadOnly) || inFile.getZipError()!=UNZ_OK)
            {
                ret = OtherError;
            }
        }
    }

    if (ret == NoError)
    {
        ret = makepath(fileDest);
    }

    if (ret == NoError)
    {
        srcPerm = info.getPermissions();
        if (fileDest.endsWith(FILEPATH_SEP) && QFileInfo(fileDest).isDir()) {
            if (srcPerm != 0)
            {
                QFile(fileDest).setPermissions(srcPerm);
            }
        } else
        {
            ret = extractData(srcPerm, inFile, fileDest);
        }
    }

    return ret;
}

ZipError ZipHandler::extractData(const QFile::Permissions& srcPerm, QuaZipFile& inFile, const QString& fileDest)
{
    QFile outFile;
    ZipError ret = NoError;

    outFile.setFileName(fileDest);
    if (!outFile.open(QIODevice::WriteOnly))
    {
        ret = NoPermission;
    }

    if (ret == NoError)
    {
        if (!copyData(inFile, outFile) || inFile.getZipError()!=UNZ_OK)
        {
            outFile.close();
            removeFile(QStringList(fileDest));
            ret = NoSpace;
        }
    }

    if (ret == NoError)
    {
        outFile.close();
        inFile.close();

        if (inFile.getZipError() != UNZ_OK)
        {
            removeFile(QStringList(fileDest));
            ret = OtherError;
        }
    }

    if (ret == NoError)
    {
        if (srcPerm != 0) {
            outFile.setPermissions(srcPerm);
        }
    }

    return ret;
}

bool ZipHandler::copyData(QIODevice &inFile, QIODevice &outFile)
{
    char buf[writeBuff];
    qint64 readLen = 0;
    bool ret = true;
    qint64 writeBuffNum = 4096;

    while (!inFile.atEnd())
    {
        readLen = inFile.read(buf, writeBuffNum);
        if (readLen <= 0)
        {
            ret = false;
            break;
        }
        if (outFile.write(buf, readLen) != readLen)
        {
            ret = false;
            break;
        }
    }
    return ret;
}

ZipError ZipHandler::makepath(const QString& fileDest)
{
    QDir curDir;
    ZipError ret = NoError;


    if (fileDest.endsWith(FILEPATH_SEP))
    {
        if (!curDir.mkpath(fileDest))
        {
            ret = NoPermission;
        }
    } else
    {
        if (!curDir.mkpath(QFileInfo(fileDest).absolutePath()))
        {
            ret = NoPermission;
        }
    }

    return ret;
}

bool ZipHandler::removeFile(const QStringList& listFile)
{
    bool ret = true;

    for (int i=0; i<listFile.count(); ++i)
    {
        ret = ret && QFile::remove(listFile.at(i));
    }
    return ret;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants