Changeset View
Changeset View
Standalone View
Standalone View
mtp/kio_mtp.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Main implementation for KIO-MTP | 2 | * Main implementation for KIO-MTP | ||
3 | * Copyright (C) 2012 Philipp Schmidt <philschmidt@gmx.net> | 3 | * Copyright (C) 2012 Philipp Schmidt <philschmidt@gmx.net> | ||
4 | * Copyright (C) 2018 Andreas Krutzler <andreas.krutzler@gmx.net> | ||||
4 | * | 5 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | 8 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | 9 | * (at your option) any later version. | ||
9 | * | 10 | * | ||
10 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. | ||
14 | * | 15 | * | ||
15 | * You should have received a copy of the GNU General Public License along | 16 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, write to the Free Software Foundation, Inc., | 17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | */ | 19 | */ | ||
19 | 20 | | |||
20 | #include "kio_mtp.h" | 21 | #include "kio_mtp.h" | ||
21 | #include "kio_mtp_helpers.h" | | |||
22 | 22 | | |||
23 | // #include <KComponentData> | 23 | // #include <KComponentData> | ||
24 | #include <QTemporaryFile> | 24 | #include <QTemporaryFile> | ||
25 | #include <QFileInfo> | 25 | #include <QFileInfo> | ||
26 | #include <QDateTime> | 26 | #include <QDateTime> | ||
27 | #include <QCoreApplication> | 27 | #include <QCoreApplication> | ||
28 | #include <QTimer> | 28 | #include <QTimer> | ||
29 | 29 | | |||
30 | #include <sys/stat.h> | 30 | #include <sys/stat.h> | ||
31 | #include <sys/types.h> | 31 | #include <sys/types.h> | ||
32 | #include <utime.h> | 32 | #include <utime.h> | ||
33 | 33 | | |||
34 | #include <solid/device.h> | 34 | #include "kmtpdeviceinterface.h" | ||
35 | #include <solid/genericinterface.h> | 35 | #include "kmtpstorageinterface.h" | ||
36 | | ||||
37 | UDSEntry getEntry(const KMTPDeviceInterface *device) | ||||
38 | { | ||||
39 | UDSEntry entry; | ||||
40 | entry.fastInsert(UDSEntry::UDS_NAME, device->friendlyName()); | ||||
41 | entry.fastInsert(UDSEntry::UDS_ICON_NAME, QStringLiteral("multimedia-player")); | ||||
42 | entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||||
43 | entry.fastInsert(UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH); | ||||
44 | entry.fastInsert(UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | ||||
45 | return entry; | ||||
46 | } | ||||
47 | | ||||
48 | UDSEntry getEntry(const KMTPStorageInterface *storage) | ||||
49 | { | ||||
50 | UDSEntry entry; | ||||
51 | entry.fastInsert(UDSEntry::UDS_NAME, storage->description()); | ||||
52 | entry.fastInsert(UDSEntry::UDS_ICON_NAME, QStringLiteral("drive-removable-media")); | ||||
53 | entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||||
54 | entry.fastInsert(UDSEntry::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); | ||||
55 | entry.fastInsert(UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | ||||
56 | return entry; | ||||
57 | } | ||||
58 | | ||||
59 | UDSEntry getEntry(const KMTPFile &file) | ||||
60 | { | ||||
61 | UDSEntry entry; | ||||
62 | entry.fastInsert(UDSEntry::UDS_NAME, file.filename()); | ||||
63 | if (file.isFolder()) { | ||||
64 | entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||||
65 | entry.fastInsert(UDSEntry::UDS_ACCESS, S_IRWXU | S_IRWXG | S_IRWXO); | ||||
66 | } else { | ||||
67 | entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFREG); | ||||
68 | entry.fastInsert(UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH); | ||||
69 | entry.fastInsert(UDSEntry::UDS_SIZE, file.filesize()); | ||||
70 | } | ||||
71 | entry.fastInsert(UDSEntry::UDS_MIME_TYPE, file.filetype()); | ||||
72 | entry.fastInsert(UDSEntry::UDS_INODE, file.itemId()); | ||||
73 | entry.fastInsert(UDSEntry::UDS_ACCESS_TIME, file.modificationdate()); | ||||
74 | entry.fastInsert(UDSEntry::UDS_MODIFICATION_TIME, file.modificationdate()); | ||||
75 | entry.fastInsert(UDSEntry::UDS_CREATION_TIME, file.modificationdate()); | ||||
76 | return entry; | ||||
77 | } | ||||
78 | | ||||
79 | QString urlDirectory(const QUrl &url, bool appendTrailingSlash = false) | ||||
80 | { | ||||
81 | if (!appendTrailingSlash) { | ||||
82 | return url.adjusted(QUrl::StripTrailingSlash | QUrl::RemoveFilename).path(); | ||||
83 | } | ||||
84 | return url.adjusted(QUrl::RemoveFilename).path(); | ||||
85 | } | ||||
86 | | ||||
87 | QString urlFileName(const QUrl &url) | ||||
88 | { | ||||
89 | return url.fileName(); | ||||
90 | } | ||||
91 | | ||||
92 | QString convertPath(const QString &slavePath) | ||||
93 | { | ||||
94 | return slavePath.section(QLatin1Char('/'), 3, -1, QString::SectionIncludeLeadingSep); | ||||
95 | } | ||||
36 | 96 | | |||
37 | ////////////////////////////////////////////////////////////////////////////// | 97 | ////////////////////////////////////////////////////////////////////////////// | ||
38 | ///////////////////////////// Slave Implementation /////////////////////////// | 98 | ///////////////////////////// Slave Implementation /////////////////////////// | ||
39 | ////////////////////////////////////////////////////////////////////////////// | 99 | ////////////////////////////////////////////////////////////////////////////// | ||
40 | 100 | | |||
41 | Q_LOGGING_CATEGORY(LOG_KIO_MTP, "kde.kio-mtp") | 101 | Q_LOGGING_CATEGORY(LOG_KIO_MTP, "kde.kio-mtp") | ||
42 | 102 | | |||
43 | extern "C" | 103 | extern "C" | ||
Show All 14 Lines | 105 | { | |||
58 | qCDebug(LOG_KIO_MTP) << "Slave EventLoop ended"; | 118 | qCDebug(LOG_KIO_MTP) << "Slave EventLoop ended"; | ||
59 | 119 | | |||
60 | return 0; | 120 | return 0; | ||
61 | } | 121 | } | ||
62 | 122 | | |||
63 | MTPSlave::MTPSlave(const QByteArray &pool, const QByteArray &app) | 123 | MTPSlave::MTPSlave(const QByteArray &pool, const QByteArray &app) | ||
64 | : SlaveBase("mtp", pool, app) | 124 | : SlaveBase("mtp", pool, app) | ||
65 | { | 125 | { | ||
66 | LIBMTP_Init(); | | |||
67 | | ||||
68 | qCDebug(LOG_KIO_MTP) << "Slave started"; | 126 | qCDebug(LOG_KIO_MTP) << "Slave started"; | ||
69 | 127 | qCDebug(LOG_KIO_MTP) << "Connected to kiod5 module:" << m_kmtpDaemon.isValid(); | |||
70 | deviceCache = new DeviceCache(60000); | | |||
71 | fileCache = new FileCache(this); | | |||
72 | | ||||
73 | qCDebug(LOG_KIO_MTP) << "Caches created"; | | |||
74 | } | 128 | } | ||
75 | 129 | | |||
76 | MTPSlave::~MTPSlave() | 130 | MTPSlave::~MTPSlave() | ||
77 | { | 131 | { | ||
78 | qCDebug(LOG_KIO_MTP) << "Slave destroyed"; | 132 | qCDebug(LOG_KIO_MTP) << "Slave destroyed"; | ||
79 | delete fileCache; | | |||
80 | delete deviceCache; | | |||
81 | } | | |||
82 | | ||||
83 | /** | | |||
84 | * @brief Get's the correct object from the device. | | |||
85 | * @param pathItems A QStringList containing the items of the filepath | | |||
86 | * @return QPair with the object and its device. pair.first is a nullpointer if the object doesn't exist or for root or, depending on the pathItems size device (1), storage (2) or file (>=3) | | |||
87 | */ | | |||
88 | QPair<void *, LIBMTP_mtpdevice_t *> MTPSlave::getPath(const QString &path) | | |||
89 | { | | |||
90 | QStringList pathItems = path.split(QLatin1Char('/'), QString::SkipEmptyParts); | | |||
91 | | ||||
92 | qCDebug(LOG_KIO_MTP) << path << pathItems.size(); | | |||
93 | | ||||
94 | QPair<void *, LIBMTP_mtpdevice_t *> ret; | | |||
95 | | ||||
96 | // Don' handle the root directory | | |||
97 | if (pathItems.size() <= 0) { | | |||
98 | return ret; | | |||
99 | } | | |||
100 | | ||||
101 | if (deviceCache->contains(pathItems.at(0))) { | | |||
102 | LIBMTP_mtpdevice_t *device = deviceCache->get(pathItems.at(0))->getDevice(); | | |||
103 | | ||||
104 | // return specific device | | |||
105 | if (pathItems.size() == 1) { | | |||
106 | ret.first = device; | | |||
107 | ret.second = device; | | |||
108 | | ||||
109 | qCDebug(LOG_KIO_MTP) << "returning LIBMTP_mtpdevice_t"; | | |||
110 | } | | |||
111 | | ||||
112 | if (pathItems.size() > 2) { | | |||
113 | // Query Cache after we have the device | | |||
114 | uint32_t c_fileID = fileCache->queryPath(path); | | |||
115 | if (c_fileID != 0) { | | |||
116 | qCDebug(LOG_KIO_MTP) << "Match found in cache, checking device"; | | |||
117 | | ||||
118 | LIBMTP_file_t *file = LIBMTP_Get_Filemetadata(device, c_fileID); | | |||
119 | if (file) { | | |||
120 | qCDebug(LOG_KIO_MTP) << "Found file in cache"; | | |||
121 | ret.first = file; | | |||
122 | ret.second = device; | | |||
123 | | ||||
124 | qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t from cache"; | | |||
125 | | ||||
126 | return ret; | | |||
127 | } | | |||
128 | } | | |||
129 | // Query cache for parent | | |||
130 | else if (pathItems.size() > 3) { | | |||
131 | QString parentPath = convertToPath(pathItems, pathItems.size() - 1); | | |||
132 | uint32_t c_parentID = fileCache->queryPath(parentPath); | | |||
133 | | ||||
134 | qCDebug(LOG_KIO_MTP) << "Match for parent found in cache, checking device. Parent id = " << c_parentID; | | |||
135 | | ||||
136 | LIBMTP_file_t *parent = LIBMTP_Get_Filemetadata(device, c_parentID); | | |||
137 | if (parent) { | | |||
138 | qCDebug(LOG_KIO_MTP) << "Found parent in cache"; | | |||
139 | // fileCache->addPath( parentPath, c_parentID ); | | |||
140 | | ||||
141 | QMap<QString, LIBMTP_file_t *> files = getFiles(device, parent->storage_id, c_parentID); | | |||
142 | | ||||
143 | for (QMap<QString, LIBMTP_file_t *>::iterator it = files.begin(); it != files.end(); ++it) { | | |||
144 | QString filePath = parentPath; | | |||
145 | filePath.append(QString::fromUtf8("/")).append(it.key()); | | |||
146 | fileCache->addPath(filePath, it.value()->item_id); | | |||
147 | } | | |||
148 | | ||||
149 | if (files.contains(pathItems.last())) { | | |||
150 | LIBMTP_file_t *file = files.value(pathItems.last()); | | |||
151 | | ||||
152 | ret.first = file; | | |||
153 | ret.second = device; | | |||
154 | | ||||
155 | qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t from cached parent" ; | | |||
156 | | ||||
157 | fileCache->addPath(path, file->item_id); | | |||
158 | } | | |||
159 | | ||||
160 | return ret; | | |||
161 | } | | |||
162 | } | | |||
163 | } | | |||
164 | | ||||
165 | QMap<QString, LIBMTP_devicestorage_t *> storages = getDevicestorages(device); | | |||
166 | | ||||
167 | if (pathItems.size() > 1 && storages.contains(pathItems.at(1))) { | | |||
168 | LIBMTP_devicestorage_t *storage = storages.value(pathItems.at(1)); | | |||
169 | | ||||
170 | if (pathItems.size() == 2) { | | |||
171 | ret.first = storage; | | |||
172 | ret.second = device; | | |||
173 | | ||||
174 | qCDebug(LOG_KIO_MTP) << "returning LIBMTP_devicestorage_t"; | | |||
175 | | ||||
176 | return ret; | | |||
177 | } | | |||
178 | | ||||
179 | int currentLevel = 2, currentParent = 0xFFFFFFFF; | | |||
180 | | ||||
181 | QMap<QString, LIBMTP_file_t *> files; | | |||
182 | | ||||
183 | // traverse further while depth not reached | | |||
184 | while (currentLevel < pathItems.size()) { | | |||
185 | files = getFiles(device, storage->id, currentParent); | | |||
186 | | ||||
187 | if (files.contains(pathItems.at(currentLevel))) { | | |||
188 | currentParent = files.value(pathItems.at(currentLevel))->item_id; | | |||
189 | } else { | | |||
190 | qCDebug(LOG_KIO_MTP) << "returning LIBMTP_file_t using tree walk"; | | |||
191 | | ||||
192 | return ret; | | |||
193 | } | | |||
194 | currentLevel++; | | |||
195 | } | | |||
196 | | ||||
197 | ret.first = LIBMTP_Get_Filemetadata(device, currentParent); | | |||
198 | ret.second = device; | | |||
199 | | ||||
200 | fileCache->addPath(path, currentParent); | | |||
201 | } | | |||
202 | } | | |||
203 | | ||||
204 | return ret; | | |||
205 | } | 133 | } | ||
206 | 134 | | |||
207 | int MTPSlave::checkUrl(const QUrl &url, bool redirect) | 135 | int MTPSlave::checkUrl(const QUrl &url, bool redirect) | ||
208 | { | 136 | { | ||
209 | qCDebug(LOG_KIO_MTP) << url; | | |||
210 | | ||||
211 | if (url.path().startsWith(QLatin1String("udi=")) && redirect) { | 137 | if (url.path().startsWith(QLatin1String("udi=")) && redirect) { | ||
212 | QString udi = url.adjusted(QUrl::StripTrailingSlash).path().remove(0, 4); | 138 | const QString udi = url.adjusted(QUrl::StripTrailingSlash).path().remove(0, 4); | ||
213 | 139 | | |||
214 | qCDebug(LOG_KIO_MTP) << "udi = " << udi; | 140 | qCDebug(LOG_KIO_MTP) << "udi = " << udi; | ||
215 | 141 | | |||
216 | if (deviceCache->contains(udi, true)) { | 142 | const KMTPDeviceInterface *device = m_kmtpDaemon.deviceFromUdi(udi); | ||
143 | if (device) { | ||||
217 | QUrl newUrl; | 144 | QUrl newUrl; | ||
218 | newUrl.setScheme(QLatin1String("mtp")); | 145 | newUrl.setScheme(QStringLiteral("mtp")); | ||
219 | newUrl.setPath(QLatin1Char('/') + deviceCache->get(udi, true)->getName()); | 146 | newUrl.setPath(QLatin1Char('/') + device->friendlyName()); | ||
220 | redirection(newUrl); | 147 | redirection(newUrl); | ||
221 | 148 | | |||
222 | return 1; | 149 | return 1; | ||
223 | } else { | 150 | } else { | ||
224 | return 2; | 151 | return 2; | ||
225 | } | 152 | } | ||
226 | } else if (url.path().startsWith(QLatin1Char('/'))) { | 153 | } else if (url.path().startsWith(QLatin1Char('/'))) { | ||
227 | return 0; | 154 | return 0; | ||
228 | } | 155 | } | ||
229 | return -1; | 156 | return -1; | ||
230 | } | 157 | } | ||
231 | 158 | | |||
232 | QString MTPSlave::urlDirectory(const QUrl &url, bool appendTrailingSlash) | | |||
233 | { | | |||
234 | if (!appendTrailingSlash) { | | |||
235 | return url.adjusted(QUrl::StripTrailingSlash | QUrl::RemoveFilename).path(); | | |||
236 | } | | |||
237 | return url.adjusted(QUrl::RemoveFilename).path(); | | |||
238 | } | | |||
239 | | ||||
240 | QString MTPSlave::urlFileName(const QUrl &url) | | |||
241 | { | | |||
242 | return url.fileName(); | | |||
243 | } | | |||
244 | | ||||
245 | void MTPSlave::listDir(const QUrl &url) | 159 | void MTPSlave::listDir(const QUrl &url) | ||
246 | { | 160 | { | ||
247 | qCDebug(LOG_KIO_MTP) << url.path(); | 161 | const int check = checkUrl(url); | ||
dfaure: Don't use Q_FUNC_INFO in code.
Instead, set up your environment with %{function} in… | |||||
248 | | ||||
249 | int check = checkUrl(url); | | |||
250 | switch (check) { | 162 | switch (check) { | ||
251 | case 0: | 163 | case 0: | ||
252 | break; | 164 | break; | ||
253 | case 1: | 165 | case 1: | ||
254 | finished(); | 166 | finished(); | ||
255 | return; | 167 | return; | ||
256 | case 2: | 168 | case 2: | ||
257 | error(ERR_DOES_NOT_EXIST, url.path()); | 169 | error(ERR_DOES_NOT_EXIST, url.path()); | ||
258 | return; | 170 | return; | ||
259 | default: | 171 | default: | ||
260 | error(ERR_MALFORMED_URL, url.path()); | 172 | error(ERR_MALFORMED_URL, url.path()); | ||
261 | return; | 173 | return; | ||
262 | } | 174 | } | ||
263 | 175 | | |||
264 | QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 176 | // list '.' entry, otherwise files cannot be pasted to empty folders | ||
265 | 177 | KIO::UDSEntry entry; | |||
266 | UDSEntry entry; | 178 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); | ||
179 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||||
180 | entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0); | ||||
181 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); | ||||
182 | listEntry(entry); | ||||
267 | 183 | | |||
184 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||||
268 | // list devices | 185 | // list devices | ||
269 | if (pathItems.size() == 0) { | 186 | if (pathItems.isEmpty()) { | ||
270 | qCDebug(LOG_KIO_MTP) << "Root directory, listing devices"; | 187 | qCDebug(LOG_KIO_MTP) << "Root directory, listing devices"; | ||
271 | totalSize(deviceCache->size()); | | |||
272 | | ||||
273 | foreach(CachedDevice * cachedDevice, deviceCache->getAll().values()) { | | |||
274 | LIBMTP_mtpdevice_t *device = cachedDevice->getDevice(); | | |||
275 | 188 | | |||
276 | getEntry(entry, device); | 189 | totalSize(filesize_t(m_kmtpDaemon.devices().size())); | ||
277 | 190 | | |||
278 | listEntry(entry); | 191 | const auto devices = m_kmtpDaemon.devices(); | ||
279 | entry.clear(); | 192 | for (const KMTPDeviceInterface *device : devices) { | ||
193 | listEntry(getEntry(device)); | ||||
move m_kmtpDaemon.devices to a const local variable, to avoid a detach. Yes this is annoying. dfaure: move m_kmtpDaemon.devices to a const local variable, to avoid a detach. Yes this is annoying. | |||||
280 | } | 194 | } | ||
281 | 195 | | |||
282 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Devices"; | 196 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Devices:" << m_kmtpDaemon.devices().size(); | ||
283 | finished(); | 197 | finished(); | ||
198 | return; | ||||
284 | } | 199 | } | ||
285 | // traverse into device | | |||
286 | else if (deviceCache->contains(pathItems.at(0))) { | | |||
287 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(url.path()); | | |||
288 | UDSEntry entry; | | |||
289 | | ||||
290 | if (pair.first) { | | |||
291 | LIBMTP_mtpdevice_t *device = pair.second; | | |||
292 | 200 | | |||
293 | // Device, list storages | 201 | // traverse into device | ||
202 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||||
203 | if (mtpDevice) { | ||||
204 | // list storages | ||||
294 | if (pathItems.size() == 1) { | 205 | if (pathItems.size() == 1) { | ||
295 | QMap<QString, LIBMTP_devicestorage_t *> storages = getDevicestorages(device); | 206 | qCDebug(LOG_KIO_MTP) << "Listing storages for device " << pathItems.first(); | ||
296 | 207 | | |||
297 | qCDebug(LOG_KIO_MTP) << "Listing storages for device " << pathItems.at(0); | 208 | const auto storages = mtpDevice->storages(); | ||
298 | totalSize(storages.size()); | 209 | totalSize(filesize_t(storages.size())); | ||
299 | 210 | | |||
300 | if (storages.size() > 0) { | 211 | if (storages.count() > 0) { | ||
301 | foreach(const QString & storageName, storages.keys()) { | 212 | for (KMTPStorageInterface *storage : storages) { | ||
302 | getEntry(entry, storages.value(storageName)); | 213 | listEntry(getEntry(storage)); | ||
303 | | ||||
304 | listEntry(entry); | | |||
305 | entry.clear(); | | |||
306 | } | 214 | } | ||
307 | 215 | | |||
308 | finished(); | 216 | finished(); | ||
309 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Storages"; | 217 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Storages:" << storages.count(); | ||
310 | } else { | 218 | } else { | ||
311 | warning(i18n("No Storages found. Make sure your device is unlocked and has MTP enabled in its USB connection settings.")); | 219 | warning(i18n("No storages found. Make sure your device is unlocked and has MTP enabled in its USB connection settings.")); | ||
312 | } | 220 | } | ||
313 | } | | |||
314 | // Storage, list files and folders of storage root | | |||
315 | else { | | |||
316 | QMap<QString, LIBMTP_file_t *> files; | | |||
317 | | ||||
318 | if (pathItems.size() == 2) { | | |||
319 | qCDebug(LOG_KIO_MTP) << "Getting storage root listing"; | | |||
320 | | ||||
321 | LIBMTP_devicestorage_t *storage = (LIBMTP_devicestorage_t *)pair.first; | | |||
322 | | ||||
323 | qCDebug(LOG_KIO_MTP) << "We have a storage:" << (storage == nullptr); | | |||
324 | | ||||
325 | files = getFiles(device, storage->id); | | |||
326 | } else { | 221 | } else { | ||
327 | LIBMTP_file_t *parent = (LIBMTP_file_t *)pair.first; | 222 | // list files and folders | ||
328 | 223 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | |||
329 | files = getFiles(device, parent->storage_id, parent->item_id); | 224 | if (storage) { | ||
330 | } | 225 | int result; | ||
226 | const QString path = convertPath(url.path()); | ||||
227 | const KMTPFileList files = storage->getFilesAndFolders(path, result); | ||||
331 | 228 | | |||
332 | for (QMap<QString, LIBMTP_file_t *>::iterator it = files.begin(); it != files.end(); ++it) { | 229 | switch (result) { | ||
333 | LIBMTP_file_t *file = it.value(); | 230 | case 0: | ||
334 | 231 | break; | |||
335 | QString filePath; | 232 | case 2: | ||
336 | if (url.path().endsWith(QLatin1Char('/'))) { | 233 | error(ERR_IS_FILE, url.path()); | ||
337 | filePath = url.path().append(it.key()); | 234 | return; | ||
338 | } else { | 235 | default: | ||
339 | filePath = url.path().append(QLatin1Char('/')).append(it.key()); | 236 | // path not found | ||
237 | error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); | ||||
238 | return; | ||||
340 | } | 239 | } | ||
341 | 240 | | |||
342 | fileCache->addPath(filePath, file->item_id); | 241 | for (const KMTPFile &file : files) { | ||
343 | 242 | listEntry(getEntry(file)); | |||
344 | getEntry(entry, file); | | |||
345 | | ||||
346 | listEntry(entry); | | |||
347 | entry.clear(); | | |||
348 | } | 243 | } | ||
349 | 244 | | |||
350 | finished(); | 245 | finished(); | ||
351 | 246 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] :: Files:" << files.count(); | |||
352 | qCDebug(LOG_KIO_MTP) << "[SUCCESS] Files"; | | |||
353 | } | | |||
354 | } else { | 247 | } else { | ||
248 | // storage not found | ||||
355 | error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); | 249 | error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); | ||
356 | qCDebug(LOG_KIO_MTP) << "[ERROR]"; | 250 | qCDebug(LOG_KIO_MTP) << "[ERROR] :: Storage"; | ||
251 | } | ||||
357 | } | 252 | } | ||
358 | } else { | 253 | } else { | ||
254 | // device not found | ||||
359 | error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); | 255 | error(ERR_CANNOT_ENTER_DIRECTORY, url.path()); | ||
360 | qCDebug(LOG_KIO_MTP) << "[ERROR]"; | 256 | qCDebug(LOG_KIO_MTP) << "[ERROR] :: Device"; | ||
361 | } | 257 | } | ||
362 | } | 258 | } | ||
363 | 259 | | |||
364 | void MTPSlave::stat(const QUrl &url) | 260 | void MTPSlave::stat(const QUrl &url) | ||
365 | { | 261 | { | ||
366 | qCDebug(LOG_KIO_MTP) << url.path(); | 262 | const int check = checkUrl(url); | ||
367 | | ||||
368 | int check = checkUrl(url); | | |||
369 | switch (check) { | 263 | switch (check) { | ||
370 | case 0: | 264 | case 0: | ||
371 | break; | 265 | break; | ||
372 | case 1: | 266 | case 1: | ||
373 | finished(); | 267 | finished(); | ||
374 | return; | 268 | return; | ||
375 | case 2: | 269 | case 2: | ||
376 | error(ERR_DOES_NOT_EXIST, url.path()); | 270 | error(ERR_DOES_NOT_EXIST, url.path()); | ||
377 | return; | 271 | return; | ||
378 | default: | 272 | default: | ||
379 | error(ERR_MALFORMED_URL, url.path()); | 273 | error(ERR_MALFORMED_URL, url.path()); | ||
380 | return; | 274 | return; | ||
381 | } | 275 | } | ||
382 | 276 | | |||
383 | QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 277 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
384 | | ||||
385 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(url.path()); | | |||
386 | UDSEntry entry; | 278 | UDSEntry entry; | ||
387 | 279 | // root | |||
388 | if (pair.first) { | | |||
389 | // Root | | |||
390 | if (pathItems.size() < 1) { | 280 | if (pathItems.size() < 1) { | ||
391 | entry.insert(UDSEntry::UDS_NAME, QLatin1String("mtp:///")); | 281 | entry.fastInsert(UDSEntry::UDS_NAME, QLatin1String("mtp:///")); | ||
392 | entry.insert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); | 282 | entry.fastInsert(UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||
393 | entry.insert(UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH); | 283 | entry.fastInsert(UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH | S_IXUSR | S_IXGRP | S_IXOTH); | ||
394 | entry.insert(UDSEntry::UDS_MIME_TYPE, QLatin1String("inode/directory")); | 284 | entry.fastInsert(UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | ||
395 | } | 285 | } else { | ||
396 | // Device | 286 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
397 | else if (pathItems.size() < 2) { | 287 | if (mtpDevice) { | ||
398 | getEntry(entry, pair.second); | 288 | | ||
399 | } | 289 | // device | ||
400 | // Storage | 290 | if (pathItems.size() < 2) { | ||
401 | else if (pathItems.size() < 3) { | 291 | entry = getEntry(mtpDevice); | ||
402 | getEntry(entry, (LIBMTP_devicestorage_t *) pair.first); | 292 | } else { | ||
293 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | ||||
294 | if (storage) { | ||||
295 | | ||||
296 | // storage | ||||
297 | if (pathItems.size() < 3) { | ||||
298 | entry = getEntry(storage); | ||||
403 | } | 299 | } | ||
404 | // Folder/File | 300 | // folder/file | ||
405 | else { | 301 | else { | ||
406 | getEntry(entry, (LIBMTP_file_t *) pair.first); | 302 | entry = getEntry(storage->getFileMetadata(convertPath(url.path()))); | ||
303 | } | ||||
304 | } | ||||
407 | } | 305 | } | ||
408 | } | 306 | } | ||
307 | } | ||||
308 | | ||||
409 | statEntry(entry); | 309 | statEntry(entry); | ||
410 | finished(); | 310 | finished(); | ||
411 | } | 311 | } | ||
412 | 312 | | |||
413 | void MTPSlave::mimetype(const QUrl &url) | 313 | void MTPSlave::mimetype(const QUrl &url) | ||
414 | { | 314 | { | ||
415 | int check = checkUrl(url); | 315 | const int check = checkUrl(url); | ||
416 | switch (check) { | 316 | switch (check) { | ||
417 | case 0: | 317 | case 0: | ||
418 | break; | 318 | break; | ||
419 | case 1: | 319 | case 1: | ||
420 | finished(); | 320 | finished(); | ||
421 | return; | 321 | return; | ||
422 | case 2: | 322 | case 2: | ||
423 | error(ERR_DOES_NOT_EXIST, url.path()); | 323 | error(ERR_DOES_NOT_EXIST, url.path()); | ||
424 | return; | 324 | return; | ||
425 | default: | 325 | default: | ||
426 | error(ERR_MALFORMED_URL, url.path()); | 326 | error(ERR_MALFORMED_URL, url.path()); | ||
427 | return; | 327 | return; | ||
428 | } | 328 | } | ||
429 | 329 | | |||
430 | qCDebug(LOG_KIO_MTP) << url.path(); | 330 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
431 | | ||||
432 | QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | | |||
433 | | ||||
434 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(url.path()); | | |||
435 | | ||||
436 | if (pair.first) { | | |||
437 | // NOTE the difference between calling mimetype and mimeType | | |||
438 | if (pathItems.size() > 2) { | 331 | if (pathItems.size() > 2) { | ||
439 | mimeType(getMimetype(((LIBMTP_file_t *) pair.first)->filetype)); | 332 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
440 | } else { | 333 | if (mtpDevice) { | ||
441 | mimeType(QString::fromLatin1("inode/directory")); | 334 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | ||
335 | if (storage) { | ||||
336 | const KMTPFile file = storage->getFileMetadata(convertPath(url.path())); | ||||
337 | // NOTE the difference between calling mimetype and mimeType | ||||
338 | mimeType(file.filetype()); | ||||
339 | return; | ||||
340 | } | ||||
442 | } | 341 | } | ||
443 | } else { | 342 | } else { | ||
444 | error(ERR_DOES_NOT_EXIST, url.path()); | 343 | mimeType(QStringLiteral("inode/directory")); | ||
445 | return; | 344 | return; | ||
446 | } | 345 | } | ||
346 | | ||||
347 | error(ERR_DOES_NOT_EXIST, url.path()); | ||||
447 | } | 348 | } | ||
448 | 349 | | |||
449 | void MTPSlave::put(const QUrl &url, int, JobFlags flags) | 350 | void MTPSlave::get(const QUrl &url) | ||
450 | { | 351 | { | ||
451 | int check = checkUrl(url); | 352 | const int check = checkUrl(url); | ||
452 | switch (check) { | 353 | switch (check) { | ||
453 | case 0: | 354 | case 0: | ||
454 | break; | 355 | break; | ||
455 | default: | 356 | default: | ||
456 | error(ERR_MALFORMED_URL, url.path()); | 357 | error(ERR_MALFORMED_URL, url.path()); | ||
457 | return; | 358 | return; | ||
458 | } | 359 | } | ||
459 | 360 | | |||
460 | qCDebug(LOG_KIO_MTP) << url.path(); | 361 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
461 | 362 | | |||
462 | QStringList destItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 363 | // file | ||
364 | if (pathItems.size() > 2) { | ||||
463 | 365 | | |||
464 | // Can't copy to root or device, needs storage | 366 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
465 | if (destItems.size() < 2) { | 367 | if (mtpDevice) { | ||
466 | error(ERR_UNSUPPORTED_ACTION, url.path()); | 368 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | ||
369 | if (storage) { | ||||
370 | const QString path = convertPath(url.path()); | ||||
371 | const KMTPFile source = storage->getFileMetadata(path); | ||||
372 | if (!source.isValid()) { | ||||
373 | error(KIO::ERR_DOES_NOT_EXIST, url.path()); | ||||
467 | return; | 374 | return; | ||
468 | } | 375 | } | ||
469 | 376 | | |||
470 | if (!(flags & KIO::Overwrite) && getPath(url.path()).first) { | 377 | mimeType(source.filetype()); | ||
471 | error(ERR_FILE_ALREADY_EXIST, url.path()); | 378 | totalSize(source.filesize()); | ||
and if mtpDevice is nullptr? This code doesn't seem to emit either error or finished, which is a bug. Or if it can't be null, use Q_ASSERT instead of if() ;) (Same in most other SlaveBase methods, please check that they all end up with either error() or finished()) dfaure: and if mtpDevice is nullptr? This code doesn't seem to emit either error or finished, which is… | |||||
379 | | ||||
380 | int result = storage->getFileToHandler(path); | ||||
381 | if (result) { | ||||
382 | error(KIO::ERR_CANNOT_READ, url.path()); | ||||
472 | return; | 383 | return; | ||
473 | } | 384 | } | ||
474 | 385 | | |||
475 | destItems.takeLast(); | 386 | QEventLoop loop; | ||
387 | connect(storage, &KMTPStorageInterface::dataReady, this, [this] (const QByteArray &data) { | ||||
388 | MTPSlave::data(data); | ||||
389 | }); | ||||
390 | connect(storage, &KMTPStorageInterface::copyFinished, &loop, &QEventLoop::exit); | ||||
391 | result = loop.exec(); | ||||
Let's hope this signal is always ALWAYS emitted, including in all error cases... dfaure: Let's hope this signal is always ALWAYS emitted, including in all error cases... | |||||
476 | 392 | | |||
477 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(urlDirectory(url)); | 393 | qCDebug(LOG_KIO_MTP) << "data received"; | ||
478 | 394 | | |||
479 | if (!pair.first) { | 395 | if (result) { | ||
480 | error(ERR_DOES_NOT_EXIST, url.path()); | 396 | error(ERR_CANNOT_READ, url.path()); | ||
481 | return; | 397 | return; | ||
482 | } | 398 | } | ||
483 | 399 | | |||
484 | LIBMTP_mtpdevice_t *device = pair.second; | 400 | data(QByteArray()); | ||
485 | LIBMTP_file_t *parent = (LIBMTP_file_t *) pair.first; | 401 | finished(); | ||
486 | if (parent->filetype != LIBMTP_FILETYPE_FOLDER) { | 402 | } | ||
487 | error(ERR_IS_FILE, urlDirectory(url)); | 403 | } else { | ||
488 | return; | 404 | error(ERR_CANNOT_READ, url.path()); | ||
405 | } | ||||
406 | } else { | ||||
407 | error(ERR_UNSUPPORTED_ACTION, url.path()); | ||||
408 | } | ||||
489 | } | 409 | } | ||
490 | 410 | | |||
491 | // We did get a total size from the application | 411 | void MTPSlave::put(const QUrl &url, int, JobFlags flags) | ||
492 | if (hasMetaData(QLatin1String("sourceSize"))) { | 412 | { | ||
493 | qCDebug(LOG_KIO_MTP) << "direct put"; | 413 | const int check = checkUrl(url); | ||
494 | 414 | switch (check) { | |||
495 | LIBMTP_file_t *file = LIBMTP_new_file_t(); | 415 | case 0: | ||
496 | file->parent_id = parent->item_id; | 416 | break; | ||
497 | file->filename = strdup(urlFileName(url).toUtf8().data()); | 417 | default: | ||
498 | file->filetype = getFiletype(urlFileName(url)); | 418 | error(ERR_MALFORMED_URL, url.path()); | ||
499 | file->filesize = metaData(QLatin1String("sourceSize")).toULongLong(); | 419 | return; | ||
500 | file->modificationdate = QDateTime::currentDateTime().toTime_t(); | 420 | } | ||
501 | file->storage_id = parent->storage_id; | | |||
502 | | ||||
503 | qCDebug(LOG_KIO_MTP) << "Sending file" << file->filename; | | |||
504 | 421 | | |||
505 | int ret = LIBMTP_Send_File_From_Handler(device, &dataGet, this, file, &dataProgress, this); | 422 | const QStringList destItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
506 | LIBMTP_destroy_file_t(file); | | |||
507 | 423 | | |||
508 | if (ret != 0) { | 424 | // can't copy to root or device, needs storage | ||
509 | error(KIO::ERR_COULD_NOT_WRITE, urlFileName(url)); | 425 | if (destItems.size() < 2) { | ||
510 | LIBMTP_Dump_Errorstack(device); | 426 | error(ERR_UNSUPPORTED_ACTION, url.path()); | ||
511 | LIBMTP_Clear_Errorstack(device); | | |||
512 | return; | 427 | return; | ||
513 | } | 428 | } | ||
514 | } | 429 | | ||
515 | // We need to get the entire file first, then we can upload | 430 | // we need to get the entire file first, then we can upload | ||
516 | else { | | |||
517 | qCDebug(LOG_KIO_MTP) << "use temp file"; | 431 | qCDebug(LOG_KIO_MTP) << "use temp file"; | ||
518 | 432 | | |||
519 | QTemporaryFile temp; | 433 | QTemporaryFile temp; | ||
434 | if (temp.open()) { | ||||
520 | QByteArray buffer; | 435 | QByteArray buffer; | ||
521 | int len = 0; | 436 | int len = 0; | ||
522 | 437 | | |||
523 | do { | 438 | do { | ||
524 | dataReq(); | 439 | dataReq(); | ||
525 | len = readData(buffer); | 440 | len = readData(buffer); | ||
526 | temp.write(buffer); | 441 | temp.write(buffer); | ||
527 | } while (len > 0); | 442 | } while (len > 0); | ||
528 | 443 | | |||
529 | QFileInfo info(temp); | 444 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(destItems.first()); | ||
530 | 445 | if (mtpDevice) { | |||
531 | LIBMTP_file_t *file = LIBMTP_new_file_t(); | 446 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(destItems.at(1)); | ||
532 | file->parent_id = parent->item_id; | 447 | if (storage) { | ||
533 | file->filename = strdup(urlFileName(url).toUtf8().data()); | 448 | const QString destinationPath = convertPath(url.path()); | ||
534 | file->filetype = getFiletype(urlFileName(url)); | 449 | | ||
535 | file->filesize = info.size(); | 450 | // check if the file already exists on the device | ||
536 | file->modificationdate = QDateTime::currentDateTime().toTime_t(); | 451 | const KMTPFile destinationFile = storage->getFileMetadata(destinationPath); | ||
537 | file->storage_id = parent->storage_id; | 452 | if (destinationFile.isValid()) { | ||
538 | 453 | if (flags & KIO::Overwrite) { | |||
539 | int ret = LIBMTP_Send_File_From_File_Descriptor(device, temp.handle(), file, nullptr, nullptr); | 454 | // delete existing file on the device | ||
540 | LIBMTP_destroy_file_t(file); | 455 | const int result = storage->deleteObject(destinationPath); | ||
541 | 456 | if (result) { | |||
542 | if (ret != 0) { | 457 | error(ERR_CANNOT_DELETE, url.path()); | ||
543 | error(KIO::ERR_COULD_NOT_WRITE, urlFileName(url)); | | |||
544 | return; | 458 | return; | ||
545 | } | 459 | } | ||
546 | finished(); | 460 | } else { | ||
461 | error(ERR_FILE_ALREADY_EXIST, url.path()); | ||||
462 | return; | ||||
547 | } | 463 | } | ||
548 | } | 464 | } | ||
549 | 465 | | |||
550 | void MTPSlave::get(const QUrl &url) | 466 | totalSize(quint64(temp.size())); | ||
551 | { | 467 | | ||
552 | int check = checkUrl(url); | 468 | QDBusUnixFileDescriptor descriptor(temp.handle()); | ||
553 | switch (check) { | 469 | int result = storage->sendFileFromFileDescriptor(descriptor, destinationPath); | ||
470 | if (result) { | ||||
471 | error(KIO::ERR_CANNOT_WRITE, urlFileName(url)); | ||||
472 | return; | ||||
473 | } | ||||
474 | | ||||
475 | result = waitForCopyOperation(storage); | ||||
476 | processedSize(quint64(temp.size())); | ||||
477 | temp.close(); | ||||
478 | | ||||
479 | switch (result) { | ||||
554 | case 0: | 480 | case 0: | ||
555 | break; | 481 | break; | ||
482 | case 2: | ||||
483 | error(ERR_IS_FILE, urlDirectory(url)); | ||||
484 | return; | ||||
556 | default: | 485 | default: | ||
557 | error(ERR_MALFORMED_URL, url.path()); | 486 | error(KIO::ERR_CANNOT_WRITE, urlFileName(url)); | ||
558 | return; | 487 | return; | ||
559 | } | 488 | } | ||
560 | 489 | | |||
561 | qCDebug(LOG_KIO_MTP) << url.path(); | 490 | qCDebug(LOG_KIO_MTP) << "data sent"; | ||
562 | 491 | finished(); | |||
563 | QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | | |||
564 | | ||||
565 | // File | | |||
566 | if (pathItems.size() > 2) { | | |||
567 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(url.path()); | | |||
568 | | ||||
569 | if (pair.first) { | | |||
570 | LIBMTP_file_t *file = (LIBMTP_file_t *) pair.first; | | |||
571 | | ||||
572 | mimeType(getMimetype(file->filetype)); | | |||
573 | totalSize(file->filesize); | | |||
574 | | ||||
575 | LIBMTP_mtpdevice_t *device = pair.second; | | |||
576 | | ||||
577 | int ret = LIBMTP_Get_File_To_Handler(device, file->item_id, &dataPut, this, &dataProgress, this); | | |||
578 | if (ret != 0) { | | |||
579 | error(ERR_COULD_NOT_READ, url.path()); | | |||
580 | return; | 492 | return; | ||
581 | } | 493 | } | ||
582 | data(QByteArray()); | | |||
583 | finished(); | | |||
584 | } else { | | |||
585 | error(ERR_DOES_NOT_EXIST, url.path()); | | |||
586 | } | 494 | } | ||
587 | } else { | | |||
588 | error(ERR_UNSUPPORTED_ACTION, url.path()); | | |||
589 | } | 495 | } | ||
496 | | ||||
497 | error(KIO::ERR_CANNOT_WRITE, urlFileName(url)); | ||||
590 | } | 498 | } | ||
591 | 499 | | |||
592 | void MTPSlave::copy(const QUrl &src, const QUrl &dest, int, JobFlags flags) | 500 | void MTPSlave::copy(const QUrl &src, const QUrl &dest, int, JobFlags flags) | ||
593 | { | 501 | { | ||
594 | qCDebug(LOG_KIO_MTP) << src.path() << dest.path(); | | |||
595 | | ||||
596 | if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("mtp")) { | 502 | if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("mtp")) { | ||
597 | qCDebug(LOG_KIO_MTP) << "Copy on device: Not supported"; | 503 | qCDebug(LOG_KIO_MTP) << "Copy on device: Not supported"; | ||
598 | // MTP doesn't support moving files directly on the device, so we have to download and then upload... | 504 | // MTP doesn't support moving files directly on the device, so we have to download and then upload... | ||
599 | 505 | | |||
600 | error(ERR_UNSUPPORTED_ACTION, i18n("Cannot copy/move files on the device itself")); | 506 | error(ERR_UNSUPPORTED_ACTION, i18n("Cannot copy/move files on the device itself")); | ||
507 | return; | ||||
601 | } else if (src.scheme() == QLatin1String("file") && dest.scheme() == QLatin1String("mtp")) { | 508 | } else if (src.scheme() == QLatin1String("file") && dest.scheme() == QLatin1String("mtp")) { | ||
602 | int check = checkUrl(dest); | 509 | // copy from filesystem to the device | ||
510 | | ||||
511 | const int check = checkUrl(dest); | ||||
603 | switch (check) { | 512 | switch (check) { | ||
604 | case 0: | 513 | case 0: | ||
605 | break; | 514 | break; | ||
606 | default: | 515 | default: | ||
607 | error(ERR_MALFORMED_URL, dest.path()); | 516 | error(ERR_MALFORMED_URL, dest.path()); | ||
608 | return; | 517 | return; | ||
609 | } | 518 | } | ||
610 | 519 | | |||
611 | QStringList destItems = dest.path().split(QLatin1Char('/') , QString::SkipEmptyParts); | 520 | QStringList destItems = dest.path().split(QLatin1Char('/') , QString::SkipEmptyParts); | ||
612 | 521 | | |||
613 | // Can't copy to root or device, needs storage | 522 | // can't copy to root or device, needs storage | ||
614 | if (destItems.size() < 2) { | 523 | if (destItems.size() < 2) { | ||
615 | error(ERR_UNSUPPORTED_ACTION, dest.path()); | 524 | error(ERR_UNSUPPORTED_ACTION, dest.path()); | ||
616 | return; | 525 | return; | ||
617 | } | 526 | } | ||
618 | 527 | | |||
619 | qCDebug(LOG_KIO_MTP) << "Copy file " << urlFileName(src) << "from filesystem to device" << urlDirectory(src, true) << urlDirectory(dest, true); | 528 | qCDebug(LOG_KIO_MTP) << "Copy file " << urlFileName(src) << "from filesystem to device" << urlDirectory(src, true) << urlDirectory(dest, true); | ||
620 | 529 | | |||
621 | if (!(flags & KIO::Overwrite) && getPath(dest.path()).first) { | 530 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(destItems.first()); | ||
531 | if (mtpDevice) { | ||||
532 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(destItems.at(1)); | ||||
533 | if (storage) { | ||||
534 | const QString destinationPath = convertPath(dest.path()); | ||||
535 | | ||||
536 | // check if the file already exists on the device | ||||
537 | const KMTPFile destinationFile = storage->getFileMetadata(destinationPath); | ||||
538 | if (destinationFile.isValid()) { | ||||
539 | if (flags & KIO::Overwrite) { | ||||
540 | // delete existing file on the device | ||||
541 | const int result = storage->deleteObject(destinationPath); | ||||
542 | if (result) { | ||||
543 | error(ERR_CANNOT_DELETE, dest.path()); | ||||
544 | return; | ||||
545 | } | ||||
546 | } else { | ||||
622 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | 547 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | ||
623 | return; | 548 | return; | ||
624 | } | 549 | } | ||
550 | } | ||||
625 | 551 | | |||
626 | destItems.takeLast(); | 552 | QFile srcFile(src.path()); | ||
627 | 553 | if (!srcFile.open(QIODevice::ReadOnly)) { | |||
628 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(urlDirectory(dest)); | 554 | error(KIO::ERR_CANNOT_OPEN_FOR_READING, src.path()); | ||
629 | | ||||
630 | if (!pair.first) { | | |||
631 | error(ERR_DOES_NOT_EXIST, urlDirectory(dest, true)); | | |||
632 | return; | 555 | return; | ||
633 | } | 556 | } | ||
634 | 557 | | |||
635 | LIBMTP_mtpdevice_t *device = pair.second; | 558 | qCDebug(LOG_KIO_MTP) << "Sending file" << srcFile.fileName() << "with size" << srcFile.size(); | ||
636 | | ||||
637 | uint32_t parent_id = 0xFFFFFFFF, storage_id = 0; | | |||
638 | | ||||
639 | if (destItems.size() == 2) { | | |||
640 | LIBMTP_devicestorage_t *storage = (LIBMTP_devicestorage_t *) pair.first; | | |||
641 | | ||||
642 | storage_id = storage->id; | | |||
643 | } else { | | |||
644 | LIBMTP_file_t *parent = (LIBMTP_file_t *) pair.first; | | |||
645 | 559 | | |||
646 | storage_id = parent->storage_id; | 560 | totalSize(quint64(srcFile.size())); | ||
647 | parent_id = parent->item_id; | | |||
648 | 561 | | |||
649 | if (parent->filetype != LIBMTP_FILETYPE_FOLDER) { | 562 | QDBusUnixFileDescriptor descriptor(srcFile.handle()); | ||
650 | error(ERR_IS_FILE, urlDirectory(dest)); | 563 | int result = storage->sendFileFromFileDescriptor(descriptor, destinationPath); | ||
564 | if (result) { | ||||
565 | error(KIO::ERR_CANNOT_WRITE, urlFileName(dest)); | ||||
651 | return; | 566 | return; | ||
652 | } | 567 | } | ||
653 | } | | |||
654 | | ||||
655 | QFileInfo source(src.path()); | | |||
656 | | ||||
657 | LIBMTP_file_t *file = LIBMTP_new_file_t(); | | |||
658 | file->parent_id = parent_id; | | |||
659 | file->filename = strdup(urlFileName(dest).toUtf8().data()); | | |||
660 | file->filetype = getFiletype(urlFileName(dest)); | | |||
661 | file->filesize = source.size(); | | |||
662 | file->modificationdate = source.lastModified().toTime_t(); | | |||
663 | file->storage_id = storage_id; | | |||
664 | | ||||
665 | qCDebug(LOG_KIO_MTP) << "Sending file" << file->filename << "with size" << file->filesize; | | |||
666 | 568 | | |||
667 | totalSize(source.size()); | 569 | result = waitForCopyOperation(storage); | ||
570 | processedSize(quint64(srcFile.size())); | ||||
571 | srcFile.close(); | ||||
668 | 572 | | |||
669 | int ret = LIBMTP_Send_File_From_File(device, src.path().toUtf8().data(), file, (LIBMTP_progressfunc_t) &dataProgress, this); | 573 | if (result) { | ||
670 | LIBMTP_destroy_file_t(file); | 574 | error(KIO::ERR_CANNOT_WRITE, urlFileName(dest)); | ||
671 | | ||||
672 | if (ret != 0) { | | |||
673 | error(KIO::ERR_COULD_NOT_WRITE, urlFileName(dest)); | | |||
674 | LIBMTP_Dump_Errorstack(device); | | |||
675 | LIBMTP_Clear_Errorstack(device); | | |||
676 | return; | 575 | return; | ||
677 | } | 576 | } | ||
678 | 577 | | |||
679 | qCDebug(LOG_KIO_MTP) << "Sent file"; | 578 | qCDebug(LOG_KIO_MTP) << "Sent file"; | ||
579 | } | ||||
580 | } | ||||
581 | | ||||
680 | } else if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("file")) { | 582 | } else if (src.scheme() == QLatin1String("mtp") && dest.scheme() == QLatin1String("file")) { | ||
681 | int check = checkUrl(src); | 583 | // copy from the device to filesystem | ||
584 | | ||||
585 | const int check = checkUrl(src); | ||||
682 | switch (check) { | 586 | switch (check) { | ||
683 | case 0: | 587 | case 0: | ||
684 | break; | 588 | break; | ||
685 | default: | 589 | default: | ||
686 | error(ERR_MALFORMED_URL, src.path()); | 590 | error(ERR_MALFORMED_URL, src.path()); | ||
687 | return; | 591 | return; | ||
688 | } | 592 | } | ||
689 | 593 | | |||
690 | qCDebug(LOG_KIO_MTP) << "Copy file " << urlFileName(src) << "from device to filesystem" << urlDirectory(src, true) << urlDirectory(dest, true); | 594 | qCDebug(LOG_KIO_MTP) << "Copy file" << urlFileName(src) << "from device to filesystem" << urlDirectory(src, true) << urlDirectory(dest, true); | ||
691 | 595 | | |||
692 | QFileInfo destination(dest.path()); | 596 | QFileInfo destination(dest.path()); | ||
693 | 597 | | |||
694 | if (!(flags & KIO::Overwrite) && destination.exists()) { | 598 | if (!(flags & KIO::Overwrite) && destination.exists()) { | ||
695 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | 599 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | ||
696 | return; | 600 | return; | ||
697 | } | 601 | } | ||
698 | 602 | | |||
699 | QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 603 | const QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
700 | 604 | | |||
701 | // Can't copy to root or device, needs storage | 605 | // can't copy to root or device, needs storage | ||
702 | if (srcItems.size() < 2) { | 606 | if (srcItems.size() < 2) { | ||
703 | error(ERR_UNSUPPORTED_ACTION, src.path()); | 607 | error(ERR_UNSUPPORTED_ACTION, src.path()); | ||
704 | return; | 608 | return; | ||
705 | } | 609 | } | ||
706 | 610 | | |||
707 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(src.path()); | 611 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(srcItems.first()); | ||
708 | if (!pair.first) { | 612 | if (mtpDevice) { | ||
709 | error(ERR_COULD_NOT_READ, src.path()); | 613 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(srcItems.at(1)); | ||
614 | if (storage) { | ||||
615 | | ||||
616 | QFile destFile(dest.path()); | ||||
617 | if (!destFile.open(QIODevice::Truncate | QIODevice::WriteOnly)) { | ||||
618 | error(KIO::ERR_WRITE_ACCESS_DENIED, dest.path()); | ||||
710 | return; | 619 | return; | ||
711 | } | 620 | } | ||
712 | 621 | | |||
713 | LIBMTP_mtpdevice_t *device = pair.second; | 622 | const KMTPFile source = storage->getFileMetadata(convertPath(src.path())); | ||
714 | LIBMTP_file_t *source = (LIBMTP_file_t *) pair.first; | 623 | if (!source.isValid()) { | ||
715 | if (source->filetype == LIBMTP_FILETYPE_FOLDER) { | 624 | error(KIO::ERR_DOES_NOT_EXIST, src.path()); | ||
716 | error(ERR_IS_DIRECTORY, urlDirectory(src)); | | |||
717 | return; | 625 | return; | ||
718 | } | 626 | } | ||
719 | 627 | | |||
720 | qCDebug(LOG_KIO_MTP) << "Getting file" << source->filename << urlFileName(dest) << source->filesize; | 628 | totalSize(source.filesize()); | ||
721 | | ||||
722 | totalSize(source->filesize); | | |||
723 | 629 | | |||
724 | int ret = LIBMTP_Get_File_To_File(device, source->item_id, dest.path().toUtf8().data(), (LIBMTP_progressfunc_t) &dataProgress, this); | 630 | QDBusUnixFileDescriptor descriptor(destFile.handle()); | ||
725 | if (ret != 0) { | 631 | int result = storage->getFileToFileDescriptor(descriptor, convertPath(src.path())); | ||
726 | error(KIO::ERR_COULD_NOT_WRITE, urlFileName(dest)); | 632 | if (result) { | ||
727 | LIBMTP_Dump_Errorstack(device); | 633 | error(KIO::ERR_CANNOT_READ, urlFileName(src)); | ||
728 | LIBMTP_Clear_Errorstack(device); | | |||
729 | return; | 634 | return; | ||
730 | } | 635 | } | ||
731 | 636 | | |||
732 | struct utimbuf *times = (utimbuf *) malloc(sizeof(utimbuf)); | 637 | result = waitForCopyOperation(storage); | ||
733 | times->actime = QDateTime::currentDateTime().toTime_t(); | 638 | processedSize(quint64(source.filesize())); | ||
734 | times->modtime = source->modificationdate; | 639 | destFile.close(); | ||
735 | 640 | | |||
736 | int result = utime(dest.path().toUtf8().data(), times); | 641 | if (result) { | ||
642 | error(KIO::ERR_CANNOT_READ, urlFileName(src)); | ||||
643 | return; | ||||
644 | } | ||||
737 | 645 | | |||
738 | free(times); | 646 | // set correct modification time | ||
647 | struct ::utimbuf times; | ||||
648 | times.actime = QDateTime::currentDateTime().toTime_t(); | ||||
649 | times.modtime = source.modificationdate(); | ||||
739 | 650 | | |||
740 | qCDebug(LOG_KIO_MTP) << "Sent file"; | 651 | ::utime(dest.path().toUtf8().data(), ×); | ||
741 | 652 | | |||
653 | qCDebug(LOG_KIO_MTP) << "Received file"; | ||||
654 | } | ||||
655 | } | ||||
742 | } | 656 | } | ||
743 | finished(); | 657 | finished(); | ||
744 | } | 658 | } | ||
745 | 659 | | |||
746 | void MTPSlave::mkdir(const QUrl &url, int) | 660 | void MTPSlave::mkdir(const QUrl &url, int) | ||
747 | { | 661 | { | ||
748 | int check = checkUrl(url); | 662 | const int check = checkUrl(url); | ||
749 | switch (check) { | 663 | switch (check) { | ||
750 | case 0: | 664 | case 0: | ||
751 | break; | 665 | break; | ||
752 | default: | 666 | default: | ||
753 | error(ERR_MALFORMED_URL, url.path()); | 667 | error(ERR_MALFORMED_URL, url.path()); | ||
754 | return; | 668 | return; | ||
755 | } | 669 | } | ||
756 | 670 | | |||
757 | qCDebug(LOG_KIO_MTP) << url.path(); | 671 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
758 | 672 | if (pathItems.size() > 2) { | |||
759 | QStringList pathItems = url.path().split(QLatin1Char('/') , QString::SkipEmptyParts); | 673 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
760 | int pathDepth = pathItems.size(); | 674 | if (mtpDevice) { | ||
761 | 675 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | |||
762 | if (pathItems.size() > 2 && !getPath(url.path()).first) { | 676 | if (storage) { | ||
763 | char *dirName = strdup(pathItems.takeLast().toUtf8().data()); | 677 | // TODO: folder already exists | ||
764 | 678 | const quint32 itemId = storage->createFolder(convertPath(url.path())); | |||
765 | LIBMTP_mtpdevice_t *device; | 679 | if (itemId) { | ||
766 | LIBMTP_file_t *file; | | |||
767 | LIBMTP_devicestorage_t *storage; | | |||
768 | int ret = 0; | | |||
769 | | ||||
770 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(urlDirectory(url)); | | |||
771 | | ||||
772 | if (pathDepth == 3) { | | |||
773 | //the folder need to be created straight to a storage device | | |||
774 | storage = (LIBMTP_devicestorage_t *) pair.first; | | |||
775 | device = pair.second; | | |||
776 | ret = LIBMTP_Create_Folder(device, dirName, 0xFFFFFFFF, storage->id); | | |||
777 | } else if (pair.first) { | | |||
778 | file = (LIBMTP_file_t *) pair.first; | | |||
779 | device = pair.second; | | |||
780 | | ||||
781 | if (file && file->filetype == LIBMTP_FILETYPE_FOLDER) { | | |||
782 | qCDebug(LOG_KIO_MTP) << "Found parent" << file->item_id << file->filename; | | |||
783 | qCDebug(LOG_KIO_MTP) << "Attempting to create folder" << dirName; | | |||
784 | | ||||
785 | ret = LIBMTP_Create_Folder(device, dirName, file->item_id, file->storage_id); | | |||
786 | | ||||
787 | } | | |||
788 | } | | |||
789 | if (ret != 0) { | | |||
790 | fileCache->addPath(url.path(), ret); | | |||
791 | finished(); | 680 | finished(); | ||
792 | return; | 681 | return; | ||
793 | } else { | | |||
794 | LIBMTP_Dump_Errorstack(device); | | |||
795 | LIBMTP_Clear_Errorstack(device); | | |||
796 | } | 682 | } | ||
797 | } else { | | |||
798 | error(ERR_DIR_ALREADY_EXIST, url.path()); | | |||
799 | return; | | |||
800 | } | 683 | } | ||
801 | 684 | } | |||
802 | error(ERR_COULD_NOT_MKDIR, url.path()); | 685 | } | ||
686 | error(ERR_CANNOT_MKDIR, url.path()); | ||||
803 | } | 687 | } | ||
804 | 688 | | |||
805 | void MTPSlave::del(const QUrl &url, bool) | 689 | void MTPSlave::del(const QUrl &url, bool) | ||
806 | { | 690 | { | ||
807 | int check = checkUrl(url); | 691 | const int check = checkUrl(url); | ||
808 | switch (check) { | 692 | switch (check) { | ||
809 | case 0: | 693 | case 0: | ||
810 | break; | 694 | break; | ||
811 | default: | 695 | default: | ||
812 | error(ERR_MALFORMED_URL, url.path()); | 696 | error(ERR_MALFORMED_URL, url.path()); | ||
813 | return; | 697 | return; | ||
814 | } | 698 | } | ||
815 | 699 | | |||
816 | qCDebug(LOG_KIO_MTP) << url.path(); | 700 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
817 | 701 | if (pathItems.size() >= 2) { | |||
818 | QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 702 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
819 | 703 | if (mtpDevice) { | |||
820 | if (pathItems.size() < 2) { | 704 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | ||
821 | error(ERR_CANNOT_DELETE, url.path()); | 705 | if (storage) { | ||
706 | const int result = storage->deleteObject(convertPath(url.path())); | ||||
707 | if (!result) { | ||||
708 | finished(); | ||||
822 | return; | 709 | return; | ||
823 | } | 710 | } | ||
824 | | ||||
825 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(url.path()); | | |||
826 | | ||||
827 | LIBMTP_file_t *file = (LIBMTP_file_t *) pair.first; | | |||
828 | | ||||
829 | int ret = LIBMTP_Delete_Object(pair.second, file->item_id); | | |||
830 | | ||||
831 | LIBMTP_destroy_file_t (file); | | |||
832 | | ||||
833 | if (ret != 0) { | | |||
834 | error(ERR_CANNOT_DELETE, url.path()); | | |||
835 | return; | | |||
836 | } | 711 | } | ||
837 | 712 | } | |||
838 | fileCache->removePath(url.path()); | 713 | } | ||
839 | finished(); | 714 | error(ERR_CANNOT_DELETE, url.path()); | ||
840 | } | 715 | } | ||
841 | 716 | | |||
842 | void MTPSlave::rename(const QUrl &src, const QUrl &dest, JobFlags flags) | 717 | void MTPSlave::rename(const QUrl &src, const QUrl &dest, JobFlags flags) | ||
843 | { | 718 | { | ||
844 | int check = checkUrl(src); | 719 | int check = checkUrl(src); | ||
845 | switch (check) { | 720 | switch (check) { | ||
846 | case 0: | 721 | case 0: | ||
847 | break; | 722 | break; | ||
848 | default: | 723 | default: | ||
849 | error(ERR_MALFORMED_URL, src.path()); | 724 | error(ERR_MALFORMED_URL, src.path()); | ||
850 | return; | 725 | return; | ||
851 | } | 726 | } | ||
852 | check = checkUrl(dest); | 727 | check = checkUrl(dest); | ||
853 | switch (check) { | 728 | switch (check) { | ||
854 | case 0: | 729 | case 0: | ||
855 | break; | 730 | break; | ||
856 | default: | 731 | default: | ||
857 | error(ERR_MALFORMED_URL, dest.path()); | 732 | error(ERR_MALFORMED_URL, dest.path()); | ||
858 | return; | 733 | return; | ||
859 | } | 734 | } | ||
860 | 735 | | |||
861 | qCDebug(LOG_KIO_MTP) << src.path(); | 736 | if (src.scheme() != QLatin1String("mtp")) { | ||
862 | 737 | // Kate: when editing files directly on the device and the user wants to save the changes, | |||
863 | QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | 738 | // Kate tries to move the file from /tmp/xxx to the MTP device which is not supported. | ||
864 | QPair<void *, LIBMTP_mtpdevice_t *> pair = getPath(src.path()); | 739 | // The ERR_UNSUPPORTED_ACTION error tells Kate to copy the file instead of moving. | ||
740 | error(ERR_UNSUPPORTED_ACTION, src.path()); | ||||
741 | return; | ||||
742 | } | ||||
865 | 743 | | |||
866 | if (pair.first) { | 744 | const QStringList srcItems = src.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
867 | // Rename Device | 745 | KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(srcItems.first()); | ||
746 | if (mtpDevice) { | ||||
747 | // rename Device | ||||
868 | if (srcItems.size() == 1) { | 748 | if (srcItems.size() == 1) { | ||
869 | LIBMTP_Set_Friendlyname(pair.second, urlFileName(dest).toUtf8().data()); | 749 | const int result = mtpDevice->setFriendlyName(urlFileName(dest)); | ||
750 | if (!result) { | ||||
751 | finished(); | ||||
752 | return; | ||||
753 | } | ||||
870 | } | 754 | } | ||
871 | // Rename Storage | 755 | // rename Storage | ||
872 | else if (srcItems.size() == 2) { | 756 | else if (srcItems.size() == 2) { | ||
873 | error(ERR_CANNOT_RENAME, src.path()); | 757 | error(ERR_CANNOT_RENAME, src.path()); | ||
874 | return; | 758 | return; | ||
875 | } else { | 759 | } else { | ||
876 | LIBMTP_file_t *destination = (LIBMTP_file_t *) getPath(dest.path()).first; | 760 | // rename file or folder | ||
877 | LIBMTP_file_t *source = (LIBMTP_file_t *) pair.first; | 761 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(srcItems.at(1)); | ||
878 | 762 | if (storage) { | |||
879 | if (!(flags & KIO::Overwrite) && destination) { | 763 | | ||
880 | if (destination->filetype == LIBMTP_FILETYPE_FOLDER) { | 764 | // check if the file already exists on the device | ||
881 | error(ERR_DIR_ALREADY_EXIST, dest.path()); | 765 | const QString destinationPath = convertPath(dest.path()); | ||
766 | const KMTPFile destinationFile = storage->getFileMetadata(destinationPath); | ||||
767 | if (destinationFile.isValid()) { | ||||
768 | if (flags & KIO::Overwrite) { | ||||
769 | // delete existing file on the device | ||||
770 | const int result = storage->deleteObject(destinationPath); | ||||
771 | if (result) { | ||||
772 | error(ERR_CANNOT_DELETE, dest.path()); | ||||
773 | return; | ||||
774 | } | ||||
882 | } else { | 775 | } else { | ||
883 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | 776 | error(ERR_FILE_ALREADY_EXIST, dest.path()); | ||
884 | } | | |||
885 | return; | 777 | return; | ||
886 | } | 778 | } | ||
779 | } | ||||
887 | 780 | | |||
888 | int ret = LIBMTP_Set_File_Name(pair.second, source, urlFileName(dest).toUtf8().data()); | 781 | const int result = storage->setFileName(convertPath(src.path()), dest.fileName()); | ||
889 | 782 | if (!result) { | |||
890 | if (ret != 0) { | 783 | finished(); | ||
891 | error(ERR_CANNOT_RENAME, src.path()); | | |||
892 | return; | 784 | return; | ||
893 | } else { | | |||
894 | fileCache->addPath(dest.path(), source->item_id); | | |||
895 | fileCache->removePath(src.path()); | | |||
896 | } | 785 | } | ||
897 | | ||||
898 | LIBMTP_destroy_file_t (source); | | |||
899 | } | 786 | } | ||
900 | | ||||
901 | finished(); | | |||
902 | } else { | | |||
903 | error(ERR_DOES_NOT_EXIST, src.path()); | | |||
904 | } | 787 | } | ||
905 | } | 788 | } | ||
789 | error(ERR_CANNOT_RENAME, src.path()); | ||||
790 | } | ||||
906 | 791 | | |||
907 | void MTPSlave::virtual_hook(int id, void *data) | 792 | void MTPSlave::virtual_hook(int id, void *data) | ||
908 | { | 793 | { | ||
909 | switch(id) { | 794 | switch(id) { | ||
910 | case SlaveBase::GetFileSystemFreeSpace: { | 795 | case SlaveBase::GetFileSystemFreeSpace: { | ||
911 | QUrl *url = static_cast<QUrl *>(data); | 796 | QUrl *url = static_cast<QUrl *>(data); | ||
912 | fileSystemFreeSpace(*url); | 797 | fileSystemFreeSpace(*url); | ||
913 | } break; | 798 | } break; | ||
Show All 16 Lines | 805 | { | |||
930 | case 2: | 815 | case 2: | ||
931 | error(ERR_DOES_NOT_EXIST, url.toDisplayString()); | 816 | error(ERR_DOES_NOT_EXIST, url.toDisplayString()); | ||
932 | return; | 817 | return; | ||
933 | default: | 818 | default: | ||
934 | error(ERR_MALFORMED_URL, url.toDisplayString()); | 819 | error(ERR_MALFORMED_URL, url.toDisplayString()); | ||
935 | return; | 820 | return; | ||
936 | } | 821 | } | ||
937 | 822 | | |||
938 | const auto path = url.path(); | 823 | const QStringList pathItems = url.path().split(QLatin1Char('/'), QString::SkipEmptyParts); | ||
939 | 824 | | |||
940 | const auto storagePath = path.section(QLatin1Char('/'), 0, 2, QString::SectionIncludeLeadingSep); | 825 | // Storage | ||
941 | if (storagePath.count(QLatin1Char('/')) != 2) { // /{device}/{storage} | 826 | if (pathItems.size() > 1) { | ||
942 | error(KIO::ERR_COULD_NOT_STAT, url.toDisplayString()); | 827 | const KMTPDeviceInterface *mtpDevice = m_kmtpDaemon.deviceFromName(pathItems.first()); | ||
828 | if (mtpDevice) { | ||||
829 | const KMTPStorageInterface *storage = mtpDevice->storageFromDescription(pathItems.at(1)); | ||||
830 | if (storage) { | ||||
831 | setMetaData(QStringLiteral("total"), QString::number(storage->maxCapacity())); | ||||
832 | setMetaData(QStringLiteral("available"), QString::number(storage->freeSpaceInBytes())); | ||||
833 | finished(); | ||||
943 | return; | 834 | return; | ||
944 | } | 835 | } | ||
945 | 836 | } | |||
946 | const auto pair = getPath(storagePath); | 837 | } | ||
947 | auto storage = (LIBMTP_devicestorage_t *)pair.first; | | |||
948 | if (!storage) { | | |||
949 | error(KIO::ERR_COULD_NOT_STAT, url.toDisplayString()); | 838 | error(KIO::ERR_COULD_NOT_STAT, url.toDisplayString()); | ||
950 | return; | | |||
951 | } | 839 | } | ||
952 | 840 | | |||
953 | setMetaData(QStringLiteral("total"), QString::number(storage->MaxCapacity)); | 841 | int MTPSlave::waitForCopyOperation(const KMTPStorageInterface *storage) | ||
954 | setMetaData(QStringLiteral("available"), QString::number(storage->FreeSpaceInBytes)); | 842 | { | ||
955 | 843 | QEventLoop loop; | |||
956 | finished(); | 844 | connect(storage, &KMTPStorageInterface::copyProgress, this, [this] (qulonglong sent, qulonglong total) { | ||
845 | Q_UNUSED(total) | ||||
846 | processedSize(sent); | ||||
847 | }); | ||||
848 | | ||||
849 | // any chance to 'miss' the copyFinished signal and dead lock the slave? | ||||
850 | connect(storage, &KMTPStorageInterface::copyFinished, &loop, &QEventLoop::exit); | ||||
851 | return loop.exec(); | ||||
957 | } | 852 | } | ||
958 | 853 | | |||
959 | 854 | |
Don't use Q_FUNC_INFO in code.
Instead, set up your environment with %{function} in QT_MESSAGE_PATTERN.
For instance, mine says
'%{time h:mm:ss.zzz} %{appname}(%{pid}) %{if-category}%{category}: %{endif}%{if-debug}%{function}%{endif}%{if-warning}%{backtrace depth=3}%{endif}%{if-critical}%{backtrace depth=3}%{endif}%{if-fatal}%{backtrace depth=3}%{endif} %{message}'
See https://woboq.com/blog/nice-debug-output-with-qt.html for more info.