2
* \file movetotrash.cpp
3
* Move file or directory to trash.
9
* Copyright (C) 2011 Urs Fleisch
11
* This file is part of Kid3.
13
* Kid3 is free software; you can redistribute it and/or modify
14
* it under the terms of the GNU General Public License as published by
15
* the Free Software Foundation; either version 2 of the License, or
16
* (at your option) any later version.
18
* Kid3 is distributed in the hope that it will be useful,
19
* but WITHOUT ANY WARRANTY; without even the implied warranty of
20
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
* GNU General Public License for more details.
23
* You should have received a copy of the GNU General Public License
24
* along with this program. If not, see <http://www.gnu.org/licenses/>.
27
#include "movetotrash.h"
38
bool Utils::moveToTrash(const QString& path)
40
typedef int (WINAPI *SHFileOperationW_t)(LPSHFILEOPSTRUCTW);
41
HMODULE hshell32 = GetModuleHandleA("shell32.dll");
42
SHFileOperationW_t pSHFileOperationW = reinterpret_cast<SHFileOperationW_t>(
43
GetProcAddress(hshell32, "SHFileOperationW"));
44
if (!pSHFileOperationW) {
45
// SHFileOperationW is only available since Windows XP.
50
const QString absPath(fi.absoluteFilePath());
52
QVector<WCHAR> from(absPath.length() + 2);
54
for (i = 0; i < absPath.length(); i++) {
55
from[i] = absPath.at(i).unicode();
60
SHFILEOPSTRUCTW fileOp;
62
fileOp.wFunc = FO_DELETE;
63
fileOp.pFrom = from.data();
65
fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
66
fileOp.fAnyOperationsAborted = 0;
67
fileOp.hNameMappings = 0;
68
fileOp.lpszProgressTitle = 0;
69
return pSHFileOperationW(&fileOp) == 0;
72
#elif defined Q_OS_MAC
74
#include <CoreServices/CoreServices.h>
76
bool Utils::moveToTrash(const QString& path)
79
const QString absPath(fi.absoluteFilePath());
81
OSErr err = FSPathMakeRefWithOptions(
82
reinterpret_cast<const UInt8*>(
83
QFile::encodeName(absPath).constData()),
84
kFSPathMakeRefDoNotFollowLeafSymlink, &fsRef, 0);
88
return FSMoveObjectToTrashSync(&fsRef, 0, kFSFileOperationDefaultOptions) == noErr;
91
#elif defined CONFIG_USE_KDE
94
#include <kio/copyjob.h>
95
#include <kio/netaccess.h>
97
bool Utils::moveToTrash(const QString& path)
101
KIO::Job* job = KIO::trash(src);
102
return KIO::NetAccess::synchronousRun(job, 0);
108
* Implemented according to Desktop Trash Can Specification at
109
* http://www.freedesktop.org/wiki/Specifications/trash-spec
114
#include <QTextStream>
117
#include <sys/types.h>
118
#include <sys/stat.h>
126
bool moveToTrashDir(const QFileInfo& fi, const QString& trashDir)
128
QString absPath(fi.absoluteFilePath());
129
QString fileName(fi.fileName());
130
QString filesPath(trashDir + "/files");
131
QString infoPath(trashDir + "/info");
132
QString baseName(fi.baseName());
133
QString suffix(fi.completeSuffix());
134
QString destName(fileName);
136
while (QFile::exists(filesPath + "/" + destName) ||
137
QFile::exists(infoPath + "/" + destName + ".trashinfo")) {
139
destName = QString("%1.%2.%3").arg(baseName).arg(counter).arg(suffix);
141
if (!(QDir(filesPath).exists() ||
142
QDir().mkpath(filesPath)) ||
143
!(QDir(infoPath).exists() ||
144
QDir().mkpath(infoPath)))
147
QFile file(infoPath + "/" + destName + ".trashinfo");
148
if (!file.open(QIODevice::WriteOnly))
150
QTextStream stream(&file);
151
stream << QString("[Trash Info]\nPath=%1\nDeletionDate=%2\n").
153
arg(QDateTime::currentDateTime().toString(Qt::ISODate));
155
return QDir().rename(absPath, filesPath + "/" + destName);
158
bool findMountPoint(dev_t dev, QString& mountPoint)
161
if (FILE* fp = ::setmntent("/proc/mounts", "r")) {
164
while ((mnt = ::getmntent(fp)) != 0) {
165
if (::stat(mnt->mnt_dir, &st) != 0) {
169
if (st.st_dev == dev) {
171
mountPoint = mnt->mnt_dir;
181
bool findExtVolumeTrash(const QString& volumeRoot, QString& trashDir)
184
trashDir = volumeRoot + "/.Trash";
185
uid_t uid = ::getuid();
186
if (QDir(trashDir).exists() &&
187
::lstat(trashDir.toLocal8Bit().data(), &st) == 0 &&
188
(S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && (st.st_mode & S_ISVTX))) {
189
trashDir += QString("/%1").arg(uid);
191
trashDir += QString("-%1").arg(uid);
193
if (QDir(trashDir).exists() ||
194
QDir().mkpath(trashDir)) {
200
} // anonymous namespace
202
bool Utils::moveToTrash(const QString& path)
205
const QString absPath(fi.absoluteFilePath());
207
if (!fi.exists() || !fi.isWritable())
210
struct stat pathStat;
211
struct stat trashStat;
212
if (::lstat(QFile::encodeName(absPath).constData(), &pathStat) != 0 ||
213
::lstat(QFile::encodeName(QDir::homePath()).constData(), &trashStat) != 0)
218
if (pathStat.st_dev == trashStat.st_dev) {
219
char* xdhEnv = ::getenv("XDG_DATA_HOME");
220
topDir = xdhEnv ? QString(xdhEnv) : QDir::homePath() + "/.local/share";
221
trashDir = topDir + "/Trash";
222
} else if (!(findMountPoint(pathStat.st_dev, topDir) &&
223
findExtVolumeTrash(topDir, trashDir))) {
226
return moveToTrashDir(fi, trashDir);