From 390071ed0bc8aaf378c3468989ccde1c93817542 Mon Sep 17 00:00:00 2001 From: Roland Knall Date: Thu, 11 Jul 2019 00:25:45 +0200 Subject: [PATCH] Qt: Import Profile information Allow easy import of profiles. Profiles must be stored inside a zip file, with no additional hierarchy. Change-Id: I0ae77460c20ef6b3e447906e671b0cefa6b9b032 Reviewed-on: https://code.wireshark.org/review/33881 Petri-Dish: Roland Knall Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall --- CMakeLists.txt | 18 ++++ CMakeOptions.txt | 1 + cmake/modules/FindMinizip.cmake | 52 +++++++++++ cmakeconfig.h.in | 3 + tools/alpine-setup.sh | 1 + tools/debian-setup.sh | 1 + tools/macos-setup-brew.sh | 2 +- tools/rpm-setup.sh | 1 + tools/win-setup.ps1 | 7 +- ui/profile.c | 4 + ui/qt/CMakeLists.txt | 4 +- ui/qt/main_status_bar.cpp | 3 + ui/qt/models/profile_model.cpp | 121 +++++++++++++++++++++++--- ui/qt/models/profile_model.h | 10 +++ ui/qt/profile_dialog.cpp | 38 ++++++++- ui/qt/profile_dialog.h | 7 +- ui/qt/profile_dialog.ui | 25 +----- ui/qt/utils/wireshark_zip_helper.cpp | 123 +++++++++++++++++++++++++++ ui/qt/utils/wireshark_zip_helper.h | 42 +++++++++ 19 files changed, 422 insertions(+), 41 deletions(-) create mode 100644 cmake/modules/FindMinizip.cmake create mode 100644 ui/qt/utils/wireshark_zip_helper.cpp create mode 100644 ui/qt/utils/wireshark_zip_helper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 159f66f89f..cce671d7db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1126,6 +1126,9 @@ ws_find_package(CARES ENABLE_CARES HAVE_C_ARES "1.5.0") # Zlib compression ws_find_package(ZLIB ENABLE_ZLIB HAVE_ZLIB) +# Minizip compression +ws_find_package(Minizip ENABLE_MINIZIP HAVE_MINIZIP) + # Brotli compression ws_find_package(BROTLI ENABLE_BROTLI HAVE_BROTLI) @@ -1173,6 +1176,15 @@ if(NOT WIN32) find_package(SETCAP) endif() +# Include minizip include directories +if(MINIZIP_FOUND) + include_directories( + ${MINIZIP_INCLUDE_DIRS} + ) +else() + set(MINIZIP_LIBRARY "") +endif() + # Windows version updates ws_find_package(WINSPARKLE ENABLE_WINSPARKLE HAVE_SOFTWARE_UPDATE) @@ -1608,6 +1620,11 @@ set_package_properties(SpeexDSP PROPERTIES DESCRIPTION "SpeexDSP is a patent-free, Open Source/Free Software DSP library" PURPOSE "RTP audio resampling" ) +set_package_properties(Minizip PROPERTIES + URL "https://www.winimage.com/zLibDll/minizip.html" + DESCRIPTION "C library for supporting zip/unzip functionality" + PURPOSE "Support for profiles import/export" +) string(TOUPPER "${CMAKE_BUILD_TYPE}" _build_type) message(STATUS "C-Flags: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${_build_type}}") @@ -2232,6 +2249,7 @@ if(BUILD_wireshark AND QT_FOUND) ${WINSPARKLE_LIBRARIES} $<$:UxTheme.lib> ${SPEEXDSP_LIBRARIES} + ${MINIZIP_LIBRARIES} ) add_executable(wireshark WIN32 MACOSX_BUNDLE ${wireshark_FILES} ${EXTRA_BUNDLE_FILES}) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index a605f7b49c..ff71677eb1 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -65,6 +65,7 @@ option(ENABLE_PLUGIN_IFDEMO "Build with plugin interface demo" OFF) option(ENABLE_PCAP_NG_DEFAULT "Enable pcapng as default file format" ON) option(ENABLE_ZLIB "Build with zlib compression support" ON) +option(ENABLE_MINIZIP "Build with zip file compression support" ON) option(ENABLE_LZ4 "Build with LZ4 compression support" ON) option(ENABLE_BROTLI "Build with brotli compression support" ON) option(ENABLE_SNAPPY "Build with Snappy compression support" ON) diff --git a/cmake/modules/FindMinizip.cmake b/cmake/modules/FindMinizip.cmake new file mode 100644 index 0000000000..88dd349f82 --- /dev/null +++ b/cmake/modules/FindMinizip.cmake @@ -0,0 +1,52 @@ +# +# - Find minizip libraries +# +# MINIZIP_INCLUDE_DIRS - where to find minizip headers. +# MINIZIP_LIBRARIES - List of libraries when using minizip. +# MINIZIP_FOUND - True if minizip is found. + +FindWSWinLibs( "minizip-*" "MINIZIP_HINTS" ) + +if(NOT WIN32) + find_package(PkgConfig QUIET) + pkg_search_module(MINIZIP QUIET minizip) +endif() + +find_path(MINIZIP_INCLUDE_DIR + NAMES + unzip.h + minizip/unzip.h + HINTS + ${MINIZIP_INCLUDE_DIRS} + "${MINIZIP_HINTS}/include" +) + +get_filename_component(MINIZIP_PARENT_DIR ${MINIZIP_INCLUDE_DIR} DIRECTORY) +if(EXISTS "${MINIZIP_PARENT_DIR}/minizip/unzip.h") + set(MINIZIP_INCLUDE_DIR "${MINIZIP_PARENT_DIR}") +endif() + +find_library(MINIZIP_LIBRARY + NAMES + minizip + HINTS + ${MINIZIP_LIBRARY_DIRS} + "${MINIZIP_HINTS}/lib" +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MINIZIP + REQUIRED_VARS MINIZIP_LIBRARY MINIZIP_INCLUDE_DIR + VERSION_VAR MINIZIP_VERSION) + +if(MINIZIP_FOUND) + set(MINIZIP_LIBRARIES ${MINIZIP_LIBRARY}) + set(MINIZIP_INCLUDE_DIRS ${MINIZIP_INCLUDE_DIR}) + SET(HAVE_MINIZIP ON) +else() + set(MINIZIP_LIBRARIES) + set(MINIZIP_INCLUDE_DIRS) +endif() + +mark_as_advanced(MINIZIP_LIBRARIES MINIZIP_INCLUDE_DIRS) + diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in index 32c812981a..bcf16d6597 100644 --- a/cmakeconfig.h.in +++ b/cmakeconfig.h.in @@ -136,6 +136,9 @@ /* Define to use zlib library */ #cmakedefine HAVE_ZLIB 1 +/* Define to use the minizip library */ +#cmakedefine HAVE_MINIZIP 1 + /* Define to use brotli library */ #cmakedefine HAVE_BROTLI 1 diff --git a/tools/alpine-setup.sh b/tools/alpine-setup.sh index 482568668f..05c36b2186 100755 --- a/tools/alpine-setup.sh +++ b/tools/alpine-setup.sh @@ -74,6 +74,7 @@ ADDITIONAL_LIST=" lua5.2-dev \ libnl3-dev \ sbc-dev \ + minizip-dev \ speexdsp-dev \ " diff --git a/tools/debian-setup.sh b/tools/debian-setup.sh index 36d1497a29..49c2abdf1d 100755 --- a/tools/debian-setup.sh +++ b/tools/debian-setup.sh @@ -78,6 +78,7 @@ ADDITIONAL_LIST="libnl-3-dev \ libsnappy-dev \ libspandsp-dev \ libxml2-dev \ + libminizip-dev \ git \ ninja-build \ doxygen \ diff --git a/tools/macos-setup-brew.sh b/tools/macos-setup-brew.sh index 15cd58478b..fe77814a47 100755 --- a/tools/macos-setup-brew.sh +++ b/tools/macos-setup-brew.sh @@ -13,7 +13,7 @@ brew update #install some libs needed by Wireshark -brew install c-ares glib libgcrypt gnutls lua@5.1 cmake python nghttp2 snappy lz4 libxml2 ninja libmaxminddb doxygen libsmi spandsp brotli +brew install c-ares glib libgcrypt gnutls lua@5.1 cmake python nghttp2 snappy lz4 libxml2 ninja libmaxminddb doxygen libsmi spandsp brotli minizip #install Qt5 brew install qt5 diff --git a/tools/rpm-setup.sh b/tools/rpm-setup.sh index 214707f630..29e2fb0884 100755 --- a/tools/rpm-setup.sh +++ b/tools/rpm-setup.sh @@ -59,6 +59,7 @@ ADDITIONAL_LIST="libnl3-devel \ sbc-devel \ libsmi-devel \ snappy-devel \ + minizip-devel \ lz4 \ doxygen \ libxml2-devel \ diff --git a/tools/win-setup.ps1 b/tools/win-setup.ps1 index b050606945..7bc055e87f 100644 --- a/tools/win-setup.ps1 +++ b/tools/win-setup.ps1 @@ -69,8 +69,8 @@ Param( # trouble instead of trying to catch exceptions everywhere. $ErrorActionPreference = "Stop" -$Win64CurrentTag = "2019-07-03" -$Win32CurrentTag = "2019-07-03" +$Win64CurrentTag = "2019-07-17" +$Win32CurrentTag = "2019-07-17" # Archive file / SHA256 $Win64Archives = @{ @@ -86,6 +86,7 @@ $Win64Archives = @{ "lua-5.2.4-unicode-win64-vc14.zip" = "e8968d2c7871ce1ea82cbd29ac1b3a2c59d3dec25e483c5e12de85df66f5d928"; "lz4-1.8.3-win64ws.zip" = "2634ed7c132f50e8cf6dc25e9f1b6f2a3b3e4980b812cada7ef42b20fc1fba17"; "MaxMindDB-1.3.2-win64ws.zip" = "9025c43e9b21ff0bfbaf206b8ed96e2920ef1434107f789e4c7c0c1d8b508952"; + "minizip-1.2.11-4-win64ws.zip" = "dd6bf24e2d946465ad19aa4f8c38e0db91da6585887935de68011982cd6fb2cb"; "nghttp2-1.14.0-1-win64ws.zip" = "a4f15854f30b4fbb65cbf150011612e4580683dc9bbb339c632c37e414c938cb"; "sbc-1.3-1-win64ws.zip" = "08cef6898c421277a6582ef3225d8820f74a037cbd5b6e673a4d8f4593ce80a1"; "snappy-1.1.3-1-win64ws.zip" = "692a15e70f2cdeca621988a46e936d3651e7feb5176981f2656a5e913c394bcc"; @@ -108,6 +109,7 @@ $Win32Archives = @{ "lua-5.2.4-unicode-win32-vc14.zip" = "ca2368a83f623674178e9441f71fb791e3c0b46f208e3dac28c6ac735f034bff"; "lz4-1.8.3-win32ws.zip" = "6f77128f901d480ee0b82ab4fe5956e0f02fdcb2198d6d349e0b2fa1ac77c805"; "MaxMindDB-1.3.2-win32ws.zip" = "5c8b4bf3092da8fad6edb005a5283c6a74b7e115a50da010953eed77d33c11b7"; + "minizip-1.2.11-4-win32ws.zip" = "41e113930902c2519c4644e8307a0cc51c5855e001e1e69768c48deb376142d0"; "nghttp2-1.14.0-1-win32ws.zip" = "939ec18c81fed2e44270dc924fad8beffe90a74300cc98360442300fb0a5c292"; "sbc-1.3-1-win32ws.zip" = "ad37825e9ace4b849a5442c08f1ed7e30634e6b774bba4307fb86f35f82e71ba"; "snappy-1.1.3-1-win32ws.zip" = "2508ef7c5d27655c356d7b86a00ac887fc178eab5df63595b8793953dae5c379"; @@ -167,6 +169,7 @@ $CleanupItems = @( "lua-5.?.?-unicode-win??-vc??" "lz4-*-win??ws" "MaxMindDB-1.3.2-win??ws" + "minizip-*-win??ws" "nghttp2-*-win??ws" "portaudio_v19" "portaudio_v19_2" diff --git a/ui/profile.c b/ui/profile.c index 786b6bedcb..6b16e4af4f 100644 --- a/ui/profile.c +++ b/ui/profile.c @@ -262,6 +262,8 @@ empty_profile_list(gboolean edit_list) } g_assert(g_list_length(*flpp) == 0); + if ( ! edited_profiles ) + edited_profiles = NULL; } flpp = ¤t_profiles; @@ -271,6 +273,8 @@ empty_profile_list(gboolean edit_list) } g_assert(g_list_length(*flpp) == 0); + if ( ! current_profiles ) + current_profiles = NULL; } void diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 1d1ae00a49..8c7c888898 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -63,6 +63,7 @@ set(WIRESHARK_UTILS_HEADERS utils/tango_colors.h utils/variant_pointer.h utils/wireshark_mime_data.h + utils/wireshark_zip_helper.h ) set(WIRESHARK_MODEL_HEADERS @@ -90,7 +91,7 @@ set(WIRESHARK_MODEL_HEADERS models/percent_bar_delegate.h models/pref_delegate.h models/pref_models.h - models/profile_model.h + models/profile_model.h models/proto_tree_model.h models/related_packet_delegate.h models/sparkline_delegate.h @@ -288,6 +289,7 @@ set(WIRESHARK_UTILS_SRCS utils/qt_ui_utils.cpp utils/stock_icon.cpp utils/wireshark_mime_data.cpp + utils/wireshark_zip_helper.cpp ) set(WIRESHARK_MODEL_SRCS diff --git a/ui/qt/main_status_bar.cpp b/ui/qt/main_status_bar.cpp index 2bd86d1451..2aac68b053 100644 --- a/ui/qt/main_status_bar.cpp +++ b/ui/qt/main_status_bar.cpp @@ -591,6 +591,9 @@ void MainStatusBar::showProfileMenu(const QPoint &global_pos, Qt::MouseButton bu QAction * action = ctx_menu_.addAction(tr("Manage Profiles" UTF8_HORIZONTAL_ELLIPSIS)); action->setProperty("dialog_action_", (int)ProfileDialog::ShowProfiles); connect(action, SIGNAL(triggered()), this, SLOT(manageProfile())); + action = ctx_menu_.addAction(tr("Import" UTF8_HORIZONTAL_ELLIPSIS)); + action->setProperty("dialog_action_", (int)ProfileDialog::ImportProfile); + connect(action, SIGNAL(triggered()), this, SLOT(manageProfile())); ctx_menu_.addSeparator(); action = ctx_menu_.addAction(tr("New" UTF8_HORIZONTAL_ELLIPSIS)); action->setProperty("dialog_action_", (int)ProfileDialog::NewProfile); diff --git a/ui/qt/models/profile_model.cpp b/ui/qt/models/profile_model.cpp index 98b1938b44..64360cbcbf 100644 --- a/ui/qt/models/profile_model.cpp +++ b/ui/qt/models/profile_model.cpp @@ -7,6 +7,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "config.h" #include "glib.h" #include "ui/profile.h" @@ -17,9 +18,11 @@ #include #include +#include #include #include +#include #define gxx_list_next(list) ((list) ? ((reinterpret_cast(list))->next) : Q_NULLPTR) @@ -114,14 +117,14 @@ ProfileModel::ProfileModel(QObject * parent) : void ProfileModel::loadProfiles() { + emit beginResetModel(); + bool refresh = profiles_.count() > 0; - if ( refresh ) - profiles_.clear(); - else - init_profile_list(); - - emit beginResetModel(); + if ( refresh ) + profiles_.clear(); + else + init_profile_list(); GList *fl_entry = edited_profile_list(); while (fl_entry && fl_entry->data) @@ -166,6 +169,20 @@ int ProfileModel::columnCount(const QModelIndex &) const return static_cast(_LAST_ENTRY); } +profile_def * ProfileModel::guard(int row) const +{ + if ( profiles_.count() <= row ) + return Q_NULLPTR; + + if ( ! edited_profile_list() ) + { + static_cast>(profiles_).clear(); + return Q_NULLPTR; + } + + return profiles_.at(row); +} + QVariant ProfileModel::data(const QModelIndex &index, int role) const { QString msg; @@ -173,7 +190,7 @@ QVariant ProfileModel::data(const QModelIndex &index, int role) const if ( ! index.isValid() || profiles_.count() <= index.row() ) return QVariant(); - profile_def * prof = profiles_.at(index.row()); + profile_def * prof = guard(index.row()); if ( ! prof ) return QVariant(); @@ -225,7 +242,7 @@ QVariant ProfileModel::data(const QModelIndex &index, int role) const if ( set_profile_.compare(prof->name) == 0 ) { - profile_def * act = profiles_.at(activeProfile().row()); + profile_def * act = guard(activeProfile().row()); if ( act->is_global == prof->is_global ) font.setBold(true); } @@ -316,7 +333,7 @@ QVariant ProfileModel::data(const QModelIndex &index, int role) const case ProfileModel::DATA_IS_SELECTED: { QModelIndex selected = activeProfile(); - profile_def * selprof = profiles_.at(selected.row()); + profile_def * selprof = guard(selected.row()); if ( selprof ) { if ( selprof->is_global != prof->is_global ) @@ -388,7 +405,7 @@ Qt::ItemFlags ProfileModel::flags(const QModelIndex &index) const if ( ! index.isValid() || profiles_.count() <= index.row() ) return fl; - profile_def * prof = profiles_.at(index.row()); + profile_def * prof = guard(index.row()); if ( ! prof ) return fl; @@ -419,7 +436,7 @@ QList ProfileModel::findAllByNameAndVisibility(QString name, bool isGlobal) for ( int cnt = 0; cnt < profiles_.count(); cnt++ ) { - profile_def * prof = profiles_.at(cnt); + profile_def * prof = guard(cnt); if ( prof && static_cast(prof->is_global) == isGlobal && name.compare(prof->name) == 0 ) result << cnt; } @@ -441,7 +458,7 @@ QModelIndex ProfileModel::duplicateEntry(QModelIndex idx) if ( ! idx.isValid() || profiles_.count() <= idx.row() ) return QModelIndex(); - profile_def * prof = profiles_.at(idx.row()); + profile_def * prof = guard(idx.row()); if ( ! prof ) return QModelIndex(); @@ -476,7 +493,7 @@ void ProfileModel::deleteEntry(QModelIndex idx) if ( ! idx.isValid() ) return; - profile_def * prof = profiles_.at(idx.row()); + profile_def * prof = guard(idx.row()); if ( ! prof ) return; @@ -530,7 +547,7 @@ bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int ro if ( ! value.isValid() || value.toString().isEmpty() ) return false; - profile_def * prof = profiles_.at(idx.row()); + profile_def * prof = guard(idx.row()); if ( ! prof || prof->status == PROF_STAT_DEFAULT ) return false; @@ -552,6 +569,82 @@ bool ProfileModel::setData(const QModelIndex &idx, const QVariant &value, int ro return true; } +#ifdef HAVE_MINIZIP +/* This check runs BEFORE the file has been unzipped! */ +bool ProfileModel::acceptFile(QString fileName, int fileSize) +{ + if ( fileName.contains(".") || fileName.startsWith("_") ) + return false; + + if ( fileSize > 1024 * 512 ) + return false; + + /* config_file_exists_with_entries cannot be used, due to the fact, that the file has not been extracted yet */ + + return true; +} + +int ProfileModel::unzipProfiles(QString filename) +{ + int count = 0; + QDir profileDir(get_profiles_dir()); + QTemporaryDir dir; +#if 0 + dir.setAutoRemove(false); +#endif + + if ( dir.isValid() ) + { + WireSharkZipHelper::unzip(filename, dir.path(), &ProfileModel::acceptFile); + + QDir temp(dir.path()); + temp.setSorting(QDir::Name); + temp.setFilter(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); + QFileInfoList entries = temp.entryInfoList(); + + foreach ( QFileInfo fentry, entries ) + { + QString profilePath = profileDir.absolutePath() + QDir::separator() + fentry.fileName(); + QString tempPath = fentry.absoluteFilePath(); + + if ( QFile::exists(profilePath) ) + continue; + + QDir profileDir(profilePath); + if ( ! profileDir.mkpath(profilePath) || ! QFile::exists(tempPath) ) + continue; + + QDir tempProfile(tempPath); + tempProfile.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks); + QFileInfoList files = tempProfile.entryInfoList(); + if ( files.count() <= 0 ) + continue; + + int created = 0; + foreach ( QFileInfo finfo, files) + { + QString tempFile = finfo.absoluteFilePath(); + QString profileFile = profilePath + QDir::separator() + finfo.fileName(); + + if ( ! QFile::exists(tempFile) || QFile::exists(profileFile) ) + continue; + + if ( QFile::copy(tempFile, profileFile) ) + created++; + } + + if ( created > 0 ) + count++; + } + } + + if ( count > 0 ) + loadProfiles(); + + return count; +} +#endif + bool ProfileModel::checkNameValidity(QString name, QString *msg) { QString message; diff --git a/ui/qt/models/profile_model.h b/ui/qt/models/profile_model.h index b49b593377..527101c531 100644 --- a/ui/qt/models/profile_model.h +++ b/ui/qt/models/profile_model.h @@ -10,6 +10,7 @@ #ifndef PROFILE_MODEL_H #define PROFILE_MODEL_H +#include "config.h" #include "glib.h" #include @@ -88,6 +89,10 @@ public: GList * at(int row) const; +#ifdef HAVE_MINIZIP + int unzipProfiles(QString filename); +#endif + static bool checkNameValidity(QString name, QString *msg = Q_NULLPTR); QList findAllByNameAndVisibility(QString name, bool isGlobal = false); @@ -97,9 +102,14 @@ private: bool reset_default_; void loadProfiles(); + profile_def * guard(int row) const; GList * entry(profile_def *) const; int findByNameAndVisibility(QString name, bool isGlobal = false); + +#ifdef HAVE_MINIZIP + static bool acceptFile(QString fileName, int fileSize); +#endif }; #endif diff --git a/ui/qt/profile_dialog.cpp b/ui/qt/profile_dialog.cpp index 158b6322b2..6bf2e689bc 100644 --- a/ui/qt/profile_dialog.cpp +++ b/ui/qt/profile_dialog.cpp @@ -25,6 +25,7 @@ #include #include "wireshark_application.h" #include +#include #include #include @@ -35,6 +36,8 @@ #include #include #include +#include +#include ProfileDialog::ProfileDialog(QWidget *parent) : GeometryStateDialog(parent), @@ -59,7 +62,11 @@ ProfileDialog::ProfileDialog(QWidget *parent) : pd_ui_->newToolButton->setAttribute(Qt::WA_MacSmallSize, true); pd_ui_->deleteToolButton->setAttribute(Qt::WA_MacSmallSize, true); pd_ui_->copyToolButton->setAttribute(Qt::WA_MacSmallSize, true); - pd_ui_->infoLabel->setAttribute(Qt::WA_MacSmallSize, true); + pd_ui_->btnImport->setAttribute(Qt::WA_MacSmallSize, true); +#endif + +#ifndef HAVE_MINIZIP + pd_ui_->btnImport->setVisible(false); #endif model_ = new ProfileModel(this); @@ -118,6 +125,11 @@ int ProfileDialog::execAction(ProfileDialog::ProfileAction profile_action) on_newToolButton_clicked(); ret = exec(); break; + case ImportProfile: +#ifdef HAVE_MINIZIP + on_btnImport_clicked(); +#endif + break; case EditCurrentProfile: item = pd_ui_->profileTreeView->currentIndex(); if (item.isValid()) { @@ -315,6 +327,30 @@ void ProfileDialog::filterChanged(const QString &text) pd_ui_->profileTreeView->setCurrentIndex(active); } +#ifdef HAVE_MINIZIP +void ProfileDialog::on_btnImport_clicked() +{ + QString docDir = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); + QString zipFile = QFileDialog::getOpenFileName(this, tr("Select zip file for import"), + docDir, tr("Zip File (*.zip)")); + + QFileInfo fi(zipFile); + if ( ! fi.exists() ) + return; + + int count = 0; + if ( ( count = model_->unzipProfiles(zipFile) ) == 0 ) + { + QString msg = tr("An error occured, while importing profiles from %1").arg(fi.fileName()); + QMessageBox::warning(this, tr("Error importing profiles"), msg ); + } + else { + QString msg = tr("%1 profiles have been imported").arg(QString::number(count)); + QMessageBox::information(this, tr("Importing profiles"), msg ); + } +} +#endif + /* * Editor modelines * diff --git a/ui/qt/profile_dialog.h b/ui/qt/profile_dialog.h index 407ad910e0..05bc708beb 100644 --- a/ui/qt/profile_dialog.h +++ b/ui/qt/profile_dialog.h @@ -10,6 +10,8 @@ #ifndef PROFILE_DIALOG_H #define PROFILE_DIALOG_H +#include "config.h" + #include #include #include @@ -26,7 +28,7 @@ class ProfileDialog : public GeometryStateDialog Q_OBJECT public: - enum ProfileAction { ShowProfiles, NewProfile, EditCurrentProfile, DeleteCurrentProfile }; + enum ProfileAction { ShowProfiles, NewProfile, ImportProfile, EditCurrentProfile, DeleteCurrentProfile }; explicit ProfileDialog(QWidget *parent = Q_NULLPTR); ~ProfileDialog(); @@ -52,6 +54,9 @@ private: private slots: void currentItemChanged(); +#ifdef HAVE_MINIZIP + void on_btnImport_clicked(); +#endif void on_newToolButton_clicked(); void on_deleteToolButton_clicked(); diff --git a/ui/qt/profile_dialog.ui b/ui/qt/profile_dialog.ui index 0a4f14716d..63da77d532 100644 --- a/ui/qt/profile_dialog.ui +++ b/ui/qt/profile_dialog.ui @@ -6,7 +6,7 @@ 0 0 - 470 + 557 386 @@ -51,7 +51,7 @@ - + @@ -93,21 +93,9 @@ - - - - 1 - 0 - - + - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true + Import @@ -126,11 +114,6 @@ - - ElidedLabel - QLabel -
widgets/elided_label.h
-
StockIconToolButton QToolButton diff --git a/ui/qt/utils/wireshark_zip_helper.cpp b/ui/qt/utils/wireshark_zip_helper.cpp new file mode 100644 index 0000000000..78dc9949fe --- /dev/null +++ b/ui/qt/utils/wireshark_zip_helper.cpp @@ -0,0 +1,123 @@ +/* wireshark_zip_helper.cpp + * + * Definitions for zip / unzip of files + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include + +#ifdef HAVE_MINIZIP +#include "config.h" + +#include "glib.h" + +#include +#include +#include + +#include "epan/prefs.h" +#include "wsutil/file_util.h" + +#include +#include +#include + +bool WireSharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int)) +{ + unzFile uf = Q_NULLPTR; + QFileInfo fi(zipFile); + QDir di(directory); + int files = 0; + + if ( ! fi.exists() || ! di.exists() ) + return false; + + if ( ( uf = unzOpen64(zipFile.toUtf8().constData()) ) == Q_NULLPTR ) + return false; + + unz_global_info64 gi; + int err = unzGetGlobalInfo64(uf,&gi); + unsigned int nmbr = static_cast(gi.number_entry); + if (nmbr <= 0) + return false; + + for ( unsigned int cnt = 0; cnt < nmbr; cnt++ ) + { + char filename_inzip[256]; + unz_file_info64 file_info; + err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), + Q_NULLPTR, 0, Q_NULLPTR, 0); + if ( err == UNZ_OK ) + { + QString fileInZip(filename_inzip); + int fileSize = static_cast(file_info.uncompressed_size); + /* Sanity check for the filenames as well as the file size (max 512kb) */ + if ( fileCheck && fileCheck(fileInZip, fileSize) && di.exists() ) + { + QFileInfo fi(di.path() + QDir::separator() + fileInZip); + QDir tP(fi.absolutePath()); + if ( ! tP.exists() ) + di.mkpath(fi.absolutePath()); + + if ( fileInZip.contains("/") ) + { + QString filePath = fi.absoluteFilePath(); + + err = unzOpenCurrentFile(uf); + if ( err == UNZ_OK ) + { + char * buf = static_cast(malloc(IO_BUF_SIZE)); + QFile file(filePath); + if ( file.open(QIODevice::WriteOnly) ) + { + QDataStream out(&file); + while ( ( err = unzReadCurrentFile(uf, buf, IO_BUF_SIZE) ) != UNZ_EOF ) + { + QByteArray buffer(buf, err); + out << buffer; + } + + file.close(); + } + unzCloseCurrentFile(uf); + + files++; + } + } + } + } + + if ((cnt+1) < nmbr) + { + err = unzGoToNextFile(uf); + if (err!=UNZ_OK) + { + break; + } + } + } + + unzClose(uf); + + return files > 0 ? true : false; +} + +#endif + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/utils/wireshark_zip_helper.h b/ui/qt/utils/wireshark_zip_helper.h new file mode 100644 index 0000000000..8b232dc966 --- /dev/null +++ b/ui/qt/utils/wireshark_zip_helper.h @@ -0,0 +1,42 @@ +/* wireshark_zip_helper.h + * + * Definitions for zip / unzip of files + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef WS_ZIP_HELPER_H +#define WS_ZIP_HELPER_H + +#include "config.h" + +#include + +#ifdef HAVE_MINIZIP + +class WireSharkZipHelper +{ +public: + static bool unzip(QString zipFile, QString directory, bool (*fileCheck)(QString fileName, int fileSize) ); +}; + +#endif + +#endif // WS_ZIP_HELPER_H + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */