Line data Source code
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 59 : static bool resizeAndInit(QByteArray &ba, size_t newSize)
50 : {
51 59 : const size_t oldSize = ba.size();
52 59 : ba.resize(newSize);
53 59 : const bool ok = (newSize == static_cast<size_t>(ba.size()));
54 59 : if (ok) {
55 59 : memset(ba.data() + oldSize, 0, newSize - oldSize);
56 : }
57 59 : return ok;
58 : }
59 :
60 57 : QByteArrayDataProvider::QByteArrayDataProvider()
61 57 : : GpgME::DataProvider(), mOff(0) {}
62 :
63 4 : QByteArrayDataProvider::QByteArrayDataProvider(const QByteArray &initialData)
64 4 : : GpgME::DataProvider(), mArray(initialData), mOff(0) {}
65 :
66 61 : QByteArrayDataProvider::~QByteArrayDataProvider() {}
67 :
68 11 : ssize_t QByteArrayDataProvider::read(void *buffer, size_t bufSize)
69 : {
70 : #ifndef NDEBUG
71 : //qDebug( "QByteArrayDataProvider::read( %p, %d )", buffer, bufSize );
72 : #endif
73 11 : if (bufSize == 0) {
74 0 : return 0;
75 : }
76 11 : if (!buffer) {
77 0 : Error::setSystemError(GPG_ERR_EINVAL);
78 0 : return -1;
79 : }
80 11 : if (mOff >= mArray.size()) {
81 5 : return 0; // EOF
82 : }
83 6 : size_t amount = qMin(bufSize, static_cast<size_t>(mArray.size() - mOff));
84 6 : assert(amount > 0);
85 6 : memcpy(buffer, mArray.data() + mOff, amount);
86 6 : mOff += amount;
87 6 : return amount;
88 : }
89 :
90 59 : 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 59 : if (bufSize == 0) {
96 0 : return 0;
97 : }
98 59 : if (!buffer) {
99 0 : Error::setSystemError(GPG_ERR_EINVAL);
100 0 : return -1;
101 : }
102 59 : if (mOff >= mArray.size()) {
103 59 : resizeAndInit(mArray, mOff + bufSize);
104 : }
105 59 : if (mOff >= mArray.size()) {
106 0 : Error::setSystemError(GPG_ERR_EIO);
107 0 : return -1;
108 : }
109 59 : assert(bufSize <= static_cast<size_t>(mArray.size()) - mOff);
110 59 : memcpy(mArray.data() + mOff, buffer, bufSize);
111 59 : mOff += bufSize;
112 59 : return bufSize;
113 : }
114 :
115 151 : 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 151 : int newOffset = mOff;
121 151 : switch (whence) {
122 : case SEEK_SET:
123 90 : newOffset = offset;
124 90 : break;
125 : case SEEK_CUR:
126 0 : newOffset += offset;
127 0 : break;
128 : case SEEK_END:
129 61 : newOffset = mArray.size() + offset;
130 61 : break;
131 : default:
132 0 : Error::setSystemError(GPG_ERR_EINVAL);
133 0 : return (off_t) - 1;
134 : }
135 151 : return mOff = newOffset;
136 : }
137 :
138 61 : void QByteArrayDataProvider::release()
139 : {
140 : #ifndef NDEBUG
141 : //qDebug( "QByteArrayDataProvider::release()" );
142 : #endif
143 61 : mArray = QByteArray();
144 61 : }
145 :
146 : //
147 : //
148 : // QIODeviceDataProvider
149 : //
150 : //
151 :
152 27 : QIODeviceDataProvider::QIODeviceDataProvider(const std::shared_ptr<QIODevice> &io)
153 : : GpgME::DataProvider(),
154 : mIO(io),
155 : mErrorOccurred(false),
156 27 : mHaveQProcess(qobject_cast<QProcess *>(io.get()))
157 : {
158 27 : assert(mIO);
159 27 : }
160 :
161 27 : QIODeviceDataProvider::~QIODeviceDataProvider() {}
162 :
163 135 : bool QIODeviceDataProvider::isSupported(Operation op) const
164 : {
165 135 : const QProcess *const proc = qobject_cast<QProcess *>(mIO.get());
166 135 : bool canRead = true;
167 135 : if (proc) {
168 0 : canRead = proc->readChannel() == QProcess::StandardOutput;
169 : }
170 :
171 135 : switch (op) {
172 27 : case Read: return mIO->isReadable() && canRead;
173 27 : case Write: return mIO->isWritable();
174 54 : case Seek: return !mIO->isSequential();
175 27 : case Release: return true;
176 0 : default: return false;
177 : }
178 : }
179 :
180 0 : static qint64 blocking_read(const std::shared_ptr<QIODevice> &io, char *buffer, qint64 maxSize)
181 : {
182 0 : while (!io->bytesAvailable()) {
183 0 : if (!io->waitForReadyRead(-1)) {
184 0 : if (const QProcess *const p = qobject_cast<QProcess *>(io.get())) {
185 0 : if (p->error() == QProcess::UnknownError &&
186 0 : p->exitStatus() == QProcess::NormalExit &&
187 0 : p->exitCode() == 0) {
188 0 : if (io->atEnd()) {
189 : // EOF
190 0 : return 0;
191 : } // continue reading even if process ended to ensure
192 : // everything is read.
193 : } else {
194 0 : Error::setSystemError(GPG_ERR_EIO);
195 0 : return -1;
196 : }
197 : } else {
198 0 : return 0; // assume EOF (loses error cases :/ )
199 : }
200 : }
201 : }
202 0 : return io->read(buffer, maxSize);
203 : }
204 :
205 307 : ssize_t QIODeviceDataProvider::read(void *buffer, size_t bufSize)
206 : {
207 : #ifndef NDEBUG
208 : //qDebug( "QIODeviceDataProvider::read( %p, %lu )", buffer, bufSize );
209 : #endif
210 307 : if (bufSize == 0) {
211 0 : return 0;
212 : }
213 307 : if (!buffer) {
214 0 : Error::setSystemError(GPG_ERR_EINVAL);
215 0 : return -1;
216 : }
217 307 : const qint64 numRead = mHaveQProcess
218 614 : ? blocking_read(mIO, static_cast<char *>(buffer), bufSize)
219 614 : : mIO->read(static_cast<char *>(buffer), bufSize);
220 :
221 : //workaround: some QIODevices (known example: QProcess) might not return 0 (EOF), but immediately -1 when finished. If no
222 : //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
223 :
224 307 : ssize_t rc = numRead;
225 307 : if (numRead < 0 && !Error::hasSystemError()) {
226 0 : if (mErrorOccurred) {
227 0 : Error::setSystemError(GPG_ERR_EIO);
228 : } else {
229 0 : rc = 0;
230 : }
231 : }
232 307 : if (numRead < 0) {
233 0 : mErrorOccurred = true;
234 : }
235 307 : return rc;
236 : }
237 :
238 2 : ssize_t QIODeviceDataProvider::write(const void *buffer, size_t bufSize)
239 : {
240 : #ifndef NDEBUG
241 : //qDebug( "QIODeviceDataProvider::write( %p, %lu )", buffer, static_cast<unsigned long>( bufSize ) );
242 : #endif
243 2 : if (bufSize == 0) {
244 0 : return 0;
245 : }
246 2 : if (!buffer) {
247 0 : Error::setSystemError(GPG_ERR_EINVAL);
248 0 : return -1;
249 : }
250 :
251 2 : return mIO->write(static_cast<const char *>(buffer), bufSize);
252 : }
253 :
254 54 : off_t QIODeviceDataProvider::seek(off_t offset, int whence)
255 : {
256 : #ifndef NDEBUG
257 : //qDebug( "QIODeviceDataProvider::seek( %d, %d )", int(offset), whence );
258 : #endif
259 54 : if (mIO->isSequential()) {
260 0 : Error::setSystemError(GPG_ERR_ESPIPE);
261 0 : return (off_t) - 1;
262 : }
263 54 : qint64 newOffset = mIO->pos();
264 54 : switch (whence) {
265 : case SEEK_SET:
266 27 : newOffset = offset;
267 27 : break;
268 : case SEEK_CUR:
269 0 : newOffset += offset;
270 0 : break;
271 : case SEEK_END:
272 27 : newOffset = mIO->size() + offset;
273 27 : break;
274 : default:
275 0 : Error::setSystemError(GPG_ERR_EINVAL);
276 0 : return (off_t) - 1;
277 : }
278 54 : if (!mIO->seek(newOffset)) {
279 0 : Error::setSystemError(GPG_ERR_EINVAL);
280 0 : return (off_t) - 1;
281 : }
282 54 : return newOffset;
283 : }
284 :
285 27 : void QIODeviceDataProvider::release()
286 : {
287 : #ifndef NDEBUG
288 : //qDebug( "QIODeviceDataProvider::release()" );
289 : #endif
290 27 : mIO->close();
291 27 : }
|