21
21
#include <unity/storage/internal/safe_strerror.h>
22
22
#include <unity/storage/qt/client/Account.h>
23
23
#include <unity/storage/qt/client/Exceptions.h>
24
#include <unity/storage/qt/client/internal/make_future.h>
25
24
#include <unity/storage/qt/client/internal/local_client/AccountImpl.h>
26
25
#include <unity/storage/qt/client/internal/local_client/FileImpl.h>
27
26
#include <unity/storage/qt/client/internal/local_client/RootImpl.h>
28
#include <unity/storage/qt/client/internal/local_client/tmpfile-prefix.h>
27
#include <unity/storage/qt/client/internal/local_client/storage_exception.h>
28
#include <unity/storage/qt/client/internal/local_client/tmpfile_prefix.h>
29
#include <unity/storage/qt/client/internal/make_future.h>
30
31
#include <boost/algorithm/string/predicate.hpp>
31
32
#pragma GCC diagnostic push
65
65
ItemImpl::~ItemImpl() = default;
67
QString ItemImpl::name() const
69
lock_guard<mutex> guard(mutex_);
73
throw deleted_ex("Item::name()");
78
67
QString ItemImpl::etag() const
80
lock_guard<mutex> guard(mutex_);
69
lock_guard<decltype(mutex_)> guard(mutex_);
84
throw deleted_ex("Item::etag()");
71
throw_if_destroyed("Item::etag()");
89
75
QVariantMap ItemImpl::metadata() const
91
lock_guard<mutex> guard(mutex_);
77
lock_guard<decltype(mutex_)> guard(mutex_);
95
throw deleted_ex("Item::metadata()");
79
throw_if_destroyed("Item::metadata()");
100
83
QDateTime ItemImpl::last_modified_time() const
102
lock_guard<mutex> guard(mutex_);
85
lock_guard<decltype(mutex_)> guard(mutex_);
106
throw deleted_ex("Item::last_modified_time()");
87
throw_if_destroyed("Item::last_modified_time()");
108
88
return modified_time_;
114
using namespace boost::filesystem;
116
void copy_recursively(path const& source, path const& target)
118
auto s = status(source);
119
if (is_regular_file(s))
121
copy_file(source, target);
124
else if (is_directory(s))
126
copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
127
for (directory_iterator it(source); it != directory_iterator(); ++it)
129
path source_entry = it->path();
130
path target_entry = target;
131
target_entry /= source_entry.filename();
132
copy_recursively(source_entry, target_entry);
137
// Ignore everything that's not a directory or file.
143
91
QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
95
QString msg = "Item::copy(): new_parent cannot be nullptr";
96
return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
98
auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
100
lock(mutex_, new_parent_impl->mutex_);
101
lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
102
lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
106
throw_if_destroyed("Item::copy()");
107
new_parent_impl->throw_if_destroyed("Item::copy()");
109
catch (StorageException const& e)
111
return internal::make_exceptional_future<shared_ptr<Item>>(e);
145
114
auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
146
115
auto copy = [This, new_parent, new_name]() -> Item::SPtr
148
117
auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
150
119
lock(This->mutex_, new_parent_impl->mutex_);
151
lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);
152
lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);
154
if (This->deleted_ || new_parent_impl->deleted_)
156
throw This->deleted_ex("Item::copy");
159
if (This->root()->account() != new_parent->root()->account())
120
lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
121
lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
123
This->throw_if_destroyed("Item::copy()");
124
new_parent_impl->throw_if_destroyed("Item::copy()");
126
// TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
127
if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
161
129
// Can't do cross-account copy.
162
QString msg = QString("Item::copy(): Source (") + This->name_ + ") and target ("
130
QString msg = QString("Item::copy(): source (") + This->name_ + ") and target ("
163
131
+ new_name + ") must belong to the same account";
164
132
throw LogicException(msg);
175
143
target_path /= sanitized_name;
176
144
if (is_reserved_path(target_path))
178
QString msg = "Item::copy(): names beginning with " + QString(TMPFILE_PREFIX) + " are reserved";
146
QString msg = "Item::copy(): names beginning with \"" + QString(TMPFILE_PREFIX) + "\" are reserved";
179
147
throw InvalidArgumentException(msg);
150
if (exists(target_path))
152
QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
153
throw ExistsException(msg, This->identity_, This->name_);
182
156
if (This->type_ == ItemType::file)
184
158
copy_file(source_path, target_path);
185
159
return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent_impl->root_);
188
if (exists(target_path))
190
QString msg = "Item::copy(): item with name \"" + new_name + "\" exists already";
191
throw ExistsException(msg, This->identity_, This->name_);
194
162
// For recursive copy, we create a temporary directory in lieu of target_path and recursively copy
195
163
// everything into the temporary directory. This ensures that we don't invalidate directory iterators
196
164
// by creating things while we are iterating, potentially getting trapped in an infinite loop.
209
177
path source_entry = it->path();
210
178
path target_entry = tmp_path;
211
179
target_entry /= source_entry.filename();
212
copy_recursively(source_entry, target_entry);
180
ItemImpl::copy_recursively(source_entry, target_entry);
215
183
rename(tmp_path, target_path);
216
184
return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent_impl->root_);
218
catch (std::exception const& e)
186
catch (std::exception const&)
220
throw ResourceException(QString("Item::copy(): ") + e.what());
188
throw_storage_exception("Item::copy()", current_exception());
223
191
return QtConcurrent::run(copy);
226
194
QFuture<shared_ptr<Item>> ItemImpl::move(shared_ptr<Folder> const& new_parent, QString const& new_name)
198
QString msg = "Item::move(): new_parent cannot be nullptr";
199
return internal::make_exceptional_future<shared_ptr<Item>>(InvalidArgumentException(msg));
201
auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
203
lock(mutex_, new_parent_impl->mutex_);
204
lock_guard<decltype(mutex_)> this_guard(mutex_, std::adopt_lock);
205
lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
209
throw_if_destroyed("Item::move()");
210
new_parent_impl->throw_if_destroyed("Item::move()");
212
catch (StorageException const& e)
214
return internal::make_exceptional_future<shared_ptr<Item>>(e);
228
217
auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
229
218
auto move = [This, new_parent, new_name]() -> Item::SPtr
231
220
auto new_parent_impl = dynamic_pointer_cast<FolderImpl>(new_parent->p_);
233
222
lock(This->mutex_, new_parent_impl->mutex_);
234
lock_guard<mutex> this_guard(This->mutex_, std::adopt_lock);
235
lock_guard<mutex> other_guard(new_parent_impl->mutex_, adopt_lock);
237
if (This->deleted_ || new_parent_impl->deleted_)
239
throw This->deleted_ex("Item::move");
242
if (This->root()->account() != new_parent->root()->account())
223
lock_guard<decltype(mutex_)> this_guard(This->mutex_, std::adopt_lock);
224
lock_guard<decltype(mutex_)> other_guard(new_parent_impl->mutex_, adopt_lock);
226
This->throw_if_destroyed("Item::move()");
227
new_parent_impl->throw_if_destroyed("Item::move()");
229
// TODO: This needs to deeply compare account identity because the client may have refreshed the accounts list.
230
if (This->root()->account() != new_parent->root()->account()) // Throws if account or runtime were destroyed.
244
232
// Can't do cross-account move.
245
QString msg = QString("Item::move(): Source (") + This->name_ + ") and target ("
233
QString msg = QString("Item::move(): source (") + This->name_ + ") and target ("
246
234
+ new_name + ") must belong to the same account";
247
235
throw LogicException(msg);
249
237
if (This->type_ == ItemType::root)
251
239
// Can't move a root.
252
throw LogicException("Item::move(): Cannot move root folder");
240
throw LogicException("Item::move(): cannot move root folder");
287
275
QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const
289
lock_guard<mutex> guard(mutex_);
277
lock_guard<decltype(mutex_)> guard(mutex_);
291
QFutureInterface<QVector<Folder::SPtr>> qf;
294
return make_exceptional_future<QVector<Folder::SPtr>>(deleted_ex("Item::parents()"));
281
throw_if_destroyed("Item::parents()");
297
Root::SPtr root = root_.lock();
283
catch (StorageException const& e)
300
return make_exceptional_future<QVector<Folder::SPtr>>(RuntimeDestroyedException("Item::parents()"));
285
return internal::make_exceptional_future<QVector<Folder::SPtr>>(e);
303
288
using namespace boost::filesystem;
341
324
QFuture<void> ItemImpl::delete_item()
326
lock_guard<decltype(mutex_)> guard(mutex_);
330
throw_if_destroyed("Item::delete_item()");
332
catch (StorageException const& e)
334
return internal::make_exceptional_future(e);
343
337
auto This = dynamic_pointer_cast<ItemImpl>(shared_from_this()); // Keep this item alive while the lambda is alive.
344
338
auto destroy = [This]()
346
lock_guard<mutex> guard(This->mutex_);
350
throw This->deleted_ex("Item::delete_item()");
340
lock_guard<decltype(mutex_)> guard(This->mutex_);
342
This->throw_if_destroyed("Item::delete_item()");
355
345
boost::filesystem::remove_all(This->native_identity().toStdString());
356
346
This->deleted_ = true;
358
catch (std::exception const& e)
348
catch (std::exception const&)
360
throw ResourceException(QString("Item::delete_item(): ") + e.what());
350
throw_storage_exception(QString("Item::delete_item()"), current_exception());
363
353
return QtConcurrent::run(destroy);
465
456
return boost::starts_with(filename, TMPFILE_PREFIX);
468
DeletedException ItemImpl::deleted_ex(QString const& method) const noexcept
459
void ItemImpl::copy_recursively(boost::filesystem::path const& source, boost::filesystem::path const& target)
470
QString msg = method + ": " + identity_ + " was deleted previously";
471
return DeletedException(msg, identity_, name_);
461
using namespace boost::filesystem;
463
if (is_reserved_path(source))
465
return; // Don't copy temporary directories.
468
auto s = status(source);
469
if (is_regular_file(s))
471
copy_file(source, target);
473
else if (is_directory(s))
475
copy_directory(source, target); // Poorly named in boost; this creates the target dir without recursion
476
for (directory_iterator it(source); it != directory_iterator(); ++it)
478
path source_entry = it->path();
479
path target_entry = target;
480
target_entry /= source_entry.filename();
481
copy_recursively(source_entry, target_entry);
486
// Ignore everything that's not a directory or file.
474
490
} // namespace local_client