summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLincoln Ramsay <lincoln.ramsay@nokia.com>2010-04-06 15:20:11 +1000
committerLincoln Ramsay <lincoln.ramsay@nokia.com>2010-04-14 14:15:43 +1000
commitccd0a2b9bf5adfcf8ff2a8f29e63e5d462e0bc46 (patch)
tree713c1e5a81f0383cbf38bc2dd98d3142b4f32efb
parent52a1595de5914073e69cae0086dfbc68b1dc9d61 (diff)
Limit memory use while sending messages.2010W15
Message data is streamed to a temporary file and then queued in blocks so that the sending buffers do not become large. This seems to result in a roughly 1.5MB fixed size when sending, no matter the size of the message being sent. Fixes: QTSOL-76
-rw-r--r--src/plugins/messageservices/smtp/smtpclient.cpp89
-rw-r--r--src/plugins/messageservices/smtp/smtpclient.h8
2 files changed, 86 insertions, 11 deletions
diff --git a/src/plugins/messageservices/smtp/smtpclient.cpp b/src/plugins/messageservices/smtp/smtpclient.cpp
index 08502f85..b996facb 100644
--- a/src/plugins/messageservices/smtp/smtpclient.cpp
+++ b/src/plugins/messageservices/smtp/smtpclient.cpp
@@ -46,11 +46,18 @@
#include <QHostAddress>
#include <QTextCodec>
+#include <QTemporaryFile>
+#include <QCoreApplication>
+#include <QDir>
#include <qmaillog.h>
#include <qmailaddress.h>
#include <qmailstore.h>
#include <qmailtransport.h>
+#include <qmailnamespace.h>
+// The size of the buffer used when sending messages.
+// Only this many bytes is queued to be sent at a time.
+#define SENDING_BUFFER_SIZE 5000
static bool initialiseRng()
{
@@ -80,14 +87,18 @@ static QByteArray messageId(const QByteArray& domainName, quint32 addressCompone
SmtpClient::SmtpClient(QObject* parent)
: QObject(parent)
+ , sending(false)
+ , transport(0)
+ , temporaryFile(0)
+ , waitingForBytes(0)
{
- sending = false;
- transport = 0;
}
SmtpClient::~SmtpClient()
{
delete transport;
+ if (temporaryFile)
+ delete temporaryFile;
}
QMailMessage::MessageType SmtpClient::messageType() const
@@ -577,15 +588,37 @@ void SmtpClient::nextAction(const QString &response)
// Set the message's message ID
mailItr->mail.setHeaderField("Message-ID", messageId(domainName, addressComponent));
- transport->mark();
- mailItr->mail.toRfc2822(transport->stream(), QMailMessage::TransmissionFormat);
- messageLength = transport->bytesSinceMark();
-
- transport->stream().writeRawData("\r\n.\r\n", 5);
-
- qMailLog(SMTP) << "Body: sent:" << messageLength << "bytes";
-
- status = Sent;
+ Q_ASSERT(temporaryFile == 0);
+
+ // Buffer the message to a temporary file.
+ QString tempPath = QMail::tempPath();
+ QDir dir;
+ if (!dir.exists(tempPath))
+ dir.mkpath(tempPath);
+ temporaryFile = new QTemporaryFile(tempPath + QLatin1String("/qtopiamail.XXXXXX"));
+ bool ok = temporaryFile->open();
+ Q_ASSERT(ok);
+ {
+ // Note that there is no progress update while the message is streamed to the file.
+ // This isn't optimal but it's how the original code worked and fixing it requires
+ // putting this call onto a separate thread because it blocks until done.
+ QDataStream dataStream(temporaryFile);
+ mailItr->mail.toRfc2822(dataStream, QMailMessage::TransmissionFormat);
+ }
+ messageLength = temporaryFile->size();
+ //qMailLog(SMTP) << "Body: queued" << messageLength << "bytes to" << temporaryFile->fileName();
+
+ // Now write the message to the transport in blocks, waiting for the bytes to be
+ // written each time so there is no need to allocate large buffers to hold everything.
+ temporaryFile->seek(0);
+ waitingForBytes = 0;
+ if (transport->isEncrypted())
+ connect(&(transport->socket()), SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(sendMoreData(qint64)));
+ else
+ connect(transport, SIGNAL(bytesWritten(qint64)), this, SLOT(sendMoreData(qint64)));
+
+ // trigger the sending of the first block of data
+ sendMoreData(0);
} else {
operationFailed(QMailServiceAction::Status::ErrUnknownResponse, response);
}
@@ -754,3 +787,37 @@ void SmtpClient::operationFailed(QMailServiceAction::Status::ErrorCode code, con
emit errorOccurred(code, msg);
}
+void SmtpClient::sendMoreData(qint64 bytesWritten)
+{
+ Q_ASSERT(status == Body && temporaryFile);
+
+ waitingForBytes -= bytesWritten;
+
+ // If anyone else writes bytes we end up with a negative value... just reset to 0 when that happens.
+ if (waitingForBytes < 0) waitingForBytes = 0;
+
+ // Don't send more data until all bytes have been written.
+ if (waitingForBytes) return;
+
+ // No more data to send
+ if (temporaryFile->atEnd()) {
+ if (transport->isEncrypted())
+ disconnect(&(transport->socket()), SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(sendMoreData(qint64)));
+ else
+ disconnect(transport, SIGNAL(bytesWritten(qint64)), this, SLOT(sendMoreData(qint64)));
+ delete temporaryFile;
+ temporaryFile = 0;
+ transport->stream().writeRawData("\r\n.\r\n", 5);
+ qMailLog(SMTP) << "Body: sent:" << messageLength << "bytes";
+ status = Sent;
+ return;
+ }
+
+ // Queue up to SENDING_BUFFER_SIZE bytes for transmission
+ char buffer[SENDING_BUFFER_SIZE];
+ qint64 bytes = temporaryFile->read(buffer, SENDING_BUFFER_SIZE);
+ waitingForBytes += bytes;
+ transport->stream().writeRawData(buffer, bytes);
+ //qMailLog(SMTP) << "Body: sent a" << bytes << "byte block";
+}
+
diff --git a/src/plugins/messageservices/smtp/smtpclient.h b/src/plugins/messageservices/smtp/smtpclient.h
index 27be1a37..0d92dec7 100644
--- a/src/plugins/messageservices/smtp/smtpclient.h
+++ b/src/plugins/messageservices/smtp/smtpclient.h
@@ -51,6 +51,8 @@
#include <qmailmessageserver.h>
#include <qmailtransport.h>
+class QTemporaryFile;
+
struct RawEmail
{
QString from;
@@ -91,6 +93,9 @@ protected slots:
void readyRead();
void sent(qint64);
+private slots:
+ void sendMoreData(qint64);
+
private:
void sendCommand(const char *data, int len = -1);
void sendCommand(const QString &cmd);
@@ -132,6 +137,9 @@ private:
QStringList capabilities;
quint32 addressComponent;
QByteArray domainName;
+
+ QTemporaryFile *temporaryFile;
+ qint64 waitingForBytes;
};
#endif