From becf580f6163889de683500957a0a7cfceb7ed47 Mon Sep 17 00:00:00 2001 From: Marcus Brinkmann Date: Thu, 6 Sep 2007 22:41:11 +0000 Subject: [PATCH] 2007-09-07 Marcus Brinkmann * configure.ac: Check for C++, Qt and support --enable-w32-qt. * m4/pkg.m4: New file. gpgme/ 2007-09-07 Marcus Brinkmann * kdpipeiodevice.h, kdpipeiodevice.cpp, moc_kdpipeiodevice.cpp, kdpipeiodevice.moc, w32-qt-io.c: New files. * Makefile.am (ltlib_gpgme_extra): Rename to ltlib_gpgme_glib. (ltlib_gpgme_qt): New variable. (lib_LTLIBRARIES): Add $(ltlib_gpgme_qt). (libgpgme_qt_la_SOURCES): New variable. (AM_CPPFLAGS): Add @QT4_CORE_INCLUDES@ (AM_CFLAGS): Add @QT4_CORE_CFLAGS@. (libgpgme_qt_la_LDFLAGS, libgpgme_qt_la_DEPENDENCIES) (libgpgme_qt_la_LIBADD): New variables. * sema.h (struct critsect_s): Rename "private" to "priv" to make C++ users happy. Change users. * posix-sema.c (_gpgme_sema_cs_enter, _gpgme_sema_cs_leave) (_gpgme_sema_cs_destroy): Likewise. * w32-sema.c (critsect_init, _gpgme_sema_cs_enter) (_gpgme_sema_cs_leave, _gpgme_sema_cs_destroy): Likewise. * w32-glib-io.c (gpgme_get_giochannel): Change return type to void*. (gpgme_get_fdptr): New function. * w32-io.c (gpgme_get_fdptr): New function * gpgme.def: Add gpgme_get_fdptr. --- ChangeLog | 7 +- configure.ac | 8 + gpgme/ChangeLog | 24 ++ gpgme/Makefile.am | 42 +- gpgme/gpgme.def | 2 + gpgme/kdpipeiodevice.cpp | 760 +++++++++++++++++++++++++++++++++++ gpgme/kdpipeiodevice.h | 66 +++ gpgme/kdpipeiodevice.moc | 132 ++++++ gpgme/moc_kdpipeiodevice.cpp | 60 +++ gpgme/posix-sema.c | 10 +- gpgme/sema.h | 6 +- gpgme/w32-glib-io.c | 13 +- gpgme/w32-io.c | 10 +- gpgme/w32-qt-io.cpp | 609 ++++++++++++++++++++++++++++ gpgme/w32-sema.c | 22 +- m4/pkg.m4 | 157 ++++++++ 16 files changed, 1898 insertions(+), 30 deletions(-) create mode 100644 gpgme/kdpipeiodevice.cpp create mode 100644 gpgme/kdpipeiodevice.h create mode 100644 gpgme/kdpipeiodevice.moc create mode 100644 gpgme/moc_kdpipeiodevice.cpp create mode 100644 gpgme/w32-qt-io.cpp create mode 100644 m4/pkg.m4 diff --git a/ChangeLog b/ChangeLog index 187fd198..f45f6e32 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2007-09-07 Marcus Brinkmann + + * configure.ac: Check for C++, Qt and support --enable-w32-qt. + * m4/pkg.m4: New file. + 2007-08-21 Marcus Brinkmann * configure.ac (--enable-w32-glib): Use --enableval, not @@ -775,7 +780,7 @@ * autogen.sh: Added option --build-w32. - Copyright 2001, 2002, 2003, 2004, 2005 g10 Code GmbH + Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007 g10 Code GmbH This file is free software; as a special exception the author gives unlimited permission to copy and/or distribute it, with or without diff --git a/configure.ac b/configure.ac index 923792c0..a21aaa9b 100644 --- a/configure.ac +++ b/configure.ac @@ -83,6 +83,7 @@ AH_VERBATIM([_REENTRANT], #endif]) AC_PROG_CC +AC_PROG_CXX AC_SUBST(LIBGPGME_LT_CURRENT) AC_SUBST(LIBGPGME_LT_AGE) @@ -162,6 +163,13 @@ AC_ARG_ENABLE(w32-glib, build_w32_glib=$enableval) AM_CONDITIONAL(BUILD_W32_GLIB, test "$build_w32_glib" = yes) +build_w32_qt=no +PKG_CHECK_MODULES(QT4_CORE, QtCore) +AC_ARG_ENABLE(w32-qt, + AC_HELP_STRING([--enable-w32-qt], [build GPGME Qt for W32]), + build_w32_qt=$enableval) +AM_CONDITIONAL(BUILD_W32_QT, test "$build_w32_qt" = yes) + AM_CONDITIONAL(HAVE_PTH, test "$have_pth" = "yes") AM_CONDITIONAL(HAVE_PTHREAD, test "$have_pthread" = "yes") diff --git a/gpgme/ChangeLog b/gpgme/ChangeLog index 29f496d1..7d3ae586 100644 --- a/gpgme/ChangeLog +++ b/gpgme/ChangeLog @@ -1,3 +1,27 @@ +2007-09-07 Marcus Brinkmann + + * kdpipeiodevice.h, kdpipeiodevice.cpp, moc_kdpipeiodevice.cpp, + kdpipeiodevice.moc, w32-qt-io.c: New files. + * Makefile.am (ltlib_gpgme_extra): Rename to ltlib_gpgme_glib. + (ltlib_gpgme_qt): New variable. + (lib_LTLIBRARIES): Add $(ltlib_gpgme_qt). + (libgpgme_qt_la_SOURCES): New variable. + (AM_CPPFLAGS): Add @QT4_CORE_INCLUDES@ + (AM_CFLAGS): Add @QT4_CORE_CFLAGS@. + (libgpgme_qt_la_LDFLAGS, libgpgme_qt_la_DEPENDENCIES) + (libgpgme_qt_la_LIBADD): New variables. + * sema.h (struct critsect_s): Rename "private" to "priv" to make + C++ users happy. Change users. + * posix-sema.c (_gpgme_sema_cs_enter, _gpgme_sema_cs_leave) + (_gpgme_sema_cs_destroy): Likewise. + * w32-sema.c (critsect_init, _gpgme_sema_cs_enter) + (_gpgme_sema_cs_leave, _gpgme_sema_cs_destroy): Likewise. + * w32-glib-io.c (gpgme_get_giochannel): Change return type to + void*. + (gpgme_get_fdptr): New function. + * w32-io.c (gpgme_get_fdptr): New function + * gpgme.def: Add gpgme_get_fdptr. + 2007-08-22 Marcus Brinkmann * w32-io.c (_gpgme_io_write): Return early if COUNT is zero. diff --git a/gpgme/Makefile.am b/gpgme/Makefile.am index 9c6254fd..df233bba 100644 --- a/gpgme/Makefile.am +++ b/gpgme/Makefile.am @@ -1,5 +1,5 @@ # Copyright (C) 2000 Werner Koch (dd9jn) -# Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH +# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH # # This file is part of GPGME. # @@ -40,12 +40,18 @@ ltlib_gpgme_pth = endif if BUILD_W32_GLIB -ltlib_gpgme_extra = libgpgme-glib.la +ltlib_gpgme_glib = libgpgme-glib.la else -ltlib_gpgme_extra = +ltlib_gpgme_glib = endif -lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_extra) \ +if BUILD_W32_QT +ltlib_gpgme_qt = libgpgme-qt.la +else +ltlib_gpgme_qt = +endif + +lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \ $(ltlib_gpgme_pthread) $(ltlib_gpgme_pth) if HAVE_LD_VERSION_SCRIPT @@ -107,10 +113,24 @@ if BUILD_W32_GLIB libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c endif +if BUILD_W32_QT +libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \ + kdpipeiodevice.h kdpipeiodevice.cpp moc_kdpipeiodevice.cpp \ + kdpipeiodevice.moc + +# These are built sources (normally). +# moc_kdpipeiodevice.cpp: kdpipeiodevice.h +# $(MOC4) -o $@ $< +# +# kdpipeiodevice.moc: kdpipeiodevice.cpp +# $(MOC4) -o $@ $< +endif + # We use a global CFLAGS and CPPFLAGS setting for all library # versions, because then every object file is only compiled once. -AM_CPPFLAGS = $(assuan_cppflags) @GPG_ERROR_CFLAGS@ @PTH_CPPFLAGS@ -AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@ +AM_CPPFLAGS = $(assuan_cppflags) @GPG_ERROR_CFLAGS@ @PTH_CPPFLAGS@ \ + @QT4_CORE_CFLAGS@ +AM_CFLAGS = @PTH_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@ if HAVE_W32_SYSTEM @@ -181,6 +201,16 @@ libgpgme_glib_la_LIBADD = $(assuan_libobjs) @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @GLIB_LIBS@ @NETLIBS@ endif +if BUILD_W32_QT +libgpgme_qt_la_LDFLAGS = $(gpgme_res_ldflag) $(no_undefined) \ + $(export_symbols) $(libgpgme_version_script_cmd) -version-info \ + @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ +libgpgme_qt_la_DEPENDENCIES = $(assuan_libobjs) \ + @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) +libgpgme_qt_la_LIBADD = $(assuan_libobjs) @LTLIBOBJS@ \ + @GPG_ERROR_LIBS@ @QT4_CORE_LIB@ @NETLIBS@ +endif + status-table.h : gpgme.h $(srcdir)/mkstatus < $(srcdir)/gpgme.h > status-table.h diff --git a/gpgme/gpgme.def b/gpgme/gpgme.def index f4aa1b5a..bb2caf26 100644 --- a/gpgme/gpgme.def +++ b/gpgme/gpgme.def @@ -154,5 +154,7 @@ EXPORTS gpgme_free @120 gpgme_get_giochannel @121 + gpgme_get_fdptr @122 + ; END diff --git a/gpgme/kdpipeiodevice.cpp b/gpgme/kdpipeiodevice.cpp new file mode 100644 index 00000000..f4acebee --- /dev/null +++ b/gpgme/kdpipeiodevice.cpp @@ -0,0 +1,760 @@ +/* + Copyright (C) 2007 Klarälvdalens Datakonsult AB + + KDPipeIODevice is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + KDPipeIODevice is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with KDPipeIODevice; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kdpipeiodevice.h" + +#include + +#include +#include +#include + +#ifdef Q_OS_WIN32 +# define NOMINMAX +# include +# include +#else +# include +# include +#endif + +#ifndef KDAB_CHECK_THIS +# define KDAB_CHECK_CTOR (void)1 +# define KDAB_CHECK_DTOR KDAB_CHECK_CTOR +# define KDAB_CHECK_THIS KDAB_CHECK_CTOR +#endif + +#define LOCKED( d ) const QMutexLocker locker( &d->mutex ) +#define synchronized( d ) if ( int i = 0 ) {} else for ( const QMutexLocker locker( &d->mutex ) ; !i ; ++i ) + +const unsigned int BUFFER_SIZE = 4096; +const bool ALLOW_QIODEVICE_BUFFERING = true; + +// comment to get trace output: +#define qDebug if(1){}else qDebug + +namespace { +class Reader : public QThread { + Q_OBJECT +public: + Reader( int fd, Qt::HANDLE handle ); + ~Reader(); + + qint64 readData( char * data, qint64 maxSize ); + + unsigned int bytesInBuffer() const { + return ( wptr + sizeof buffer - rptr ) % sizeof buffer ; + } + + bool bufferFull() const { + return bytesInBuffer() == sizeof buffer - 1; + } + + bool bufferEmpty() const { + return bytesInBuffer() == 0; + } + + bool bufferContains( char ch ) { + const unsigned int bib = bytesInBuffer(); + for ( unsigned int i = rptr ; i < rptr + bib ; ++i ) + if ( buffer[i%sizeof buffer] == ch ) + return true; + return false; + } + +Q_SIGNALS: + void readyRead(); + +protected: + /* reimp */ void run(); + +private: + int fd; + Qt::HANDLE handle; +public: + QMutex mutex; + QWaitCondition bufferNotFullCondition; + QWaitCondition bufferNotEmptyCondition; + QWaitCondition hasStarted; + bool cancel; + bool eof; + bool error; + bool eofShortCut; + int errorCode; +private: + unsigned int rptr, wptr; + char buffer[BUFFER_SIZE+1]; // need to keep one byte free to detect empty state +}; + + +Reader::Reader( int fd_, Qt::HANDLE handle_ ) + : QThread(), + fd( fd_ ), + handle( handle_ ), + mutex(), + bufferNotFullCondition(), + bufferNotEmptyCondition(), + hasStarted(), + cancel( false ), + eof( false ), + error( false ), + eofShortCut( false ), + errorCode( 0 ), + rptr( 0 ), wptr( 0 ) +{ + +} + +Reader::~Reader() {} + + +class Writer : public QThread { + Q_OBJECT +public: + Writer( int fd, Qt::HANDLE handle ); + ~Writer(); + + qint64 writeData( const char * data, qint64 size ); + + unsigned int bytesInBuffer() const { return numBytesInBuffer; } + + bool bufferFull() const { + return numBytesInBuffer == sizeof buffer; + } + + bool bufferEmpty() const { + return numBytesInBuffer == 0; + } + +Q_SIGNALS: + void bytesWritten( qint64 ); + +protected: + /* reimp */ void run(); + +private: + int fd; + Qt::HANDLE handle; +public: + QMutex mutex; + QWaitCondition bufferEmptyCondition; + QWaitCondition bufferNotEmptyCondition; + QWaitCondition hasStarted; + bool cancel; + bool error; + int errorCode; +private: + unsigned int numBytesInBuffer; + char buffer[BUFFER_SIZE]; +}; +} + +Writer::Writer( int fd_, Qt::HANDLE handle_ ) + : QThread(), + fd( fd_ ), + handle( handle_ ), + mutex(), + bufferEmptyCondition(), + bufferNotEmptyCondition(), + hasStarted(), + cancel( false ), + error( false ), + errorCode( 0 ), + numBytesInBuffer( 0 ) +{ + +} + +Writer::~Writer() {} + + +class KDPipeIODevice::Private { + friend class ::KDPipeIODevice; + KDPipeIODevice * const q; +public: + explicit Private( KDPipeIODevice * qq ); + ~Private(); + + bool doOpen( int, Qt::HANDLE, OpenMode ); + +private: + int fd; + Qt::HANDLE handle; + Reader * reader; + Writer * writer; +}; + +KDPipeIODevice::Private::Private( KDPipeIODevice * qq ) + : q( qq ), + fd( -1 ), + handle( 0 ), + reader( 0 ), + writer( 0 ) +{ + +} + + +KDPipeIODevice::Private::~Private() {} + + +KDPipeIODevice::KDPipeIODevice( QObject * p ) + : QIODevice( p ), d( new Private( this ) ) +{ + KDAB_CHECK_CTOR; +} + +KDPipeIODevice::KDPipeIODevice( int fd, OpenMode mode, QObject * p ) + : QIODevice( p ), d( new Private( this ) ) +{ + KDAB_CHECK_CTOR; + open( fd, mode ); +} + +KDPipeIODevice::KDPipeIODevice( Qt::HANDLE handle, OpenMode mode, QObject * p ) + : QIODevice( p ), d( new Private( this ) ) +{ + KDAB_CHECK_CTOR; + open( handle, mode ); +} + +KDPipeIODevice::~KDPipeIODevice() { KDAB_CHECK_DTOR; + if ( isOpen() ) + close(); + delete d; d = 0; +} + + +bool KDPipeIODevice::open( int fd, OpenMode mode ) { KDAB_CHECK_THIS; + +#ifdef Q_OS_WIN32 + return d->doOpen( fd, (HANDLE)_get_osfhandle( fd ), mode ); +#else + return d->doOpen( fd, 0, mode ); +#endif + +} + +bool KDPipeIODevice::open( Qt::HANDLE h, OpenMode mode ) { KDAB_CHECK_THIS; + +#ifdef Q_OS_WIN32 + return d->doOpen( 0, h, mode ); +#else + Q_UNUSED( h ); + Q_UNUSED( mode ); + assert( !"KDPipeIODevice::open( Qt::HANDLE, OpenMode ) should never be called except on Windows." ); +#endif + +} + +bool KDPipeIODevice::Private::doOpen( int fd_, Qt::HANDLE handle_, OpenMode mode_ ) { + + if ( q->isOpen() || fd_ < 0 ) + return false; + +#ifdef Q_OS_WIN32 + if ( !handle_ ) + return false; +#endif + + if ( !(mode_ & ReadWrite) ) + return false; // need to have at least read -or- write + + fd = fd_; + handle = handle_; + + std::auto_ptr reader_; + std::auto_ptr writer_; + + if ( mode_ & ReadOnly ) { + reader_.reset( new Reader( fd_, handle_ ) ); + LOCKED( reader_ ); + reader_->start( QThread::HighestPriority ); + if ( !reader_->hasStarted.wait( &reader_->mutex, 1000 ) ) + return false; + connect( reader_.get(), SIGNAL(readyRead()), q, SIGNAL(readyRead()), Qt::QueuedConnection ); + } + if ( mode_ & WriteOnly ) { + writer_.reset( new Writer( fd_, handle_ ) ); + LOCKED( writer_ ); + writer_->start( QThread::HighestPriority ); + if ( !writer_->hasStarted.wait( &writer_->mutex, 1000 ) ) + return false; + connect( writer_.get(), SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)), Qt::QueuedConnection ); + } + + // commit to *this: + fd = fd_; + handle = handle_; + reader = reader_.release(); + writer = writer_.release(); + + q->setOpenMode( mode_|Unbuffered ); + + return true; +} + +int KDPipeIODevice::descriptor() const { KDAB_CHECK_THIS; + return d->fd; +} + +Qt::HANDLE KDPipeIODevice::handle() const { KDAB_CHECK_THIS; + return d->handle; +} + +qint64 KDPipeIODevice::bytesAvailable() const { KDAB_CHECK_THIS; + const qint64 base = QIODevice::bytesAvailable(); + if ( d->reader ) + synchronized( d->reader ) return base + d->reader->bytesInBuffer(); + return base; +} + +qint64 KDPipeIODevice::bytesToWrite() const { KDAB_CHECK_THIS; + const qint64 base = QIODevice::bytesToWrite(); + if ( d->writer ) + synchronized( d->writer ) return base + d->writer->bytesInBuffer(); + return base; +} + +bool KDPipeIODevice::canReadLine() const { KDAB_CHECK_THIS; + if ( QIODevice::canReadLine() ) + return true; + if ( d->reader ) + synchronized( d->reader ) return d->reader->bufferContains( '\n' ); + return true; +} + +bool KDPipeIODevice::isSequential() const { + return true; +} + +bool KDPipeIODevice::atEnd() const { KDAB_CHECK_THIS; + if ( !QIODevice::atEnd() ) { + qDebug( "KDPipeIODevice::atEnd returns false since QIODevice::atEnd does (with bytesAvailable=%ld)", static_cast(bytesAvailable()) ); + return false; + } + if ( !isOpen() ) + return true; + if ( d->reader->eofShortCut ) + return true; + LOCKED( d->reader ); + const bool eof = ( d->reader->error || d->reader->eof ) && d->reader->bufferEmpty(); + if ( !eof ) { + if ( !d->reader->error && !d->reader->eof ) + qDebug( "KDPipeIODevice::atEnd returns false since !reader->error && !reader->eof" ); + if ( !d->reader->bufferEmpty() ) + qDebug( "KDPipeIODevice::atEnd returns false since !reader->bufferEmpty()" ); + } + return eof; +} + +bool KDPipeIODevice::waitForBytesWritten( int msecs ) { KDAB_CHECK_THIS; + Writer * const w = d->writer; + if ( !w ) + return true; + LOCKED( w ); + return w->bufferEmpty() || w->error || w->bufferEmptyCondition.wait( &w->mutex, msecs ) ; +} + +bool KDPipeIODevice::waitForReadyRead( int msecs ) { KDAB_CHECK_THIS; + if ( ALLOW_QIODEVICE_BUFFERING ) { + if ( bytesAvailable() > 0 ) + return true; + } + Reader * const r = d->reader; + if ( !r || r->eofShortCut ) + return true; + LOCKED( r ); + return r->bytesInBuffer() != 0 || r->eof || r->error || r->bufferNotEmptyCondition.wait( &r->mutex, msecs ) ; +} + +qint64 KDPipeIODevice::readData( char * data, qint64 maxSize ) { KDAB_CHECK_THIS; + + qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld", data, maxSize ); + + Reader * const r = d->reader; + + assert( r ); + //assert( r->isRunning() ); // wrong (might be eof, error) + assert( data || maxSize == 0 ); + assert( maxSize >= 0 ); + + if ( r->eofShortCut ) { + qDebug( "KDPipeIODevice::readData: hit eofShortCut, returning 0" ); + return 0; + } + + if ( maxSize < 0 ) + maxSize = 0; + + if ( ALLOW_QIODEVICE_BUFFERING ) { + if ( bytesAvailable() > 0 ) + maxSize = std::min( maxSize, bytesAvailable() ); // don't block + } + + LOCKED( r ); + if ( /* maxSize > 0 && */ r->bufferEmpty() && !r->error && !r->eof ) { // ### block on maxSize == 0? + qDebug( "KDPipeIODevice::readData: waiting for bufferNotEmptyCondition" ); + r->bufferNotEmptyCondition.wait( &r->mutex ); + } + + if ( r->bufferEmpty() ) { + qDebug( "KDPipeIODevice::readData: got empty buffer, signal eof" ); + // woken with an empty buffer must mean either EOF or error: + assert( r->eof || r->error ); + r->eofShortCut = true; + return r->eof ? 0 : -1 ; + } + + qDebug( "KDPipeIODevice::readData: got bufferNotEmptyCondition, trying to read %lld bytes", maxSize ); + const qint64 bytesRead = r->readData( data, maxSize ); + qDebug( "KDPipeIODevice::readData: read %lld bytes", bytesRead ); + return bytesRead; +} + +qint64 Reader::readData( char * data, qint64 maxSize ) { + + qint64 numRead = rptr < wptr ? wptr - rptr : sizeof buffer - rptr ; + if ( numRead > maxSize ) + numRead = maxSize; + + qDebug( "KDPipeIODevice::readData: data=%p, maxSize=%lld; rptr=%u, wptr=%u (bytesInBuffer=%u); -> numRead=%lld", + data, maxSize, rptr, wptr, bytesInBuffer(), numRead ); + + std::memcpy( data, buffer + rptr, numRead ); + + rptr = ( rptr + numRead ) % sizeof buffer ; + + if ( !bufferFull() ) { + qDebug( "KDPipeIODevice::readData: signal bufferNotFullCondition" ); + bufferNotFullCondition.wakeAll(); + } + + return numRead; +} + +qint64 KDPipeIODevice::writeData( const char * data, qint64 size ) { KDAB_CHECK_THIS; + + Writer * const w = d->writer; + + assert( w ); + assert( w->error || w->isRunning() ); + assert( data || size == 0 ); + assert( size >= 0 ); + + LOCKED( w ); + + while ( !w->error && !w->bufferEmpty() ) + w->bufferEmptyCondition.wait( &w->mutex ); + + if ( w->error ) + return -1; + + assert( w->bufferEmpty() ); + + return w->writeData( data, size ); +} + +qint64 Writer::writeData( const char * data, qint64 size ) { + + assert( bufferEmpty() ); + + if ( size > static_cast( sizeof buffer ) ) + size = sizeof buffer; + + std::memcpy( buffer, data, size ); + + numBytesInBuffer = size; + + if ( !bufferEmpty() ) + bufferNotEmptyCondition.wakeAll(); + + return size; +} + +void KDPipeIODevice::close() { KDAB_CHECK_THIS; + + if ( !isOpen() ) + return; + + // tell clients we're about to close: + emit aboutToClose(); + + if ( d->writer && bytesToWrite() > 0 ) + waitForBytesWritten( -1 ); + + assert( bytesToWrite() == 0 ); + + if ( Reader * & r = d->reader ) { + synchronized( r ) { + // tell thread to cancel: + r->cancel = true; + // and wake it, so it can terminate: + r->bufferNotFullCondition.wakeAll(); + } + r->wait(); + delete r; r = 0; + } + if ( Writer * & w = d->writer ) { + synchronized( w ) { + // tell thread to cancel: + w->cancel = true; + // and wake it, so it can terminate: + w->bufferNotEmptyCondition.wakeAll(); + } + w->wait(); + delete w; w = 0; + } + +#ifdef Q_OS_WIN32 + CloseHandle( d->handle ); +#else + ::close( d->fd ); +#endif + + setOpenMode( NotOpen ); + d->fd = -1; + d->handle = 0; +} + +void Reader::run() { + + LOCKED( this ); + + // too bad QThread doesn't have that itself; a signal isn't enough + hasStarted.wakeAll(); + + qDebug( "Reader::run: started" ); + + while ( true ) { + + while ( !cancel && bufferFull() ) { + bufferNotEmptyCondition.wakeAll(); + qDebug( "Reader::run: buffer is full, going to sleep" ); + bufferNotFullCondition.wait( &mutex ); + qDebug( "Reader::run: woke up" ); + } + + if ( cancel ) { + qDebug( "Reader::run: detected cancel" ); + goto leave; + } + + if ( rptr == wptr ) // optimize for larger chunks in case the buffer is empty + rptr = wptr = 0; + + unsigned int numBytes = ( rptr + sizeof buffer - wptr - 1 ) % sizeof buffer; + if ( numBytes > sizeof buffer - wptr ) + numBytes = sizeof buffer - wptr; + + qDebug( "Reader::run: rptr=%d, wptr=%d -> numBytes=%d", rptr, wptr, numBytes ); + + assert( numBytes > 0 ); + + qDebug( "Reader::run: trying to read %d bytes", numBytes ); +#ifdef Q_OS_WIN32 + DWORD numRead; + mutex.unlock(); + const bool ok = ReadFile( handle, buffer + wptr, numBytes, &numRead, 0 ); + mutex.lock(); + if ( !ok ) { + errorCode = static_cast( GetLastError() ); + if ( errorCode == ERROR_BROKEN_PIPE ) { + qDebug( "Reader::run: got eof" ); + eof = true; + } else { + qDebug( "Reader::run: got error: %d", errorCode ); + error = true; + } + goto leave; + } +#else + qint64 numRead; + mutex.unlock(); + do { + numRead = ::read( fd, buffer + wptr, numBytes ); + } while ( numRead == -1 && errno == EINTR ); + mutex.lock(); + + if ( numRead < 0 ) { + errorCode = errno; + error = true; + qDebug( "Reader::run: got error: %d", errorCode ); + goto leave; + } +#endif + qDebug( "Reader::run: read %ld bytes", static_cast(numRead) ); + if ( numRead == 0 ) { + qDebug( "Reader::run: eof detected" ); + eof = true; + goto leave; + } + + if ( cancel ) { + qDebug( "Reader::run: detected cancel" ); + goto leave; + } + qDebug( "Reader::run: buffer before: rptr=%4d, wptr=%4d", rptr, wptr ); + wptr = ( wptr + numRead ) % sizeof buffer; + qDebug( "Reader::run: buffer after: rptr=%4d, wptr=%4d", rptr, wptr ); + if ( !bufferEmpty() ) { + qDebug( "Reader::run: buffer no longer empty, waking everyone" ); + bufferNotEmptyCondition.wakeAll(); + emit readyRead(); + } + } + leave: + qDebug( "Reader::run: terminating" ); + bufferNotEmptyCondition.wakeAll(); + emit readyRead(); +} + +void Writer::run() { + + LOCKED( this ); + + // too bad QThread doesn't have that itself; a signal isn't enough + hasStarted.wakeAll(); + + qDebug( "Writer::run: started" ); + + while ( true ) { + + while ( !cancel && bufferEmpty() ) { + bufferEmptyCondition.wakeAll(); + qDebug( "Writer::run: buffer is empty, going to sleep" ); + bufferNotEmptyCondition.wait( &mutex ); + qDebug( "Writer::run: woke up" ); + } + + if ( cancel ) { + qDebug( "Writer::run: detected cancel" ); + goto leave; + } + + assert( numBytesInBuffer > 0 ); + + qDebug( "Writer::run: Trying to write %u bytes", numBytesInBuffer ); + qint64 totalWritten = 0; + do { + mutex.unlock(); +#ifdef Q_OS_WIN32 + DWORD numWritten; + if ( !WriteFile( handle, buffer + totalWritten, numBytesInBuffer - totalWritten, &numWritten, 0 ) ) { + mutex.lock(); + errorCode = static_cast( GetLastError() ); + qDebug( "Writer::run: got error code: %d", errorCode ); + error = true; + goto leave; + } +#else + qint64 numWritten; + do { + numWritten = ::write( fd, buffer + totalWritten, numBytesInBuffer - totalWritten ); + } while ( numWritten == -1 && errno == EINTR ); + + if ( numWritten < 0 ) { + mutex.lock(); + errorCode = errno; + qDebug( "Writer::run: got error code: %d", errorCode ); + error = true; + goto leave; + } +#endif + totalWritten += numWritten; + mutex.lock(); + } while ( totalWritten < numBytesInBuffer ); + + qDebug( "Writer::run: wrote %lld bytes", totalWritten ); + + numBytesInBuffer = 0; + bufferEmptyCondition.wakeAll(); + emit bytesWritten( totalWritten ); + } + leave: + qDebug( "Writer::run: terminating" ); + numBytesInBuffer = 0; + bufferEmptyCondition.wakeAll(); + emit bytesWritten( 0 ); +} + +// static +std::pair KDPipeIODevice::makePairOfConnectedPipes() { + KDPipeIODevice * read = 0; + KDPipeIODevice * write = 0; +#ifdef Q_OS_WIN32 + HANDLE rh; + HANDLE wh; + SECURITY_ATTRIBUTES sa; + memset( &sa, 0, sizeof(sa) ); + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + if ( CreatePipe( &rh, &wh, &sa, BUFFER_SIZE ) ) { + read = new KDPipeIODevice; + read->open( rh, ReadOnly ); + write = new KDPipeIODevice; + write->open( wh, WriteOnly ); + } +#else + int fds[2]; + if ( pipe( fds ) == 0 ) { + read = new KDPipeIODevice; + read->open( fds[0], ReadOnly ); + write = new KDPipeIODevice; + write->open( fds[1], WriteOnly ); + } +#endif + return std::make_pair( read, write ); +} + +#ifdef KDAB_DEFINE_CHECKS +KDAB_DEFINE_CHECKS( KDPipeIODevice ) { + if ( !isOpen() ) { + assert( openMode() == NotOpen ); + assert( !d->reader ); + assert( !d->writer ); +#ifdef Q_OS_WIN32 + assert( !d->handle ); +#else + assert( d->fd < 0 ); +#endif + } else { + assert( openMode() != NotOpen ); + assert( openMode() & ReadWrite ); + if ( openMode() & ReadOnly ) { + assert( d->reader ); + synchronized( d->reader ) + assert( d->reader->eof || d->reader->error || d->reader->isRunning() ); + } + if ( openMode() & WriteOnly ) { + assert( d->writer ); + synchronized( d->writer ) + assert( d->writer->error || d->writer->isRunning() ); + } +#ifdef Q_OS_WIN32 + assert( d->handle ); +#else + assert( d->fd >= 0 ); +#endif + } +} +#endif // KDAB_DEFINE_CHECKS + +#include "moc_kdpipeiodevice.cpp" +#include "kdpipeiodevice.moc" diff --git a/gpgme/kdpipeiodevice.h b/gpgme/kdpipeiodevice.h new file mode 100644 index 00000000..ce2873f9 --- /dev/null +++ b/gpgme/kdpipeiodevice.h @@ -0,0 +1,66 @@ +/* + Copyright (C) 2007 Klarälvdalens Datakonsult AB + + KDPipeIODevice is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + KDPipeIODevice is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with KDPipeIODevice; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef __KDTOOLSCORE_KDPIPEIODEVICE_H__ +#define __KDTOOLSCORE_KDPIPEIODEVICE_H__ + +#include + +#include + +//#include "checker.h" + +class KDPipeIODevice : public QIODevice { + Q_OBJECT + //KDAB_MAKE_CHECKABLE( KDPipeIODevice ) +public: + explicit KDPipeIODevice( QObject * parent=0 ); + explicit KDPipeIODevice( int fd, OpenMode=ReadOnly, QObject * parent=0 ); + explicit KDPipeIODevice( Qt::HANDLE handle, OpenMode=ReadOnly, QObject * parent=0 ); + ~KDPipeIODevice(); + + static std::pair makePairOfConnectedPipes(); + + bool open( int fd, OpenMode mode=ReadOnly ); + bool open( Qt::HANDLE handle, OpenMode mode=ReadOnly ); + + Qt::HANDLE handle() const; + int descriptor() const; + + /* reimp */ qint64 bytesAvailable() const; + /* reimp */ qint64 bytesToWrite() const; + /* reimp */ bool canReadLine() const; + /* reimp */ void close(); + /* reimp */ bool isSequential() const; + /* reimp */ bool atEnd() const; + + /* reimp */ bool waitForBytesWritten( int msecs ); + /* reimp */ bool waitForReadyRead( int msecs ); + +protected: + /* reimp */ qint64 readData( char * data, qint64 maxSize ); + /* reimp */ qint64 writeData( const char * data, qint64 maxSize ); + +private: + class Private; + Private * d; +}; + +#endif /* __KDTOOLSCORE_KDPIPEIODEVICE_H__ */ + diff --git a/gpgme/kdpipeiodevice.moc b/gpgme/kdpipeiodevice.moc new file mode 100644 index 00000000..e00d0bce --- /dev/null +++ b/gpgme/kdpipeiodevice.moc @@ -0,0 +1,132 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'kdpipeiodevice.cpp' +** +** Created: Mon Aug 27 15:17:18 2007 +** by: The Qt Meta Object Compiler version 59 (Qt 4.3.0) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'kdpipeiodevice.cpp' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 59 +#error "This file was generated using the moc from 4.3.0. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +static const uint qt_meta_data_Reader[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 1, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // signals: signature, parameters, type, tag, flags + 8, 7, 7, 7, 0x05, + + 0 // eod +}; + +static const char qt_meta_stringdata_Reader[] = { + "Reader\0\0readyRead()\0" +}; + +const QMetaObject Reader::staticMetaObject = { + { &QThread::staticMetaObject, qt_meta_stringdata_Reader, + qt_meta_data_Reader, 0 } +}; + +const QMetaObject *Reader::metaObject() const +{ + return &staticMetaObject; +} + +void *Reader::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Reader)) + return static_cast(const_cast< Reader*>(this)); + return QThread::qt_metacast(_clname); +} + +int Reader::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QThread::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: readyRead(); break; + } + _id -= 1; + } + return _id; +} + +// SIGNAL 0 +void Reader::readyRead() +{ + QMetaObject::activate(this, &staticMetaObject, 0, 0); +} +static const uint qt_meta_data_Writer[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 1, 10, // methods + 0, 0, // properties + 0, 0, // enums/sets + + // signals: signature, parameters, type, tag, flags + 8, 7, 7, 7, 0x05, + + 0 // eod +}; + +static const char qt_meta_stringdata_Writer[] = { + "Writer\0\0bytesWritten(qint64)\0" +}; + +const QMetaObject Writer::staticMetaObject = { + { &QThread::staticMetaObject, qt_meta_stringdata_Writer, + qt_meta_data_Writer, 0 } +}; + +const QMetaObject *Writer::metaObject() const +{ + return &staticMetaObject; +} + +void *Writer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_Writer)) + return static_cast(const_cast< Writer*>(this)); + return QThread::qt_metacast(_clname); +} + +int Writer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QThread::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: bytesWritten((*reinterpret_cast< qint64(*)>(_a[1]))); break; + } + _id -= 1; + } + return _id; +} + +// SIGNAL 0 +void Writer::bytesWritten(qint64 _t1) +{ + void *_a[] = { 0, const_cast(reinterpret_cast(&_t1)) }; + QMetaObject::activate(this, &staticMetaObject, 0, _a); +} diff --git a/gpgme/moc_kdpipeiodevice.cpp b/gpgme/moc_kdpipeiodevice.cpp new file mode 100644 index 00000000..eac7b23a --- /dev/null +++ b/gpgme/moc_kdpipeiodevice.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'kdpipeiodevice.h' +** +** Created: Mon Aug 27 15:17:18 2007 +** by: The Qt Meta Object Compiler version 59 (Qt 4.3.0) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "kdpipeiodevice.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'kdpipeiodevice.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 59 +#error "This file was generated using the moc from 4.3.0. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +static const uint qt_meta_data_KDPipeIODevice[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + + 0 // eod +}; + +static const char qt_meta_stringdata_KDPipeIODevice[] = { + "KDPipeIODevice\0" +}; + +const QMetaObject KDPipeIODevice::staticMetaObject = { + { &QIODevice::staticMetaObject, qt_meta_stringdata_KDPipeIODevice, + qt_meta_data_KDPipeIODevice, 0 } +}; + +const QMetaObject *KDPipeIODevice::metaObject() const +{ + return &staticMetaObject; +} + +void *KDPipeIODevice::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_KDPipeIODevice)) + return static_cast(const_cast< KDPipeIODevice*>(this)); + return QIODevice::qt_metacast(_clname); +} + +int KDPipeIODevice::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QIODevice::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} diff --git a/gpgme/posix-sema.c b/gpgme/posix-sema.c index fa2b8d37..4ec3abb0 100644 --- a/gpgme/posix-sema.c +++ b/gpgme/posix-sema.c @@ -1,6 +1,6 @@ /* posix-sema.c Copyright (C) 2001 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH This file is part of GPGME. @@ -45,18 +45,18 @@ _gpgme_sema_subsystem_init () void _gpgme_sema_cs_enter (struct critsect_s *s) { - _gpgme_ath_mutex_lock (&s->private); + _gpgme_ath_mutex_lock (&s->priv); } void _gpgme_sema_cs_leave (struct critsect_s *s) { - _gpgme_ath_mutex_unlock (&s->private); + _gpgme_ath_mutex_unlock (&s->priv); } void _gpgme_sema_cs_destroy (struct critsect_s *s) { - _gpgme_ath_mutex_destroy (&s->private); - s->private = NULL; + _gpgme_ath_mutex_destroy (&s->priv); + s->priv = NULL; } diff --git a/gpgme/sema.h b/gpgme/sema.h index 0ee20742..7d3870e9 100644 --- a/gpgme/sema.h +++ b/gpgme/sema.h @@ -1,6 +1,6 @@ /* sema.h - Definitions for semaphores. Copyright (C) 2000 Werner Koch (dd9jn) - Copyright (C) 2001, 2003, 2004 g10 Code GmbH + Copyright (C) 2001, 2003, 2004, 2007 g10 Code GmbH This file is part of GPGME. @@ -25,7 +25,7 @@ struct critsect_s { const char *name; - void *private; + void *priv; }; #define DEFINE_GLOBAL_LOCK(name) \ @@ -39,7 +39,7 @@ struct critsect_s do \ { \ (a).name = #a; \ - (a).private = NULL; \ + (a).priv = NULL; \ } \ while (0) #define DESTROY_LOCK(name) _gpgme_sema_cs_destroy (&(name)) diff --git a/gpgme/w32-glib-io.c b/gpgme/w32-glib-io.c index 232ed533..a0301b67 100644 --- a/gpgme/w32-glib-io.c +++ b/gpgme/w32-glib-io.c @@ -97,14 +97,23 @@ find_channel (int fd, int create) return giochannel_table[fd]; } -/* Look up the giochannel for "file descriptor" FD. */ -GIOChannel * + +/* Compatibility interface. Obsolete. */ +void * gpgme_get_giochannel (int fd) { return find_channel (fd, 0); } +/* Look up the giochannel for "file descriptor" FD. */ +void * +gpgme_get_fdptr (int fd) +{ + return find_channel (fd, 0); +} + + /* Write the printable version of FD to the buffer BUF of length BUFLEN. The printable version is the representation on the command line that the child process expects. */ diff --git a/gpgme/w32-io.c b/gpgme/w32-io.c index dfdf45e5..a4a42569 100644 --- a/gpgme/w32-io.c +++ b/gpgme/w32-io.c @@ -1403,9 +1403,9 @@ _gpgme_io_dup (int fd) } -/* The following interface is only useful for GPGME Glib. */ +/* The following interface is only useful for GPGME Glib and Qt. */ -/* Look up the giochannel for file descriptor FD. */ +/* Compatibility interface, obsolete. */ void * gpgme_get_giochannel (int fd) { @@ -1413,3 +1413,9 @@ gpgme_get_giochannel (int fd) } +/* Look up the giochannel or qiodevice for file descriptor FD. */ +void * +gpgme_get_fdptr (int fd) +{ + return NULL; +} diff --git a/gpgme/w32-qt-io.cpp b/gpgme/w32-qt-io.cpp new file mode 100644 index 00000000..91170667 --- /dev/null +++ b/gpgme/w32-qt-io.cpp @@ -0,0 +1,609 @@ +/* w32-glib-io.c - W32 Glib I/O functions + Copyright (C) 2000 Werner Koch (dd9jn) + Copyright (C) 2001, 2002, 2004, 2005, 2007 g10 Code GmbH + + This file is part of GPGME. + + GPGME is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + GPGME is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kdpipeiodevice.h" + +extern "C" +{ +#include "util.h" +#include "priv-io.h" +#include "sema.h" +#include "debug.h" +} + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 +#endif +#endif + + +/* This file is an ugly hack to get GPGME working with Qt on Windows + targets. On Windows, you can not select() on file descriptors. + The only way to check if there is something to read is to read + something. This means that GPGME can not let Qt check for data + without letting Qt also handle the data on Windows targets. + + The ugly consequence is that we need to work on QIODevices in + GPGME, creating a Qt dependency. Also, we need to export an + interface for the application to get at GPGME's QIODevices. There + is no good way to abstract all this with callbacks, because the + whole thing is also interconnected with the creation of pipes and + child processes. + + The following rule applies only to this I/O backend: + + * ALL operations must use the user defined event loop. GPGME can + not anymore provide its own event loop. This is mostly a sanity + requirement: Although we have in theory all information we need to + make the GPGME W32 code for select still work, it would be a big + complication and require changes throughout GPGME. + + Eventually, we probably have to bite the bullet and make some + really nice callback interfaces to let the user control all this at + a per-context level. */ + + +#define MAX_SLAFD 256 + +QIODevice *iodevice_table[MAX_SLAFD]; + + +static QIODevice * +find_channel (int fd, int create) +{ + if (fd < 0 || fd >= MAX_SLAFD) + return NULL; + + if (create && !iodevice_table[fd]) + iodevice_table[fd] = new KDPipeIODevice + (fd, QIODevice::ReadOnly|QIODevice::Unbuffered); + + return iodevice_table[fd]; +} + + +/* Write the printable version of FD to the buffer BUF of length + BUFLEN. The printable version is the representation on the command + line that the child process expects. */ +int +_gpgme_io_fd2str (char *buf, int buflen, int fd) +{ + return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd)); +} + + +void +_gpgme_io_subsystem_init (void) +{ +} + + +static struct +{ + _gpgme_close_notify_handler_t handler; + void *value; +} notify_table[MAX_SLAFD]; + + +int +_gpgme_io_read (int fd, void *buffer, size_t count) +{ + int saved_errno = 0; + qint64 nread; + QIODevice *chan; + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd, + "buffer=%p, count=%u", buffer, count); + + chan = find_channel (fd, 0); + if (!chan) + { + TRACE_LOG ("no channel registered"); + errno = EINVAL; + return TRACE_SYSRES (-1); + } + TRACE_LOG1 ("channel %p", chan); + + { +// GError *err = NULL; +// status = g_io_channel_read_chars (chan, (gchar *) buffer, +// count, &nread, &err); + nread = chan->read( buffer, count ); + if ( nread < 0 ) { + TRACE_LOG1 ("err %s", qPrintable( chan->errorString() ) ); + saved_errno = EIO; + nread = -1; + } + } + + TRACE_LOGBUF (buffer, nread); + + errno = saved_errno; + return TRACE_SYSRES (nread); +} + + +int +_gpgme_io_write (int fd, const void *buffer, size_t count) +{ + qint64 nwritten; + QIODevice *chan; + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd, + "buffer=%p, count=%u", buffer, count); + TRACE_LOGBUF (buffer, count); + + chan = find_channel (fd, 0); + if (!chan) + { + TRACE_LOG ("fd %d: no channel registered"); + errno = EINVAL; + return -1; + } + + nwritten = chan->write( buffer, count ); + + if (nwritten < 0) + { + nwritten = -1; + errno = EIO; + return TRACE_SYSRES(-1) + } + errno = 0; + return TRACE_SYSRES (nwritten); +} + + +int +_gpgme_io_pipe (int filedes[2], int inherit_idx) +{ + QIODevice *chan; + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes, + "inherit_idx=%i (GPGME uses it for %s)", + inherit_idx, inherit_idx ? "writing" : "reading"); + +#define PIPEBUF_SIZE 4096 + if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1) + return TRACE_SYSRES (-1); + + /* Make one end inheritable. */ + if (inherit_idx == 0) + { + int new_read; + + new_read = _dup (filedes[0]); + _close (filedes[0]); + filedes[0] = new_read; + + if (new_read < 0) + { + _close (filedes[1]); + return TRACE_SYSRES (-1); + } + } + else if (inherit_idx == 1) + { + int new_write; + + new_write = _dup (filedes[1]); + _close (filedes[1]); + filedes[1] = new_write; + + if (new_write < 0) + { + _close (filedes[0]); + return TRACE_SYSRES (-1); + } + } + + /* Now we have a pipe with the right end inheritable. The other end + should have a giochannel. */ + chan = find_channel (filedes[1 - inherit_idx], 1); + if (!chan) + { + int saved_errno = errno; + _close (filedes[0]); + _close (filedes[1]); + errno = saved_errno; + return TRACE_SYSRES (-1); + } + + return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p", + filedes[0], (HANDLE) _get_osfhandle (filedes[0]), + filedes[1], (HANDLE) _get_osfhandle (filedes[1]), + chan); +} + + +int +_gpgme_io_close (int fd) +{ + QIODevice *chan; + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd); + + if (fd < 0 || fd >= MAX_SLAFD) + { + errno = EBADF; + return TRACE_SYSRES (-1); + } + + /* First call the notify handler. */ + if (notify_table[fd].handler) + { + notify_table[fd].handler (fd, notify_table[fd].value); + notify_table[fd].handler = NULL; + notify_table[fd].value = NULL; + } + + /* Then do the close. */ + chan = iodevice_table[fd]; + if (chan) + { + chan->close(); + delete chan; + iodevice_table[fd] = NULL; + } + else + _close (fd); + + return 0; +} + + +int +_gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler, + void *value) +{ + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd, + "close_handler=%p/%p", handler, value); + + assert (fd != -1); + + if (fd < 0 || fd >= (int) DIM (notify_table)) + { + errno = EINVAL; + return TRACE_SYSRES (-1); + } + notify_table[fd].handler = handler; + notify_table[fd].value = value; + return TRACE_SYSRES (0); +} + + +int +_gpgme_io_set_nonblocking (int fd) +{ + /* Qt always uses non-blocking IO, except for files, maybe, but who + uses that? */ + TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd); + + return TRACE_SYSRES (0); +} + + +static char * +build_commandline (char **argv) +{ + int i; + int n = 0; + char *buf; + char *p; + + /* We have to quote some things because under Windows the program + parses the commandline and does some unquoting. We enclose the + whole argument in double-quotes, and escape literal double-quotes + as well as backslashes with a backslash. We end up with a + trailing space at the end of the line, but that is harmless. */ + for (i = 0; argv[i]; i++) + { + p = argv[i]; + /* The leading double-quote. */ + n++; + while (*p) + { + /* An extra one for each literal that must be escaped. */ + if (*p == '\\' || *p == '"') + n++; + n++; + p++; + } + /* The trailing double-quote and the delimiter. */ + n += 2; + } + /* And a trailing zero. */ + n++; + + buf = p = malloc (n); + if (!buf) + return NULL; + for (i = 0; argv[i]; i++) + { + char *argvp = argv[i]; + + *(p++) = '"'; + while (*argvp) + { + if (*argvp == '\\' || *argvp == '"') + *(p++) = '\\'; + *(p++) = *(argvp++); + } + *(p++) = '"'; + *(p++) = ' '; + } + *(p++) = 0; + + return buf; +} + + +int +_gpgme_io_spawn (const char *path, char **argv, + struct spawn_fd_item_s *fd_child_list, + struct spawn_fd_item_s *fd_parent_list) +{ + SECURITY_ATTRIBUTES sec_attr; + PROCESS_INFORMATION pi = + { + NULL, /* returns process handle */ + 0, /* returns primary thread handle */ + 0, /* returns pid */ + 0 /* returns tid */ + }; + STARTUPINFO si; + char *envblock = NULL; + int cr_flags = CREATE_DEFAULT_ERROR_MODE + | GetPriorityClass (GetCurrentProcess ()); + int i; + char *arg_string; + int duped_stdin = 0; + int duped_stderr = 0; + HANDLE hnul = INVALID_HANDLE_VALUE; + /* FIXME. */ + int debug_me = 0; + TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path, + "path=%s", path); + i = 0; + while (argv[i]) + { + TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]); + i++; + } + + memset (&sec_attr, 0, sizeof sec_attr); + sec_attr.nLength = sizeof sec_attr; + sec_attr.bInheritHandle = FALSE; + + arg_string = build_commandline (argv); + if (!arg_string) + return TRACE_SYSRES (-1); + + memset (&si, 0, sizeof si); + si.cb = sizeof (si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = debug_me? SW_SHOW : SW_HIDE; + si.hStdInput = GetStdHandle (STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle (STD_ERROR_HANDLE); + + for (i = 0; fd_child_list[i].fd != -1; i++) + { + if (fd_child_list[i].dup_to == 0) + { + si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); + TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd, + _get_osfhandle (fd_child_list[i].fd)); + duped_stdin = 1; + } + else if (fd_child_list[i].dup_to == 1) + { + si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd); + TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd, + _get_osfhandle (fd_child_list[i].fd)); + } + else if (fd_child_list[i].dup_to == 2) + { + si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd); + TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd, + _get_osfhandle (fd_child_list[i].fd)); + duped_stderr = 1; + } + } + + if (!duped_stdin || !duped_stderr) + { + SECURITY_ATTRIBUTES sa; + + memset (&sa, 0, sizeof sa); + sa.nLength = sizeof sa; + sa.bInheritHandle = TRUE; + hnul = CreateFile ("nul", + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hnul == INVALID_HANDLE_VALUE) + { + TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d", + (int) GetLastError ()); + free (arg_string); + /* FIXME: Should translate the error code. */ + errno = EIO; + return TRACE_SYSRES (-1); + } + /* Make sure that the process has a connected stdin. */ + if (!duped_stdin) + { + si.hStdInput = hnul; + TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul); + } + /* We normally don't want all the normal output. */ + if (!duped_stderr) + { + si.hStdError = hnul; + TRACE_LOG1 ("using %d for dummy stderr", (int)hnul); + } + } + + cr_flags |= CREATE_SUSPENDED; + cr_flags |= DETACHED_PROCESS; + if (!CreateProcessA (path, + arg_string, + &sec_attr, /* process security attributes */ + &sec_attr, /* thread security attributes */ + TRUE, /* inherit handles */ + cr_flags, /* creation flags */ + envblock, /* environment */ + NULL, /* use current drive/directory */ + &si, /* startup information */ + &pi)) /* returns process information */ + { + TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ()); + free (arg_string); + /* FIXME: Should translate the error code. */ + errno = EIO; + return TRACE_SYSRES (-1); + } + + /* Close the /dev/nul handle if used. */ + if (hnul != INVALID_HANDLE_VALUE) + { + if (!CloseHandle (hnul)) + TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)", + (int) GetLastError ()); + } + + /* Close the other ends of the pipes. */ + for (i = 0; fd_parent_list[i].fd != -1; i++) + _gpgme_io_close (fd_parent_list[i].fd); + + TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, " + "dwProcessID=%d, dwThreadId=%d", + pi.hProcess, pi.hThread, + (int) pi.dwProcessId, (int) pi.dwThreadId); + + if (ResumeThread (pi.hThread) < 0) + TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ()); + + if (!CloseHandle (pi.hThread)) + TRACE_LOG1 ("CloseHandle of thread failed: ec=%d", + (int) GetLastError ()); + + TRACE_SUC1 ("process=%p", pi.hProcess); + return 0; +} + + +/* Select on the list of fds. Returns: -1 = error, 0 = timeout or + nothing to select, > 0 = number of signaled fds. */ +int +_gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock) +{ + int i; + int count; + /* Use a 1s timeout. */ + + void *dbg_help = NULL; + TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds, + "nfds=%u, nonblock=%u", nfds, nonblock); + + // we only implement the special case of nonblock == true + assert( nonblock ); + + count = 0; + + TRACE_SEQ (dbg_help, "select on [ "); + for (i = 0; i < nfds; i++) + { + if (fds[i].fd == -1) + { + fds[i].signaled = 0; + } + else if (fds[i].frozen) + { + TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd); + fds[i].signaled = 0; + } + else if (fds[i].for_read ) + { + const QIODevice * const chan = find_channel (fds[i].fd, 0); + assert (chan); + fds[i].signaled = chan->bytesAvailable() > 0 ? 1 : 0 ; + TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); + count++; + } + else if (fds[i].for_write) + { + const QIODevice * const chan = find_channel (fds[i].fd, 0); + assert (chan); + fds[i].signaled = chan->bytesToWrite() > 0 ? 0 : 1 ; + TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd); + count++; + } + } + TRACE_END (dbg_help, "]"); + + return TRACE_SYSRES (count); +} + + +int +_gpgme_io_dup (int fd) +{ + return _dup (fd); +} + + +/* Look up the qiodevice for file descriptor FD. */ +extern "C" +void * +gpgme_get_fdptr (int fd) +{ + return find_channel (fd, 0); +} + + +/* Obsolete compatibility interface. */ +extern "C" +void * +gpgme_get_giochannel (int fd) +{ + return NULL; +} + diff --git a/gpgme/w32-sema.c b/gpgme/w32-sema.c index c20f1a7b..5228cc7c 100644 --- a/gpgme/w32-sema.c +++ b/gpgme/w32-sema.c @@ -1,6 +1,6 @@ /* w32-sema.c Copyright (C) 2001 Werner Koch (dd9jn) - Copyright (C) 2001, 2002, 2004 g10 Code GmbH + Copyright (C) 2001, 2002, 2004, 2007 g10 Code GmbH This file is part of GPGME. @@ -65,7 +65,7 @@ critsect_init (struct critsect_s *s) /* first test whether it is really not initialized */ EnterCriticalSection (&init_lock); - if ( s->private ) { + if ( s->priv ) { LeaveCriticalSection (&init_lock); return; } @@ -76,7 +76,7 @@ critsect_init (struct critsect_s *s) sema_fatal ("out of core while creating critical section lock"); } InitializeCriticalSection (mp); - s->private = mp; + s->priv = mp; LeaveCriticalSection (&init_lock); } @@ -91,25 +91,25 @@ _gpgme_sema_subsystem_init () void _gpgme_sema_cs_enter ( struct critsect_s *s ) { - if (!s->private) + if (!s->priv) critsect_init (s); - EnterCriticalSection ( (CRITICAL_SECTION*)s->private ); + EnterCriticalSection ( (CRITICAL_SECTION*)s->priv ); } void _gpgme_sema_cs_leave (struct critsect_s *s) { - if (!s->private) + if (!s->priv) critsect_init (s); - LeaveCriticalSection ((CRITICAL_SECTION*)s->private); + LeaveCriticalSection ((CRITICAL_SECTION*)s->priv); } void _gpgme_sema_cs_destroy ( struct critsect_s *s ) { - if (s && s->private) { - DeleteCriticalSection ((CRITICAL_SECTION*)s->private); - free (s->private); - s->private = NULL; + if (s && s->priv) { + DeleteCriticalSection ((CRITICAL_SECTION*)s->priv); + free (s->priv); + s->priv = NULL; } } diff --git a/m4/pkg.m4 b/m4/pkg.m4 new file mode 100644 index 00000000..c29b6c05 --- /dev/null +++ b/m4/pkg.m4 @@ -0,0 +1,157 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# +# Copyright © 2004 Scott James Remnant . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# PKG_PROG_PKG_CONFIG([MIN-VERSION]) +# ---------------------------------- +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi + +fi[]dnl +])# PKG_PROG_PKG_CONFIG + +# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# Check to see whether a particular set of modules exists. Similar +# to PKG_CHECK_MODULES(), but does not set variables or print errors. +# +# +# Similar to PKG_CHECK_MODULES, make sure that the first instance of +# this or PKG_CHECK_MODULES is called, or make sure to call +# PKG_CHECK_EXISTS manually +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_ifval([$2], [$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + + +# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +# --------------------------------------------- +m4_define([_PKG_CONFIG], +[if test -n "$PKG_CONFIG"; then + if test -n "$$1"; then + pkg_cv_[]$1="$$1" + else + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], + [pkg_failed=yes]) + fi +else + pkg_failed=untried +fi[]dnl +])# _PKG_CONFIG + +# _PKG_SHORT_ERRORS_SUPPORTED +# ----------------------------- +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])# _PKG_SHORT_ERRORS_SUPPORTED + + +# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# +# +# Note that if there is a possibility the first call to +# PKG_CHECK_MODULES might not happen, you should be sure to include an +# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +# +# +# -------------------------------------------------------------- +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + ifelse([$4], , [AC_MSG_ERROR(dnl +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT +])], + [AC_MSG_RESULT([no]) + $4]) +elif test $pkg_failed = untried; then + ifelse([$4], , [AC_MSG_FAILURE(dnl +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])], + [$4]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + ifelse([$3], , :, [$3]) +fi[]dnl +])# PKG_CHECK_MODULES