~michihenning/storage-framework/no-boost-with-remote-client

« back to all changes in this revision

Viewing changes to src/qt/local_client/ItemImpl.cpp

  • Committer: Michi Henning
  • Date: 2016-06-21 09:00:56 UTC
  • mfrom: (10.1.34 add_tests)
  • Revision ID: michi.henning@canonical.com-20160621090056-89bbdmj5yq3o5v0s
Merged add_tests branch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#include <unity/storage/qt/client/internal/ItemImpl.h>
2
2
 
 
3
#include <unity/storage/qt/client/Account.h>
3
4
#include <unity/storage/qt/client/Exceptions.h>
4
 
#include <unity/storage/qt/client/Root.h>
 
5
#include <unity/storage/qt/client/internal/AccountImpl.h>
 
6
#include <unity/storage/qt/client/internal/FileImpl.h>
 
7
#include <unity/storage/qt/client/internal/RootImpl.h>
5
8
 
6
 
#include <boost/filesystem.hpp>
 
9
#include <QtConcurrent>
7
10
 
8
11
#include <cassert>
 
12
#include <iostream>  // TODO: remove this
9
13
 
10
14
using namespace std;
11
15
 
20
24
namespace internal
21
25
{
22
26
 
23
 
ItemImpl::ItemImpl(QString const& identity)
 
27
ItemImpl::ItemImpl(QString const& identity, ItemType type)
 
28
    : destroyed_(false)
 
29
    , type_(type)
24
30
{
25
31
    assert(!identity.isEmpty());
26
32
    auto path = boost::filesystem::canonical(identity.toStdString());
27
 
    assert(path.is_absolute());
28
33
    identity_ = QString::fromStdString(path.native());
29
34
    name_ = QString::fromStdString(path.filename().native());
 
35
    update_modified_time();
30
36
}
31
37
 
32
38
ItemImpl::~ItemImpl() = default;
60
66
    {
61
67
        return r.get();
62
68
    }
63
 
    throw DestroyedException();  // TODO
64
 
}
65
 
 
66
 
QFuture<QVariantMap> ItemImpl::get_metadata() const
67
 
{
68
 
    QFutureInterface<QVariantMap> qf;
69
 
    if (destroyed_)
70
 
    {
71
 
        qf.reportException(DestroyedException());  // TODO
72
 
        return qf.future();
73
 
    }
74
 
    return QFuture<QVariantMap>();  // TODO
75
 
}
76
 
 
77
 
QFuture<QDateTime> ItemImpl::last_modified_time() const
78
 
{
79
 
    QFutureInterface<QDateTime> qf;
80
 
    if (destroyed_)
81
 
    {
82
 
        qf.reportException(DestroyedException());  // TODO
83
 
        return qf.future();
84
 
    }
85
 
 
86
 
    try
87
 
    {
88
 
        auto mtime = boost::filesystem::last_write_time(identity_.toStdString());
89
 
        QDateTime dt;
90
 
        dt.setTime_t(mtime);
91
 
        qf.reportResult(dt);
92
 
    }
93
 
    catch (std::exception const&)
94
 
    {
95
 
        qf.reportException(StorageException());  // TODO
96
 
    }
 
69
    throw RuntimeDestroyedException();
 
70
}
 
71
 
 
72
ItemType ItemImpl::type() const
 
73
{
 
74
    if (destroyed_)
 
75
    {
 
76
        throw DestroyedException();  // TODO
 
77
    }
 
78
    return type_;
 
79
}
 
80
 
 
81
QVariantMap ItemImpl::metadata() const
 
82
{
 
83
    if (destroyed_)
 
84
    {
 
85
        throw DestroyedException();  // TODO
 
86
    }
 
87
    return metadata_;
 
88
}
 
89
 
 
90
QDateTime ItemImpl::last_modified_time() const
 
91
{
 
92
    if (destroyed_)
 
93
    {
 
94
        throw DestroyedException();  // TODO
 
95
    }
 
96
 
 
97
    lock_guard<mutex> lock(mutex_);
 
98
    return modified_time_;
 
99
}
 
100
 
 
101
void ItemImpl::update_modified_time()
 
