From 7c85b200e6765095faa994451dacf285fef73dd8 Mon Sep 17 00:00:00 2001 From: Blottiere Paul Date: Fri, 7 Jul 2017 21:06:22 +0200 Subject: [PATCH] Add utilities functions for zip support --- python/core/core_auto.sip | 1 + python/core/qgsziputils.sip | 48 ++++++++++ src/core/CMakeLists.txt | 3 + src/core/qgsziputils.cpp | 177 ++++++++++++++++++++++++++++++++++++ src/core/qgsziputils.h | 52 +++++++++++ 5 files changed, 281 insertions(+) create mode 100644 python/core/qgsziputils.sip create mode 100644 src/core/qgsziputils.cpp create mode 100644 src/core/qgsziputils.h diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index eab43cc9702..7d751edccaf 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -127,6 +127,7 @@ %Include qgsvirtuallayerdefinitionutils.sip %Include qgsmapthemecollection.sip %Include qgsxmlutils.sip +%Include qgsziputils.sip %Include qgsvector.sip %Include auth/qgsauthcertutils.sip %Include auth/qgsauthconfig.sip diff --git a/python/core/qgsziputils.sip b/python/core/qgsziputils.sip new file mode 100644 index 00000000000..1f9b1a2a0e5 --- /dev/null +++ b/python/core/qgsziputils.sip @@ -0,0 +1,48 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsziputils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + +%ModuleHeaderCode +#include "qgsziputils.h" +%End + +namespace QgsZipUtils +{ + + bool unzip( const QString &zip, const QString &dir, QStringList &files /Out/ ); +%Docstring + Unzip a zip file in an output directory. An error is returned if the zip + filename does not exist, the output directory does not exist or is + not writable. + \param zip The zip filename + \param dir The output directory + \param files The absolute path of unzipped files +.. versionadded:: 3.0 + :rtype: bool +%End + + bool zip( const QString &zip, const QStringList &files ); +%Docstring + Zip the list of files in the zip file. If the zip file yet exists or is + empty, an error is returned. If an input file does not exist, an error is + also returned. + \param zip The zip filename + \param files The absolute path filles to embed within the zip +.. versionadded:: 3.0 + :rtype: bool +%End +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsziputils.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b529ef36066..8d2aa1c4ed9 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -305,6 +305,7 @@ SET(QGIS_CORE_SRCS qgsxmlutils.cpp qgssettings.cpp qgsstacktrace.cpp + qgsziputils.cpp composer/qgsaddremoveitemcommand.cpp composer/qgsaddremovemultiframecommand.cpp @@ -892,6 +893,7 @@ SET(QGIS_CORE_HDRS qgsvirtuallayerdefinitionutils.h qgsmapthemecollection.h qgsxmlutils.h + qgsziputils.h qgsvector.h qgslocalec.h @@ -1218,6 +1220,7 @@ TARGET_LINK_LIBRARIES(qgis_core ${EXPAT_LIBRARY} ${SQLITE3_LIBRARY} ${SPATIALITE_LIBRARY} + ${LIBZIP_LIBRARY} ) IF (Qt5Positioning_FOUND) diff --git a/src/core/qgsziputils.cpp b/src/core/qgsziputils.cpp new file mode 100644 index 00000000000..636b29c7312 --- /dev/null +++ b/src/core/qgsziputils.cpp @@ -0,0 +1,177 @@ +/*************************************************************************** + qgsziputils.cpp + --------------------- + begin : Jul 2017 + copyright : (C) 2017 by Paul Blottiere + email : paul.blottiere@oslandia.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +#include +#include + +#include "zip.h" + +#include "qgsmessagelog.h" +#include "qgsziputils.h" + +#include + +bool QgsZipUtils::unzip( const QString &zipFilename, const QString &dir, QStringList &files ) +{ + files.clear(); + + if ( !QFileInfo::exists( zipFilename ) ) + { + QString err = QObject::tr( "Error zip file does not exist: '%1'" ).arg( zipFilename ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + else if ( zipFilename.isEmpty() ) + { + QString err = QObject::tr( "Error zip filename is empty" ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + else if ( !QDir( dir ).exists( dir ) ) + { + QString err = QObject::tr( "Error output dir does not exist: '%1'" ).arg( dir ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + else if ( !QFileInfo( dir ).isDir() ) + { + QString err = QObject::tr( "Error output dir is not a directory: '%1'" ).arg( dir ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + else if ( !QFileInfo( dir ).isWritable() ) + { + QString err = QObject::tr( "Error output dir is not writable: '%1'" ).arg( dir ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + + int rc = 0; + struct zip *z = zip_open( zipFilename.toStdString().c_str(), ZIP_CHECKCONS, &rc ); + + if ( rc == ZIP_ER_OK && z != NULL ) + { + int count = zip_get_num_files( z ); + if ( count != -1 ) + { + struct zip_stat stat; + + for ( int i = 0; i < count; i++ ) + { + zip_stat_index( z, i, 0, &stat ); + size_t len = stat.size; + + struct zip_file *file = zip_fopen_index( z, i, 0 ); + char *buf = new char[len]; + if ( zip_fread( file, buf, len ) != -1 ) + { + QFileInfo newFile( QDir( dir ), QString( stat.name ) ); + std::ofstream( newFile.absoluteFilePath().toStdString() ).write( buf, len ); + zip_fclose( file ); + files.append( newFile.absoluteFilePath() ); + } + else + { + zip_fclose( file ); + QString err = QObject::tr( "Error reading file: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + } + } + else + { + zip_close( z ); + QString err = QObject::tr( "Error getting files: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + + zip_close( z ); + } + else + { + QString err = QObject::tr( "Error opening zip archive: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + + return true; +} + +bool QgsZipUtils::zip( const QString &zipFilename, const QStringList &files ) +{ + if ( QFileInfo::exists( zipFilename ) ) + { + QString err = QObject::tr( "Error zip file yet exist: '%1'" ).arg( zipFilename ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + else if ( zipFilename.isEmpty() ) + { + QString err = QObject::tr( "Error zip filename is empty" ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + + int rc = 0; + struct zip *z = zip_open( zipFilename.toStdString().c_str(), ZIP_CREATE, &rc ); + + if ( rc == ZIP_ER_OK && z != NULL ) + { + Q_FOREACH ( QString file, files ) + { + QFileInfo fileInfo( file ); + if ( !fileInfo.exists() ) + { + QString err = QObject::tr( "Error input file does not exist: '%1'" ).arg( file ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + zip_close( z ); + } + + zip_source *src = zip_source_file( z, file.toStdString().c_str(), 0, 0 ); + if ( src != NULL ) + { + if ( zip_file_add( z, fileInfo.fileName().toStdString().c_str(), src, 0 ) == -1 ) + { + QString err = QObject::tr( "Error adding file: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + zip_close( z ); + return false; + } + } + else + { + QString err = QObject::tr( "Error creating data source: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + zip_close( z ); + return false; + } + } + + zip_close( z ); + } + else + { + QString err = QObject::tr( "Error creating zip archive: '%1'" ).arg( zip_strerror( z ) ); + QgsMessageLog::logMessage( err, QStringLiteral( "QgsZipUtils" ) ); + return false; + } + + return true; +} diff --git a/src/core/qgsziputils.h b/src/core/qgsziputils.h new file mode 100644 index 00000000000..3f7c49ffffd --- /dev/null +++ b/src/core/qgsziputils.h @@ -0,0 +1,52 @@ +/*************************************************************************** + qgsziputils.h + --------------------- + begin : Jul 2017 + copyright : (C) 2017 by Paul Blottiere + email : paul.blottiere@oslandia.com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSZIPUTILS_H +#define QGSZIPUTILS_H + +#include "qgis_core.h" +#include "qgis.h" +#include + +#ifdef SIP_RUN +% ModuleHeaderCode +#include "qgsziputils.h" +% End +#endif + +namespace QgsZipUtils +{ + + /** Unzip a zip file in an output directory. An error is returned if the zip + * filename does not exist, the output directory does not exist or is + * not writable. + * \param zip The zip filename + * \param dir The output directory + * \param files The absolute path of unzipped files + * \since QGIS 3.0 + */ + CORE_EXPORT bool unzip( const QString &zip, const QString &dir, QStringList &files SIP_OUT ); + + /** Zip the list of files in the zip file. If the zip file yet exists or is + * empty, an error is returned. If an input file does not exist, an error is + * also returned. + * \param zip The zip filename + * \param files The absolute path filles to embed within the zip + * \since QGIS 3.0 + */ + CORE_EXPORT bool zip( const QString &zip, const QStringList &files ); +}; + +#endif //QGSZIPUTILS_H