Blame lang/qt/src/dataprovider.cpp

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