1 /*********
2 *
3 * This file is part of BibleTime's source code, http://www.bibletime.info/.
4 *
5 * Copyright 1999-2016 by the BibleTime developers.
6 * The BibleTime source code is licensed under the GNU General Public License version 2.0.
7 *
8 **********/
9 
10 #include "btinstallthread.h"
11 
12 #include <memory>
13 #include <QDebug>
14 #include <QDir>
15 #include <QString>
16 #include <QThread>
17 #include "btinstallbackend.h"
18 #include "managers/cswordbackend.h"
19 
20 // Sword includes:
21 #include <filemgr.h>
22 
23 
24 namespace {
25 
runMkdir(QDir & dir,const QString & dirName)26 inline bool runMkdir(QDir & dir, const QString & dirName) {
27     if (!dir.exists(dirName)) {
28         if (!dir.mkpath(dirName)) {
29             qDebug() << "failed to make directory" << dirName;
30             return false;
31         }
32         qDebug() << "made directory" << dirName;
33     }
34     return true;
35 }
36 
37 }
38 
run()39 void BtInstallThread::run() {
40     // Make sure target/mods.d and target/modules exist
41     /// \todo move this to some common precondition
42     QDir dir(m_destination);
43     if (!runMkdir(dir, m_destination)
44         || !runMkdir(dir, "modules")
45         || !runMkdir(dir, "mods.d"))
46     {
47         return;
48     }
49 
50     for (m_currentModuleIndex = 0;
51          m_currentModuleIndex < m_modules.size();
52          ++m_currentModuleIndex)
53     {
54         installModule();
55         if (m_stopRequested.load(std::memory_order_relaxed))
56             break;
57     }
58 }
59 
installModule()60 void BtInstallThread::installModule() {
61     emit preparingInstall(m_currentModuleIndex);
62 
63     const CSwordModuleInfo * const module = m_modules.at(m_currentModuleIndex);
64 
65     QVariant vModuleName = module->property("installSourceName");
66     QString moduleName = vModuleName.toString();
67     sword::InstallSource installSource = BtInstallBackend::source(moduleName);
68     std::unique_ptr<CSwordBackend> backendForSource(BtInstallBackend::backend(installSource));
69 
70     // Check whether it's an update. If yes, remove existing module first:
71     /// \todo silently removing without undo if the user cancels the update is WRONG!!!
72     if (!removeModule() && m_stopRequested.load(std::memory_order_relaxed))
73         return;
74 
75     // manager for the destination path
76     sword::SWMgr lMgr(m_destination.toLatin1());
77     if (BtInstallBackend::isRemote(installSource)) {
78         int status = m_iMgr.installModule(&lMgr,
79                                           nullptr,
80                                           module->name().toLatin1(),
81                                           &installSource);
82         if (status == 0) {
83             emit statusUpdated(m_currentModuleIndex, 100);
84         } else {
85             qWarning() << "Error with install: " << status
86                        << "module:" << module->name();
87         }
88         emit installCompleted(m_currentModuleIndex, status == 0);
89     } else { // Local source
90         int status = m_iMgr.installModule(&lMgr,
91                                           installSource.directory.c_str(),
92                                           module->name().toLatin1());
93         if (status == 0) {
94             emit statusUpdated(m_currentModuleIndex, 100);
95         } else if (status != -1) {
96             qWarning() << "Error with install: " << status
97                        << "module:" << module->name();
98         }
99         emit installCompleted(m_currentModuleIndex, status == 0);
100     }
101 }
102 
slotManagerStatusUpdated(int totalProgress,int)103 void BtInstallThread::slotManagerStatusUpdated(int totalProgress, int /*fileProgress*/) {
104     emit statusUpdated(m_currentModuleIndex, totalProgress);
105 }
106 
slotDownloadStarted()107 void BtInstallThread::slotDownloadStarted() {
108     emit downloadStarted(m_currentModuleIndex);
109 }
110 
removeModule()111 bool BtInstallThread::removeModule() {
112     CSwordModuleInfo * const installedModule = m_modules.at(m_currentModuleIndex);
113     CSwordModuleInfo * m = CSwordBackend::instance()->findModuleByName(installedModule->name());
114     if (!m)
115         m = BtInstallBackend::backend(BtInstallBackend::source(m_destination.toLatin1()))->findModuleByName(installedModule->name());
116 
117     if (!m)
118         return false;
119 
120     qDebug() << "Removing module" << installedModule->name();
121     QString prefixPath = m->config(CSwordModuleInfo::AbsoluteDataPath) + "/";
122     QString dataPath = m->config(CSwordModuleInfo::DataPath);
123     if (dataPath.left(2) == "./")
124         dataPath = dataPath.mid(2);
125 
126     if (prefixPath.contains(dataPath)) {
127         prefixPath.remove(prefixPath.indexOf(dataPath), dataPath.length());
128     } else {
129         prefixPath = QString::fromLatin1(CSwordBackend::instance()->prefixPath);
130     }
131 
132     sword::SWMgr mgr(prefixPath.toLatin1());
133     BtInstallMgr().removeModule(&mgr, m->name().toLatin1());
134     return true;
135 }
136