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