gnupgprocessbase.cpp
1/*
2 gnupgprocessbase.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 Copyright (c) 2004 Klarälvdalens Datakonsult AB
6
7 Libkleopatra is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 Libkleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the TQt library by Trolltech AS, Norway (or with modified versions
24 of TQt that use the same license as TQt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 TQt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
31*/
32
33#include "gnupgprocessbase.h"
34
35#include <kdebug.h>
36#include <kurl.h>
37
38#include <tqsocketnotifier.h>
39#include <tqtextcodec.h>
40#include <tqstringlist.h>
41
42#include <unistd.h>
43#include <fcntl.h>
44#include <errno.h>
45#include <assert.h>
46
47struct Kleo::GnuPGProcessBase::Private {
48 Private() : usetStatusFD( false ), statnot( 0 ) {
49 statusFD[0] = statusFD[1] = -1;
50 }
51
52 bool usetStatusFD;
53 int statusFD[2];
54 TQSocketNotifier * statnot;
55 TQCString statusBuffer;
56};
57
58
59Kleo::GnuPGProcessBase::GnuPGProcessBase( TQObject * parent, const char * name )
60 : TDEProcess( parent, name )
61{
62 d = new Private();
63}
64
65Kleo::GnuPGProcessBase::~GnuPGProcessBase() {
66 delete d; d = 0;
67}
68
69void Kleo::GnuPGProcessBase::setUsetStatusFD( bool use ) {
70 assert( d );
71 d->usetStatusFD = use;
72}
73
74bool Kleo::GnuPGProcessBase::start( RunMode runmode, Communication comm ) {
75 if ( d->usetStatusFD ) {
76 // set up the status-fd. This should be in setupCommunication(),
77 // but then it's too late: we need the fd of the pipe to pass it
78 // as argument to the --status-fd option:
79 // PENDING(marc) find out why TDEProcess uses both pipe() and socketpair()...
80 if ( ::pipe( d->statusFD ) < 0 ) {
81 kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::start: pipe(2) failed: " << perror << endl;
82 return false;
83 }
84 ::fcntl( d->statusFD[0], F_SETFD, FD_CLOEXEC );
85 ::fcntl( d->statusFD[1], F_SETFD, FD_CLOEXEC );
86 if ( !arguments.empty() ) {
87 TQValueList<TQCString>::iterator it = arguments.begin();
88 ++it;
89 arguments.insert( it, "--status-fd" );
90 char buf[25];
91 sprintf( buf, "%d", d->statusFD[1] );
92 arguments.insert( it, buf );
93 arguments.insert( it, "--no-tty" );
94 //arguments.insert( it, "--enable-progress-filter" ); // gpgsm doesn't know this
95 }
96 }
97 return TDEProcess::start( runmode, comm );
98}
99
101 if ( int ok = TDEProcess::setupCommunication( comm ) )
102 return ok;
103 if ( d->usetStatusFD ) {
104 // base class impl returned error, so close our fd's, too
105 ::close( d->statusFD[0] );
106 ::close( d->statusFD[1] );
107 d->statusFD[0] = d->statusFD[1] = -1;
108 }
109 return 0; // Error
110}
111
113 if ( d->usetStatusFD ) {
114 ::close( d->statusFD[1] ); // close the input end of the pipe, we're the reader
115 d->statnot = new TQSocketNotifier( d->statusFD[0], TQSocketNotifier::Read, this );
116 connect( d->statnot, TQ_SIGNAL(activated(int)), TQ_SLOT(slotChildStatus(int)) );
117 }
118 return TDEProcess::commSetupDoneP();
119}
120
122 if ( d->usetStatusFD )
123 ::fcntl( d->statusFD[1], F_SETFD, 0 );
124 return TDEProcess::commSetupDoneC();
125}
126
127void Kleo::GnuPGProcessBase::slotChildStatus( int fd ) {
128 if ( !childStatus(fd) )
129 closetStatus();
130}
131
132bool Kleo::GnuPGProcessBase::closetStatus() {
133 if ( !d->usetStatusFD )
134 return false;
135 d->usetStatusFD = false;
136 delete d->statnot; d->statnot = 0;
137 ::close( d->statusFD[0] ); d->statusFD[0] = -1;
138 return true;
139}
140
141int Kleo::GnuPGProcessBase::childStatus( int fd ) {
142 char buf[1024];
143 const int len = ::read( fd, buf, sizeof(buf)-1 );
144 if ( len > 0 ) {
145 buf[len] = 0;
146 d->statusBuffer += buf;
147 parsetStatusOutput();
148 }
149 return len;
150}
151
152static TQString fromHexEscapedUtf8( const TQCString & str ) {
153 return KURL::decode_string( str.data(), 106 /* utf-8 */ );
154}
155
156void Kleo::GnuPGProcessBase::parsetStatusOutput() {
157 static const char startToken[] = "[GNUPG:] ";
158 static const int startTokenLen = sizeof startToken / sizeof *startToken - 1;
159
160 int lineStart = 0;
161 for ( int lineEnd = d->statusBuffer.find( '\n' ) ; lineEnd >= 0 ; lineEnd = d->statusBuffer.find( '\n', lineStart = lineEnd+1 ) ) {
162 // get next line:
163 const TQCString line = d->statusBuffer.mid( lineStart, lineEnd - lineStart ).stripWhiteSpace();
164 if ( line.isEmpty() )
165 continue;
166 // check status token
167 if ( line.left( startTokenLen ) != startToken ) {
168 kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::childStatus: status-fd protocol error: line doesn't begin with \""
169 << startToken << "\"" << endl;
170 continue;
171 }
172 // remove status token:
173 const TQCString command = line.mid( startTokenLen ).simplifyWhiteSpace() + ' ';
174 if ( command == " " ) {
175 kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::childStatus: status-fd protocol error: line without content." << endl;
176 continue;
177 }
178 // split into base and args
179 TQString cmd;
180 TQStringList args;
181 int tagStart = 0;
182 for ( int tagEnd = command.find( ' ' ) ; tagEnd >= 0 ; tagEnd = command.find( ' ', tagStart = tagEnd+1 ) ) {
183 const TQCString tag = command.mid( tagStart, tagEnd - tagStart );
184 if ( cmd.isNull() )
185 cmd = fromHexEscapedUtf8( tag );
186 else
187 args.push_back( fromHexEscapedUtf8( tag ) );
188 }
189 emit status( this, cmd, args );
190 }
191 d->statusBuffer = d->statusBuffer.mid( lineStart );
192}
193
194void Kleo::GnuPGProcessBase::virtual_hook( int id, void * data ) {
195 TDEProcess::virtual_hook( id, data );
196}
197
198#include "gnupgprocessbase.moc"
int setupCommunication(Communication comm)
void virtual_hook(int id, void *data)
bool start(RunMode runmode, Communication comm)