Cleanup POP3 connection code.

* Use the ServerConnection class in POP3 too. This means no #ifdef USE_SSL for the connection anymore.
* I remember from the mailing list that there were problems with to many open connection using POP3. Thus, if there is an error or nothing to retrieve close the connection.
This commit is contained in:
czeidler
2012-04-27 16:04:23 +12:00
parent e2b24ac4ba
commit 3245d56441
2 changed files with 90 additions and 211 deletions
@@ -19,11 +19,8 @@
#include <unistd.h> #include <unistd.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/select.h>
#if USE_SSL #if USE_SSL
# include <openssl/ssl.h>
# include <openssl/rand.h>
#include <openssl/md5.h> #include <openssl/md5.h>
#else #else
#include "md5.h" #include "md5.h"
@@ -70,11 +67,8 @@ POP3Protocol::POP3Protocol(BMailAccountSettings* settings)
{ {
printf("POP3Protocol::POP3Protocol(BMailAccountSettings* settings)\n"); printf("POP3Protocol::POP3Protocol(BMailAccountSettings* settings)\n");
fSettings = fAccountSettings.InboundSettings().Settings(); fSettings = fAccountSettings.InboundSettings().Settings();
#ifdef USE_SSL
fUseSSL = (fSettings.FindInt32("flavor") == 1); fUseSSL = fSettings.FindInt32("flavor") == 1 ? true : false;
fSSL = NULL;
fSSLContext = NULL;
#endif
if (fSettings.FindString("destination", &fDestinationDir) != B_OK) if (fSettings.FindString("destination", &fDestinationDir) != B_OK)
fDestinationDir = "/boot/home/mail/in"; fDestinationDir = "/boot/home/mail/in";
@@ -107,6 +101,8 @@ POP3Protocol::Connect()
fSettings.FindInt32("auth_method")); fSettings.FindInt32("auth_method"));
delete[] password; delete[] password;
if (error != B_OK)
fServerConnection.Disconnect();
return error; return error;
} }
@@ -114,28 +110,10 @@ POP3Protocol::Connect()
status_t status_t
POP3Protocol::Disconnect() POP3Protocol::Disconnect()
{ {
#ifdef USE_SSL
if (!fUseSSL || fSSL)
#endif
SendCommand("QUIT" CRLF); SendCommand("QUIT" CRLF);
#ifdef USE_SSL fServerConnection.Disconnect();
if (fUseSSL) {
if (fSSL)
SSL_shutdown(fSSL);
if (fSSLContext)
SSL_CTX_free(fSSLContext);
if (fSSLBio)
BIO_free(fSSLBio);
fSSL = NULL;
fSSLContext = NULL;
fSSLBio = NULL;
}
#endif
close(fSocket);
fSocket = -1;
return B_OK; return B_OK;
} }
@@ -165,6 +143,7 @@ POP3Protocol::SyncMessages()
error = _UniqueIDs(); error = _UniqueIDs();
if (error < B_OK) { if (error < B_OK) {
ResetProgress(); ResetProgress();
Disconnect();
return error; return error;
} }
@@ -175,6 +154,7 @@ POP3Protocol::SyncMessages()
if (numMessages == 0) { if (numMessages == 0) {
CheckForDeletedMessages(); CheckForDeletedMessages();
ResetProgress(); ResetProgress();
Disconnect();
return B_OK; return B_OK;
} }
@@ -269,22 +249,30 @@ POP3Protocol::FetchBody(const entry_ref& ref)
return error; return error;
error = _UniqueIDs(); error = _UniqueIDs();
if (error < B_OK) if (error < B_OK) {
Disconnect();
return error; return error;
}
BFile file(&ref, B_READ_WRITE); BFile file(&ref, B_READ_WRITE);
status_t status = file.InitCheck(); status_t status = file.InitCheck();
if (status != B_OK) if (status != B_OK) {
Disconnect();
return status; return status;
}
char uidString[256]; char uidString[256];
BNode node(&ref); BNode node(&ref);
if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) {
Disconnect();
return B_ERROR; return B_ERROR;
}
int32 toRetrieve = fUniqueIDs.IndexOf(uidString); int32 toRetrieve = fUniqueIDs.IndexOf(uidString);
if (toRetrieve < 0) if (toRetrieve < 0) {
Disconnect();
return B_NAME_NOT_FOUND; return B_NAME_NOT_FOUND;
}
bool leaveOnServer; bool leaveOnServer;
if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK) if (fSettings.FindBool("leave_mail_on_server", &leaveOnServer) != B_OK)
@@ -294,8 +282,10 @@ POP3Protocol::FetchBody(const entry_ref& ref)
BMailMessageIO io(this, &file, toRetrieve); BMailMessageIO io(this, &file, toRetrieve);
// read body // read body
status = io.Seek(0, SEEK_END); status = io.Seek(0, SEEK_END);
if (status < 0) if (status < 0) {
Disconnect();
return status; return status;
}
NotifyBodyFetched(ref, &file); NotifyBodyFetched(ref, &file);
@@ -318,13 +308,18 @@ POP3Protocol::DeleteMessage(const entry_ref& ref)
return error; return error;
error = _UniqueIDs(); error = _UniqueIDs();
if (error < B_OK) if (error < B_OK) {
Disconnect();
return error; return error;
}
char uidString[256]; char uidString[256];
BNode node(&ref); BNode node(&ref);
if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) if (node.ReadAttr("MAIL:unique_id", B_STRING_TYPE, 0, uidString, 256) < 0) {
Disconnect();
return B_ERROR; return B_ERROR;
}
#if DEBUG #if DEBUG
printf("DeleteMessage: ID is %d\n", (int)fUniqueIDs.IndexOf(uidString)); printf("DeleteMessage: ID is %d\n", (int)fUniqueIDs.IndexOf(uidString));
// What should we use for int32 instead of %d? // What should we use for int32 instead of %d?
@@ -342,13 +337,8 @@ POP3Protocol::Open(const char *server, int port, int)
ReportProgress(0, 0, B_TRANSLATE("Connecting to POP3 server" ReportProgress(0, 0, B_TRANSLATE("Connecting to POP3 server"
B_UTF8_ELLIPSIS)); B_UTF8_ELLIPSIS));
if (port <= 0) { if (port <= 0)
#ifdef USE_SSL
port = fUseSSL ? 995 : 110; port = fUseSSL ? 995 : 110;
#else
port = 110;
#endif
}
fLog = ""; fLog = "";
@@ -376,63 +366,19 @@ POP3Protocol::Open(const char *server, int port, int)
return B_NAME_NOT_FOUND; return B_NAME_NOT_FOUND;
} }
fSocket = socket(AF_INET, SOCK_STREAM, 0); status_t status = B_ERROR;
if (fSocket >= 0) { if (fUseSSL)
struct sockaddr_in saAddr; status = fServerConnection.ConnectSSL(server, port);
memset(&saAddr, 0, sizeof(saAddr)); else
saAddr.sin_family = AF_INET; status = fServerConnection.ConnectSocket(server, port);
saAddr.sin_port = htons(port); if (status != B_OK)
saAddr.sin_addr.s_addr = hostIP; return status;
int result = connect(fSocket, (struct sockaddr *) &saAddr,
sizeof(saAddr));
if (result < 0) {
close(fSocket);
fSocket = -1;
error_msg << ": " << strerror(errno);
ShowError(error_msg.String());
return errno;
}
} else {
error_msg << B_TRANSLATE(": Could not allocate socket.");
ShowError(error_msg.String());
return B_ERROR;
}
#ifdef USE_SSL
if (fUseSSL) {
SSL_library_init();
SSL_load_error_strings();
RAND_seed(this,sizeof(POP3Protocol));
/*--- Because we're an add-on loaded at an unpredictable time, all
the memory addresses and things contained in ourself are
esssentially random. */
fSSLContext = SSL_CTX_new(SSLv23_method());
fSSL = SSL_new(fSSLContext);
fSSLBio = BIO_new_socket(fSocket, BIO_NOCLOSE);
SSL_set_bio(fSSL, fSSLBio, fSSLBio);
if (SSL_connect(fSSL) <= 0) {
BString error;
error << "Could not connect to POP3 server "
<< fSettings.FindString("server");
if (port != 995)
error << ":" << port;
error << ". (SSL connection error)";
ShowError(error.String());
SSL_CTX_free(fSSLContext);
close(fSocket);
return B_ERROR;
}
}
#endif
BString line; BString line;
status_t err = ReceiveLine(line); status_t err = ReceiveLine(line);
if (err < 0) { if (err < 0) {
close(fSocket); fServerConnection.Disconnect();
fSocket = -1;
error_msg << ": " << strerror(err); error_msg << ": " << strerror(err);
ShowError(error_msg.String()); ShowError(error_msg.String());
return B_ERROR; return B_ERROR;
@@ -446,6 +392,7 @@ POP3Protocol::Open(const char *server, int port, int)
error_msg << B_TRANSLATE(": No reply.\n"); error_msg << B_TRANSLATE(": No reply.\n");
ShowError(error_msg.String()); ShowError(error_msg.String());
fServerConnection.Disconnect();
return B_ERROR; return B_ERROR;
} }
@@ -679,37 +626,19 @@ POP3Protocol::RetrieveInternal(const char *command, int32 message,
if (SendCommand(command) != B_OK) if (SendCommand(command) != B_OK)
return B_ERROR; return B_ERROR;
struct timeval tv;
tv.tv_sec = POP3_RETRIEVAL_TIMEOUT / 1000000;
tv.tv_usec = POP3_RETRIEVAL_TIMEOUT % 1000000;
struct fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fSocket, &readSet);
while (cont) { while (cont) {
int result = 0; status_t result = fServerConnection.WaitForData(POP3_RETRIEVAL_TIMEOUT);
if (result == B_TIMED_OUT) {
#ifdef USE_SSL
if (fUseSSL && SSL_pending(fSSL))
result = 1;
else
#endif
result = select(fSocket + 1, &readSet, NULL, NULL, &tv);
if (result == 0) {
// No data available, even after waiting a minute. // No data available, even after waiting a minute.
fLog = "POP3 timeout - no data received after a long wait."; fLog = "POP3 timeout - no data received after a long wait.";
return B_ERROR; return B_ERROR;
} }
if (amountToReceive > bufSize - 1 - amountInBuffer) if (amountToReceive > bufSize - 1 - amountInBuffer)
amountToReceive = bufSize - 1 - amountInBuffer; amountToReceive = bufSize - 1 - amountInBuffer;
#ifdef USE_SSL
if (fUseSSL) { amountReceived = fServerConnection.Read(buf + amountInBuffer,
amountReceived = SSL_read(fSSL, buf + amountInBuffer,
amountToReceive); amountToReceive);
} else
#endif
amountReceived = recv(fSocket, buf + amountInBuffer, amountToReceive, 0);
if (amountReceived < 0) { if (amountReceived < 0) {
fLog = strerror(errno); fLog = strerror(errno);
return errno; return errno;
@@ -823,33 +752,16 @@ POP3Protocol::ReceiveLine(BString &line)
line = ""; line = "";
struct timeval tv; status_t result = fServerConnection.WaitForData(POP3_RETRIEVAL_TIMEOUT);
tv.tv_sec = POP3_RETRIEVAL_TIMEOUT / 1000000; if (result == B_TIMED_OUT)
tv.tv_usec = POP3_RETRIEVAL_TIMEOUT % 1000000; return errno;
struct fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fSocket, &readSet);
int result;
#ifdef USE_SSL
if (fUseSSL && SSL_pending(fSSL))
result = 1;
else
#endif
result = select(fSocket + 1, &readSet, NULL, NULL, &tv);
if (result > 0) {
while (true) { while (true) {
// Hope there's an end of line out there else this gets stuck. // Hope there's an end of line out there else this gets stuck.
int32 bytesReceived; int32 bytesReceived;
uint8 c = 0; uint8 c = 0;
#ifdef USE_SSL
if (fUseSSL) bytesReceived = fServerConnection.Read((char*)&c, 1);
bytesReceived = SSL_read(fSSL, (char*)&c, 1);
else
#endif
bytesReceived = recv(fSocket, &c, 1, 0);
if (bytesReceived < 0) if (bytesReceived < 0)
return errno; return errno;
@@ -868,8 +780,6 @@ POP3Protocol::ReceiveLine(BString &line)
line += (char)c; line += (char)c;
} }
} }
} else
return errno;
return len; return len;
} }
@@ -878,39 +788,18 @@ POP3Protocol::ReceiveLine(BString &line)
status_t status_t
POP3Protocol::SendCommand(const char* cmd) POP3Protocol::SendCommand(const char* cmd)
{ {
if (fSocket < 0 || fSocket >= FD_SETSIZE)
return B_FILE_ERROR;
//printf(cmd); //printf(cmd);
// Flush any accumulated garbage data before we send our command, so we // Flush any accumulated garbage data before we send our command, so we
// don't misinterrpret responses from previous commands (that got left over // don't misinterrpret responses from previous commands (that got left over
// due to bugs) as being from this command. // due to bugs) as being from this command.
struct timeval tv; status_t result = fServerConnection.WaitForData(1000);
tv.tv_sec = 0; if (result == B_OK) {
tv.tv_usec = 1000;
/* very short timeout, hangs with 0 in R5 */
struct fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fSocket, &readSet);
int result;
#ifdef USE_SSL
if (fUseSSL && SSL_pending(fSSL))
result = 1;
else
#endif
result = select(fSocket + 1, &readSet, NULL, NULL, &tv);
if (result > 0) {
int amountReceived; int amountReceived;
char tempString [1025]; char tempString [1025];
#ifdef USE_SSL
if (fUseSSL) amountReceived = fServerConnection.Read(tempString,
amountReceived = SSL_read(fSSL, tempString, sizeof(tempString) - 1); sizeof(tempString) - 1);
else
#endif
amountReceived = recv(fSocket, tempString, sizeof(tempString) - 1, 0);
if (amountReceived < 0) if (amountReceived < 0)
return errno; return errno;
@@ -921,12 +810,7 @@ POP3Protocol::SendCommand(const char *cmd)
// break; // break;
} }
#ifdef USE_SSL if (fServerConnection.Write(cmd, ::strlen(cmd)) < 0) {
if (fUseSSL) {
SSL_write(fSSL, cmd,::strlen(cmd));
} else
#endif
if (send(fSocket, cmd, ::strlen(cmd), 0) < 0) {
fLog = strerror(errno); fLog = strerror(errno);
printf("POP3Protocol::SendCommand Send \"%s\" failed, code %d: %s\n", printf("POP3Protocol::SendCommand Send \"%s\" failed, code %d: %s\n",
cmd, errno, fLog.String()); cmd, errno, fLog.String());
@@ -12,9 +12,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#ifdef USE_SSL #include "ServerConnection.h"
# include <openssl/ssl.h>
#endif
#include <DataIO.h> #include <DataIO.h>
#include <List.h> #include <List.h>
@@ -39,21 +37,23 @@ public:
status_t FetchBody(const entry_ref& ref); status_t FetchBody(const entry_ref& ref);
status_t DeleteMessage(const entry_ref& ref); status_t DeleteMessage(const entry_ref& ref);
status_t Retrieve(int32 message, BPositionIO *write_to);
status_t GetHeader(int32 message, BPositionIO *write_to);
void Delete(int32 num);
protected:
// pop3 methods // pop3 methods
status_t Open(const char *server, int port, status_t Open(const char *server, int port,
int protocol); int protocol);
status_t Login(const char *uid, const char *password, status_t Login(const char *uid, const char *password,
int method); int method);
status_t Retrieve(int32 message, BPositionIO *write_to);
status_t GetHeader(int32 message, BPositionIO *write_to);
void Delete(int32 num);
size_t MessageSize(int32 index); size_t MessageSize(int32 index);
status_t Stat(); status_t Stat();
int32 Messages(void); int32 Messages(void);
size_t MailDropSize(void); size_t MailDropSize(void);
void CheckForDeletedMessages(); void CheckForDeletedMessages();
protected:
status_t RetrieveInternal(const char *command, status_t RetrieveInternal(const char *command,
int32 message, BPositionIO *writeTo, int32 message, BPositionIO *writeTo,
bool showProgress); bool showProgress);
@@ -67,7 +67,6 @@ private:
void _ReadManifest(); void _ReadManifest();
void _WriteManifest(); void _WriteManifest();
int fSocket;
BString fLog; BString fLog;
int32 fNumMessages; int32 fNumMessages;
size_t fMailDropSize; size_t fMailDropSize;
@@ -79,13 +78,9 @@ private:
BString fDestinationDir; BString fDestinationDir;
int32 fFetchBodyLimit; int32 fFetchBodyLimit;
#ifdef USE_SSL
SSL_CTX* fSSLContext;
SSL* fSSL;
BIO* fSSLBio;
ServerConnection fServerConnection;
bool fUseSSL; bool fUseSSL;
#endif
}; };