/* TODO: maybe have a queue for aux/command ?? */ #include"gpgproc.h" #include #include #include"safedelete.h" #include"qpipe.h" #ifdef Q_WS_WIN #include #include #else #include #include"sprocess.h" #endif //---------------------------------------------------------------------------- // GPGProc //---------------------------------------------------------------------------- class GPGProc::Private { public: Private() {} QByteArray statusbuf; SafeDelete sd; #ifdef Q_WS_WIN QProcess *proc; #else SProcess *proc; #endif QPipe pipeAux, pipeCommand, pipeStatus; QTimer *t; QSocketNotifier *snStatus; }; GPGProc::GPGProc() :QObject(0) { d = new Private; d->proc = 0; d->snStatus = 0; d->t = 0; } GPGProc::~GPGProc() { closePipes(); if(d->proc) { d->proc->disconnect(this); if(d->proc->isRunning()) d->proc->kill(); d->sd.deleteLater(d->proc); } delete d; } void GPGProc::closePipes() { d->pipeAux.close(); d->pipeCommand.close(); d->pipeStatus.close(); delete d->snStatus; d->snStatus = 0; delete d->t; d->t = 0; } bool GPGProc::isRunning() const { return d->proc->isRunning(); } bool GPGProc::normalExit() const { return d->proc->normalExit(); } int GPGProc::exitStatus() const { return d->proc->exitStatus(); } bool GPGProc::start(const QString &bin, const QStringList &args, bool useExtra) { if(d->proc) { closePipes(); d->proc->disconnect(this); d->sd.deleteLater(d->proc); d->proc = 0; } d->statusbuf.resize(0); if(!d->pipeAux.open()) { #ifdef GPG_DEBUG printf("Error creating pipeAux\n"); #endif closePipes(); return false; } if(!d->pipeCommand.open()) { #ifdef GPG_DEBUG printf("Error creating pipeCommand\n"); #endif closePipes(); return false; } if(!d->pipeStatus.open()) { #ifdef GPG_DEBUG printf("Error creating pipeStatus\n"); #endif closePipes(); return false; } #ifdef Q_WS_WIN if(!d->pipeAux.writeEnd().winDupHandle()) { #ifdef GPG_DEBUG printf("Error dup'ing pipeAux\n"); #endif closePipes(); return false; } if(!d->pipeCommand.writeEnd().winDupHandle()) { #ifdef GPG_DEBUG printf("Error dup'ing pipeCommand\n"); #endif closePipes(); return false; } if(!d->pipeStatus.readEnd().winDupHandle()) { #ifdef GPG_DEBUG printf("Error dup'ing pipeStatus\n"); #endif closePipes(); return false; } // timer d->t = new QTimer; connect(d->t, SIGNAL(timeout()), SLOT(timeout())); d->t->start(100); #else // status socket notifier d->pipeStatus.readEnd().setBlock(false); d->snStatus = new QSocketNotifier(d->pipeStatus.readEnd().id(), QSocketNotifier::Read); connect(d->snStatus, SIGNAL(activated(int)), SLOT(sn_activated(int))); #endif /*{ // test: write some data into the pipe QCString cs = "Foobar"; d->pipeStatus.write(cs); QByteArray buf = d->pipeStatus.readAll(); QCString cs2 = buf.data(); if(cs != cs2) printf("Pipe test failed.\n"); else printf("Pipe test succeeded.\n"); }*/ #ifdef Q_WS_WIN d->proc = new QProcess; #else d->proc = new SProcess; #endif #ifdef GPG_DEBUG printf("Pipe setup complete.\n"); #endif QStringList fullargs; fullargs += bin; fullargs += "--no-tty"; if(useExtra) { fullargs += "--enable-special-filenames"; fullargs += "--status-fd"; fullargs += d->pipeStatus.writeEnd().toString(); fullargs += "--command-fd"; fullargs += d->pipeCommand.readEnd().toString(); } for(QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) { const QString &a = (*it); if(a == "-&?") fullargs += (QString("-&") + d->pipeAux.readEnd().toString()); else fullargs += a; } // show full command #ifdef GPG_DEBUG printf("GPGProc: "); for(QStringList::ConstIterator it2 = fullargs.begin(); it2 != fullargs.end(); ++it2) { printf("%s ", (*it2).latin1()); } printf("\n"); #endif d->proc->setArguments(fullargs); #ifndef Q_WS_WIN QValueList plist; plist += d->pipeAux.writeEnd().id(); plist += d->pipeCommand.writeEnd().id(); plist += d->pipeStatus.readEnd().id(); d->proc->setClosePipeList(plist); #endif connect(d->proc, SIGNAL(readyReadStdout()), SLOT(proc_readyReadStdout())); connect(d->proc, SIGNAL(readyReadStderr()), SLOT(proc_readyReadStderr())); connect(d->proc, SIGNAL(wroteToStdin()), SLOT(proc_wroteToStdin())); connect(d->proc, SIGNAL(processExited()), SLOT(proc_processExited())); bool ok = d->proc->start(); // we don't care about these d->pipeAux.closeReadEnd(); d->pipeCommand.closeReadEnd(); d->pipeStatus.closeWriteEnd(); if(!ok) { closePipes(); d->proc->disconnect(this); d->sd.deleteLater(d->proc); d->proc = 0; return false; } return true; } void GPGProc::writeToStdin(const QByteArray &buf) { d->proc->writeToStdin(buf); } void GPGProc::writeToAux(const QByteArray &buf) { d->pipeAux.write(buf); wroteToAux(); } void GPGProc::writeToCommand(const QByteArray &buf) { d->pipeCommand.write(buf); wroteToCommand(); } void GPGProc::closeStdin() { d->proc->closeStdin(); } void GPGProc::closeAux() { d->pipeAux.close(); } void GPGProc::closeCommand() { d->pipeCommand.close(); } QByteArray GPGProc::readStdout() { return d->proc->readStdout(); } QByteArray GPGProc::readStderr() { return d->proc->readStderr(); } void GPGProc::proc_readyReadStdout() { SafeDeleteLock s(&d->sd); readyReadStdout(); } void GPGProc::proc_readyReadStderr() { SafeDeleteLock s(&d->sd); readyReadStderr(); } void GPGProc::proc_wroteToStdin() { SafeDeleteLock s(&d->sd); wroteToStdin(); } void GPGProc::proc_processExited() { SafeDeleteLock s(&d->sd); #ifdef Q_WS_WIN timeout(); delete d->t; d->t = 0; #else // maybe there is some status left?? QByteArray buf = d->pipeStatus.readAll(); if(!buf.isEmpty()) processStatusData(buf); #endif processExited(); } #ifdef Q_WS_WIN void GPGProc::timeout() { QByteArray buf = d->pipeStatus.readAll(); if(!buf.isEmpty()) processStatusData(buf); } void GPGProc::sn_activated(int) { } #else void GPGProc::timeout() { } void GPGProc::sn_activated(int) { bool done; QByteArray buf = d->pipeStatus.readAll(&done); if(done) { d->pipeStatus.close(); delete d->snStatus; d->snStatus = 0; } if(!buf.isEmpty()) processStatusData(buf); } #endif void GPGProc::processStatusData(const QByteArray &buf) { // append to the statusbuf int oldsize = d->statusbuf.size(); d->statusbuf.resize(oldsize + buf.size()); memcpy(d->statusbuf.data() + oldsize, buf.data(), buf.size()); // extract all lines QStringList list; while(1) { int n = 0; int len = d->statusbuf.size(); char *p = (char *)d->statusbuf.data(); for(; n < len; ++n) { if(p[n] == '\n') break; } // no full line if(n >= len) break; // extract the string from statusbuf ++n; QCString cs; cs.resize(n+1); memcpy(cs.data(), p, n); int newsize = len - n; memmove(p, p + n, newsize); d->statusbuf.resize(newsize); // convert to string without newline QString str = QString::fromUtf8(cs); str.truncate(str.length()-1); // ensure it has a proper header if(str.left(9) != "[GNUPG:] ") continue; // take it off str = str.mid(9); // add to the list list += str; } for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) statusLine(*it); }