qt: Undeprecate API that I find useful
[gpgme.git] / lang / qt / src / dataprovider.cpp
1 /* dataprovider.cpp
2    Copyright (C) 2004 Klarävdalens Datakonsult AB
3     Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
4     Software engineering by Intevation GmbH
5
6    This file is part of QGPGME.
7
8    QGPGME is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Library General Public License as published
10    by the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    QGPGME is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU Library General Public License for more details.
17
18    You should have received a copy of the GNU Library General Public License
19    along with QGPGME; see the file COPYING.LIB.  If not, write to the
20    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21    Boston, MA 02110-1301, USA. */
22
23 // -*- c++ -*-
24
25 #ifdef HAVE_CONFIG_H
26  #include "config.h"
27 #endif
28
29 #include <dataprovider.h>
30
31 #include <error.h>
32
33 #include <QIODevice>
34 #include <QProcess>
35
36 #include <cstdio>
37 #include <cstring>
38 #include <cassert>
39
40 using namespace QGpgME;
41 using namespace GpgME;
42
43 //
44 //
45 // QByteArrayDataProvider
46 //
47 //
48
49 static bool resizeAndInit(QByteArray &ba, size_t newSize)
50 {
51     const size_t oldSize = ba.size();
52     ba.resize(newSize);
53     const bool ok = (newSize == static_cast<size_t>(ba.size()));
54     if (ok) {
55         memset(ba.data() + oldSize, 0, newSize - oldSize);
56     }
57     return ok;
58 }
59
60 QByteArrayDataProvider::QByteArrayDataProvider()
61     : GpgME::DataProvider(), mOff(0) {}
62
63 QByteArrayDataProvider::QByteArrayDataProvider(const QByteArray &initialData)
64     : GpgME::DataProvider(), mArray(initialData), mOff(0) {}
65
66 QByteArrayDataProvider::~QByteArrayDataProvider() {}
67
68 ssize_t QByteArrayDataProvider::read(void *buffer, size_t bufSize)
69 {
70 #ifndef NDEBUG
71     //qDebug( "QByteArrayDataProvider::read( %p, %d )", buffer, bufSize );
72 #endif
73     if (bufSize == 0) {
74         return 0;
75     }
76     if (!buffer) {
77         Error::setSystemError(GPG_ERR_EINVAL);
78         return -1;
79     }
80     if (mOff >= mArray.size()) {
81         return 0; // EOF
82     }
83     size_t amount = qMin(bufSize, static_cast<size_t>(mArray.size() - mOff));
84     assert(amount > 0);
85     memcpy(buffer, mArray.data() + mOff, amount);
86     mOff += amount;
87     return amount;
88 }
89
90 ssize_t QByteArrayDataProvider::write(const void *buffer, size_t bufSize)
91 {
92 #ifndef NDEBUG
93     //qDebug( "QByteArrayDataProvider::write( %p, %lu )", buffer, static_cast<unsigned long>( bufSize ) );
94 #endif
95     if (bufSize == 0) {
96         return 0;
97     }
98     if (!buffer) {
99         Error::setSystemError(GPG_ERR_EINVAL);
100         return -1;
101     }
102     if (mOff >= mArray.size()) {
103         resizeAndInit(mArray, mOff + bufSize);
104     }
105     if (mOff >= mArray.size()) {
106         Error::setSystemError(GPG_ERR_EIO);
107         return -1;
108     }
109     assert(bufSize <= static_cast<size_t>(mArray.size()) - mOff);
110     memcpy(mArray.data() + mOff, buffer, bufSize);
111     mOff += bufSize;
112     return bufSize;
113 }
114
115 off_t QByteArrayDataProvider::seek(off_t offset, int whence)
116 {
117 #ifndef NDEBUG
118     //qDebug( "QByteArrayDataProvider::seek( %d, %d )", int(offset), whence );
119 #endif
120     int newOffset = mOff;
121     switch (whence) {
122     case SEEK_SET:
123         newOffset = offset;
124         break;
125     case SEEK_CUR:
126         newOffset += offset;
127         break;
128     case SEEK_END:
129         newOffset = mArray.size() + offset;
130         break;
131     default:
132         Error::setSystemError(GPG_ERR_EINVAL);
133         return (off_t) - 1;
134     }
135     return mOff = newOffset;
136 }
137
138 void QByteArrayDataProvider::release()
139 {
140 #ifndef NDEBUG
141     //qDebug( "QByteArrayDataProvider::release()" );
142 #endif
143     mArray = QByteArray();
144 }
145
146 //
147 //
148 // QIODeviceDataProvider
149 //
150 //
151
152 QIODeviceDataProvider::QIODeviceDataProvider(const std::shared_ptr<QIODevice> &io)
153     : GpgME::DataProvider(),
154       mIO(io),
155       mErrorOccurred(false),
156       mHaveQProcess(qobject_cast<QProcess *>(io.get()))
157 {
158     assert(mIO);
159 }
160
161 QIODeviceDataProvider::~QIODeviceDataProvider() {}
162
163 bool QIODeviceDataProvider::isSupported(Operation op) const
164 {
165     const QProcess *const proc = qobject_cast<QProcess *>(mIO.get());
166     bool canRead = true;
167     if (proc) {
168         canRead = proc->readChannel() == QProcess::StandardOutput;
169     }
170
171     switch (op) {
172     case Read:    return mIO->isReadable() && canRead;
173     case Write:   return mIO->isWritable();
174     case Seek:    return !mIO->isSequential();
175     case Release: return true;
176     default:      return false;
177     }
178 }
179
180 static qint64 blocking_read(const std::shared_ptr<QIODevice> &io, char *buffer, qint64 maxSize)
181 {
182     while (!io->bytesAvailable()) {
183         if (!io->waitForReadyRead(-1)) {
184             if (const QProcess *const p = qobject_cast<QProcess *>(io.get())) {
185                 if (p->error() == QProcess::UnknownError &&
186                         p->exitStatus() == QProcess::NormalExit &&
187                         p->exitCode() == 0) {
188                     return 0;
189                 } else {
190                     Error::setSystemError(GPG_ERR_EIO);
191                     return -1;
192                 }
193             } else {
194                 return 0; // assume EOF (loses error cases :/ )
195             }
196         }
197     }
198     return io->read(buffer, maxSize);
199 }
200
201 ssize_t QIODeviceDataProvider::read(void *buffer, size_t bufSize)
202 {
203 #ifndef NDEBUG
204     //qDebug( "QIODeviceDataProvider::read( %p, %lu )", buffer, bufSize );
205 #endif
206     if (bufSize == 0) {
207         return 0;
208     }
209     if (!buffer) {
210         Error::setSystemError(GPG_ERR_EINVAL);
211         return -1;
212     }
213     const qint64 numRead = mHaveQProcess
214                            ? blocking_read(mIO, static_cast<char *>(buffer), bufSize)
215                            : mIO->read(static_cast<char *>(buffer), bufSize);
216
217     //workaround: some QIODevices (known example: QProcess) might not return 0 (EOF), but immediately -1 when finished. If no
218     //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
219
220     ssize_t rc = numRead;
221     if (numRead < 0 && !Error::hasSystemError()) {
222         if (mErrorOccurred) {
223             Error::setSystemError(GPG_ERR_EIO);
224         } else {
225             rc = 0;
226         }
227     }
228     if (numRead < 0) {
229         mErrorOccurred = true;
230     }
231     return rc;
232 }
233
234 ssize_t QIODeviceDataProvider::write(const void *buffer, size_t bufSize)
235 {
236 #ifndef NDEBUG
237     //qDebug( "QIODeviceDataProvider::write( %p, %lu )", buffer, static_cast<unsigned long>( bufSize ) );
238 #endif
239     if (bufSize == 0) {
240         return 0;
241     }
242     if (!buffer) {
243         Error::setSystemError(GPG_ERR_EINVAL);
244         return -1;
245     }
246
247     return mIO->write(static_cast<const char *>(buffer), bufSize);
248 }
249
250 off_t QIODeviceDataProvider::seek(off_t offset, int whence)
251 {
252 #ifndef NDEBUG
253     //qDebug( "QIODeviceDataProvider::seek( %d, %d )", int(offset), whence );
254 #endif
255     if (mIO->isSequential()) {
256         Error::setSystemError(GPG_ERR_ESPIPE);
257         return (off_t) - 1;
258     }
259     qint64 newOffset = mIO->pos();
260     switch (whence) {
261     case SEEK_SET:
262         newOffset = offset;
263         break;
264     case SEEK_CUR:
265         newOffset += offset;
266         break;
267     case SEEK_END:
268         newOffset = mIO->size() + offset;
269         break;
270     default:
271         Error::setSystemError(GPG_ERR_EINVAL);
272         return (off_t) - 1;
273     }
274     if (!mIO->seek(newOffset)) {
275         Error::setSystemError(GPG_ERR_EINVAL);
276         return (off_t) - 1;
277     }
278     return newOffset;
279 }
280
281 void QIODeviceDataProvider::release()
282 {
283 #ifndef NDEBUG
284     //qDebug( "QIODeviceDataProvider::release()" );
285 #endif
286     mIO->close();
287 }