action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
ctx_menu_.addMenu(importMenu);
+
+ QMenu * exportMenu = new QMenu(tr("Export"));
+ action = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" selected entry"));
+ action->setProperty("dialog_action_", (int)ProfileDialog::ExportSingleProfile);
+ action->setEnabled(enable_edit);
+ connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
+ action = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS" all user profiles"));
+ action->setProperty("dialog_action_", (int)ProfileDialog::ExportAllProfiles);
+ connect(action, SIGNAL(triggered()), this, SLOT(manageProfile()));
+ ctx_menu_.addMenu(exportMenu);
+
#else
action = ctx_menu_.addAction(tr("Import" UTF8_HORIZONTAL_ELLIPSIS));
action->setProperty("dialog_action_", (int)ProfileDialog::ImportDirProfile);
}
#ifdef HAVE_MINIZIP
+QStringList ProfileModel::exportFileList(QModelIndexList items)
+{
+ QStringList result;
+
+ foreach(QModelIndex idx, items)
+ {
+ profile_def * prof = guard(idx.row());
+ if ( prof->is_global || QString(prof->name).compare(DEFAULT_PROFILE) == 0 )
+ continue;
+
+ if ( ! idx.data(ProfileModel::DATA_PATH_IS_NOT_DESCRIPTION).toBool() )
+ continue;
+
+ QString path = idx.data(ProfileModel::DATA_PATH).toString();
+ QDir temp(path);
+ temp.setSorting(QDir::Name);
+ temp.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
+ QFileInfoList entries = temp.entryInfoList();
+ foreach ( QFileInfo fi, entries )
+ result << fi.absoluteFilePath();
+ }
+
+ return result;
+}
+
+bool ProfileModel::exportProfiles(QString filename, QModelIndexList items, QString *err)
+{
+ if ( changesPending() )
+ {
+ if ( err )
+ err->append(tr("Exporting profiles while changes are pending is not allowed"));
+ return false;
+ }
+
+ QStringList files = exportFileList(items);
+ if ( files.count() == 0 )
+ {
+ if ( err )
+ err->append((tr("No profiles found to export")));
+ return false;
+ }
+
+ if ( WireSharkZipHelper::zip(filename, files, QString(get_profiles_dir()) + QDir::separator() ) )
+ return true;
+
+ return false;
+}
+
/* This check runs BEFORE the file has been unzipped! */
bool ProfileModel::acceptFile(QString fileName, int fileSize)
{
bool changesPending() const;
#ifdef HAVE_MINIZIP
+ QStringList exportFileList(QModelIndexList items);
+ bool exportProfiles(QString filename, QModelIndexList items, QString * err = Q_NULLPTR);
int importProfilesFromZip(QString filename, int *skippedCnt = Q_NULLPTR);
#endif
int importProfilesFromDir(QString filename, int *skippedCnt = Q_NULLPTR, bool fromZip = false);
#include <QStandardPaths>
#include <QKeyEvent>
#include <QMenu>
+#include <QMessageBox>
+
+#define PROFILE_EXPORT_PROPERTY "export"
+#define PROFILE_EXPORT_ALL "all"
+#define PROFILE_EXPORT_SELECTED "selected"
ProfileDialog::ProfileDialog(QWidget *parent) :
GeometryStateDialog(parent),
pd_ui_->lblInfo->setAttribute(Qt::WA_MacSmallSize, true);
#endif
- import_button_ = pd_ui_->buttonBox->addButton(tr("Import"), QDialogButtonBox::ActionRole);
+ import_button_ = pd_ui_->buttonBox->addButton(tr("Import", "noun"), QDialogButtonBox::ActionRole);
#ifdef HAVE_MINIZIP
+ export_button_ = pd_ui_->buttonBox->addButton(tr("Export", "noun"), QDialogButtonBox::ActionRole);
+
QMenu * importMenu = new QMenu(import_button_);
QAction * entry = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " from Zip"));
connect( entry, &QAction::triggered, this, &ProfileDialog::importFromZip);
entry = importMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " from Directory"));
connect( entry, &QAction::triggered, this, &ProfileDialog::importFromDirectory);
import_button_->setMenu(importMenu);
+
+ QMenu * exportMenu = new QMenu(export_button_);
+ export_selected_entry_ = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " selected entry"));
+ export_selected_entry_->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_SELECTED);
+ connect( export_selected_entry_, &QAction::triggered, this, &ProfileDialog::exportProfiles);
+ entry = exportMenu->addAction(tr(UTF8_HORIZONTAL_ELLIPSIS " all user profiles"));
+ entry->setProperty(PROFILE_EXPORT_PROPERTY, PROFILE_EXPORT_ALL);
+ connect( entry, &QAction::triggered, this, &ProfileDialog::exportProfiles);
+ export_button_->setMenu(exportMenu);
#else
connect( import_button_, &QPushButton::clicked, this, &ProfileDialog::importFromDirectory);
#endif
case ImportDirProfile:
importFromDirectory();
break;
+ case ExportSingleProfile:
+#ifdef HAVE_MINIZIP
+ exportProfiles();
+#endif
+ break;
+ case ExportAllProfiles:
+#ifdef HAVE_MINIZIP
+ exportProfiles(true);
+#endif
+ break;
case EditCurrentProfile:
item = pd_ui_->profileTreeView->currentIndex();
if (item.isValid()) {
}
#ifdef HAVE_MINIZIP
+void ProfileDialog::exportProfiles(bool exportAll)
+{
+ QAction * action = qobject_cast<QAction *>(sender());
+ if ( action && action->property(PROFILE_EXPORT_PROPERTY).isValid() )
+ exportAll = action->property(PROFILE_EXPORT_PROPERTY).toString().compare(PROFILE_EXPORT_ALL) == 0;
+
+ QModelIndexList items;
+
+ if ( ! exportAll && pd_ui_->profileTreeView->currentIndex().isValid() )
+ items << sort_model_->mapToSource(pd_ui_->profileTreeView->currentIndex());
+ else if ( exportAll )
+ {
+ for ( int cnt = 0; cnt < sort_model_->rowCount(); cnt++ )
+ {
+ QModelIndex idx = sort_model_->index(cnt, ProfileModel::COL_NAME);
+ if ( ! idx.data(ProfileModel::DATA_IS_GLOBAL).toBool() && ! idx.data(ProfileModel::DATA_IS_DEFAULT).toBool() )
+ {
+ items << sort_model_->mapToSource(idx);
+ }
+ }
+ }
+ if ( items.count() == 0 )
+ {
+ QMessageBox::warning(this, tr("Exporting profiles"), tr("No profiles found for export"));
+ return;
+ }
+
+ QString zipFile = QFileDialog::getSaveFileName(this, tr("Select zip file for export"), QString(), tr("Zip File (*.zip)"));
+
+ QString err;
+ if ( model_->exportProfiles(zipFile, items, &err) )
+ QMessageBox::information(this, tr("Exporting profiles"), tr("%Ln profile(s) have been exported", "", items.count()));
+ else
+ QMessageBox::warning(this, tr("Exporting profiles"), QString("%1\n\n%2: %3").arg(tr("An error has occured while exporting profiles")).arg("Error").arg(err));
+}
+
void ProfileDialog::importFromZip()
{
QString zipFile = QFileDialog::getOpenFileName(this, tr("Select zip file for import"), QString(), tr("Zip File (*.zip)"));
Q_OBJECT
public:
- enum ProfileAction { ShowProfiles, NewProfile, ImportZipProfile, ImportDirProfile, EditCurrentProfile, DeleteCurrentProfile };
+ enum ProfileAction {
+ ShowProfiles, NewProfile, ImportZipProfile, ImportDirProfile,
+ ExportSingleProfile, ExportAllProfiles, EditCurrentProfile, DeleteCurrentProfile
+ };
explicit ProfileDialog(QWidget *parent = Q_NULLPTR);
~ProfileDialog();
Ui::ProfileDialog *pd_ui_;
QPushButton *ok_button_;
QPushButton *import_button_;
+#ifdef HAVE_MINIZIP
+ QPushButton *export_button_;
+ QAction *export_selected_entry_;
+#endif
ProfileModel *model_;
ProfileSortModel *sort_model_;
private slots:
void currentItemChanged();
#ifdef HAVE_MINIZIP
+ void exportProfiles(bool exportAll = false);
void importFromZip();
#endif
void importFromDirectory();
#include <iosfwd>
#include <iostream>
#include <minizip/unzip.h>
+#include <minizip/zip.h>
#include "epan/prefs.h"
#include "wsutil/file_util.h"
#include <QDataStream>
#include <QDir>
+#include <QFile>
#include <QFileInfo>
+#include <QDateTime>
bool WireSharkZipHelper::unzip(QString zipFile, QString directory, bool (*fileCheck)(QString, int))
{
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.write(buf, err);
file.close();
}
return files > 0 ? true : false;
}
+#ifndef UINT32_MAX
+#define UINT32_MAX (0xffffffff)
+#endif
+
+/* The following methods are being taken from https://github.com/nmoinvaz/minizip/blob/1.2/minishared.c */
+int invalid_date(const struct tm *ptm)
+{
+#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
+ return (!datevalue_in_range(0, 207, ptm->tm_year) ||
+ !datevalue_in_range(0, 11, ptm->tm_mon) ||
+ !datevalue_in_range(1, 31, ptm->tm_mday) ||
+ !datevalue_in_range(0, 23, ptm->tm_hour) ||
+ !datevalue_in_range(0, 59, ptm->tm_min) ||
+ !datevalue_in_range(0, 59, ptm->tm_sec));
+#undef datevalue_in_range
+}
+
+uint32_t tm_to_dosdate(const struct tm *ptm)
+{
+ struct tm fixed_tm;
+
+ /* Years supported:
+ * [00, 79] (assumed to be between 2000 and 2079)
+ * [80, 207] (assumed to be between 1980 and 2107, typical output of old
+ software that does 'year-1900' to get a double digit year)
+ * [1980, 2107] (due to the date format limitations, only years between 1980 and 2107 can be stored.)
+ */
+
+ memcpy(&fixed_tm, ptm, sizeof(struct tm));
+ if (fixed_tm.tm_year >= 1980) /* range [1980, 2107] */
+ fixed_tm.tm_year -= 1980;
+ else if (fixed_tm.tm_year >= 80) /* range [80, 99] */
+ fixed_tm.tm_year -= 80;
+ else /* range [00, 79] */
+ fixed_tm.tm_year += 20;
+
+ if (invalid_date(ptm))
+ return 0;
+
+ return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
+ ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
+}
+
+unsigned long qDateToDosDate(QDateTime time)
+{
+ time_t rawtime = time.toTime_t();
+ struct tm * timeinfo;
+
+ timeinfo = localtime(&rawtime);
+ timeinfo->tm_year = time.date().year() - 1900;
+ timeinfo->tm_mon = time.date().month() - 1;
+ timeinfo->tm_mday = time.date().day();
+
+ mktime(timeinfo);
+
+ return tm_to_dosdate(timeinfo);
+}
+
+void WireSharkZipHelper::addFileToZip(zipFile zf, QString filepath, QString fileInZip)
+{
+ QFileInfo fi(filepath);
+ zip_fileinfo zi;
+ int err = ZIP_OK;
+
+ memset(&zi, 0, sizeof(zi));
+
+ QDateTime fTime = fi.lastModified();
+ zi.dosDate = qDateToDosDate(fTime);
+
+ QFile fh(filepath);
+ /* Checks if a large file block has to be written */
+ bool isLarge = ( fh.size() > UINT32_MAX );
+
+ err = zipOpenNewFileInZip3_64(zf, fileInZip.toUtf8().constData(), &zi,
+ Q_NULLPTR, 0, Q_NULLPTR, 0, Q_NULLPTR, Z_DEFLATED, 9 , 0,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+ Q_NULLPTR, 0, static_cast<int>(isLarge));
+
+ if ( err != ZIP_OK )
+ return;
+
+ if ( fh.open(QIODevice::ReadOnly) )
+ {
+ char * buf = static_cast<char *>(malloc(IO_BUF_SIZE));
+ while ( ! fh.atEnd() && err == ZIP_OK )
+ {
+ qint64 bytesIn = fh.read(buf, IO_BUF_SIZE);
+ if ( bytesIn > 0 && bytesIn <= IO_BUF_SIZE)
+ {
+ err = zipWriteInFileInZip(zf, buf, (unsigned int) bytesIn);
+ }
+ }
+ fh.close();
+ }
+
+ zipCloseFileInZip(zf);
+}
+
+bool WireSharkZipHelper::zip(QString fileName, QStringList files, QString relativeTo)
+{
+
+ QFileInfo fi(fileName);
+ if ( fi.exists() )
+ QFile::remove(fileName);
+
+ zipFile zf = zipOpen(fileName.toUtf8().constData(), APPEND_STATUS_CREATE);
+ if ( zf == Q_NULLPTR )
+ return false;
+
+ for ( int cnt = 0; cnt < files.count(); cnt++ )
+ {
+ QFileInfo sf(files.at(cnt));
+ QString fileInZip = sf.absoluteFilePath();
+ fileInZip.replace(relativeTo, "");
+ /* Windows cannot open zip files, if the filenames starts with a separator */
+ while ( fileInZip.length() > 0 && fileInZip.startsWith(QDir::separator()) )
+ fileInZip = fileInZip.right(fileInZip.length() - 1);
+
+ WireSharkZipHelper::addFileToZip(zf, sf.absoluteFilePath(), fileInZip);
+
+ }
+
+ if ( zipClose(zf, Q_NULLPTR) )
+ return false;
+
+ return true;
+}
+
#endif
/*
#ifdef HAVE_MINIZIP
+#include "minizip/zip.h"
+
class WireSharkZipHelper
{
public:
+ static bool zip(QString zipFile, QStringList files, QString relativeTo = QString());
static bool unzip(QString zipFile, QString directory, bool (*fileCheck)(QString fileName, int fileSize) );
+
+protected:
+ static void addFileToZip(zipFile zf, QString filepath, QString fileInZip);
+
};
#endif