102
{
 
103
    lock_guard<mutex> lock(mutex_);
 
104
    auto mtime = boost::filesystem::last_write_time(native_identity().toStdString());
 
105
    modified_time_ = QDateTime::fromTime_t(mtime);
 
106
}
 
107
 
 
108
namespace
 
109
{
 
110
 
 
111
using namespace boost::filesystem;
 
112
 
 
113
void copy_recursively(path const& source, path const& target)
 
114
{
 
115
    auto s = status(source);
 
116
    if (is_regular_file(s))
 
117
    {
 
118
        copy_file(source, target);
 
119
        return;
 
120
    }
 
121
    else if (is_directory(s))
 
122
    {
 
123
        copy_directory(source, target);  // Poorly named in boost; this creates the target dir without recursion
 
124
        for (directory_iterator it(source); it != directory_iterator(); ++it)
 
125
        {
 
126
            path source_entry = it->path();
 
127
            path target_entry = target;
 
128
            target_entry /= source_entry.filename();
 
129
            copy_recursively(source_entry, target_entry);
 
130
        }
 
131
    }
 
132
    else
 
133
    {
 
134
        // Ignore everything that's not a directory or file.
 
135
    }
 
136
}
 
137
 
 
138
}  // namespace
 
139
 
 
140
QFuture<shared_ptr<Item>> ItemImpl::copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
 
141
{
 
142
    using namespace boost::filesystem;
 
143
 
 
144
    if (destroyed_)
 
145
    {
 
146
        QFutureInterface<shared_ptr<Item>> qf;
 
147
        qf.reportException(DestroyedException());  // TODO
 
148
        qf.reportFinished();
 
149
        return qf.future();
 
150
    }
 
151
 
 
152
    auto This = static_pointer_cast<ItemImpl>(shared_from_this());  // Keep this item alive while the lambda is alive.
 
153
    auto copy = [This, new_parent, new_name]() -> Item::SPtr
 
154
    {
 
155
        try
 
156
        {
 
157
            if (new_parent->p_->destroyed_)
 
158
            {
 
159
                throw DestroyedException();  // TODO
 
160
            }
 
161
            if (This->root()->account() != new_parent->root()->account())
 
162
            {
 
163
                // Can't do cross-account copy.
 
164
                throw StorageException();  // TODO
 
165
            }
 
166
 
 
167
            path source_path = This->native_identity().toStdString();
 
168
            path parent_path = new_parent->native_identity().toStdString();
 
169
            path target_path = parent_path;
 
170
            path sanitized_name = sanitize(new_name);
 
171
            target_path /= sanitized_name;
 
172
 
 
173
            if (This->type_ == ItemType::file)
 
174
            {
 
175
                copy_file(source_path, target_path);
 
176
                return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent->p_->root_);
 
177
            }
 
178
 
 
179
            // Allow only one recursive copy per account at a time, otherwise source and destination
 
180
            // sub-trees for recursive copies could potentially overlap, creating chaos due to iterator invalidation.
 
181
            auto root_impl = static_pointer_cast<Root>(This->root_.lock());
 
182
            if (!root_impl)
 
183
            {
 
184
                throw RuntimeDestroyedException();
 
185
            }
 
186
 
 
187
            auto guard = root_impl->account()->p_->get_copy_guard();
 
188
 
 
189
            if (exists(target_path))
 
190
            {
 
191
                throw StorageException();  // TODO
 
192
            }
 
193
 
 
194
            // For recursive copy, we create a temporary directory in lieu of target_path and recursively copy
 
195
            // everything into the temporary directory. This ensures that we don't invalidate directory iterators
 
196
            // by creating things while we are iterating, potentially getting trapped in an infinite loop.
 
197
            path tmp_path = canonical(parent_path);
 
198
            tmp_path /= unique_path(".%%%%-%%%%-%%%%-%%%%");
 
199
            create_directories(tmp_path);
 
200
            for (directory_iterator it(source_path); it != directory_iterator(); ++it)
 
201
            {
 
202
                if (tmp_path.compare(canonical(it->path())) == 0)
 
203
                {
 
204
                    continue;  // Don't recurse into the temporary directory
 
205
                }
 
206
                file_status s = it->status();
 
207
                if (is_directory(s) || is_regular_file(s))
 
208
                {
 
209
                    path source_entry = it->path();
 
210
                    path target_entry = tmp_path;
 
211
                    target_entry /= source_entry.filename();
 
212
                    copy_recursively(source_entry, target_entry);
 
213
                }
 
214
            }
 
215
            rename(tmp_path, target_path);
 
216
            return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent->p_->root_);
 
217
        }
 
