1 #include <errno.h>
2 #include <cstdio>
3 #include <sqlite3.h>
4
5 #include <QDir>
6 #include <QDateTime>
7
8 #include "utils/file-utils.h"
9 #include "utils/utils.h"
10 #include "configurator.h"
11 #include "seafile-applet.h"
12 #include "rpc/rpc-client.h"
13 #include "auto-update-mgr.h"
14 #include "api/requests.h"
15 #include "repo-service.h"
16 #include "account-mgr.h"
17
18 #include "filebrowser/file-browser-requests.h"
19 #include "filebrowser/tasks.h"
20 #include "filebrowser/transfer-mgr.h"
21 #include "filebrowser/data-cache.h"
22 #include "filebrowser/data-mgr.h"
23
24 namespace {
25
26 const char *kFileCacheTopDirName = "file-cache";
27 const int kPasswordCacheExpirationMSecs = 30 * 60 * 1000;
28
29 const int kQueryAsyncOperationProgressInterval = 1000;
30 } // namespace
31
32 /**
33 * Cache loaded dirents. But default cache expires after 1 minute.
34 */
35
SINGLETON_IMPL(DataManager)36 SINGLETON_IMPL(DataManager)
37
38 DataManager::DataManager()
39 : filecache_(FileCache::instance()),
40 dirents_cache_(DirentsCache::instance())
41 {
42 }
43
~DataManager()44 DataManager::~DataManager()
45 {
46 emit aboutToDestroy();
47 Q_FOREACH(SeafileApiRequest *req, reqs_)
48 {
49 req->deleteLater();
50 }
51 }
52
start()53 void DataManager::start()
54 {
55 account_ = seafApplet->accountManager()->currentAccount();
56
57 connect(seafApplet->accountManager(), SIGNAL(accountsChanged()),
58 this, SLOT(onAccountChanged()));
59 }
60
getDirents(const QString & repo_id,const QString & path,QList<SeafDirent> * dirents,bool * current_readonly)61 bool DataManager::getDirents(const QString& repo_id,
62 const QString& path,
63 QList<SeafDirent> *dirents,
64 bool *current_readonly)
65 {
66 DirentsCache::ReturnEntry retval = dirents_cache_->getCachedDirents(repo_id, path);
67 QList<SeafDirent> *l = retval.second;
68 if (l != NULL) {
69 dirents->append(*l);
70 *current_readonly = retval.first;
71 return true;
72 } else {
73 return false;
74 }
75 }
76
getDirentsFromServer(const QString & repo_id,const QString & path)77 void DataManager::getDirentsFromServer(const QString& repo_id,
78 const QString& path)
79 {
80 GetDirentsRequest *get_dirents_req = new GetDirentsRequest(account_, repo_id, path);
81 connect(get_dirents_req, SIGNAL(success(bool, const QList<SeafDirent>&, const QString&)),
82 this, SLOT(onGetDirentsSuccess(bool, const QList<SeafDirent>&, const QString&)));
83 connect(get_dirents_req, SIGNAL(failed(const ApiError&, const QString&)),
84 this, SIGNAL(getDirentsFailed(const ApiError&, const QString&)));
85 get_dirents_req->send();
86 reqs_.push_back(get_dirents_req);
87 }
88
createDirectory(const QString & repo_id,const QString & path)89 void DataManager::createDirectory(const QString &repo_id,
90 const QString &path)
91 {
92 CreateDirectoryRequest *req = new CreateDirectoryRequest(account_, repo_id, path);
93 connect(req, SIGNAL(success(const QString&)),
94 SLOT(onCreateDirectorySuccess(const QString&)));
95
96 connect(req, SIGNAL(failed(const ApiError&)),
97 SIGNAL(createDirectoryFailed(const ApiError&)));
98
99 req->send();
100 reqs_.push_back(req);
101 }
102
lockFile(const QString & repo_id,const QString & path,bool lock)103 void DataManager::lockFile(const QString &repo_id,
104 const QString &path,
105 bool lock)
106 {
107 LockFileRequest *req = new LockFileRequest(account_, repo_id, path, lock);
108 connect(req, SIGNAL(success(const QString&)),
109 SLOT(onLockFileSuccess(const QString&)));
110
111 connect(req, SIGNAL(failed(const ApiError&)),
112 SIGNAL(lockFileFailed(const ApiError&)));
113
114 req->send();
115 reqs_.push_back(req);
116 }
117
renameDirent(const QString & repo_id,const QString & path,const QString & new_path,bool is_file)118 void DataManager::renameDirent(const QString &repo_id,
119 const QString &path,
120 const QString &new_path,
121 bool is_file)
122 {
123 RenameDirentRequest *req = new RenameDirentRequest(account_, repo_id, path,
124 new_path, is_file);
125 connect(req, SIGNAL(success(const QString&)),
126 SLOT(onRenameDirentSuccess(const QString&)));
127
128 connect(req, SIGNAL(failed(const ApiError&)),
129 SIGNAL(renameDirentFailed(const ApiError&)));
130 req->send();
131 reqs_.push_back(req);
132 }
133
134
removeDirent(const QString & repo_id,const QString & path,bool is_file)135 void DataManager::removeDirent(const QString &repo_id,
136 const QString &path,
137 bool is_file)
138 {
139 RemoveDirentRequest *req = new RemoveDirentRequest(account_, repo_id, path,
140 is_file);
141 connect(req, SIGNAL(success(const QString&)),
142 SLOT(onRemoveDirentSuccess(const QString&)));
143
144 connect(req, SIGNAL(failed(const ApiError&)),
145 SIGNAL(removeDirentFailed(const ApiError&)));
146 req->send();
147 reqs_.push_back(req);
148 }
149
removeDirents(const QString & repo_id,const QString & parent_path,const QStringList & filenames)150 void DataManager::removeDirents(const QString &repo_id,
151 const QString &parent_path,
152 const QStringList &filenames)
153 {
154 RemoveDirentsRequest *req = new RemoveDirentsRequest(account_, repo_id, parent_path,
155 filenames);
156 connect(req, SIGNAL(success(const QString&)),
157 SLOT(onRemoveDirentsSuccess(const QString&)));
158
159 connect(req, SIGNAL(failed(const ApiError&)),
160 SIGNAL(removeDirentsFailed(const ApiError&)));
161 req->send();
162 reqs_.push_back(req);
163 }
164
shareDirent(const QString & repo_id,const QString & path,bool is_file)165 void DataManager::shareDirent(const QString &repo_id,
166 const QString &path,
167 bool is_file)
168 {
169 GetSharedLinkRequest *req = new GetSharedLinkRequest(account_, repo_id,
170 path, is_file);
171 connect(req, SIGNAL(success(const QString&, const QString&)),
172 SIGNAL(shareDirentSuccess(const QString&, const QString&)));
173
174 connect(req, SIGNAL(failed(const ApiError&)),
175 SIGNAL(shareDirentFailed(const ApiError&)));
176 reqs_.push_back(req);
177 req->send();
178 }
179
copyDirents(const QString & repo_id,const QString & dir_path,const QMap<QString,int> & dict_file_names,const QString & dst_repo_id,const QString & dst_dir_path)180 void DataManager::copyDirents(const QString &repo_id,
181 const QString &dir_path,
182 const QMap<QString, int> &dict_file_names,
183 const QString &dst_repo_id,
184 const QString &dst_dir_path)
185 {
186 if(copy_move_in_progress_) {
187 seafApplet->warningBox(tr("Another copy or move operation is in progress. Please wait until it finishes."));
188 return;
189 }
190
191 copy_move_in_progress_ = true;
192
193 repo_id_ = repo_id;
194 dir_path_ = dir_path;
195 dst_repo_id_ = dst_repo_id;
196 dst_dir_path_ = dst_dir_path;
197 src_dirents_ = dict_file_names;
198
199 query_async_opera_progress_timer_ = new QTimer(this);
200 query_async_opera_progress_timer_->setInterval(kQueryAsyncOperationProgressInterval);
201 connect(query_async_opera_progress_timer_, SIGNAL(timeout()),
202 this, SLOT(slotQueryAsyncCopyOperaProgress()));
203
204 if (repo_id == dst_repo_id) {
205 QStringList file_names;
206 for(const QString &file_name : dict_file_names.keys()) {
207 file_names.push_back(file_name);
208 }
209
210 CopyMultipleFilesRequest *req =
211 new CopyMultipleFilesRequest(account_,
212 repo_id,
213 dir_path,
214 file_names,
215 dst_repo_id,
216 dst_dir_path);
217
218 connect(req, SIGNAL(success(const QString&)),
219 SLOT(onCopyDirentsSuccess(const QString&)));
220
221 connect(req, SIGNAL(failed(const ApiError&)),
222 SIGNAL(copyDirentsFailed(const ApiError&)));
223 reqs_.push_back(req);
224 req->send();
225
226 } else {
227 // First to invoke ssync api v2.1 if async api return 404 ,then invoke v2.0 async api
228 AsyncCopyMultipleItemsRequest *req =
229 new AsyncCopyMultipleItemsRequest(account_,
230 repo_id,
231 dir_path,
232 dict_file_names,
233 dst_repo_id,
234 dst_dir_path);
235
236 connect(req, SIGNAL(success(const QString&)),
237 SLOT(slotAsyncCopyMutipleItemsSuccess(const QString&)));
238 connect(req, SIGNAL(failed(const ApiError&)),
239 SLOT(slotAsyncCopyMutipleItemsFailed(const ApiError&)));
240 reqs_.push_back(req);
241 req->send();
242 }
243 }
244
slotAsyncCopyMutipleItemsSuccess(const QString & task_id)245 void DataManager::slotAsyncCopyMutipleItemsSuccess(const QString& task_id)
246 {
247 task_id_ = task_id;
248 is_batch_operation_ = true;
249 query_async_opera_progress_timer_->start();
250 }
251
slotAsyncCopyMutipleItemsFailed(const ApiError & error)252 void DataManager::slotAsyncCopyMutipleItemsFailed(const ApiError& error)
253 {
254 if (error.httpErrorCode() == 404) {
255 qWarning("new async copy API is not available on server, use old async copy single item API");
256 asyncCopyOneItemApi();
257 is_batch_operation_ = false;
258 } else {
259 emit copyDirentsFailed(error);
260 copy_move_in_progress_ = false;
261 }
262 }
263
asyncCopyOneItemApi()264 void DataManager::asyncCopyOneItemApi()
265 {
266 qWarning("use old sync copy task api");
267 if(src_dirents_.isEmpty()) {
268 copy_move_in_progress_ = false;
269 return ;
270 }
271
272 const QString& filename = src_dirents_.firstKey();
273 async_copy_one_item_req_.reset(new AsyncCopyAndMoveOneItemRequest(account_,
274 repo_id_,
275 dir_path_,
276 filename,
277 dst_repo_id_,
278 dst_dir_path_,
279 "copy",
280 src_dirents_[filename] == 1 ? "DIR" : "FILE"));
281
282 connect(async_copy_one_item_req_.data(), SIGNAL(success(const QString&)),
283 this, SLOT(slotAsyncCopyOneItemSuccess(const QString&)));
284
285 connect(async_copy_one_item_req_.data(), SIGNAL(failed( const ApiError&)),
286 SLOT(slotAsyncCopyOneItemFailed( const ApiError&)));
287
288 async_copy_one_item_req_->send();
289 src_dirents_.remove(filename);
290 }
slotAsyncCopyOneItemSuccess(const QString & task_id)291 void DataManager::slotAsyncCopyOneItemSuccess(const QString& task_id)
292 {
293 task_id_ = task_id;
294 query_async_opera_progress_timer_->start();
295 }
296
slotAsyncCopyOneItemFailed(const ApiError & error)297 void DataManager::slotAsyncCopyOneItemFailed(const ApiError& error)
298 {
299 query_async_opera_progress_timer_->stop();
300 emit copyDirentsFailed(error);
301 copy_move_in_progress_ = false;
302 }
303
slotQueryAsyncCopyOperaProgress()304 void DataManager::slotQueryAsyncCopyOperaProgress()
305 {
306 QueryAsyncOperationProgress* query_async_opera_progress_req = new QueryAsyncOperationProgress(account_,
307 task_id_);
308 connect(query_async_opera_progress_req, SIGNAL(success()),
309 this, SLOT(slotQueryAsyncCopyOperationProgressSuccess()));
310
311 connect(query_async_opera_progress_req, SIGNAL(failed(const ApiError&)),
312 this, SLOT(slotQueryAsyncCopyOperationProgressFailed(const ApiError&)));
313
314 query_async_opera_progress_req->send();
315 }
316
slotQueryAsyncCopyOperationProgressSuccess()317 void DataManager::slotQueryAsyncCopyOperationProgressSuccess()
318 {
319 QueryAsyncOperationProgress * req = qobject_cast<QueryAsyncOperationProgress *>(sender());
320 req->deleteLater();
321 query_async_opera_progress_timer_->stop();
322 onCopyDirentsSuccess(dst_repo_id_);
323 if (!is_batch_operation_) {
324 asyncCopyOneItemApi();
325 } else {
326 copy_move_in_progress_ = false;
327 }
328 }
329
slotQueryAsyncCopyOperationProgressFailed(const ApiError & error)330 void DataManager::slotQueryAsyncCopyOperationProgressFailed(const ApiError& error)
331 {
332 QueryAsyncOperationProgress * req = qobject_cast<QueryAsyncOperationProgress *>(sender());
333 req->deleteLater();
334 query_async_opera_progress_timer_->stop();
335 emit copyDirentsFailed(error);
336 copy_move_in_progress_ = false;
337 }
338
moveDirents(const QString & repo_id,const QString & dir_path,const QMap<QString,int> & dict_file_names,const QString & dst_repo_id,const QString & dst_dir_path)339 void DataManager::moveDirents(const QString &repo_id,
340 const QString &dir_path,
341 const QMap<QString, int> &dict_file_names,
342 const QString &dst_repo_id,
343 const QString &dst_dir_path)
344 {
345
346 if(copy_move_in_progress_) {
347 seafApplet->warningBox(tr("Another copy or move operation is in progress. Please wait until it finishes."));
348 return;
349 }
350
351 copy_move_in_progress_ = true;
352
353 repo_id_ = repo_id;
354 dir_path_ = dir_path;
355 dst_repo_id_ = dst_repo_id;
356 dst_dir_path_ = dst_dir_path;
357 src_dirents_ = dict_file_names;
358
359
360 query_async_opera_progress_timer_ = new QTimer(this);
361 query_async_opera_progress_timer_->setInterval(kQueryAsyncOperationProgressInterval);
362 connect(query_async_opera_progress_timer_, SIGNAL(timeout()),
363 this, SLOT(slotQueryAsyncMoveOperaProgress()));
364
365 if (repo_id == dst_repo_id) {
366 QStringList file_names;
367 for(const QString &file_name : dict_file_names.keys()) {
368 file_names.push_back(file_name);
369 }
370
371 MoveMultipleFilesRequest *req =
372 new MoveMultipleFilesRequest(account_,
373 repo_id, dir_path, file_names,
374 dst_repo_id,
375 dst_dir_path);
376
377 connect(req, SIGNAL(success(const QString&)),
378 SLOT(onMoveDirentsSuccess(const QString&)));
379
380 connect(req, SIGNAL(failed(const ApiError&)),
381 SIGNAL(moveDirentsFailed(const ApiError&)));
382 reqs_.push_back(req);
383 req->send();
384 } else {
385 // First to invoke ssync api v2.1 if async api return 404 ,then invoke v2.0 async api
386 AsyncMoveMultipleItemsRequest *req =
387 new AsyncMoveMultipleItemsRequest(account_,
388 repo_id,
389 dir_path,
390 dict_file_names,
391 dst_repo_id,
392 dst_dir_path);
393
394 connect(req, SIGNAL(success(const QString&)),
395 SLOT(slotAsyncMoveMutipleItemsSuccess(const QString&)));
396 connect(req, SIGNAL(failed(const ApiError&)),
397 SLOT(slotAsyncMoveMutipleItemsFailed(const ApiError&)));
398 reqs_.push_back(req);
399 req->send();
400 }
401 }
402
slotAsyncMoveMutipleItemsSuccess(const QString & task_id)403 void DataManager::slotAsyncMoveMutipleItemsSuccess(const QString& task_id)
404 {
405 task_id_ = task_id;
406 is_batch_operation_ = true;
407 query_async_opera_progress_timer_->start();
408 }
409
slotAsyncMoveMutipleItemsFailed(const ApiError & error)410 void DataManager::slotAsyncMoveMutipleItemsFailed(const ApiError& error)
411 {
412 if (error.httpErrorCode() == 404) {
413 qWarning("new async move API is not available on server, use old async move single item API");
414 asyncMoveOneItemApi();
415 is_batch_operation_ = false;
416 } else {
417 emit moveDirentsFailed(error);
418 copy_move_in_progress_ = false;
419 }
420 }
421
asyncMoveOneItemApi()422 void DataManager::asyncMoveOneItemApi()
423 {
424 qWarning("use async move one item API");
425 if(src_dirents_.isEmpty()) {
426 copy_move_in_progress_ = false;
427 return ;
428 }
429
430 const QString& filename = src_dirents_.firstKey();
431 async_copy_one_item_req_.reset(new AsyncCopyAndMoveOneItemRequest(account_,
432 repo_id_,
433 dir_path_,
434 filename,
435 dst_repo_id_,
436 dst_dir_path_,
437 "move",
438 src_dirents_[filename] == 1 ? "DIR" : "FILE"));
439
440 connect(async_copy_one_item_req_.data(), SIGNAL(success(const QString&)),
441 this, SLOT(slotAsyncMoveOneItemSuccess(const QString&)));
442
443 connect(async_copy_one_item_req_.data(), SIGNAL(failed( const ApiError&)),
444 SLOT(slotAsyncMoveOneItemFailed( const ApiError&)));
445
446 async_copy_one_item_req_->send();
447 src_dirents_.remove(filename);
448 }
449
slotAsyncMoveOneItemSuccess(const QString & task_id)450 void DataManager::slotAsyncMoveOneItemSuccess(const QString& task_id)
451 {
452 task_id_ = task_id;
453 query_async_opera_progress_timer_->start();
454 }
455
slotAsyncMoveOneItemFailed(const ApiError & error)456 void DataManager::slotAsyncMoveOneItemFailed(const ApiError& error)
457 {
458 query_async_opera_progress_timer_->stop();
459 emit moveDirentsFailed(error);
460 copy_move_in_progress_ = false;
461 }
462
slotQueryAsyncMoveOperaProgress()463 void DataManager::slotQueryAsyncMoveOperaProgress()
464 {
465 QueryAsyncOperationProgress* query_async_opera_progress_req = new QueryAsyncOperationProgress(account_,
466 task_id_);
467 connect(query_async_opera_progress_req, SIGNAL(success()),
468 this, SLOT(slotQueryAsyncMoveOperationProgressSuccess()));
469
470 connect(query_async_opera_progress_req, SIGNAL(failed(const ApiError&)),
471 this, SLOT(slotQueryAsyncMoveOperationProgressFailed(const ApiError&)));
472
473 query_async_opera_progress_req->send();
474 }
475
slotQueryAsyncMoveOperationProgressSuccess()476 void DataManager::slotQueryAsyncMoveOperationProgressSuccess()
477 {
478 QueryAsyncOperationProgress * req = qobject_cast<QueryAsyncOperationProgress *>(sender());
479 req->deleteLater();
480 query_async_opera_progress_timer_->stop();
481 dirents_cache_->expireCachedDirents(repo_id_, dir_path_);
482 moveDirentsSuccess(dst_repo_id_);
483 if (!is_batch_operation_) {
484 asyncMoveOneItemApi();
485 } else {
486 copy_move_in_progress_ = false;
487 }
488 }
489
slotQueryAsyncMoveOperationProgressFailed(const ApiError & error)490 void DataManager::slotQueryAsyncMoveOperationProgressFailed(const ApiError& error)
491 {
492 QueryAsyncOperationProgress * req = qobject_cast<QueryAsyncOperationProgress *>(sender());
493 req->deleteLater();
494 query_async_opera_progress_timer_->stop();
495 emit moveDirentsFailed(error);
496 copy_move_in_progress_ = false;
497 }
498
499
onGetDirentsSuccess(bool current_readonly,const QList<SeafDirent> & dirents,const QString & repo_id)500 void DataManager::onGetDirentsSuccess(bool current_readonly, const QList<SeafDirent> &dirents, const QString& repo_id)
501 {
502 GetDirentsRequest *get_dirents_req = qobject_cast<GetDirentsRequest *>(sender());
503 dirents_cache_->saveCachedDirents(get_dirents_req->repoId(),
504 get_dirents_req->path(),
505 current_readonly,
506 dirents);
507
508 emit getDirentsSuccess(current_readonly, dirents, repo_id);
509 }
510
onCreateDirectorySuccess(const QString & repo_id)511 void DataManager::onCreateDirectorySuccess(const QString& repo_id)
512 {
513 CreateDirectoryRequest *req = qobject_cast<CreateDirectoryRequest*>(sender());
514
515 if(req == NULL)
516 return;
517
518 removeDirentsCache(req->repoId(), req->path(), false);
519 emit createDirectorySuccess(req->path(), repo_id);
520 }
521
onLockFileSuccess(const QString & repo_id)522 void DataManager::onLockFileSuccess(const QString& repo_id)
523 {
524 LockFileRequest *req = qobject_cast<LockFileRequest *>(sender());
525 if (!req)
526 return;
527
528 removeDirentsCache(req->repoId(), req->path(), false);
529 seafApplet->rpcClient()->markFileLockState(req->repoId(), req->path(), req->lock());
530 emit lockFileSuccess(req->path(), req->lock(), repo_id);
531 }
532
onRenameDirentSuccess(const QString & repo_id)533 void DataManager::onRenameDirentSuccess(const QString& repo_id)
534 {
535 RenameDirentRequest *req = qobject_cast<RenameDirentRequest*>(sender());
536
537 if(req == NULL)
538 return;
539
540 removeDirentsCache(req->repoId(), req->path(), req->isFile());
541 emit renameDirentSuccess(req->path(), req->newName(), repo_id);
542 }
543
onRemoveDirentSuccess(const QString & repo_id)544 void DataManager::onRemoveDirentSuccess(const QString& repo_id)
545 {
546 RemoveDirentRequest *req = qobject_cast<RemoveDirentRequest*>(sender());
547
548 if(req == NULL)
549 return;
550
551 removeDirentsCache(req->repoId(), req->path(), req->isFile());
552 emit removeDirentSuccess(req->path(), repo_id);
553 }
554
onRemoveDirentsSuccess(const QString & repo_id)555 void DataManager::onRemoveDirentsSuccess(const QString& repo_id)
556 {
557 RemoveDirentsRequest *req = qobject_cast<RemoveDirentsRequest*>(sender());
558
559 if(req == NULL)
560 return;
561
562 removeDirentsCache(req->repoId(), req->parentPath(), false);
563 emit removeDirentsSuccess(req->parentPath(), req->filenames(), repo_id);
564 }
565
onCopyDirentsSuccess(const QString & dst_repo_id)566 void DataManager::onCopyDirentsSuccess(const QString& dst_repo_id)
567 {
568 emit copyDirentsSuccess(dst_repo_id);
569 }
570
onMoveDirentsSuccess(const QString & dst_repo_id)571 void DataManager::onMoveDirentsSuccess(const QString& dst_repo_id)
572 {
573 MoveMultipleFilesRequest *req = qobject_cast<MoveMultipleFilesRequest*>(sender());
574 dirents_cache_->expireCachedDirents(req->srcRepoId(), req->srcPath());
575
576 emit moveDirentsSuccess(dst_repo_id);
577 }
578
removeDirentsCache(const QString & repo_id,const QString & path,bool is_file)579 void DataManager::removeDirentsCache(const QString& repo_id,
580 const QString& path,
581 bool is_file)
582 {
583 // expire its parent's cache
584 dirents_cache_->expireCachedDirents(repo_id, ::getParentPath(path));
585 // if the object is a folder, then expire its self cache
586 if (!is_file)
587 dirents_cache_->expireCachedDirents(repo_id, path);
588 }
589
590
getLocalCachedFile(const QString & repo_id,const QString & fpath,const QString & file_id)591 QString DataManager::getLocalCachedFile(const QString& repo_id,
592 const QString& fpath,
593 const QString& file_id)
594 {
595 QString local_file_path = getLocalCacheFilePath(repo_id, fpath);
596 QFileInfo finfo(local_file_path);
597 if (!finfo.exists()) {
598 qWarning ("No cache file for %s\n", toCStr(fpath));
599 return "";
600 }
601
602 FileCache::CacheEntry entry;
603 if (!filecache_->getCacheEntry(repo_id, fpath, &entry)) {
604 qWarning ("No cache db entry for %s\n", toCStr(fpath));
605 return "";
606 }
607
608 if (entry.file_id == file_id) {
609 qWarning ("cache file id matched for %s: %s\n", toCStr(fpath), toCStr(file_id));
610 return local_file_path;
611 } else {
612 // The file is updated on server
613 qint64 mtime = finfo.lastModified().toMSecsSinceEpoch();
614 bool use_cached = false;
615 if (mtime != entry.seafile_mtime) {
616 qWarning(
617 "cache file is updated locally (mtime changed from %lld to "
618 "%lld), use it: %s\n",
619 entry.seafile_mtime,
620 mtime,
621 toCStr(fpath));
622 use_cached = true;
623 } else if (finfo.size() != entry.seafile_size) {
624 qWarning(
625 "cache file is updated locally (size changed from %lld to "
626 "%lld), use it: %s\n",
627 entry.seafile_size,
628 finfo.size(),
629 toCStr(fpath));
630 use_cached = true;
631 }
632
633 if (use_cached) {
634 // If the file is also updated locally, open it directly
635 return local_file_path;
636 } else {
637 qWarning ("cache file is outdated, download newer version: %s\n", toCStr(fpath));
638 // Otherwise the newer version would be downloaded
639 return "";
640 }
641 }
642 }
643
createDownloadTask(const QString & repo_id,const QString & path)644 FileDownloadTask* DataManager::createDownloadTask(const QString& repo_id,
645 const QString& path)
646 {
647 QString local_path = getLocalCacheFilePath(repo_id, path);
648 FileDownloadTask* task = TransferManager::instance()->addDownloadTask(
649 account_, repo_id, path, local_path);
650 connect(task, SIGNAL(finished(bool)),
651 this, SLOT(onFileDownloadFinished(bool)), Qt::UniqueConnection);
652 setupTaskCleanup(task);
653
654 return task;
655 }
656
createSaveAsTask(const QString & repo_id,const QString & path,const QString & local_path)657 FileDownloadTask* DataManager::createSaveAsTask(const QString& repo_id,
658 const QString& path,
659 const QString& local_path)
660 {
661 FileDownloadTask* task = TransferManager::instance()->addDownloadTask(
662 account_, repo_id, path, local_path, true);
663 setupTaskCleanup(task);
664
665 return task;
666 }
667
onFileDownloadFinished(bool success)668 void DataManager::onFileDownloadFinished(bool success)
669 {
670 qDebug("[onFileDownloadFinished function] invoked,is success %s",
671 success ? "true" : "false");
672 FileDownloadTask *task = qobject_cast<FileDownloadTask *>(sender());
673 if (task == NULL)
674 return;
675 if (success) {
676 filecache_->saveCachedFileId(task->repoId(),
677 task->path(),
678 account_.getSignature(),
679 task->fileId(),
680 task->localFilePath());
681 // TODO we don't want to watch readonly files
682 AutoUpdateManager::instance()->watchCachedFile(account_, task->repoId(), task->path());
683 }
684 }
685
createUploadTask(const QString & repo_id,const QString & parent_dir,const QString & local_path,const QString & name,const bool overwrite)686 FileUploadTask* DataManager::createUploadTask(const QString& repo_id,
687 const QString& parent_dir,
688 const QString& local_path,
689 const QString& name,
690 const bool overwrite)
691 {
692 FileUploadTask *task;
693 if (QFileInfo(local_path).isFile())
694 task = new FileUploadTask(account_, repo_id, parent_dir,
695 local_path, name, !overwrite);
696 else
697 task = new FileUploadDirectoryTask(account_, repo_id, parent_dir,
698 local_path, name);
699 connect(task, SIGNAL(finished(bool)),
700 this, SLOT(onFileUploadFinished(bool)));
701 setupTaskCleanup(task);
702
703 return task;
704 }
705
setupTaskCleanup(FileNetworkTask * task)706 void DataManager::setupTaskCleanup(FileNetworkTask *task)
707 {
708 connect(this, SIGNAL(aboutToDestroy()),
709 task, SLOT(cancel()));
710 }
711
createUploadMultipleTask(const QString & repo_id,const QString & parent_dir,const QString & local_path,const QStringList & names,const bool overwrite)712 FileUploadTask* DataManager::createUploadMultipleTask(const QString& repo_id,
713 const QString& parent_dir,
714 const QString& local_path,
715 const QStringList& names,
716 const bool overwrite)
717 {
718 FileUploadTask *task = new FileUploadMultipleTask(account_, repo_id, parent_dir,
719 local_path, names, !overwrite);
720
721 connect(task, SIGNAL(finished(bool)),
722 this, SLOT(onFileUploadFinished(bool)));
723 setupTaskCleanup(task);
724
725 return task;
726 }
727
onFileUploadFinished(bool success)728 void DataManager::onFileUploadFinished(bool success)
729 {
730 FileUploadTask *task = qobject_cast<FileUploadTask *>(sender());
731 if (task == NULL)
732 return;
733 if (success) {
734 //expire the parent path
735 dirents_cache_->expireCachedDirents(task->repoId(),
736 task->path());
737 }
738 }
739
getLocalCacheFilePath(const QString & repo_id,const QString & path)740 QString DataManager::getLocalCacheFilePath(const QString& repo_id,
741 const QString& path)
742 {
743 QString seafdir = seafApplet->configurator()->seafileDir();
744 return ::pathJoin(seafdir, kFileCacheTopDirName, repo_id, path);
745 }
746
747 QHash<QString, std::pair<qint64, QString> > DataManager::passwords_cache_;
748
isRepoPasswordSet(const QString & repo_id) const749 bool DataManager::isRepoPasswordSet(const QString& repo_id) const
750 {
751 if (!passwords_cache_.contains(repo_id)) {
752 return false;
753 }
754 qint64 expiration_time = passwords_cache_[repo_id].first;
755 qint64 now = QDateTime::currentMSecsSinceEpoch();
756 return now < expiration_time;
757 }
758
setRepoPasswordSet(const QString & repo_id,const QString & password)759 void DataManager::setRepoPasswordSet(const QString& repo_id, const QString& password)
760 {
761 passwords_cache_[repo_id] =
762 std::pair<qint64, QString>(QDateTime::currentMSecsSinceEpoch() + kPasswordCacheExpirationMSecs, password);
763 }
764
getRepoCacheFolder(const QString & repo_id) const765 QString DataManager::getRepoCacheFolder(const QString& repo_id) const
766 {
767 QString seafdir = seafApplet->configurator()->seafileDir();
768 return ::pathJoin(seafdir, kFileCacheTopDirName, repo_id);
769 }
770
createSubrepo(const QString & name,const QString & repo_id,const QString & path)771 void DataManager::createSubrepo(const QString &name, const QString& repo_id, const QString &path)
772 {
773 old_repo_id_ = repo_id;
774 const QString password = repoPassword(repo_id);
775 const QString fixed_path = path.left(path.endsWith('/') && path.size() != 1 ? path.size() -1 : path.size());
776 create_subrepo_req_.reset(new CreateSubrepoRequest(account_, name, repo_id, fixed_path, password));
777 // we might have cleaned this value when do a new request while old request is still there
778 get_repo_req_.reset(NULL);
779 create_subrepo_parent_repo_id_ = repo_id;
780 create_subrepo_parent_path_ = fixed_path;
781
782 connect(create_subrepo_req_.data(), SIGNAL(success(const QString&)),
783 this, SLOT(onCreateSubrepoSuccess(const QString&)));
784 connect(create_subrepo_req_.data(), SIGNAL(failed(const ApiError&)),
785 this, SIGNAL(createSubrepoFailed(const ApiError&)));
786
787 create_subrepo_req_->send();
788 }
789
onCreateSubrepoSuccess(const QString & new_repoid)790 void DataManager::onCreateSubrepoSuccess(const QString& new_repoid)
791 {
792 // if we have it, we are lucky
793 ServerRepo repo = RepoService::instance()->getRepo(new_repoid);
794 if (repo.isValid()) {
795 ServerRepo fixed_repo = repo;
796 fixed_repo.parent_path = create_subrepo_parent_path_;
797 fixed_repo.parent_repo_id = create_subrepo_parent_repo_id_;
798 emit createSubrepoSuccess(fixed_repo, old_repo_id_);
799 return;
800 }
801
802 // if not found, we need call get repo (list repo is not reliable here)
803 get_repo_req_.reset(new GetRepoRequest(account_, new_repoid));
804
805 // connect
806 connect(get_repo_req_.data(), SIGNAL(success(const ServerRepo&)),
807 this, SLOT(onCreateSubrepoRefreshSuccess(const ServerRepo&)));
808 connect(get_repo_req_.data(), SIGNAL(failed(const ApiError&)),
809 this, SIGNAL(createSubrepoFailed(const ApiError&)));
810
811 get_repo_req_->send();
812 }
813
onCreateSubrepoRefreshSuccess(const ServerRepo & repo)814 void DataManager::onCreateSubrepoRefreshSuccess(const ServerRepo& repo)
815 {
816 // okay, all green
817 if (repo.isValid()) {
818 ServerRepo fixed_repo = repo;
819 fixed_repo.parent_path = create_subrepo_parent_path_;
820 fixed_repo.parent_repo_id = create_subrepo_parent_repo_id_;
821 emit createSubrepoSuccess(fixed_repo, old_repo_id_);
822 return;
823 }
824
825 // it is not expected
826 emit createSubrepoFailed(ApiError::fromHttpError(500));
827 }
828
onAccountChanged()829 void DataManager::onAccountChanged()
830 {
831 account_ = seafApplet->accountManager()->currentAccount();
832 }
833