You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
836 lines
16 KiB
836 lines
16 KiB
#include "kpgp.h" |
|
|
|
#include <stdio.h> |
|
#include <time.h> |
|
#include <stdlib.h> |
|
#include <assert.h> |
|
#include <stdarg.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <sys/socket.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <sys/wait.h> |
|
#include <signal.h> |
|
|
|
#include <qfile.h> |
|
#include <qregexp.h> |
|
#include <qcursor.h> |
|
#include <qstrlist.h> |
|
#include <qlabel.h> |
|
#include <qlined.h> |
|
|
|
#include <kapp.h> |
|
#include <klocale.h> |
|
#include <kiconloader.h> |
|
|
|
#define translate(X) klocale->translate(X) |
|
|
|
|
|
static void |
|
pgpSigHandler(int) |
|
{ |
|
debug("alarm"); |
|
} |
|
|
|
|
|
Kpgp *Kpgp::kpgpObject = 0L; |
|
|
|
Kpgp::Kpgp() |
|
: input(0), signature(0), signatureID(0), keyNeeded(0), persons(), |
|
publicKeys() |
|
{ |
|
initMetaObject(); |
|
flagNoPGP = TRUE; |
|
|
|
kpgpObject=this; |
|
havePassPhrase = FALSE; |
|
storePass = FALSE; |
|
passPhrase = 0; |
|
pgpRunning = FALSE; |
|
|
|
// shouldn't we use our own config file for kpgp??? |
|
if(!checkForPGP()) return; |
|
readConfig(); |
|
|
|
// get public keys |
|
runPGP(PUBKEYS); |
|
} |
|
|
|
Kpgp::~Kpgp() |
|
{ |
|
clear(TRUE); |
|
} |
|
|
|
// ----------------- public methods ------------------------- |
|
|
|
void |
|
Kpgp::readConfig() |
|
{ |
|
KConfig *config = kapp->getConfig(); |
|
config->setGroup("Kpgp"); |
|
storePass = config->readNumEntry("storePass"); |
|
flagEncryptToSelf = config->readNumEntry("encryptToSelf"); |
|
pgpUser = config->readEntry("user"); |
|
} |
|
|
|
void |
|
Kpgp::writeConfig(bool sync) |
|
{ |
|
KConfig *config = kapp->getConfig(); |
|
config->setGroup("Kpgp"); |
|
config->writeEntry("storePass",storePass); |
|
config->writeEntry("encryptToSelf",flagEncryptToSelf); |
|
config->writeEntry("user",pgpUser); |
|
|
|
if(sync) |
|
config->sync(); |
|
} |
|
|
|
void |
|
Kpgp::setUser(const QString aUser) |
|
{ |
|
pgpUser = aUser.copy(); |
|
} |
|
|
|
bool |
|
Kpgp::setMessage(const QString mess) |
|
{ |
|
int index; |
|
|
|
clear(); |
|
input = mess; |
|
|
|
flagEncrypted = FALSE; |
|
flagSigned = FALSE; |
|
flagSigIsGood = FALSE; |
|
|
|
if((index = input.find("-----BEGIN PGP")) != -1) |
|
{ |
|
if(flagNoPGP) |
|
{ |
|
errMsg = translate("Couldn't find PGP executable.\n" |
|
"Please check your PATH is set correctly."); |
|
return FALSE; |
|
} |
|
// front and backmatter... |
|
front = input.left(index); |
|
index = input.find("-----END PGP",index); |
|
index = input.find("\n",index+1); |
|
back = input.right(input.size() - index - 1); |
|
|
|
runPGP(); |
|
return TRUE; |
|
} |
|
// debug("Kpgp: message does not contain PGP parts"); |
|
return FALSE; |
|
} |
|
|
|
const QString |
|
Kpgp::frontmatter(void) const |
|
{ |
|
return front; |
|
} |
|
|
|
const QString |
|
Kpgp::backmatter(void) const |
|
{ |
|
return back; |
|
} |
|
|
|
const QString |
|
Kpgp::message(void) const |
|
{ |
|
// do we have a deciphered text? |
|
if(!output.isEmpty()) return output; |
|
|
|
// no, then return the original one |
|
debug("Kpgp: No output!"); |
|
return input; |
|
} |
|
|
|
bool |
|
Kpgp::prepare(bool needPassPhrase) |
|
{ |
|
if(flagNoPGP) |
|
{ |
|
errMsg = translate("Could not find PGP executable.\n" |
|
"Please check your PATH is set correctly."); |
|
return FALSE; |
|
} |
|
if(needPassPhrase) |
|
{ |
|
if(!havePassPhrase) |
|
setPassPhrase(askForPass()); |
|
if(!havePassPhrase) |
|
{ |
|
errMsg = translate("The pass phrase is missing."); |
|
return FALSE; |
|
} |
|
} |
|
return TRUE; |
|
} |
|
|
|
void |
|
Kpgp::cleanupPass(void) |
|
{ |
|
if(!storePassPhrase()) |
|
{ |
|
passPhrase.replace(QRegExp(".")," "); |
|
passPhrase = 0; |
|
} |
|
} |
|
|
|
bool |
|
Kpgp::decrypt(void) |
|
{ |
|
bool retval; |
|
|
|
// do we need to do anything? |
|
if(!flagEncrypted) return TRUE; |
|
// everything prepared? |
|
if(!prepare(TRUE)) return FALSE; |
|
// ok now try to decrypt the message. |
|
retval = runPGP(DECRYPT); |
|
// erase the passphrase if we do not want to keep it |
|
cleanupPass(); |
|
|
|
return retval; |
|
} |
|
|
|
bool |
|
Kpgp::encryptFor(const QStrList& aPers, bool sign) |
|
{ |
|
int action = ENCRYPT; |
|
QString persStr; |
|
QStrList* pl; |
|
char* pers; |
|
|
|
if(!prepare(TRUE)) return FALSE; |
|
|
|
persons.clear(); |
|
pl = (QStrList*)&aPers; |
|
for(pers=pl->first(); pers; pers=pl->next()) |
|
{ |
|
persStr += "\""; |
|
persStr += pers; |
|
persStr += "\" "; |
|
persons.append(pers); |
|
} |
|
|
|
if(sign) action += SIGN; |
|
return runPGP(action, persStr); |
|
} |
|
|
|
const QStrList* |
|
Kpgp::keys(void) |
|
{ |
|
if (!prepare()) return NULL; |
|
|
|
runPGP(PUBKEYS); |
|
return &publicKeys; |
|
} |
|
|
|
bool |
|
Kpgp::havePublicKey(QString _person) |
|
{ |
|
QString str; |
|
|
|
return TRUE; // DEBUG ONLY !! |
|
|
|
for(str=publicKeys.first(); str!=0; str=publicKeys.next()) |
|
if(str.contains(_person)) return TRUE; |
|
|
|
return FALSE; |
|
} |
|
|
|
bool |
|
Kpgp::sign(void) |
|
{ |
|
if (!prepare(TRUE)) return FALSE; |
|
return runPGP(SIGN); |
|
} |
|
|
|
bool |
|
Kpgp::signKey(QString _key) |
|
{ |
|
if (!prepare(TRUE)) return FALSE; |
|
return runPGP(SIGNKEY, _key); |
|
} |
|
|
|
|
|
bool |
|
Kpgp::isEncrypted(void) const |
|
{ |
|
return flagEncrypted; |
|
} |
|
|
|
const QStrList* |
|
Kpgp::receivers(void) const |
|
{ |
|
if (persons.count()<=0) return NULL; |
|
return &persons; |
|
} |
|
|
|
const QString |
|
Kpgp::KeyToDecrypt(void) const |
|
{ |
|
return keyNeeded; |
|
} |
|
|
|
bool |
|
Kpgp::isSigned(void) const |
|
{ |
|
return flagSigned; |
|
} |
|
|
|
QString |
|
Kpgp::signedBy(void) const |
|
{ |
|
return signature; |
|
} |
|
|
|
QString |
|
Kpgp::signedByKey(void) const |
|
{ |
|
return signatureID; |
|
} |
|
|
|
bool |
|
Kpgp::goodSignature(void) const |
|
{ |
|
return flagSigIsGood; |
|
} |
|
|
|
void |
|
Kpgp::setEncryptToSelf(bool flag) |
|
{ |
|
flagEncryptToSelf = flag; |
|
} |
|
|
|
bool |
|
Kpgp::encryptToSelf(void) const |
|
{ |
|
return flagEncryptToSelf; |
|
} |
|
|
|
bool |
|
Kpgp::storePassPhrase(void) const |
|
{ |
|
return storePass; |
|
} |
|
|
|
void |
|
Kpgp::setStorePassPhrase(bool setTo) |
|
{ |
|
storePass = setTo; |
|
} |
|
|
|
void |
|
Kpgp::setPassPhrase(const QString aPass) |
|
{ |
|
if (!aPass.isEmpty()) |
|
{ |
|
passPhrase = aPass; |
|
havePassPhrase = TRUE; |
|
} |
|
else |
|
{ |
|
if (!passPhrase.isEmpty()) |
|
passPhrase.replace(QRegExp(".")," "); |
|
passPhrase = 0; |
|
havePassPhrase = FALSE; |
|
} |
|
} |
|
|
|
bool |
|
Kpgp::changePassPhrase(const QString /*oldPass*/, |
|
const QString /*newPass*/) |
|
{ |
|
//FIXME... |
|
warning(translate("Sorry, but this feature\nis still missing")); |
|
return FALSE; |
|
} |
|
|
|
void |
|
Kpgp::clear(bool erasePassPhrase) |
|
{ |
|
if(erasePassPhrase && havePassPhrase && !passPhrase.isEmpty()) |
|
{ |
|
passPhrase.replace(QRegExp(".")," "); |
|
passPhrase = 0; |
|
} |
|
// erase strings from memory |
|
#ifdef BROKEN |
|
input.replace(QRegExp(".")," "); |
|
signature.replace(QRegExp(".")," "); |
|
signatureID.replace(QRegExp(".")," "); |
|
keyNeeded.replace(QRegExp(".")," "); |
|
#endif |
|
input = 0; |
|
signature = 0; |
|
signatureID = 0; |
|
keyNeeded = 0; |
|
output = 0; |
|
info = 0; |
|
persons.clear(); |
|
|
|
flagEncrypted = FALSE; |
|
flagSigned = FALSE; |
|
flagSigIsGood = FALSE; |
|
} |
|
|
|
const QString |
|
Kpgp::lastErrorMsg(void) const |
|
{ |
|
return errMsg; |
|
} |
|
|
|
bool |
|
Kpgp::havePGP(void) const |
|
{ |
|
return !flagNoPGP; |
|
} |
|
|
|
|
|
|
|
// ------------------ static methods ---------------------------- |
|
Kpgp * |
|
Kpgp::getKpgp() |
|
{ |
|
if (!kpgpObject) |
|
{ |
|
kpgpObject = new Kpgp; |
|
kpgpObject->readConfig(); |
|
} |
|
return kpgpObject; |
|
} |
|
|
|
const QString |
|
Kpgp::askForPass(QWidget *parent) |
|
{ |
|
return KpgpPass::getPassphrase(parent); |
|
} |
|
|
|
const QString |
|
Kpgp::decode(const QString text, bool returnHTML) |
|
{ |
|
QString deciphered; |
|
|
|
if(text.find("-----BEGIN PGP") == -1) return text; |
|
Kpgp* pgp=Kpgp::getKpgp(); |
|
pgp->setMessage(text); |
|
// first check if text is encrypted |
|
if(!pgp->isEncrypted()) return text; |
|
|
|
//need to decrypt it... |
|
if(!pgp->havePassPhrase) pgp->askForPass(); |
|
pgp->decrypt(); |
|
|
|
// o.k. put it together... |
|
deciphered = pgp->frontmatter(); |
|
|
|
if(!pgp->isSigned()) { |
|
deciphered += pgp->message(); |
|
} else { |
|
if(returnHTML) deciphered += "<B>"; |
|
else deciphered += "----- "; |
|
if(pgp->goodSignature()){ |
|
deciphered += translate("Message signed by"); |
|
} else { |
|
deciphered += translate("Warning"); |
|
deciphered += ":"; |
|
deciphered += translate("Message has a bad signature from"); |
|
} |
|
deciphered += " " + pgp->signedBy(); |
|
if(returnHTML) deciphered += "</B><BR>"; |
|
else deciphered += " -----\n\n"; |
|
deciphered += pgp->message(); |
|
if(returnHTML) deciphered += "<B><BR>"; |
|
else deciphered += "\n\n----- "; |
|
deciphered += translate("End PGP signed message"); |
|
if(returnHTML) deciphered += "</B><BR>"; |
|
else deciphered += " -----\n"; |
|
} |
|
|
|
//add backmatter |
|
deciphered += pgp->backmatter(); |
|
|
|
// convert to HTML |
|
if(returnHTML) |
|
{ |
|
deciphered.replace(QRegExp("\n"),"<BR>"); |
|
deciphered.replace(QRegExp("\\x20",FALSE,FALSE)," "); // SP |
|
} |
|
//debug("Kpgp: message is: %s",(const char *)deciphered); |
|
|
|
return deciphered; |
|
} |
|
|
|
|
|
|
|
// --------------------- private functions ------------------- |
|
|
|
// check if pgp installed |
|
// currently only supports 2.6.x |
|
bool |
|
Kpgp::checkForPGP(void) |
|
{ |
|
// get path |
|
QString path; |
|
QStrList pSearchPaths; |
|
int index = 0; |
|
int lastindex = 0; |
|
|
|
flagNoPGP=TRUE; |
|
|
|
path = getenv("PATH"); |
|
while((index = path.find(":",lastindex+1)) != -1) |
|
{ |
|
pSearchPaths.append(path.mid(lastindex+1,index-lastindex-1)); |
|
lastindex = index; |
|
} |
|
if(lastindex != (int)path.size() - 2) |
|
pSearchPaths.append( path.mid(lastindex+1,path.size()-lastindex-1) ); |
|
|
|
QStrListIterator it(pSearchPaths); |
|
|
|
while ( it.current() ) |
|
{ |
|
path = it.current(); |
|
path += "/pgp"; |
|
if ( !access( path, X_OK ) ) |
|
{ |
|
flagNoPGP=FALSE; |
|
//debug("Kpgp: found pgp"); |
|
return TRUE; |
|
} |
|
++it; |
|
} |
|
debug("Kpgp: didn't find pgp"); |
|
return FALSE; |
|
} |
|
|
|
bool |
|
Kpgp::runPGP(int action, const char* args) |
|
{ |
|
int len, rc; |
|
char str[1024]; |
|
bool addUserId = FALSE; |
|
QString cmd, tmpName(256), inName, outName, errName; |
|
int infd, outfd, errfd; |
|
void (*oldsig)(int); |
|
|
|
cmd = "pgp +batchmode"; |
|
|
|
switch (action) |
|
{ |
|
case ENCRYPT: |
|
cmd += " -ea"; |
|
break; |
|
case SIGN: |
|
cmd += " -sat"; |
|
addUserId=TRUE; |
|
break; |
|
case ENCSIGN: |
|
cmd += " -seat"; |
|
addUserId=TRUE; |
|
break; |
|
case SIGNKEY: |
|
cmd += " -ks"; |
|
addUserId=TRUE; |
|
break; |
|
case DECRYPT: |
|
case TEST: |
|
break; |
|
case PUBKEYS: |
|
cmd += " -kv"; |
|
break; |
|
default: |
|
warning("kpgp: wrong action given to runPGP()"); |
|
return false; |
|
} |
|
|
|
if(addUserId && !pgpUser.isEmpty()) |
|
{ |
|
cmd += " -u \""; |
|
cmd += pgpUser; |
|
cmd += '"'; |
|
} |
|
|
|
// add additional arguments |
|
if (args) |
|
{ |
|
cmd += ' '; |
|
cmd += args; |
|
} |
|
|
|
// add passphrase |
|
if(havePassPhrase) |
|
{ |
|
sprintf(str," \"-z%s\"",(const char *)passPhrase); |
|
debug("Kpgp: phrase is: \"%s\"",str); |
|
cmd += str; |
|
} |
|
cmd += " -f"; |
|
|
|
tmpName.sprintf("/tmp/.kmail-"); |
|
inName = tmpName + "in"; |
|
outName = tmpName + "out"; |
|
errName = tmpName + "err"; |
|
|
|
cmd = cmd + " <"+inName+" >"+outName+" 2>"+errName; |
|
|
|
infd = open(inName.data(), O_RDWR|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE); |
|
if (!input.isEmpty()) write(infd, input.data(), input.length()); |
|
close(infd); |
|
|
|
debug("pgp: executing: %s", cmd.data()); |
|
oldsig = signal(SIGALRM,pgpSigHandler); |
|
alarm(5); |
|
rc = system(cmd.data()); |
|
alarm(0); |
|
signal(SIGALRM,oldsig); |
|
debug("pgp: system() rc=%d. Reading results", rc); |
|
|
|
output = 0; |
|
outfd = open(outName.data(), O_RDONLY); |
|
if (outfd >= 0) |
|
{ |
|
while ((len=read(outfd,str,1023))>0) |
|
{ |
|
str[len] ='\0'; |
|
output += str; |
|
} |
|
close(outfd); |
|
} |
|
|
|
info = 0; |
|
errfd = open(errName.data(), O_RDONLY); |
|
if (errfd >= 0) |
|
{ |
|
while ((len=read(errfd,str,1023))>0) |
|
{ |
|
str[len] ='\0'; |
|
info += str; |
|
} |
|
close(errfd); |
|
} |
|
|
|
#ifdef TESTING |
|
unlink(inName.data()); |
|
unlink(outName.data()); |
|
unlink(errName.data()); |
|
#endif |
|
|
|
debug("pgp: parsing results"); |
|
return parseInfo(action); |
|
} |
|
|
|
bool Kpgp::parseInfo(int action) |
|
{ |
|
bool returnFlag = TRUE; |
|
int index, index2; |
|
switch (action) |
|
{ |
|
case DECRYPT: |
|
case TEST: |
|
if( info.find("File contains key") != -1) |
|
{ |
|
output = ""; |
|
// FIXME: should do something with it... |
|
} |
|
|
|
if( info.find("Bad pass phrase") != -1) |
|
{ |
|
// debug("Kpgp: isEncrypted"); |
|
if(action == DECRYPT) |
|
{ |
|
errMsg = translate("Bad pass Phrase; couldn't decrypt"); |
|
debug("Kpgp: passphrase is bad"); |
|
havePassPhrase = FALSE; |
|
returnFlag = FALSE; |
|
} |
|
flagEncrypted = TRUE; |
|
// check for persons |
|
index = info.find("can only be read by:"); |
|
if(index != -1) |
|
{ |
|
index = info.find("\n",index); |
|
int end = info.find("\n\n",index); |
|
|
|
persons.clear(); |
|
while( (index2 = info.find("\n",index+1)) <= end ) |
|
{ |
|
QString item = info.mid(index+1,index2-index-1); |
|
item.stripWhiteSpace(); |
|
persons.append(item); |
|
index = index2; |
|
} |
|
} |
|
} |
|
if((index = info.find("File has signature")) != -1) |
|
{ |
|
debug("Kpgp: message is signed"); |
|
flagSigned = TRUE; |
|
flagSigIsGood = FALSE; |
|
if( info.find("Key matching expected") != -1) |
|
{ |
|
index = info.find("Key ID ",index); |
|
signatureID = info.mid(index+7,8); |
|
signature = "unknown key ID " + signatureID + " "; |
|
// FIXME: not a very good solution... |
|
flagSigIsGood = TRUE; |
|
} |
|
else |
|
{ |
|
if( info.find("Good signature") != -1 ) |
|
flagSigIsGood = TRUE; |
|
|
|
// get signer |
|
index = info.find("\"",index); |
|
index2 = info.find("\"", index+1); |
|
signature = info.mid(index+1, index2-index-1); |
|
|
|
// get key ID of signer |
|
index = info.find("key ID ",index2); |
|
signatureID = info.mid(index+7,8); |
|
} |
|
} |
|
if(info.find("Pass phrase is good") != -1) |
|
flagEncrypted = FALSE; |
|
break; |
|
|
|
case ENCRYPT: |
|
case ENCSIGN: |
|
{ |
|
index = 0; |
|
bool bad = FALSE; |
|
QString badkeys = ""; |
|
while((index = info.find("Cannot find the public key",index)) |
|
!= -1) |
|
{ |
|
bad = TRUE; |
|
index = info.find("'",index); |
|
index2 = info.find("'",index+1); |
|
badkeys += info.mid(index, index2-index+1) + ' '; |
|
} |
|
if(bad) |
|
{ |
|
badkeys.stripWhiteSpace(); |
|
badkeys.replace(QRegExp(" "),", "); |
|
errMsg.sprintf("Could not find public keys matching the\n" |
|
"userid(s) %s. These persons won't be able\n" |
|
"to read the message.", |
|
(const char *)badkeys); |
|
returnFlag = FALSE; |
|
} |
|
break; |
|
} |
|
case SIGN: |
|
if(info.find("Pass phrase is good") != -1) |
|
{ |
|
debug("Kpgp: Good Passphrase!"); |
|
flagEncrypted = TRUE; |
|
} |
|
if( info.find("Bad pass phrase") != -1) |
|
{ |
|
errMsg = translate("Bad pass Phrase; couldn't sign"); |
|
returnFlag = FALSE; |
|
havePassPhrase = FALSE; |
|
} |
|
break; |
|
case PUBKEYS: |
|
publicKeys.clear(); |
|
index = output.find("\n",1)+1; // skip first to "\n" |
|
while( (index = output.find("\n",index)) != -1) |
|
{ |
|
//parse line |
|
QString line; |
|
if( (index2 = output.find("\n",index+1)) != -1) |
|
// skip last line |
|
{ |
|
int index3 = output.find("pub ",index); |
|
|
|
if( (index3 >index2) || (index3 == -1) ) |
|
{ |
|
// second adress for the same key |
|
line = output.mid(index+1,index2-index-1); |
|
line = line.stripWhiteSpace(); |
|
} else { |
|
// line with new key |
|
int index3 = output.find( |
|
QRegExp("/[0-9][0-9]/[0-9][0-9] "), |
|
index); |
|
line = output.mid(index3+7,index2-index3-7); |
|
} |
|
//debug("kpgp: found key for %s",(const char *)line); |
|
publicKeys.append(line); |
|
} |
|
index = index2; |
|
} |
|
break; |
|
default: |
|
warning("Kpgp: bad action %d in parseInfo()", action); |
|
returnFlag = FALSE; |
|
} |
|
|
|
return returnFlag; |
|
} |
|
|
|
// ----------------------------------------------------------------------- |
|
|
|
KpgpPass::KpgpPass(QWidget *parent, const char *name) |
|
: QDialog(parent, 0, TRUE) |
|
{ |
|
KIconLoader* loader = kapp->getIconLoader(); |
|
QPixmap pixm; |
|
|
|
setCaption(name); |
|
setFixedSize(264,80); |
|
cursor = kapp->overrideCursor(); |
|
if(cursor != 0) |
|
kapp->setOverrideCursor(QCursor(ibeamCursor)); |
|
this->setCursor(QCursor(ibeamCursor)); |
|
QLabel *text = new QLabel(translate("Please enter your\nPGP passphrase"),this); |
|
text->move(56,4); |
|
text->setAutoResize(TRUE); |
|
QLabel *icon = new QLabel(this); |
|
pixm = loader->loadIcon("pgp-keys.xpm"); |
|
icon->setPixmap(pixm); |
|
icon->move(4,8); |
|
icon->resize(48,48); |
|
|
|
lineedit = new QLineEdit(this); |
|
lineedit->setEchoMode(QLineEdit::Password); |
|
lineedit->move(56, 8+text->size().height()); |
|
lineedit->resize(200, lineedit->size().height()); |
|
lineedit->setFocus(); |
|
|
|
connect(lineedit,SIGNAL(returnPressed()),this,SLOT(accept()) ); |
|
} |
|
|
|
KpgpPass::~KpgpPass() |
|
{ |
|
if(cursor != 0) |
|
kapp->restoreOverrideCursor(); |
|
} |
|
|
|
QString |
|
KpgpPass::getPassphrase(QWidget *parent) |
|
{ |
|
KpgpPass kpgppass(parent, translate("PGP Security Check")); |
|
kpgppass.exec(); |
|
return kpgppass.getPhrase().copy(); |
|
} |
|
|
|
QString |
|
KpgpPass::getPhrase() |
|
{ |
|
return lineedit->text(); |
|
} |
|
|
|
|
|
// ------------------------------------------------------------------------ |
|
#include "kpgp.moc" |
|
|
|
|