218
        catch (std::exception const&)
 
219
        {
 
220
            throw StorageException();  // TODO
 
221
        }
 
222
    };
 
223
    return QtConcurrent::run(copy);
 
224
}
 
225
 
 
226
QFuture<shared_ptr<Item>> ItemImpl::move(shared_ptr<Folder> const& new_parent, QString const& new_name)
 
227
{
 
228
    if (destroyed_)
 
229
    {
 
230
        QFutureInterface<shared_ptr<Item>> qf;
 
231
        qf.reportException(DestroyedException());  // TODO
 
232
        qf.reportFinished();
 
233
        return qf.future();
 
234
    }
 
235
 
 
236
    auto This = static_pointer_cast<ItemImpl>(shared_from_this());  // Keep this item alive while the lambda is alive.
 
237
    auto move = [This, new_parent, new_name]() -> Item::SPtr
 
238
    {
 
239
        using namespace boost::filesystem;
 
240
 
 
241
        try
 
242
        {
 
243
            if (new_parent->p_->destroyed_)
 
244
            {
 
245
                throw DestroyedException();  // TODO
 
246
            }
 
247
            if (This->root()->account() != new_parent->root()->account())
 
248
            {
 
249
                // Can't do cross-account move.
 
250
                throw StorageException();  // TODO
 
251
            }
 
252
            if (This->type_ == ItemType::root)
 
253
            {
 
254
                // Can't move a root.
 
255
                throw StorageException();  // TODO
 
256
            }
 
257
 
 
258
            path target_path = new_parent->native_identity().toStdString();
 
259
            target_path /= sanitize(new_name);
 
260
            if (exists(target_path))
 
261
            {
 
262
                throw StorageException();  // TODO
 
263
            }
 
264
            rename(This->native_identity().toStdString(), target_path);
 
265
            This->destroyed_ = true;
 
266
            if (This->type_ == ItemType::folder)
 
267
            {
 
268
                return FolderImpl::make_folder(QString::fromStdString(target_path.native()), new_parent->p_->root_);
 
269
            }
 
270
            return FileImpl::make_file(QString::fromStdString(target_path.native()), new_parent->p_->root_);
 
271
        }
 
272
        catch (std::exception const&)
 
273
        {
 
274
            throw StorageException();  // TODO
 
275
        }
 
276
    };
 
277
    return QtConcurrent::run(move);
 
278
}
 
279
 
 
280
QFuture<QVector<Folder::SPtr>> ItemImpl::parents() const
 
281
{
 
282
    QFutureInterface<QVector<Folder::SPtr>> qf;
 
283
    if (destroyed_)
 
284
    {
 
285
        qf.reportException(DestroyedException());
 
286
        qf.reportFinished();
 
287
        return qf.future();
 
288
    }
 
289
 
 
290
    Root::SPtr root = root_.lock();
 
291
    if (!root)
 
292
    {
 
293
        throw RuntimeDestroyedException();
 
294
    }
 
295
 
 
296
    using namespace boost::filesystem;
 
297
 
 
298
    // We do this synchronously because we don't need to hit the file system.
 
299
    path p = native_identity().toStdString();
 
300
    QString parent_path = QString::fromStdString(p.parent_path().native());
 
301
 
 
302
    QVector<Folder::SPtr> results;
 
303
    if (parent_path != root->p_->identity_)
 
304
    {
 
305
        results.append(FolderImpl::make_folder(parent_path, root_));
 
306
    }
 
307
    else
 
308
    {
 
309
        results.append(root);
 
310
    }
 
311
    qf.reportResult(results);
 
312
    qf.reportFinished();
97
313
    return qf.future();
98
314
}
99
315
 
