/* dataprovider.cpp Copyright (C) 2004 Klar�vdalens Datakonsult AB Copyright (c) 2016 Intevation GmbH This file is part of QGPGME. QGPGME is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. QGPGME is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with QGPGME; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // -*- c++ -*- #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include using namespace QGpgME; using namespace GpgME; // // // QByteArrayDataProvider // // static bool resizeAndInit(QByteArray &ba, size_t newSize) { const size_t oldSize = ba.size(); ba.resize(newSize); const bool ok = (newSize == static_cast(ba.size())); if (ok) { memset(ba.data() + oldSize, 0, newSize - oldSize); } return ok; } QByteArrayDataProvider::QByteArrayDataProvider() : GpgME::DataProvider(), mOff(0) {} QByteArrayDataProvider::QByteArrayDataProvider(const QByteArray &initialData) : GpgME::DataProvider(), mArray(initialData), mOff(0) {} QByteArrayDataProvider::~QByteArrayDataProvider() {} ssize_t QByteArrayDataProvider::read(void *buffer, size_t bufSize) { #ifndef NDEBUG //qDebug( "QByteArrayDataProvider::read( %p, %d )", buffer, bufSize ); #endif if (bufSize == 0) { return 0; } if (!buffer) { Error::setSystemError(GPG_ERR_EINVAL); return -1; } if (mOff >= mArray.size()) { return 0; // EOF } size_t amount = qMin(bufSize, static_cast(mArray.size() - mOff)); assert(amount > 0); memcpy(buffer, mArray.data() + mOff, amount); mOff += amount; return amount; } ssize_t QByteArrayDataProvider::write(const void *buffer, size_t bufSize) { #ifndef NDEBUG //qDebug( "QByteArrayDataProvider::write( %p, %lu )", buffer, static_cast( bufSize ) ); #endif if (bufSize == 0) { return 0; } if (!buffer) { Error::setSystemError(GPG_ERR_EINVAL); return -1; } if (mOff >= mArray.size()) { resizeAndInit(mArray, mOff + bufSize); } if (mOff >= mArray.size()) { Error::setSystemError(GPG_ERR_EIO); return -1; } assert(bufSize <= static_cast(mArray.size()) - mOff); memcpy(mArray.data() + mOff, buffer, bufSize); mOff += bufSize; return bufSize; } off_t QByteArrayDataProvider::seek(off_t offset, int whence) { #ifndef NDEBUG //qDebug( "QByteArrayDataProvider::seek( %d, %d )", int(offset), whence ); #endif int newOffset = mOff; switch (whence) { case SEEK_SET: newOffset = offset; break; case SEEK_CUR: newOffset += offset; break; case SEEK_END: newOffset = mArray.size() + offset; break; default: Error::setSystemError(GPG_ERR_EINVAL); return (off_t) - 1; } return mOff = newOffset; } void QByteArrayDataProvider::release() { #ifndef NDEBUG //qDebug( "QByteArrayDataProvider::release()" ); #endif mArray = QByteArray(); } // // // QIODeviceDataProvider // // QIODeviceDataProvider::QIODeviceDataProvider(const std::shared_ptr &io) : GpgME::DataProvider(), mIO(io), mErrorOccurred(false), mHaveQProcess(qobject_cast(io.get())) { assert(mIO); } QIODeviceDataProvider::~QIODeviceDataProvider() {} bool QIODeviceDataProvider::isSupported(Operation op) const { const QProcess *const proc = qobject_cast(mIO.get()); bool canRead = true; if (proc) { canRead = proc->readChannel() == QProcess::StandardOutput; } switch (op) { case Read: return mIO->isReadable() && canRead; case Write: return mIO->isWritable(); case Seek: return !mIO->isSequential(); case Release: return true; default: return false; } } static qint64 blocking_read(const std::shared_ptr &io, char *buffer, qint64 maxSize) { while (!io->bytesAvailable()) { if (!io->waitForReadyRead(-1)) { if (const QProcess *const p = qobject_cast(io.get())) { if (p->error() == QProcess::UnknownError && p->exitStatus() == QProcess::NormalExit && p->exitCode() == 0) { return 0; } else { Error::setSystemError(GPG_ERR_EIO); return -1; } } else { return 0; // assume EOF (loses error cases :/ ) } } } return io->read(buffer, maxSize); } ssize_t QIODeviceDataProvider::read(void *buffer, size_t bufSize) { #ifndef NDEBUG //qDebug( "QIODeviceDataProvider::read( %p, %lu )", buffer, bufSize ); #endif if (bufSize == 0) { return 0; } if (!buffer) { Error::setSystemError(GPG_ERR_EINVAL); return -1; } const qint64 numRead = mHaveQProcess ? blocking_read(mIO, static_cast(buffer), bufSize) : mIO->read(static_cast(buffer), bufSize); //workaround: some QIODevices (known example: QProcess) might not return 0 (EOF), but immediately -1 when finished. If no //errno is set, gpgme doesn't detect the error and loops forever. So return 0 on the very first -1 in case errno is 0 ssize_t rc = numRead; if (numRead < 0 && !Error::hasSystemError()) { if (mErrorOccurred) { Error::setSystemError(GPG_ERR_EIO); } else { rc = 0; } } if (numRead < 0) { mErrorOccurred = true; } return rc; } ssize_t QIODeviceDataProvider::write(const void *buffer, size_t bufSize) { #ifndef NDEBUG //qDebug( "QIODeviceDataProvider::write( %p, %lu )", buffer, static_cast( bufSize ) ); #endif if (bufSize == 0) { return 0; } if (!buffer) { Error::setSystemError(GPG_ERR_EINVAL); return -1; } return mIO->write(static_cast(buffer), bufSize); } off_t QIODeviceDataProvider::seek(off_t offset, int whence) { #ifndef NDEBUG //qDebug( "QIODeviceDataProvider::seek( %d, %d )", int(offset), whence ); #endif if (mIO->isSequential()) { Error::setSystemError(GPG_ERR_ESPIPE); return (off_t) - 1; } qint64 newOffset = mIO->pos(); switch (whence) { case SEEK_SET: newOffset = offset; break; case SEEK_CUR: newOffset += offset; break; case SEEK_END: newOffset = mIO->size() + offset; break; default: Error::setSystemError(GPG_ERR_EINVAL); return (off_t) - 1; } if (!mIO->seek(newOffset)) { Error::setSystemError(GPG_ERR_EINVAL); return (off_t) - 1; } return newOffset; } void QIODeviceDataProvider::release() { #ifndef NDEBUG //qDebug( "QIODeviceDataProvider::release()" ); #endif mIO->close(); }