File indexing completed on 2024-12-22 04:16:01

0001 /*
0002  * SPDX-FileCopyrightText: 2022 Sharaf Zaman <shzam@sdf.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KisExiv2IODevice.h"
0008 
0009 #include <QDebug>
0010 #include <QFileInfo>
0011 
0012 KisExiv2IODevice::KisExiv2IODevice(QString path)
0013     : m_file(path)
0014     , m_mappedArea(nullptr)
0015 {
0016 }
0017 
0018 KisExiv2IODevice::~KisExiv2IODevice()
0019 {
0020     m_file.close();
0021 }
0022 
0023 int KisExiv2IODevice::open()
0024 {
0025     if (m_file.isOpen()) {
0026         m_file.close();
0027     }
0028 
0029     // return zero if successful
0030     return !m_file.open(QFile::ReadWrite);
0031 }
0032 
0033 int KisExiv2IODevice::close()
0034 {
0035     if (munmap() != 0) {
0036         return 1;
0037     }
0038     m_file.close();
0039     return 0;
0040 }
0041 
0042 #if EXIV2_TEST_VERSION(0,28,0)
0043 size_t KisExiv2IODevice::write(const Exiv2::byte *data, size_t wcount)
0044 #else
0045 long KisExiv2IODevice::write(const Exiv2::byte *data, long wcount)
0046 #endif
0047 {
0048     if (!m_file.isWritable()) {
0049         qWarning() << "KisExiv2IODevice: File not open for writing.";
0050         return 0;
0051     }
0052     const qint64 writeCount = m_file.write(reinterpret_cast<const char *>(data), wcount);
0053     if (writeCount > 0) {
0054         return writeCount;
0055     }
0056 
0057     return 0;
0058 }
0059 
0060 #if EXIV2_TEST_VERSION(0,28,0)
0061 size_t KisExiv2IODevice::write(Exiv2::BasicIo &src)
0062 #else
0063 long KisExiv2IODevice::write(Exiv2::BasicIo &src)
0064 #endif
0065 {
0066     if (static_cast<BasicIo *>(this) == &src) {
0067         return 0;
0068     }
0069     if (!src.isopen()) {
0070         return 0;
0071     }
0072     if (!m_file.isWritable()) {
0073         qWarning() << "KisExiv2IODevice: File not open for writing.";
0074         return 0;
0075     }
0076     Exiv2::byte buffer[4096];
0077     long readCount = 0;
0078     long totalWriteCount = 0;
0079     while ((readCount = src.read(buffer, sizeof(buffer))) != 0) {
0080         totalWriteCount += write(buffer, readCount);
0081     }
0082 
0083     return totalWriteCount;
0084 }
0085 
0086 int KisExiv2IODevice::putb(Exiv2::byte data)
0087 {
0088     if (!m_file.isWritable()) {
0089         qWarning() << "KisExiv2IODevice: File not open for writing.";
0090         return 0;
0091     }
0092     if (m_file.putChar(data)) {
0093         return data;
0094     } else {
0095         return EOF;
0096     }
0097 }
0098 
0099 #if EXIV2_TEST_VERSION(0,28,0)
0100 Exiv2::DataBuf KisExiv2IODevice::read(size_t rcount)
0101 #else
0102 Exiv2::DataBuf KisExiv2IODevice::read(long rcount)
0103 #endif
0104 {
0105     Exiv2::DataBuf buf(rcount);
0106 #if EXIV2_TEST_VERSION(0,28,0)
0107     const size_t readCount = read(buf.data(), buf.size());
0108     buf.resize(readCount);
0109 #else
0110     const long readCount = read(buf.pData_, buf.size_);
0111     buf.size_ = readCount;
0112 #endif
0113     return buf;
0114 }
0115 
0116 #if EXIV2_TEST_VERSION(0,28,0)
0117 size_t KisExiv2IODevice::read(Exiv2::byte *buf, size_t rcount)
0118 #else
0119 long KisExiv2IODevice::read(Exiv2::byte *buf, long rcount)
0120 #endif
0121 {
0122     const qint64 bytesRead = m_file.read(reinterpret_cast<char *>(buf), rcount);
0123     if (bytesRead > 0) {
0124         return bytesRead;
0125     } else {
0126         qWarning() << "KisExiv2IODevice: Couldn't read file:" << m_file.errorString();
0127         // some error or EOF
0128         return 0;
0129     }
0130 }
0131 
0132 int KisExiv2IODevice::getb()
0133 {
0134     char c;
0135     if (m_file.getChar(&c)) {
0136         return c;
0137     } else {
0138         return EOF;
0139     }
0140 }
0141 
0142 void KisExiv2IODevice::transfer(Exiv2::BasicIo &src)
0143 {
0144     bool isFileBased = (dynamic_cast<Exiv2::FileIo *>(&src) || dynamic_cast<KisExiv2IODevice *>(&src));
0145     bool useFallback = false;
0146 
0147     if (isFileBased) {
0148         const QString srcPath = QString::fromStdString(src.path());
0149         // use fallback if copying failed (e.g on Android :( )
0150         useFallback = !renameToCurrent(srcPath);
0151     }
0152 
0153     if (!isFileBased || useFallback) {
0154         const bool wasOpen = isopen();
0155         const QIODevice::OpenMode oldMode = m_file.openMode();
0156 
0157         // this sets file positioner to the beginning.
0158         if (src.open() != 0) {
0159             qWarning() << "KisExiv2IODevice::transfer: Couldn't open src file" << QString::fromStdString(src.path());
0160             return;
0161         }
0162 
0163         if (!open(QIODevice::ReadWrite | QIODevice::Truncate)) {
0164             qWarning() << "KisExiv2IODevice::transfer: Couldn't open dest file" << filePathQString();
0165             return;
0166         }
0167         write(src);
0168         src.close();
0169 
0170         if (wasOpen) {
0171             open(oldMode);
0172         } else {
0173             close();
0174         }
0175     }
0176 }
0177 
0178 #if defined(_MSC_VER) || EXIV2_TEST_VERSION(0,28,0)
0179 int KisExiv2IODevice::seek(int64_t offset, Exiv2::BasicIo::Position position)
0180 #else
0181 int KisExiv2IODevice::seek(long offset, Exiv2::BasicIo::Position position)
0182 #endif
0183 {
0184     qint64 pos = 0;
0185     switch (position) {
0186     case Exiv2::BasicIo::beg:
0187         pos = offset;
0188         break;
0189     case Exiv2::BasicIo::cur:
0190         pos = tell() + offset;
0191         break;
0192     case Exiv2::BasicIo::end:
0193         pos = size() + offset;
0194         break;
0195     }
0196     return m_file.seek(pos);
0197 }
0198 
0199 Exiv2::byte *KisExiv2IODevice::mmap(bool isWriteable)
0200 {
0201     Q_UNUSED(isWriteable);
0202 
0203     if (munmap() != 0) {
0204         qWarning() << "KisExiv2IODevice::mmap: Couldn't unmap the mapped file";
0205         return nullptr;
0206     }
0207 
0208     m_mappedArea = m_file.map(0, size(), QFile::NoOptions);
0209     if (!m_mappedArea) {
0210         // We show a warning, but originally we should be throwing an exception.
0211         qWarning() << "KisExiv2IODevice::mmap: Couldn't map the file" << m_file.fileName();
0212     }
0213     return m_mappedArea;
0214 }
0215 
0216 int KisExiv2IODevice::munmap()
0217 {
0218     if (m_mappedArea) {
0219         bool successful = m_file.unmap(m_mappedArea);
0220         m_mappedArea = nullptr;
0221         return !successful;
0222     }
0223     return 0;
0224 }
0225 
0226 #if EXIV2_TEST_VERSION(0,28,0)
0227 void KisExiv2IODevice::populateFakeData()
0228 {
0229     return;
0230 }
0231 #endif
0232 
0233 #if EXIV2_TEST_VERSION(0,28,0)
0234 size_t KisExiv2IODevice::tell() const
0235 #else
0236 long KisExiv2IODevice::tell() const
0237 #endif
0238 {
0239     return m_file.pos();
0240 }
0241 
0242 size_t KisExiv2IODevice::size() const
0243 {
0244     if (m_file.isWritable()) {
0245         m_file.flush();
0246     }
0247     return m_file.size();
0248 }
0249 
0250 bool KisExiv2IODevice::isopen() const
0251 {
0252     return m_file.isOpen();
0253 }
0254 
0255 int KisExiv2IODevice::error() const
0256 {
0257     // zero if no error
0258     return m_file.error() != QFileDevice::NoError;
0259 }
0260 
0261 bool KisExiv2IODevice::eof() const
0262 {
0263     return m_file.atEnd();
0264 }
0265 
0266 #if EXIV2_TEST_VERSION(0,28,0)
0267 const std::string& KisExiv2IODevice::path() const noexcept
0268 #else
0269 std::string KisExiv2IODevice::path() const
0270 #endif
0271 {
0272     return filePathQString().toStdString();
0273 }
0274 
0275 bool KisExiv2IODevice::open(QFile::OpenMode mode)
0276 {
0277     if (m_file.isOpen()) {
0278         m_file.close();
0279     }
0280     return m_file.open(mode);
0281 }
0282 
0283 bool KisExiv2IODevice::renameToCurrent(const QString srcPath)
0284 {
0285     QFile::Permissions permissions = QFile::permissions(filePathQString());
0286     if (QFile::exists(filePathQString())) {
0287         QFile::remove(filePathQString());
0288     }
0289 
0290     if (!QFile(srcPath).rename(filePathQString())) {
0291         qWarning() << "KisExiv2IODevice:renameToCurrent Couldn't copy file from" << srcPath << "to" << filePathQString();
0292         return false;
0293     }
0294     return QFile(filePathQString()).setPermissions(permissions);
0295 }
0296 
0297 QString KisExiv2IODevice::filePathQString() const
0298 {
0299     return QFileInfo(m_file).absoluteFilePath();
0300 }