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