100
 
common::ItemType ItemImpl::type() const
 
316
QVector<QString> ItemImpl::parent_ids() const
101
317
{
102
318
    if (destroyed_)
103
319
    {
104
 
        throw DestroyedException();  // TODO
 
320
        throw DestroyedException();
105
321
    }
106
322
 
107
 
    return type_;
108
 
}
109
 
 
110
 
QFuture<shared_ptr<Item>> copy(shared_ptr<Folder> const& new_parent, QString const& new_name)
111
 
{
112
 
    return QFuture<shared_ptr<Item>>();  // TODO
113
 
}
114
 
 
115
 
QFuture<shared_ptr<Item>> move(shared_ptr<Folder> const& new_parent, QString const& new_name)
116
 
{
117
 
    return QFuture<shared_ptr<Item>>();  // TODO
 
323
    using namespace boost::filesystem;
 
324
 
 
325
    // We do this synchronously because we don't need to hit the file system.
 
326
    path p = native_identity().toStdString();
 
327
    QString parent_path = QString::fromStdString(p.parent_path().native());
 
328
 
 
329
    QVector<QString> results;
 
330
    results.append(parent_path);
 
331
    return results;
118
332
}
119
333
 
120
334
QFuture<void> ItemImpl::destroy()
121
335
{
122
 
    QFutureInterface<void> qf;
123
336
    if (destroyed_)
124
337
    {
125
 
        qf.reportException(DestroyedException());  // TODO
 
338
        QFutureInterface<void> qf;
 
339
        qf.reportException(DestroyedException());
 
340
        qf.reportFinished();
126
341
        return qf.future();
127
342
    }
128
 
    return QFuture<QString>();  // TODO
 
343
 
 
344
    auto This = shared_from_this();  // Keep this item alive while the lambda is alive.
 
345
    auto destroy = [This]()
 
346
    {
 
347
        using namespace boost::filesystem;
 
348
 
 
349
        try
 
350
        {
 
351
            remove_all(This->identity_.toStdString());
 
352
            This->destroyed_ = true;
 
353
        }
 
354
        catch (std::exception const& e)
 
355
        {
 
356
            throw StorageException();  // TODO
 
357
        }
 
358
    };
 
359
    return QtConcurrent::run(destroy);
129
360
}
130
361
 
131
362
void ItemImpl::set_root(weak_ptr<Root> p)
136
367
 
137
368
void ItemImpl::set_public_instance(weak_ptr<Item> p)
138
369
{
139
 
    assert(public_instance_.lock());
 
370
    assert(p.lock());
140
371
    public_instance_ = p;
141
372
}
142
373
 
143
 
weak_ptr<Item> ItemImpl::public_instance() const
144
 
{
145
 
    assert(public_instance_.lock());
146
 
    return public_instance_;
 
374
bool ItemImpl::operator==(ItemImpl const& other) const noexcept
 
375
{
 
376
    if (destroyed_ || other.destroyed_)
 
377
    {
 
378
        return false;
 
379
    }
 
380
    return identity_ == other.identity_;
 
381
}
 
382
 
 
383
// Throw if name contains more than one path component.
 
384
// Otherwise, return the relative path for the name.
 
385
// This is to make sure that calling, say, create_file()
 
386
// with a name such as "../../whatever" cannot lead
 
387
// outside the root.
 
388
 
 
389
boost::filesystem::path ItemImpl::sanitize(QString const& name)
 
390
{
 
391
    using namespace boost::filesystem;
 
392
 
 
393
    path p = name.toStdString();
 
394
    if (!p.parent_path().empty())
 
395
    {
 
396
        // name contains more than one component.
 
397
        throw StorageException();  // TODO.
 
398
    }
 
399
    path filename = p.filename();
 
400
    if (filename.empty() || filename == "." || filename == "..")
 
401
    {
 
402
        // Not an allowable file name.
 
403
        throw StorageException();  // TODO.
 
404
    }
 
405
    return p;
147
406
}
148
407
 
149
408
}  // namespace internal