2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2020 Mike Tzou (Chocobo1)
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
34 #include <libtorrent/bencode.hpp>
35 #include <libtorrent/entry.hpp>
37 #include <QCoreApplication>
40 #include <QFileDevice>
43 #include <QTemporaryFile>
45 #include "base/path.h"
46 #include "base/utils/fs.h"
48 Utils::IO::FileDeviceOutputIterator::FileDeviceOutputIterator(QFileDevice
&device
, const int bufferSize
)
50 , m_buffer
{std::make_shared
<QByteArray
>()}
51 , m_bufferSize
{bufferSize
}
53 m_buffer
->reserve(bufferSize
);
56 Utils::IO::FileDeviceOutputIterator::~FileDeviceOutputIterator()
58 if (m_buffer
.use_count() == 1)
60 if (m_device
->error() == QFileDevice::NoError
)
61 m_device
->write(*m_buffer
);
66 Utils::IO::FileDeviceOutputIterator
&Utils::IO::FileDeviceOutputIterator::operator=(const char c
)
69 if (m_buffer
->size() >= m_bufferSize
)
71 if (m_device
->error() == QFileDevice::NoError
)
72 m_device
->write(*m_buffer
);
78 nonstd::expected
<QByteArray
, Utils::IO::ReadError
> Utils::IO::readFile(const Path
&path
, const qint64 maxSize
, const QIODevice::OpenMode additionalMode
)
80 QFile file
{path
.data()};
81 if (!file
.open(QIODevice::ReadOnly
| additionalMode
))
83 const QString message
= QCoreApplication::translate("Utils::IO", "File open error. File: \"%1\". Error: \"%2\"")
84 .arg(file
.fileName(), file
.errorString());
85 return nonstd::make_unexpected(ReadError
{ReadError::NotExist
, message
});
88 const qint64 fileSize
= file
.size();
89 if ((maxSize
>= 0) && (fileSize
> maxSize
))
91 const QString message
= QCoreApplication::translate("Utils::IO", "File size exceeds limit. File: \"%1\". File size: %2. Size limit: %3")
92 .arg(file
.fileName(), QString::number(fileSize
), QString::number(maxSize
));
93 return nonstd::make_unexpected(ReadError
{ReadError::ExceedSize
, message
});
95 if (!std::in_range
<qsizetype
>(fileSize
))
97 const QString message
= QCoreApplication::translate("Utils::IO", "File size exceeds data size limit. File: \"%1\". File size: %2. Array limit: %3")
98 .arg(file
.fileName(), QString::number(fileSize
), QString::number(std::numeric_limits
<qsizetype
>::max()));
99 return nonstd::make_unexpected(ReadError
{ReadError::ExceedSize
, message
});
102 QByteArray ret
{static_cast<qsizetype
>(fileSize
), Qt::Uninitialized
};
103 const qint64 actualSize
= file
.read(ret
.data(), fileSize
);
107 const QString message
= QCoreApplication::translate("Utils::IO", "File read error. File: \"%1\". Error: \"%2\"")
108 .arg(file
.fileName(), file
.errorString());
109 return nonstd::make_unexpected(ReadError
{ReadError::Failed
, message
});
112 if (actualSize
< fileSize
)
114 // `QIODevice::Text` will convert CRLF to LF on-the-fly and affects return value
115 // of `qint64 QIODevice::read(char *data, qint64 maxSize)`
116 if (additionalMode
.testFlag(QIODevice::Text
))
118 ret
.truncate(actualSize
);
122 const QString message
= QCoreApplication::translate("Utils::IO", "Read size mismatch. File: \"%1\". Expected: %2. Actual: %3")
123 .arg(file
.fileName(), QString::number(fileSize
), QString::number(actualSize
));
124 return nonstd::make_unexpected(ReadError
{ReadError::SizeMismatch
, message
});
131 nonstd::expected
<void, QString
> Utils::IO::saveToFile(const Path
&path
, const QByteArray
&data
)
133 if (const Path parentPath
= path
.parentPath(); !parentPath
.isEmpty())
134 Utils::Fs::mkpath(parentPath
);
135 QSaveFile file
{path
.data()};
136 if (!file
.open(QIODevice::WriteOnly
) || (file
.write(data
) != data
.size()) || !file
.flush() || !file
.commit())
137 return nonstd::make_unexpected(file
.errorString());
141 nonstd::expected
<void, QString
> Utils::IO::saveToFile(const Path
&path
, const lt::entry
&data
)
143 QSaveFile file
{path
.data()};
144 if (!file
.open(QIODevice::WriteOnly
))
145 return nonstd::make_unexpected(file
.errorString());
147 const int bencodedDataSize
= lt::bencode(Utils::IO::FileDeviceOutputIterator
{file
}, data
);
148 if ((file
.size() != bencodedDataSize
) || !file
.flush() || !file
.commit())
149 return nonstd::make_unexpected(file
.errorString());
154 nonstd::expected
<Path
, QString
> Utils::IO::saveToTempFile(const QByteArray
&data
)
156 QTemporaryFile file
{(Utils::Fs::tempPath() / Path(u
"file_"_s
)).data()};
157 if (!file
.open() || (file
.write(data
) != data
.length()) || !file
.flush())
158 return nonstd::make_unexpected(file
.errorString());
160 file
.setAutoRemove(false);
161 return Path(file
.fileName());
164 nonstd::expected
<Path
, QString
> Utils::IO::saveToTempFile(const lt::entry
&data
)
166 QTemporaryFile file
{(Utils::Fs::tempPath() / Path(u
"file_"_s
)).data()};
168 return nonstd::make_unexpected(file
.errorString());
170 const int bencodedDataSize
= lt::bencode(Utils::IO::FileDeviceOutputIterator
{file
}, data
);
171 if ((file
.size() != bencodedDataSize
) || !file
.flush())
172 return nonstd::make_unexpected(file
.errorString());
174 file
.setAutoRemove(false);
175 return Path(file
.fileName());