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