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