QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
storemodel.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2014 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include "storemodel.h"
4#include "qtpasssettings.h"
5
6#include "util.h"
7#include <QApplication>
8#include <QDebug>
9#include <QFileSystemModel>
10#include <QMessageBox>
11#include <QMimeData>
12#include <QRegularExpression>
13#include <QtGlobal>
14
16 QDataStream &out,
18 -> QDataStream & {
19 out << static_cast<quint8>(dragAndDropInfoPasswordStore.kind)
21 return out;
22}
23
45
51StoreModel::StoreModel() { fs = nullptr; }
52
61 const QModelIndex &sourceParent) const
62 -> bool {
63 QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
64 return showThis(index);
65}
66
73auto StoreModel::showThis(const QModelIndex &index) const -> bool {
74 bool retVal = false;
75 if (fs == nullptr) {
76 return retVal;
77 }
78 // Gives you the info for number of children with a parent
79 if (sourceModel()->rowCount(index) > 0) {
80 for (int nChild = 0; nChild < sourceModel()->rowCount(index); ++nChild) {
81 QModelIndex childIndex = sourceModel()->index(nChild, 0, index);
82 if (!childIndex.isValid()) {
83 break;
84 }
85 retVal = showThis(childIndex);
86 if (retVal) {
87 break;
88 }
89 }
90 } else {
91 QModelIndex useIndex = sourceModel()->index(index.row(), 0, index.parent());
92 QString path = fs->filePath(useIndex);
93 path = QDir(store).relativeFilePath(path);
94 if (path.startsWith(".git")) {
95 return false;
96 }
97 path.replace(Util::endsWithGpg(), "");
98 retVal = path.contains(filterRegularExpression());
99 }
100 return retVal;
101}
102
108void StoreModel::setModelAndStore(QFileSystemModel *sourceModel,
109 const QString &passStore) {
110 setSourceModel(sourceModel);
111 fs = sourceModel;
112 store = passStore;
113}
114
115void StoreModel::setStore(const QString &passStore) {
116#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
117 // beginFilterChange() is available since Qt 6.9, but the Direction-scoped
118 // endFilterChange(QSortFilterProxyModel::Direction) overload is only
119 // available since Qt 6.10, which is the preferred API for scoped and more
120 // efficient filter updates.
121 beginFilterChange();
122 store = passStore;
123 endFilterChange(QSortFilterProxyModel::Direction::Rows);
124#else
125 // Direction-scoped filter changes are unavailable before Qt 6.10, so older
126 // Qt versions must manually invalidate filters. We update the store and
127 // manually invalidate the filter as a compatibility path.
128 store = passStore;
129#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
130 QSortFilterProxyModel::invalidateFilter();
131#else
132 invalidateFilter();
133#endif
134#endif
135}
136
143auto StoreModel::data(const QModelIndex &index, int role) const -> QVariant {
144 if (!index.isValid()) {
145 return {};
146 }
147
148 QVariant initial_value;
149 initial_value = QSortFilterProxyModel::data(index, role);
150
151 if (role == Qt::DisplayRole) {
152 QString name = initial_value.toString();
153 name.replace(Util::endsWithGpg(), "");
154 initial_value.setValue(name);
155 }
156
157 return initial_value;
158}
159
164auto StoreModel::supportedDropActions() const -> Qt::DropActions {
165 return Qt::CopyAction | Qt::MoveAction;
166}
167
172auto StoreModel::supportedDragActions() const -> Qt::DropActions {
173 return Qt::CopyAction | Qt::MoveAction;
174}
175
181auto StoreModel::flags(const QModelIndex &index) const -> Qt::ItemFlags {
182 Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index);
183
184 if (index.isValid()) {
185 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
186 }
187 return Qt::ItemIsDropEnabled | defaultFlags;
188}
189
194auto StoreModel::mimeTypes() const -> QStringList {
195 QStringList types;
196 types << "application/vnd+qtpass.dragAndDropInfoPasswordStore";
197 return types;
198}
199
205auto StoreModel::mimeData(const QModelIndexList &indexes) const -> QMimeData * {
207
208 QByteArray encodedData;
209 // only use the first, otherwise we should enable multiselection
210 QModelIndex index = indexes.at(0);
211 if (index.isValid()) {
212 QModelIndex useIndex = mapToSource(index);
213 const QFileInfo fileInfo = fs->fileInfo(useIndex);
214
215 if (fileInfo.isDir()) {
217 } else if (fileInfo.isFile()) {
219 }
220 info.path = fileInfo.absoluteFilePath();
221 QDataStream stream(&encodedData, QIODevice::WriteOnly);
222 stream << info;
223 }
224
225 auto *mimeData = new QMimeData();
226 mimeData->setData("application/vnd+qtpass.dragAndDropInfoPasswordStore",
227 encodedData);
228 return mimeData;
229}
230
240auto StoreModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
241 int row, int column,
242 const QModelIndex &parent) const -> bool {
243#ifdef QT_DEBUG
244 qDebug() << action << row;
245#else
246 Q_UNUSED(action)
247 Q_UNUSED(row)
248#endif
249
250 if (data == nullptr ||
251 !data->hasFormat("application/vnd+qtpass.dragAndDropInfoPasswordStore")) {
252 return false;
253 }
254
255 QByteArray encodedData =
256 data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
257 if (encodedData.isEmpty()) {
258 return false;
259 }
260 QDataStream stream(&encodedData, QIODevice::ReadOnly);
262 stream >> info;
263 if (stream.status() != QDataStream::Ok) {
264 return false;
265 }
266
267 QModelIndex useIndex =
268 this->index(parent.row(), parent.column(), parent.parent());
269
270 if (column > 0) {
271 return false;
272 }
273
275 // you can drop a folder on a folder
276 if (fs->fileInfo(mapToSource(useIndex)).isDir() &&
277 info.kind == IK::Directory) {
278 return true;
279 }
280 // you can drop a file on a folder
281 if (fs->fileInfo(mapToSource(useIndex)).isDir() && info.kind == IK::File) {
282 return true;
283 }
284 // you can drop a file on a file
285 if (fs->fileInfo(mapToSource(useIndex)).isFile() && info.kind == IK::File) {
286 return true;
287 }
288
289 return false;
290}
291
301auto StoreModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
302 int row, int column, const QModelIndex &parent)
303 -> bool {
304 if (!canDropMimeData(data, action, row, column, parent)) {
305 return false;
306 }
307
308 if (action == Qt::IgnoreAction) {
309 return true;
310 }
311
312 if (action != Qt::MoveAction && action != Qt::CopyAction) {
313 return false;
314 }
315
317 if (!parseDropData(data, &info)) {
318 return false;
319 }
320
321 return executeDropAction(info, action, parent);
322}
323
324auto StoreModel::parseDropData(const QMimeData *data,
325 dragAndDropInfoPasswordStore *outInfo) -> bool {
326 QByteArray encodedData =
327 data->data("application/vnd+qtpass.dragAndDropInfoPasswordStore");
328 if (encodedData.isEmpty()) {
329 return false;
330 }
331
332 QDataStream stream(&encodedData, QIODevice::ReadOnly);
334 stream >> info;
335 if (stream.status() != QDataStream::Ok) {
336 return false;
337 }
338
339 *outInfo = info;
340 return true;
341}
342
343auto StoreModel::executeDropAction(const dragAndDropInfoPasswordStore &info,
344 Qt::DropAction action,
345 const QModelIndex &parent) -> bool {
346 QModelIndex destIndex =
347 this->index(parent.row(), parent.column(), parent.parent());
348 QFileInfo destFileinfo = fs->fileInfo(mapToSource(destIndex));
349 QFileInfo srcFileInfo = QFileInfo(info.path);
350
351 QString cleanedSrc = QDir::cleanPath(srcFileInfo.absoluteFilePath());
352 QString cleanedDest = QDir::cleanPath(destFileinfo.absoluteFilePath());
353
354 switch (info.kind) {
356 return handleDirDrop(cleanedSrc, destFileinfo, srcFileInfo, action);
358 return handleFileDrop(cleanedSrc, cleanedDest, destFileinfo, action);
359 default:
360 qWarning() << "executeDropAction: unexpected ItemKind, ignoring drop";
361 return false;
362 }
363}
364
365auto StoreModel::handleDirDrop(const QString &cleanedSrc,
366 const QFileInfo &destFileinfo,
367 const QFileInfo &srcFileInfo,
368 Qt::DropAction action) -> bool {
369 if (!destFileinfo.isDir()) {
370 return false;
371 }
372
373 QDir destDir = QDir(QDir::cleanPath(destFileinfo.absoluteFilePath()))
374 .filePath(srcFileInfo.fileName());
375 QString cleanedDestDir = QDir::cleanPath(destDir.absolutePath());
376
377 if (action == Qt::MoveAction) {
378 QtPassSettings::getPass()->Move(cleanedSrc, cleanedDestDir);
379 } else if (action == Qt::CopyAction) {
380 QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDestDir);
381 }
382 return true;
383}
384
385auto StoreModel::handleFileDrop(const QString &cleanedSrc,
386 const QString &cleanedDest,
387 const QFileInfo &destFileinfo,
388 Qt::DropAction action) -> bool {
389 if (destFileinfo.isDir()) {
390 return handleFileToDirDrop(cleanedSrc, cleanedDest, action);
391 }
392 return handleFileToFileDrop(cleanedSrc, cleanedDest, action);
393}
394
395auto StoreModel::handleFileToDirDrop(const QString &cleanedSrc,
396 const QString &cleanedDest,
397 Qt::DropAction action) -> bool {
398 if (action == Qt::MoveAction) {
399 QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest);
400 } else if (action == Qt::CopyAction) {
401 QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest);
402 }
403 return true;
404}
405
406auto StoreModel::handleFileToFileDrop(const QString &cleanedSrc,
407 const QString &cleanedDest,
408 Qt::DropAction action) -> bool {
409 QWidget *parentWidget = qobject_cast<QWidget *>(parent());
410 int answer = QMessageBox::question(
411 parentWidget, tr("Force overwrite?"),
412 tr("overwrite %1 with %2?").arg(cleanedDest, cleanedSrc),
413 QMessageBox::Yes | QMessageBox::No);
414 bool force = answer == QMessageBox::Yes;
415
416 if (action == Qt::MoveAction) {
417 QtPassSettings::getPass()->Move(cleanedSrc, cleanedDest, force);
418 } else if (action == Qt::CopyAction) {
419 QtPassSettings::getPass()->Copy(cleanedSrc, cleanedDest, force);
420 }
421 return true;
422}
423
430auto StoreModel::lessThan(const QModelIndex &source_left,
431 const QModelIndex &source_right) const -> bool {
432/* matches logic in QFileSystemModelSorter::compareNodes() */
433#ifndef Q_OS_MAC
434 if (fs && (source_left.column() == 0 || source_left.column() == 1)) {
435 bool leftD = fs->isDir(source_left);
436 bool rightD = fs->isDir(source_right);
437
438 if (leftD ^ rightD) {
439 return leftD;
440 }
441 }
442#endif
443
444 return QSortFilterProxyModel::lessThan(source_left, source_right);
445}
static auto getPass() -> Pass *
Get currently active pass backend instance.
auto filterAcceptsRow(int source_row, const QModelIndex &source_parent) const -> bool override
Filter whether a row should be displayed.
auto data(const QModelIndex &index, int role) const -> QVariant override
Get display data for index.
StoreModel()
Construct a StoreModel.
auto showThis(const QModelIndex &index) const -> bool
Check if a specific index should be shown.
auto mimeTypes() const -> QStringList override
Get supported MIME types for drag/drop.
void setModelAndStore(QFileSystemModel *sourceModel, const QString &passStore)
Initialize model with source model and store path.
auto dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) -> bool override
Handle dropped MIME data.
void setStore(const QString &passStore)
Update the store path used for filtering without changing the source model.
auto mimeData(const QModelIndexList &indexes) const -> QMimeData *override
Create MIME data from indexes.
auto supportedDragActions() const -> Qt::DropActions override
Get supported drag actions.
auto flags(const QModelIndex &index) const -> Qt::ItemFlags override
Get item flags for index.
auto canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const -> bool override
Check if drop is possible.
auto lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const -> bool override
Compare two indices for sorting.
auto supportedDropActions() const -> Qt::DropActions override
Get supported drop actions.
static auto endsWithGpg() -> const QRegularExpression &
Returns a regex to match .gpg file extensions.
Definition util.cpp:252
auto operator>>(QDataStream &in, dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore) -> QDataStream &
auto operator<<(QDataStream &out, const dragAndDropInfoPasswordStore &dragAndDropInfoPasswordStore) -> QDataStream &
Holds information for drag and drop operations in the password store.
Definition storemodel.h:26