1 /*
2 * LibrePCB - Professional EDA for everyone!
3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4 * https://librepcb.org/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "librarydownload.h"
24
25 #include <librepcb/common/fileio/fileutils.h>
26 #include <librepcb/common/network/filedownload.h>
27 #include <librepcb/library/library.h>
28
29 #include <QtCore>
30
31 /*******************************************************************************
32 * Namespace
33 ******************************************************************************/
34 namespace librepcb {
35 namespace library {
36 namespace manager {
37
38 /*******************************************************************************
39 * Constructors / Destructor
40 ******************************************************************************/
41
LibraryDownload(const QUrl & urlToZip,const FilePath & destDir)42 LibraryDownload::LibraryDownload(const QUrl& urlToZip,
43 const FilePath& destDir) noexcept
44 : QObject(nullptr),
45 mDestDir(destDir),
46 mTempDestDir(destDir.toStr() % ".tmp"),
47 mTempZipFile(mDestDir.toStr() % ".zip") {
48 mFileDownload.reset(new FileDownload(urlToZip, mTempZipFile));
49 mFileDownload->setZipExtractionDirectory(mTempDestDir);
50 connect(mFileDownload.data(), &FileDownload::progressState, this,
51 &LibraryDownload::progressState, Qt::QueuedConnection);
52 connect(mFileDownload.data(), &FileDownload::progressPercent, this,
53 &LibraryDownload::progressPercent, Qt::QueuedConnection);
54 connect(mFileDownload.data(), &FileDownload::errored, this,
55 &LibraryDownload::downloadErrored, Qt::QueuedConnection);
56 connect(mFileDownload.data(), &FileDownload::aborted, this,
57 &LibraryDownload::downloadAborted, Qt::QueuedConnection);
58 connect(mFileDownload.data(), &FileDownload::succeeded, this,
59 &LibraryDownload::downloadSucceeded, Qt::QueuedConnection);
60 connect(this, &LibraryDownload::abortRequested, mFileDownload.data(),
61 &FileDownload::abort, Qt::QueuedConnection);
62 }
63
~LibraryDownload()64 LibraryDownload::~LibraryDownload() noexcept {
65 abort();
66 }
67
68 /*******************************************************************************
69 * Setters
70 ******************************************************************************/
71
setExpectedZipFileSize(qint64 bytes)72 void LibraryDownload::setExpectedZipFileSize(qint64 bytes) noexcept {
73 if (mFileDownload) {
74 mFileDownload->setExpectedReplyContentSize(bytes);
75 } else {
76 qCritical() << "Calling this method after start() is not allowed!";
77 }
78 }
79
setExpectedChecksum(QCryptographicHash::Algorithm algorithm,const QByteArray & checksum)80 void LibraryDownload::setExpectedChecksum(
81 QCryptographicHash::Algorithm algorithm,
82 const QByteArray& checksum) noexcept {
83 if (mFileDownload) {
84 mFileDownload->setExpectedChecksum(algorithm, checksum);
85 } else {
86 qCritical() << "Calling this method after start() is not allowed!";
87 }
88 }
89
90 /*******************************************************************************
91 * Public Slots
92 ******************************************************************************/
93
start()94 void LibraryDownload::start() noexcept {
95 if (!mFileDownload) {
96 qCritical() << "Calling this method multiple times is not allowed!";
97 return;
98 }
99
100 // Delete the temporary destination directory if it already exists. It might
101 // be left there after a failed or aborted download attempt.
102 if (mTempDestDir.isExistingDir()) {
103 try {
104 FileUtils::removeDirRecursively(mTempDestDir);
105 } catch (const Exception& e) {
106 emit finished(false, e.getMsg());
107 return;
108 }
109 }
110
111 // Delete the temporary ZIP file if it already exists. It might
112 // be left there after a failed or aborted download attempt.
113 if (mTempZipFile.isExistingFile()) {
114 try {
115 FileUtils::removeFile(mTempZipFile);
116 } catch (const Exception& e) {
117 emit finished(false, e.getMsg());
118 return;
119 }
120 }
121
122 // Release ownership of the FileDownload object because it will be deleted by
123 // itself after the download finished!
124 mFileDownload.take()->start();
125 }
126
abort()127 void LibraryDownload::abort() noexcept {
128 emit abortRequested();
129 }
130
131 /*******************************************************************************
132 * Private Methods
133 ******************************************************************************/
134
downloadErrored(const QString & errMsg)135 void LibraryDownload::downloadErrored(const QString& errMsg) noexcept {
136 emit LibraryDownload::finished(false, errMsg);
137 }
138
downloadAborted()139 void LibraryDownload::downloadAborted() noexcept {
140 emit LibraryDownload::finished(false, QString());
141 }
142
downloadSucceeded()143 void LibraryDownload::downloadSucceeded() noexcept {
144 // check if directory contains a library
145 FilePath libDir = getPathToLibDir();
146 if (!libDir.isValid()) {
147 try {
148 FileUtils::removeDirRecursively(mDestDir);
149 } catch (...) {
150 } // clean up
151 emit finished(
152 false,
153 tr("The downloaded ZIP file does not contain a LibrePCB library."));
154 return;
155 }
156
157 // back-up existing library (if any)
158 FilePath backupDir = FilePath(mDestDir.toStr() % ".backup");
159 try {
160 FileUtils::removeDirRecursively(backupDir); // can throw
161 if (mDestDir.isExistingDir())
162 FileUtils::move(mDestDir, backupDir); // can throw
163 } catch (const Exception& e) {
164 try {
165 FileUtils::removeDirRecursively(backupDir);
166 } catch (...) {
167 }
168 emit finished(false, e.getMsg());
169 return;
170 }
171
172 // move downloaded directory to destination
173 try {
174 FileUtils::move(libDir, mDestDir); // can throw
175 } catch (const Exception& e) {
176 try {
177 FileUtils::removeDirRecursively(mDestDir);
178 FileUtils::move(backupDir, mDestDir);
179 FileUtils::removeDirRecursively(mTempDestDir);
180 } catch (...) {
181 }
182 emit finished(false, e.getMsg());
183 return;
184 }
185
186 // clean up
187 try {
188 FileUtils::removeDirRecursively(mTempDestDir); // can throw
189 FileUtils::removeDirRecursively(backupDir); // can throw
190 } catch (...) {
191 }
192
193 emit finished(true, QString());
194 }
195
getPathToLibDir()196 FilePath LibraryDownload::getPathToLibDir() noexcept {
197 if (library::Library::isValidElementDirectory<library::Library>(
198 mTempDestDir)) {
199 return mTempDestDir;
200 }
201
202 QStringList subdirs =
203 QDir(mTempDestDir.toStr()).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
204 if (subdirs.count() != 1) {
205 return FilePath();
206 }
207
208 FilePath subdir = mTempDestDir.getPathTo(subdirs.first());
209 if (library::Library::isValidElementDirectory<library::Library>(subdir)) {
210 return subdir;
211 } else {
212 return FilePath();
213 }
214 }
215
216 /*******************************************************************************
217 * End of File
218 ******************************************************************************/
219
220 } // namespace manager
221 } // namespace library
222 } // namespace librepcb
223