Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 87 additions & 7 deletions src/mail/mailattachment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class QxtMailAttachmentPrivate : public QSharedData
{
public:
QHash<QString, QString> extraHeaders;
QByteArray boundary; // in case of embedded multipart
QMap<QString, QxtMailAttachment> attachments; // in case of embedded multipart. QMap because order makes sense
QString contentType;
// those two members are mutable because they may change in the const rawData() method of QxtMailAttachment,
// while caching the raw data for the attachment if needed.
Expand Down Expand Up @@ -146,6 +148,15 @@ QString QxtMailAttachment::contentType() const
void QxtMailAttachment::setContentType(const QString& contentType)
{
qxt_d->contentType = contentType;
if (contentType.startsWith("multipart", Qt::CaseInsensitive)) {
QRegExp re(QStringLiteral("boundary=([^ ;\\r]+)"));
if (re.indexIn(contentType) != -1)
qxt_d->boundary = re.capturedTexts()[1].toLatin1();
else {
qxt_d->boundary = qxt_gen_boundary();
qxt_d->contentType += (QStringLiteral("; boundary=") + qxt_d->boundary);
}
}
}

QHash<QString, QString> QxtMailAttachment::extraHeaders() const
Expand All @@ -165,16 +176,18 @@ bool QxtMailAttachment::hasExtraHeader(const QString& key) const

void QxtMailAttachment::setExtraHeader(const QString& key, const QString& value)
{
qxt_d->extraHeaders[key.toLower()] = value;
if (key.compare(QStringLiteral("Content-Type"), Qt::CaseInsensitive) == 0)
setContentType(value);
else
qxt_d->extraHeaders[key.toLower()] = value;
}

void QxtMailAttachment::setExtraHeaders(const QHash<QString, QString>& a)
{
QHash<QString, QString>& headers = qxt_d->extraHeaders;
headers.clear();
qxt_d->extraHeaders.clear();
foreach(const QString& key, a.keys())
{
headers[key.toLower()] = a[key];
setExtraHeader(key, a[key]);
}
}

Expand All @@ -183,21 +196,83 @@ void QxtMailAttachment::removeExtraHeader(const QString& key)
qxt_d->extraHeaders.remove(key.toLower());
}

QMap<QString, QxtMailAttachment> QxtMailAttachment::attachments() const
{
return qxt_d->attachments;
}

QxtMailAttachment QxtMailAttachment::attachment(const QString& filename) const
{
return qxt_d->attachments[filename];
}

void QxtMailAttachment::addAttachment(const QString& filename, const QxtMailAttachment& attach)
{
if (qxt_d->attachments.contains(filename))
{
qWarning() << "QxtMailMessage::addAttachment: " << filename << " already in use";
int i = 1;
while (qxt_d->attachments.contains(filename + QLatin1Char('.') + QString::number(i)))
{
i++;
}
qxt_d->attachments[filename+QLatin1Char('.')+QString::number(i)] = attach;
}
else
{
qxt_d->attachments[filename] = attach;
}
}

void QxtMailAttachment::removeAttachment(const QString& filename)
{
qxt_d->attachments.remove(filename);
}

QByteArray QxtMailAttachment::mimeData()
{
bool isMultipart = false;
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
QByteArray rv = "Content-Type: " + qxt_d->contentType.toLatin1() + "\r\nContent-Transfer-Encoding: base64\r\n";

if (qxt_d->attachments.count()) {
if (!qxt_d->contentType.startsWith("multipart/", Qt::CaseInsensitive))
setExtraHeader(QStringLiteral("Content-Type"), QStringLiteral("multipart/mixed"));
}

QByteArray rv = "Content-Type: " + qxt_d->contentType.toLatin1() + "\r\n";
if (qxt_d->contentType.startsWith("multipart/", Qt::CaseInsensitive)) {
isMultipart = true;
}
else {
rv += "Content-Transfer-Encoding: base64\r\n";
}

foreach(const QString& r, qxt_d->extraHeaders.keys())
{
rv += qxt_fold_mime_header(r, extraHeader(r), latin1);
}
rv += "\r\n";

const QByteArray& d = rawData();
for (int pos = 0; pos < d.length(); pos += 57)
int chars = isMultipart? 73 : 57; // multipart preamble supposed to be 7bit latin1
for (int pos = 0; pos < d.length(); pos += chars)
{
rv += d.mid(pos, 57).toBase64() + "\r\n";
if (isMultipart) {
rv += d.mid(pos, chars) + "\r\n";
} else {
rv += d.mid(pos, chars).toBase64() + "\r\n";
}
}

if (isMultipart) {
QMutableMapIterator<QString, QxtMailAttachment> it(qxt_d->attachments);
while (it.hasNext()) {
rv += "--" + qxt_d->boundary + "\r\n";
rv += it.next().value().mimeData();
}
rv += "--" + qxt_d->boundary + "--\r\n";
}

return rv;
}

Expand Down Expand Up @@ -244,6 +319,11 @@ bool QxtMailAttachment::isText() const
return isTextMedia(contentType());
}

bool QxtMailAttachment::isMultipart() const
{
return qxt_d->attachments.count() || qxt_d->contentType.startsWith("multipart/", Qt::CaseInsensitive);
}

QxtMailAttachment QxtMailAttachment::fromFile(const QString& filename)
{
QxtMailAttachment rv(new QFile(filename));
Expand Down
7 changes: 7 additions & 0 deletions src/mail/mailattachment.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <QByteArray>
#include <QMetaType>
#include <QSharedDataPointer>
#include <QIODevice>

class QxtMailAttachmentPrivate;
class Q_MAIL_EXPORT QxtMailAttachment
Expand Down Expand Up @@ -69,9 +70,15 @@ class Q_MAIL_EXPORT QxtMailAttachment
void setExtraHeaders(const QHash<QString, QString>&);
void removeExtraHeader(const QString& key);

QMap<QString, QxtMailAttachment> attachments() const;
QxtMailAttachment attachment(const QString& filename) const;
void addAttachment(const QString& filename, const QxtMailAttachment& attach);
void removeAttachment(const QString& filename);

QByteArray mimeData();
const QByteArray& rawData() const;
bool isText() const;
bool isMultipart() const;

private:
QSharedDataPointer<QxtMailAttachmentPrivate> qxt_d;
Expand Down
4 changes: 4 additions & 0 deletions src/mail/mailglobal.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@
# define Q_MAIL_EXPORT
#endif

#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
# define QStringLiteral QLatin1String
#endif

#endif // MAILGLOBAL_H
Loading