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 | |
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 | 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 | |
53 | QByteArrayDataProvider::QByteArrayDataProvider() |
54 | : GpgME::DataProvider(), mOff( 0 ) {} |
55 | |
56 | QByteArrayDataProvider::QByteArrayDataProvider( const QByteArray & initialData ) |
57 | : GpgME::DataProvider(), mArray( initialData ), mOff( 0 ) {} |
58 | |
59 | QByteArrayDataProvider::~QByteArrayDataProvider() {} |
60 | |
61 | ssize_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 | |
82 | ssize_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 | |
106 | off_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 | |
128 | void QByteArrayDataProvider::release() { |
129 | #ifndef NDEBUG |
130 | //qDebug( "QByteArrayDataProvider::release()" ); |
131 | #endif |
132 | mArray = QByteArray(); |
133 | } |
134 | |
135 | |
136 | // |
137 | // |
138 | // QIODeviceDataProvider |
139 | // |
140 | // |
141 | |
142 | QIODeviceDataProvider::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 | |
151 | QIODeviceDataProvider::~QIODeviceDataProvider() {} |
152 | |
153 | bool 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 | |
169 | static 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 | |
189 | ssize_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 | |
221 | ssize_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 | |
236 | off_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 | |
266 | void QIODeviceDataProvider::release() { |
267 | #ifndef NDEBUG |
268 | //qDebug( "QIODeviceDataProvider::release()" ); |
269 | #endif |
270 | mIO->close(); |
271 | } |
272 | |