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.
614 lines
16 KiB
614 lines
16 KiB
// kmacctpop.cpp |
|
// Authors: Stefan Taferner and Markus Wuebben |
|
|
|
#include "kmacctpop.moc" |
|
|
|
#include <assert.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <signal.h> |
|
#include <mimelib/mimepp.h> |
|
|
|
#include <qtextstream.h> |
|
#include <qlineedit.h> |
|
#include <qpushbutton.h> |
|
|
|
#include <kmfolder.h> |
|
#include <kmmessage.h> |
|
#include <kconfig.h> |
|
#include <kapp.h> |
|
#include <kdebug.h> |
|
|
|
#include "kmacctpop.h" |
|
#include "kalarmtimer.h" |
|
#include "kmglobal.h" |
|
#include "kbusyptr.h" |
|
#include "kmacctfolder.h" |
|
#include "kmfiltermgr.h" |
|
#include "kmbroadcaststatus.h" |
|
#include "kprocess.h" |
|
#include <klocale.h> |
|
#include <kmessagebox.h> |
|
|
|
#include <qlayout.h> |
|
#include <kwin.h> |
|
#include <kstddirs.h> |
|
#include <kbuttonbox.h> |
|
|
|
//----------------------------------------------------------------------------- |
|
KMAcctPop::KMAcctPop(KMAcctMgr* aOwner, const char* aAccountName): |
|
KMAcctPopInherited(aOwner, aAccountName) |
|
{ |
|
initMetaObject(); |
|
|
|
mStorePasswd = FALSE; |
|
mUseSSL = FALSE; |
|
mLeaveOnServer = FALSE; |
|
mRetrieveAll = TRUE; |
|
mProtocol = 3; |
|
mPort = 110; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
KMAcctPop::~KMAcctPop() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const char* KMAcctPop::type(void) const |
|
{ |
|
return "pop"; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::init(void) |
|
{ |
|
mHost = ""; |
|
mPort = 110; |
|
mLogin = ""; |
|
mPasswd = ""; |
|
mProtocol = 3; |
|
mUseSSL = FALSE; |
|
mStorePasswd = FALSE; |
|
mLeaveOnServer = FALSE; |
|
mRetrieveAll = TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::pseudoAssign(KMAccount* account) |
|
{ |
|
assert(account->type() == "pop"); |
|
KMAcctPop *acct = static_cast<KMAcctPop*>(account); |
|
setName(acct->name()); |
|
setCheckInterval(acct->checkInterval()); |
|
setCheckExclude(acct->checkExclude()); |
|
setFolder(acct->folder()); |
|
setHost(acct->host()); |
|
setPort(acct->port()); |
|
setLogin(acct->login()); |
|
setUseSSL(acct->useSSL()); |
|
setStorePasswd(acct->storePasswd()); |
|
setPasswd(acct->passwd(), acct->storePasswd()); |
|
setLeaveOnServer(acct->leaveOnServer()); |
|
setRetrieveAll(acct->retrieveAll()); |
|
setPrecommand(acct->precommand()); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::processNewMail(bool interactive) |
|
{ |
|
void (*oldHandler)(int); |
|
void (*pipeHandler)(int); |
|
bool hasNewMail; |
|
|
|
// Before we do anything else let's ignore the friggin' SIGALRM signal |
|
// This signal somehow interrupts the network functions and messed up |
|
// DwPopClient::Open(). |
|
oldHandler = signal(SIGALRM, SIG_IGN); |
|
// Another one of those nice little SIGNALS which default action is to |
|
// abort the app when received. SIGPIPE is send when e.g the client attempts |
|
// to write to a TCP socket when the connection was shutdown by the server. |
|
pipeHandler = signal(SIGPIPE, SIG_IGN); |
|
KMBroadcastStatus::instance()->reset(); |
|
KMBroadcastStatus::instance()->setStatusMsg( |
|
i18n("Preparing transmission from %1...").arg(mHost)); |
|
KMBroadcastStatus::instance()->setStatusProgressEnable( true ); |
|
hasNewMail = doProcessNewMail(interactive); |
|
KMBroadcastStatus::instance()->setStatusProgressEnable( false ); |
|
KMBroadcastStatus::instance()->reset(); |
|
|
|
signal(SIGALRM, oldHandler); |
|
signal(SIGPIPE, pipeHandler); |
|
emit finishedCheck(hasNewMail); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMAcctPop::authenticate(DwPopClient& client) |
|
{ |
|
QString passwd; |
|
QString msg = QString::null; |
|
KMPasswdDialog* dlg; |
|
bool opened = FALSE; |
|
int replyCode; |
|
KProcess precommandProcess; |
|
|
|
if(mPasswd.isEmpty() || mLogin.isEmpty()) |
|
msg = i18n("Please set Password and Username"); |
|
|
|
passwd = decryptStr(mPasswd); |
|
|
|
|
|
while (1) |
|
{ |
|
if (!msg.isNull()) |
|
{ |
|
dlg = new KMPasswdDialog(NULL, NULL, this, msg, |
|
mLogin, passwd); |
|
if (!dlg->exec()) return FALSE; |
|
delete dlg; |
|
msg = QString::null; |
|
} |
|
|
|
// Some POP servers close the connection upon failed |
|
// user/password. So we close the connection here to |
|
// be sure that everything below runs as we expect it. |
|
if (opened) |
|
{ |
|
client.Quit(); |
|
client.Close(); |
|
} |
|
|
|
// Run the pre command if there is one |
|
// Not sure if this should be outside the while loop or not - mpilone |
|
if (!runPrecommand(precommand())) |
|
return popError(QString("Couldn't execute precommand:\n") + precommand(), client); |
|
|
|
// Open connection to server |
|
if (client.Open(mHost,mPort) != '+') |
|
return popError("OPEN", client); |
|
opened = TRUE; |
|
kapp->processEvents(); |
|
|
|
// Send user name |
|
replyCode = client.User((const char*)mLogin); |
|
if (replyCode == '-') |
|
{ |
|
msg = i18n("Incorrect Username"); |
|
continue; |
|
} |
|
else if (replyCode != '+') |
|
return popError("USER", client); |
|
kapp->processEvents(); |
|
|
|
// Send password |
|
passwd = decryptStr(mPasswd); |
|
|
|
replyCode = client.Pass((const char*)passwd); |
|
if (replyCode == '-') |
|
{ |
|
msg = i18n("Incorrect Password"); |
|
continue; |
|
} |
|
else if (replyCode != '+') |
|
return popError("PASS", client); |
|
|
|
// Ok, we are done |
|
break; |
|
} |
|
|
|
KMBroadcastStatus::instance()->setStatusMsg( |
|
i18n( "Transmission completed..." )); |
|
return TRUE; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMAcctPop::doProcessNewMail(bool /* interactive */) |
|
{ |
|
DwPopClient client; |
|
QString passwd; |
|
QString response, status; |
|
int num, size; // number of all msgs / size of all msgs |
|
int id, i; // id of message to read |
|
int tmout; |
|
int dummy; |
|
char dummyStr[32]; |
|
// int replyCode; // ReplyCode need from User & Passwd call. |
|
KMMessage* msg; |
|
gotMsgs = FALSE; |
|
bool doFetchMsg; |
|
bool addedOk; //Flag if msg was delivered succesfully |
|
|
|
// is everything specified ? |
|
kapp->processEvents(); |
|
|
|
if (mHost.isEmpty() || mPort==0) |
|
{ |
|
KMessageBox::information(0,i18n("Please specify Host, Port and\n" |
|
"destination folder in the settings\n" |
|
"and try again.")); |
|
return FALSE; |
|
} |
|
|
|
client.SetReceiveTimeout(20); |
|
|
|
// Handle connect, user, and password. |
|
if (!authenticate(client)) return FALSE; |
|
|
|
if (client.Stat() != '+') return popError("STAT", client); |
|
response = client.SingleLineResponse().c_str(); |
|
sscanf(response.data(), "%3s %d %d", dummyStr, &num, &size); |
|
|
|
//#warning "*** If client.Last() cannot be found then install the latest kdesupport" |
|
if (client.Last() == '+' && !mRetrieveAll) |
|
{ |
|
response = client.SingleLineResponse().c_str(); |
|
sscanf(response.data(), "%3s %d", dummyStr, &id); |
|
id++; |
|
} |
|
else id = 1; |
|
|
|
// workaround but still is no good. If msgs are too big in size |
|
// we will get a timeout. |
|
client.SetReceiveTimeout(40); |
|
|
|
addedOk = true; |
|
|
|
// do while there are mesages to take and last msg wass added succesfully |
|
while (id <= num && addedOk) |
|
{ |
|
client.SetReceiveTimeout(40); |
|
|
|
if (KMBroadcastStatus::instance()->abortRequested()) { |
|
client.Quit(); |
|
return gotMsgs; |
|
} |
|
|
|
KMBroadcastStatus::instance()->setStatusMsg( i18n("Message ") + |
|
QString("%1/%2").arg(id).arg(num) ); |
|
KMBroadcastStatus::instance()->setStatusProgressPercent( (id*100) / num ); |
|
|
|
kapp->processEvents(); |
|
if (client.List(id) != '+') |
|
return popError("LIST", client); |
|
response = client.SingleLineResponse().c_str(); |
|
sscanf(response.data(), "%3s %d %d", dummyStr, &dummy, &size); |
|
|
|
doFetchMsg = TRUE; |
|
if (size > 4500 && !mRetrieveAll) |
|
{ |
|
// If the message is large it is cheaper to first load |
|
// the header to check the status of the message. |
|
// We will have to load the entire message with the RETR |
|
// command below, so we will not fetch the header for |
|
// small messages. |
|
if (client.Top(id,1) != '+') |
|
return popError("TOP", client); |
|
response = client.MultiLineResponse().c_str(); |
|
i = response.find("\nStatus:"); |
|
if (i<0) i = response.find("\rStatus:"); |
|
if (i>=0) |
|
{ |
|
status = response.mid(i+8,32).stripWhiteSpace(); |
|
if (strnicmp(status,"RO",2)==0 || |
|
strnicmp(status,"OR",2)==0) |
|
{ |
|
doFetchMsg=FALSE; |
|
kdDebug() << "message " << id << " is old -- no need to download it" << endl; |
|
} |
|
} |
|
} |
|
|
|
if (doFetchMsg) |
|
{ |
|
// set timeout depending on size |
|
tmout = size >> 8; |
|
if (tmout < 30) tmout = 30; |
|
client.SetReceiveTimeout(tmout); |
|
|
|
if (client.Retr(id) != '+') |
|
return popError("RETR", client); |
|
response = client.MultiLineResponse().c_str(); |
|
|
|
msg = new KMMessage; |
|
msg->fromString(response,TRUE); |
|
if (mRetrieveAll || msg->status()!=KMMsgStatusOld) |
|
addedOk = processNewMsg(msg); //added ok? Error displayed if not. |
|
else delete msg; |
|
} |
|
|
|
// If we should delete from server _and_ we added ok then delete it |
|
if(!mLeaveOnServer && addedOk) |
|
{ |
|
if(client.Dele(id) != '+') |
|
return popError("DELE",client); |
|
else |
|
kdDebug() << client.SingleLineResponse().c_str() << endl; |
|
} |
|
|
|
gotMsgs = TRUE; |
|
id++; |
|
} |
|
client.Quit(); |
|
|
|
return gotMsgs; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMAcctPop::popError(const QString aStage, DwPopClient& aClient) const |
|
{ |
|
QString msg, caption; |
|
kernel->kbp()->idle(); |
|
|
|
caption = i18n("Pop Mail Error"); |
|
|
|
// First we assume the worst: A network error |
|
if (aClient.LastFailure() != DwProtocolClient::kFailNoFailure) |
|
{ |
|
caption = i18n("Pop Mail Network Error"); |
|
msg = aClient.LastFailureStr(); |
|
} |
|
|
|
// Maybe it is an app specific error |
|
else if (aClient.LastError() != DwProtocolClient::kErrNoError) |
|
{ |
|
msg = aClient.LastErrorStr(); |
|
} |
|
|
|
// Not all commands return multiLineResponses. If they do not |
|
// they return singleLineResponses and the multiLR command return NULL |
|
else |
|
{ |
|
msg = aClient.MultiLineResponse().c_str(); |
|
if (msg.isEmpty()) msg = aClient.SingleLineResponse().c_str(); |
|
if (msg.isEmpty()) msg = i18n("Unknown error"); |
|
// Negative response by the server e.g STAT responses '- ....' |
|
} |
|
|
|
QString tmp; |
|
tmp = i18n("Account: %1\nIn %2:\n%3") |
|
.arg(name()) |
|
.arg(aStage) |
|
.arg(msg); |
|
KMessageBox::information(0, tmp, caption); |
|
//kernel->kbp()->busy(); |
|
aClient.Quit(); |
|
return gotMsgs; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::readConfig(KConfig& config) |
|
{ |
|
KMAcctPopInherited::readConfig(config); |
|
|
|
|
|
mLogin = config.readEntry("login", ""); |
|
mUseSSL = config.readNumEntry("use-ssl", FALSE); |
|
mStorePasswd = config.readNumEntry("store-passwd", TRUE); |
|
if (mStorePasswd) mPasswd = config.readEntry("passwd"); |
|
else mPasswd = ""; |
|
mHost = config.readEntry("host"); |
|
mPort = config.readNumEntry("port"); |
|
mProtocol = config.readNumEntry("protocol"); |
|
mLeaveOnServer = config.readNumEntry("leave-on-server", FALSE); |
|
mRetrieveAll = config.readNumEntry("retrieve-all", FALSE); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::writeConfig(KConfig& config) |
|
{ |
|
KMAcctPopInherited::writeConfig(config); |
|
|
|
config.writeEntry("login", mLogin); |
|
config.writeEntry("use-ssl", mUseSSL); |
|
config.writeEntry("store-passwd", mStorePasswd); |
|
if (mStorePasswd) config.writeEntry("passwd", mPasswd); |
|
else config.writeEntry("passwd", ""); |
|
|
|
config.writeEntry("host", mHost); |
|
config.writeEntry("port", static_cast<int>(mPort)); |
|
config.writeEntry("protocol", mProtocol); |
|
config.writeEntry("leave-on-server", mLeaveOnServer); |
|
config.writeEntry("retrieve-all", mRetrieveAll); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const QString KMAcctPop::encryptStr(const QString aStr) const |
|
{ |
|
unsigned int i, val; |
|
unsigned int len = aStr.length(); |
|
QCString result; |
|
result.resize(len+1); |
|
|
|
for (i=0; i<len; i++) |
|
{ |
|
val = aStr[i] - ' '; |
|
val = (255-' ') - val; |
|
result[i] = (char)(val + ' '); |
|
} |
|
result[i] = '\0'; |
|
|
|
return result; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
const QString KMAcctPop::decryptStr(const QString aStr) const |
|
{ |
|
return encryptStr(aStr); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setUseSSL(bool b) |
|
{ |
|
mUseSSL = b; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setStorePasswd(bool b) |
|
{ |
|
mStorePasswd = b; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setLeaveOnServer(bool b) |
|
{ |
|
mLeaveOnServer = b; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setRetrieveAll(bool b) |
|
{ |
|
mRetrieveAll = b; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setLogin(const QString& aLogin) |
|
{ |
|
mLogin = aLogin; |
|
} |
|
//----------------------------------------------------------------------------- |
|
const QString KMAcctPop::passwd(void) const |
|
{ |
|
return decryptStr(mPasswd); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setPasswd(const QString& aPasswd, bool aStoreInConfig) |
|
{ |
|
mPasswd = encryptStr(aPasswd); |
|
mStorePasswd = aStoreInConfig; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setHost(const QString& aHost) |
|
{ |
|
mHost = aHost; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
void KMAcctPop::setPort(unsigned short int aPort) |
|
{ |
|
mPort = aPort; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
bool KMAcctPop::setProtocol(short aProtocol) |
|
{ |
|
//assert(aProtocol==2 || aProtocol==3); |
|
if(aProtocol != 2 || aProtocol != 3) |
|
return false; |
|
mProtocol = aProtocol; |
|
return true; |
|
} |
|
|
|
|
|
//============================================================================= |
|
// |
|
// Class KMPasswdDialog |
|
// |
|
//============================================================================= |
|
|
|
KMPasswdDialog::KMPasswdDialog(QWidget *parent, const char *name, |
|
KMAcctPop *account , const QString caption, |
|
const char *login, QString passwd) |
|
:QDialog(parent,name,true) |
|
{ |
|
// This function pops up a little dialog which asks you |
|
// for a new username and password if one of them was wrong or not set. |
|
QLabel *l; |
|
|
|
kernel->kbp()->idle(); |
|
act = account; |
|
KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon()); |
|
if (!caption.isNull()) |
|
setCaption(caption); |
|
|
|
QGridLayout *gl = new QGridLayout(this, 4, 2, 10); |
|
|
|
QPixmap pix(locate("data", QString::fromLatin1("kdeui/pics/keys.png"))); |
|
if(!pix.isNull()) { |
|
l = new QLabel(this); |
|
l->setPixmap(pix); |
|
l->setFixedSize(l->sizeHint()); |
|
gl->addWidget(l, 0, 0); |
|
} |
|
|
|
l = new QLabel(i18n("You need to supply a username and a\n" |
|
"password to access this mailbox."), |
|
this); |
|
l->setFixedSize(l->sizeHint()); |
|
gl->addWidget(l, 0, 1); |
|
|
|
l = new QLabel(i18n("Login Name:"), this); |
|
l->setMinimumSize(l->sizeHint()); |
|
gl->addWidget(l, 1, 0); |
|
|
|
usernameLEdit = new QLineEdit(login, this); |
|
usernameLEdit->setFixedHeight(usernameLEdit->sizeHint().height()); |
|
usernameLEdit->setMinimumWidth(usernameLEdit->sizeHint().width()); |
|
gl->addWidget(usernameLEdit, 1, 1); |
|
|
|
l = new QLabel(i18n("Password:"), this); |
|
l->setMinimumSize(l->sizeHint()); |
|
gl->addWidget(l, 2, 0); |
|
|
|
passwdLEdit = new QLineEdit(this,"NULL"); |
|
passwdLEdit->setEchoMode(QLineEdit::Password); |
|
passwdLEdit->setText(passwd); |
|
passwdLEdit->setFixedHeight(passwdLEdit->sizeHint().height()); |
|
passwdLEdit->setMinimumWidth(passwdLEdit->sizeHint().width()); |
|
gl->addWidget(passwdLEdit, 2, 1); |
|
connect(passwdLEdit, SIGNAL(returnPressed()), |
|
SLOT(slotOkPressed())); |
|
|
|
KButtonBox *bbox = new KButtonBox(this); |
|
bbox->addStretch(1); |
|
ok = bbox->addButton(i18n("OK")); |
|
ok->setDefault(true); |
|
cancel = bbox->addButton(i18n("Cancel")); |
|
bbox->layout(); |
|
gl->addMultiCellWidget(bbox, 3, 3, 0, 1); |
|
|
|
connect(ok, SIGNAL(pressed()), |
|
this, SLOT(slotOkPressed())); |
|
connect(cancel, SIGNAL(pressed()), |
|
this, SLOT(slotCancelPressed())); |
|
|
|
if(strlen(login) > 0) |
|
passwdLEdit->setFocus(); |
|
else |
|
usernameLEdit->setFocus(); |
|
gl->activate(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMPasswdDialog::slotOkPressed() |
|
{ |
|
//kernel->kbp()->busy(); |
|
act->setLogin(usernameLEdit->text()); |
|
act->setPasswd(passwdLEdit->text(), act->storePasswd()); |
|
done(1); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
void KMPasswdDialog::slotCancelPressed() |
|
{ |
|
//kernel->kbp()->busy(); |
|
done(0); |
|
}
|
|
|