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