mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-06 13:33:55 -07:00
Merge branch 'Cockatrice:master' into master
This commit is contained in:
commit
f226a961c4
365 changed files with 23933 additions and 3642 deletions
|
|
@ -169,27 +169,27 @@ minpasswordlength = 6
|
|||
|
||||
[forgotpassword]
|
||||
|
||||
; Servatrice can process forgot password requests allowing users to reset their account
|
||||
; Servatrice can process reset password requests allowing users to reset their account
|
||||
; passwords in the event they forget it. Should this feature be enabled? Default: false.
|
||||
; enable=false
|
||||
|
||||
; Forgot password request should not be allowed to stay valid forever. This settings
|
||||
; informs servatrice how long a players forgot password reset token is valid for (in minutes).
|
||||
; Reset password request should not be allowed to stay valid forever. This settings
|
||||
; informs servatrice how long a players reset password reset token is valid for (in minutes).
|
||||
; Default: 60
|
||||
; tokenlife=60
|
||||
|
||||
; Servatrice can challenge users that are making forgot password requests to answer
|
||||
; Servatrice can challenge users that are making reset password requests to answer
|
||||
; questions in regards to their account to help validate they are the true owner of the account.
|
||||
; Should this feature be enabled? Default: false
|
||||
; enablechallenge=false
|
||||
|
||||
; Email subject for the forgot password emails
|
||||
; subject="Cockatrice forgot password token"
|
||||
; Email subject for the reset password emails
|
||||
; subject="Cockatrice reset password token"
|
||||
|
||||
; Forgot password email body. You can use these tags here: %username %token
|
||||
; Reset password email body. You can use these tags here: %username %token
|
||||
; They will be substituted with the actual values in the email
|
||||
;
|
||||
; body="Hi %username, sorry to hear you forgot your password on our Cockatrice server\r\nHere's the token to use to reset your account password:\r\n\r\n%token\r\n\r\nHappy gaming!"
|
||||
; body="Hi %username,\r\nthanks for reaching out to us with your password reset request for our Cockatrice server.\r\nHere's your unique token in order to reset your account password in the app:\r\n\r\n%token\r\n\r\nHappy gaming!"
|
||||
|
||||
|
||||
[smtp]
|
||||
|
|
@ -347,7 +347,7 @@ max_message_size_per_interval=1000
|
|||
; Maximum number of messages in an interval before new messages gets dropped; default is 10
|
||||
max_message_count_per_interval=10
|
||||
|
||||
; Maximum number of games a single user can create; default is 5
|
||||
; Maximum number of games a single user can create; default is 5; set to -1 to disable; 0 disallows game creation
|
||||
max_games_per_user=5
|
||||
|
||||
; Servatrice can avoid users from flooding games with large number of game commands in an interval of time.
|
||||
|
|
|
|||
|
|
@ -285,16 +285,16 @@ bool Servatrice::initServer()
|
|||
}
|
||||
}
|
||||
|
||||
qDebug() << "Forgot password enabled: " << getEnableForgotPassword();
|
||||
qDebug() << "Reset password enabled: " << getEnableForgotPassword();
|
||||
if (getEnableForgotPassword()) {
|
||||
qDebug() << "Forgot password token life (in minutes): " << getForgotPasswordTokenLife();
|
||||
qDebug() << "Forgot password challenge on: " << getEnableForgotPasswordChallenge();
|
||||
qDebug() << "Reset password token life (in minutes): " << getForgotPasswordTokenLife();
|
||||
qDebug() << "Reset password challenge on: " << getEnableForgotPasswordChallenge();
|
||||
}
|
||||
|
||||
qDebug() << "Auditing enabled: " << getEnableAudit();
|
||||
if (getEnableAudit()) {
|
||||
qDebug() << "Audit registration attempts enabled: " << getEnableRegistrationAudit();
|
||||
qDebug() << "Audit forgot password attepts enabled: " << getEnableForgotPasswordAudit();
|
||||
qDebug() << "Audit reset password attepts enabled: " << getEnableForgotPasswordAudit();
|
||||
}
|
||||
|
||||
if (getDBTypeString() == "mysql") {
|
||||
|
|
@ -960,12 +960,12 @@ int Servatrice::getMaxGamesPerUser() const
|
|||
|
||||
int Servatrice::getCommandCountingInterval() const
|
||||
{
|
||||
return settingsCache->value("game/command_counting_interval", 10).toInt();
|
||||
return settingsCache->value("security/command_counting_interval", 10).toInt();
|
||||
}
|
||||
|
||||
int Servatrice::getMaxCommandCountPerInterval() const
|
||||
{
|
||||
return settingsCache->value("game/max_command_count_per_interval", 20).toInt();
|
||||
return settingsCache->value("security/max_command_count_per_interval", 20).toInt();
|
||||
}
|
||||
|
||||
int Servatrice::getServerStatusUpdateTime() const
|
||||
|
|
|
|||
|
|
@ -201,14 +201,13 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
|
|||
const QString &password,
|
||||
const QString &emailAddress,
|
||||
const QString &country,
|
||||
QString &token,
|
||||
bool active)
|
||||
{
|
||||
if (!checkSql())
|
||||
return false;
|
||||
|
||||
QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
||||
token = active ? QString() : PasswordHasher::generateActivationToken();
|
||||
QString token = active ? QString() : PasswordHasher::generateActivationToken();
|
||||
|
||||
QSqlQuery *query =
|
||||
prepareQuery("insert into {prefix}_users "
|
||||
|
|
|
|||
|
|
@ -99,7 +99,6 @@ public:
|
|||
const QString &password,
|
||||
const QString &emailAddress,
|
||||
const QString &country,
|
||||
QString &token,
|
||||
bool active = false);
|
||||
bool activateUser(const QString &userName, const QString &token);
|
||||
void updateUsersClientID(const QString &userName, const QString &userClientID);
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ bool AbstractServerSocketInterface::initSession()
|
|||
return true;
|
||||
|
||||
int maxUsers = servatrice->getMaxUsersPerAddress();
|
||||
if ((maxUsers > 0) && (servatrice->getUsersWithAddress(getPeerAddress()) >= maxUsers)) {
|
||||
if ((maxUsers > 0) && (servatrice->getUsersWithAddress(getPeerAddress()) > maxUsers)) {
|
||||
Event_ConnectionClosed event;
|
||||
event.set_reason(Event_ConnectionClosed::TOO_MANY_CONNECTIONS);
|
||||
SessionEvent *se = prepareSessionEvent(event);
|
||||
|
|
@ -1106,9 +1106,8 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C
|
|||
return Response::RespPasswordTooShort;
|
||||
}
|
||||
|
||||
QString token;
|
||||
bool requireEmailActivation = settingsCache->value("registration/requireemailactivation", true).toBool();
|
||||
bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country, token,
|
||||
bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country,
|
||||
!requireEmailActivation);
|
||||
|
||||
if (regSucceeded) {
|
||||
|
|
@ -1256,7 +1255,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdAccountPassword(const C
|
|||
Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordRequest(const Command_ForgotPasswordRequest &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
qDebug() << "Received forgot password request from user: " << QString::fromStdString(cmd.user_name());
|
||||
qDebug() << "Received reset password request from user: " << QString::fromStdString(cmd.user_name());
|
||||
|
||||
if (!servatrice->getEnableForgotPassword()) {
|
||||
if (servatrice->getEnableForgotPasswordAudit())
|
||||
|
|
@ -1334,7 +1333,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(con
|
|||
ResponseContainer &rc)
|
||||
{
|
||||
Q_UNUSED(rc);
|
||||
qDebug() << "Received forgot password reset from user: " << QString::fromStdString(cmd.user_name());
|
||||
qDebug() << "Received reset password reset from user: " << QString::fromStdString(cmd.user_name());
|
||||
|
||||
if (!sqlInterface->doesForgotPasswordExist(QString::fromStdString(cmd.user_name()))) {
|
||||
if (servatrice->getEnableForgotPasswordAudit())
|
||||
|
|
@ -1374,7 +1373,7 @@ AbstractServerSocketInterface::cmdForgotPasswordChallenge(const Command_ForgotPa
|
|||
ResponseContainer &rc)
|
||||
{
|
||||
Q_UNUSED(rc);
|
||||
qDebug() << "Received forgot password challenge from user: " << QString::fromStdString(cmd.user_name());
|
||||
qDebug() << "Received reset password challenge from user: " << QString::fromStdString(cmd.user_name());
|
||||
|
||||
if (sqlInterface->doesForgotPasswordExist(QString::fromStdString(cmd.user_name()))) {
|
||||
if (servatrice->getEnableForgotPasswordAudit())
|
||||
|
|
@ -1699,6 +1698,9 @@ WebsocketServerSocketInterface::~WebsocketServerSocketInterface()
|
|||
|
||||
void WebsocketServerSocketInterface::initConnection(void *_socket)
|
||||
{
|
||||
if (_socket == nullptr) {
|
||||
return;
|
||||
}
|
||||
socket = (QWebSocket *)_socket;
|
||||
socket->setParent(this);
|
||||
address = socket->peerAddress();
|
||||
|
|
|
|||
|
|
@ -27,41 +27,41 @@
|
|||
|
||||
#include "qxtglobal.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QIODevice>
|
||||
#include <QMetaType>
|
||||
#include <QSharedDataPointer>
|
||||
#include <QIODevice>
|
||||
#include <QStringList>
|
||||
|
||||
struct QxtMailAttachmentPrivate;
|
||||
class QXT_NETWORK_EXPORT QxtMailAttachment
|
||||
{
|
||||
public:
|
||||
QxtMailAttachment();
|
||||
QxtMailAttachment(const QxtMailAttachment& other);
|
||||
QxtMailAttachment(const QByteArray& content, const QString& contentType = QString("application/octet-stream"));
|
||||
QxtMailAttachment(QIODevice* content, const QString& contentType = QString("application/octet-stream"));
|
||||
QxtMailAttachment& operator=(const QxtMailAttachment& other);
|
||||
QxtMailAttachment(const QxtMailAttachment &other);
|
||||
QxtMailAttachment(const QByteArray &content, const QString &contentType = QString("application/octet-stream"));
|
||||
QxtMailAttachment(QIODevice *content, const QString &contentType = QString("application/octet-stream"));
|
||||
QxtMailAttachment &operator=(const QxtMailAttachment &other);
|
||||
~QxtMailAttachment();
|
||||
static QxtMailAttachment fromFile(const QString& filename);
|
||||
static QxtMailAttachment fromFile(const QString &filename);
|
||||
|
||||
QIODevice* content() const;
|
||||
void setContent(const QByteArray& content);
|
||||
void setContent(QIODevice* content);
|
||||
QIODevice *content() const;
|
||||
void setContent(const QByteArray &content);
|
||||
void setContent(QIODevice *content);
|
||||
|
||||
bool deleteContent() const;
|
||||
void setDeleteContent(bool enable);
|
||||
|
||||
QString contentType() const;
|
||||
void setContentType(const QString& contentType);
|
||||
void setContentType(const QString &contentType);
|
||||
|
||||
QHash<QString, QString> extraHeaders() const;
|
||||
QByteArray extraHeader(const QString&) const;
|
||||
bool hasExtraHeader(const QString&) const;
|
||||
void setExtraHeader(const QString& key, const QString& value);
|
||||
void setExtraHeaders(const QHash<QString, QString>&);
|
||||
void removeExtraHeader(const QString& key);
|
||||
QByteArray extraHeader(const QString &) const;
|
||||
bool hasExtraHeader(const QString &) const;
|
||||
void setExtraHeader(const QString &key, const QString &value);
|
||||
void setExtraHeaders(const QHash<QString, QString> &);
|
||||
void removeExtraHeader(const QString &key);
|
||||
|
||||
QByteArray mimeData();
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
/*!
|
||||
* \class QxtMailMessage
|
||||
* \inmodule QxtNetwork
|
||||
|
|
@ -31,22 +30,26 @@
|
|||
* TODO: {implicitshared}
|
||||
*/
|
||||
|
||||
|
||||
#include "qxtmailmessage.h"
|
||||
|
||||
#include "qxtmail_p.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QTextCodec>
|
||||
#include <QUuid>
|
||||
#include <QDir>
|
||||
#include <QtDebug>
|
||||
|
||||
|
||||
struct QxtMailMessagePrivate : public QSharedData
|
||||
{
|
||||
QxtMailMessagePrivate() {}
|
||||
QxtMailMessagePrivate(const QxtMailMessagePrivate& other)
|
||||
: QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc),
|
||||
subject(other.subject), body(other.body), sender(other.sender),
|
||||
extraHeaders(other.extraHeaders), attachments(other.attachments) {}
|
||||
QxtMailMessagePrivate()
|
||||
{
|
||||
}
|
||||
QxtMailMessagePrivate(const QxtMailMessagePrivate &other)
|
||||
: QSharedData(other), rcptTo(other.rcptTo), rcptCc(other.rcptCc), rcptBcc(other.rcptBcc),
|
||||
subject(other.subject), body(other.body), sender(other.sender), extraHeaders(other.extraHeaders),
|
||||
attachments(other.attachments)
|
||||
{
|
||||
}
|
||||
QStringList rcptTo, rcptCc, rcptBcc;
|
||||
QString subject, body, sender;
|
||||
QHash<QString, QString> extraHeaders;
|
||||
|
|
@ -59,12 +62,12 @@ QxtMailMessage::QxtMailMessage()
|
|||
qxt_d = new QxtMailMessagePrivate;
|
||||
}
|
||||
|
||||
QxtMailMessage::QxtMailMessage(const QxtMailMessage& other) : qxt_d(other.qxt_d)
|
||||
QxtMailMessage::QxtMailMessage(const QxtMailMessage &other) : qxt_d(other.qxt_d)
|
||||
{
|
||||
// trivial copy constructor
|
||||
}
|
||||
|
||||
QxtMailMessage::QxtMailMessage(const QString& sender, const QString& recipient)
|
||||
QxtMailMessage::QxtMailMessage(const QString &sender, const QString &recipient)
|
||||
{
|
||||
qxt_d = new QxtMailMessagePrivate;
|
||||
setSender(sender);
|
||||
|
|
@ -76,7 +79,7 @@ QxtMailMessage::~QxtMailMessage()
|
|||
// trivial destructor
|
||||
}
|
||||
|
||||
QxtMailMessage& QxtMailMessage::operator=(const QxtMailMessage & other)
|
||||
QxtMailMessage &QxtMailMessage::operator=(const QxtMailMessage &other)
|
||||
{
|
||||
qxt_d = other.qxt_d;
|
||||
return *this;
|
||||
|
|
@ -87,7 +90,7 @@ QString QxtMailMessage::sender() const
|
|||
return qxt_d->sender;
|
||||
}
|
||||
|
||||
void QxtMailMessage::setSender(const QString& a)
|
||||
void QxtMailMessage::setSender(const QString &a)
|
||||
{
|
||||
qxt_d->sender = a;
|
||||
}
|
||||
|
|
@ -97,7 +100,7 @@ QString QxtMailMessage::subject() const
|
|||
return qxt_d->subject;
|
||||
}
|
||||
|
||||
void QxtMailMessage::setSubject(const QString& a)
|
||||
void QxtMailMessage::setSubject(const QString &a)
|
||||
{
|
||||
qxt_d->subject = a;
|
||||
}
|
||||
|
|
@ -107,7 +110,7 @@ QString QxtMailMessage::body() const
|
|||
return qxt_d->body;
|
||||
}
|
||||
|
||||
void QxtMailMessage::setBody(const QString& a)
|
||||
void QxtMailMessage::setBody(const QString &a)
|
||||
{
|
||||
qxt_d->body = a;
|
||||
}
|
||||
|
|
@ -121,7 +124,7 @@ QStringList QxtMailMessage::recipients(QxtMailMessage::RecipientType type) const
|
|||
return qxt_d->rcptTo;
|
||||
}
|
||||
|
||||
void QxtMailMessage::addRecipient(const QString& a, QxtMailMessage::RecipientType type)
|
||||
void QxtMailMessage::addRecipient(const QString &a, QxtMailMessage::RecipientType type)
|
||||
{
|
||||
if (type == Bcc)
|
||||
qxt_d->rcptBcc.append(a);
|
||||
|
|
@ -131,7 +134,7 @@ void QxtMailMessage::addRecipient(const QString& a, QxtMailMessage::RecipientTyp
|
|||
qxt_d->rcptTo.append(a);
|
||||
}
|
||||
|
||||
void QxtMailMessage::removeRecipient(const QString& a)
|
||||
void QxtMailMessage::removeRecipient(const QString &a)
|
||||
{
|
||||
qxt_d->rcptTo.removeAll(a);
|
||||
qxt_d->rcptCc.removeAll(a);
|
||||
|
|
@ -143,32 +146,31 @@ QHash<QString, QString> QxtMailMessage::extraHeaders() const
|
|||
return qxt_d->extraHeaders;
|
||||
}
|
||||
|
||||
QByteArray QxtMailMessage::extraHeader(const QString& key) const
|
||||
QByteArray QxtMailMessage::extraHeader(const QString &key) const
|
||||
{
|
||||
return qxt_d->extraHeaders[key.toLower()].toLatin1();
|
||||
}
|
||||
|
||||
bool QxtMailMessage::hasExtraHeader(const QString& key) const
|
||||
bool QxtMailMessage::hasExtraHeader(const QString &key) const
|
||||
{
|
||||
return qxt_d->extraHeaders.contains(key.toLower());
|
||||
}
|
||||
|
||||
void QxtMailMessage::setExtraHeader(const QString& key, const QString& value)
|
||||
void QxtMailMessage::setExtraHeader(const QString &key, const QString &value)
|
||||
{
|
||||
qxt_d->extraHeaders[key.toLower()] = value;
|
||||
}
|
||||
|
||||
void QxtMailMessage::setExtraHeaders(const QHash<QString, QString>& a)
|
||||
void QxtMailMessage::setExtraHeaders(const QHash<QString, QString> &a)
|
||||
{
|
||||
QHash<QString, QString>& headers = qxt_d->extraHeaders;
|
||||
QHash<QString, QString> &headers = qxt_d->extraHeaders;
|
||||
headers.clear();
|
||||
foreach(const QString& key, a.keys())
|
||||
{
|
||||
foreach (const QString &key, a.keys()) {
|
||||
headers[key.toLower()] = a[key];
|
||||
}
|
||||
}
|
||||
|
||||
void QxtMailMessage::removeExtraHeader(const QString& key)
|
||||
void QxtMailMessage::removeExtraHeader(const QString &key)
|
||||
{
|
||||
qxt_d->extraHeaders.remove(key.toLower());
|
||||
}
|
||||
|
|
@ -178,46 +180,40 @@ QHash<QString, QxtMailAttachment> QxtMailMessage::attachments() const
|
|||
return qxt_d->attachments;
|
||||
}
|
||||
|
||||
QxtMailAttachment QxtMailMessage::attachment(const QString& filename) const
|
||||
QxtMailAttachment QxtMailMessage::attachment(const QString &filename) const
|
||||
{
|
||||
return qxt_d->attachments[filename];
|
||||
}
|
||||
|
||||
void QxtMailMessage::addAttachment(const QString& filename, const QxtMailAttachment& attach)
|
||||
void QxtMailMessage::addAttachment(const QString &filename, const QxtMailAttachment &attach)
|
||||
{
|
||||
if (qxt_d->attachments.contains(filename))
|
||||
{
|
||||
if (qxt_d->attachments.contains(filename)) {
|
||||
qWarning() << "QxtMailMessage::addAttachment: " << filename << " already in use";
|
||||
int i = 1;
|
||||
while (qxt_d->attachments.contains(filename + "." + QString::number(i)))
|
||||
{
|
||||
while (qxt_d->attachments.contains(filename + "." + QString::number(i))) {
|
||||
i++;
|
||||
}
|
||||
qxt_d->attachments[filename+"."+QString::number(i)] = attach;
|
||||
}
|
||||
else
|
||||
{
|
||||
qxt_d->attachments[filename + "." + QString::number(i)] = attach;
|
||||
} else {
|
||||
qxt_d->attachments[filename] = attach;
|
||||
}
|
||||
}
|
||||
|
||||
void QxtMailMessage::removeAttachment(const QString& filename)
|
||||
void QxtMailMessage::removeAttachment(const QString &filename)
|
||||
{
|
||||
qxt_d->attachments.remove(filename);
|
||||
}
|
||||
|
||||
QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextCodec* latin1, const QByteArray& prefix)
|
||||
QByteArray qxt_fold_mime_header(const QString &key, const QString &value, QTextCodec *latin1, const QByteArray &prefix)
|
||||
{
|
||||
QByteArray rv = "";
|
||||
QByteArray line = key.toLatin1() + ": ";
|
||||
if (!prefix.isEmpty()) line += prefix;
|
||||
if (!value.contains("=?") && latin1->canEncode(value))
|
||||
{
|
||||
if (!prefix.isEmpty())
|
||||
line += prefix;
|
||||
if (!value.contains("=?") && latin1->canEncode(value)) {
|
||||
bool firstWord = true;
|
||||
foreach(const QByteArray& word, value.toLatin1().split(' '))
|
||||
{
|
||||
if (line.size() > 78)
|
||||
{
|
||||
foreach (const QByteArray &word, value.toLatin1().split(' ')) {
|
||||
if (line.size() > 78) {
|
||||
rv = rv + line + "\r\n";
|
||||
line.clear();
|
||||
}
|
||||
|
|
@ -227,9 +223,7 @@ QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextC
|
|||
line += " " + word;
|
||||
firstWord = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The text cannot be losslessly encoded as Latin-1. Therefore, we
|
||||
// must use quoted-printable or base64 encoding. This is a quick
|
||||
// heuristic based on the first 100 characters to see which
|
||||
|
|
@ -237,43 +231,33 @@ QByteArray qxt_fold_mime_header(const QString& key, const QString& value, QTextC
|
|||
QByteArray utf8 = value.toUtf8();
|
||||
int ct = utf8.length();
|
||||
int nonAscii = 0;
|
||||
for (int i = 0; i < ct && i < 100; i++)
|
||||
{
|
||||
if (QXT_MUST_QP(utf8[i])) nonAscii++;
|
||||
for (int i = 0; i < ct && i < 100; i++) {
|
||||
if (QXT_MUST_QP(utf8[i]))
|
||||
nonAscii++;
|
||||
}
|
||||
if (nonAscii > 20)
|
||||
{
|
||||
if (nonAscii > 20) {
|
||||
// more than 20%-ish non-ASCII characters: use base64
|
||||
QByteArray base64 = utf8.toBase64();
|
||||
ct = base64.length();
|
||||
line += "=?utf-8?b?";
|
||||
for (int i = 0; i < ct; i += 4)
|
||||
{
|
||||
if (line.length() > 72)
|
||||
{
|
||||
for (int i = 0; i < ct; i += 4) {
|
||||
if (line.length() > 72) {
|
||||
rv += line + "?\r\n";
|
||||
line = " =?utf-8?b?";
|
||||
}
|
||||
line = line + base64.mid(i, 4);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// otherwise use Q-encoding
|
||||
line += "=?utf-8?q?";
|
||||
for (int i = 0; i < ct; i++)
|
||||
{
|
||||
if (line.length() > 73)
|
||||
{
|
||||
for (int i = 0; i < ct; i++) {
|
||||
if (line.length() > 73) {
|
||||
rv += line + "?\r\n";
|
||||
line = " =?utf-8?q?";
|
||||
}
|
||||
if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ')
|
||||
{
|
||||
if (QXT_MUST_QP(utf8[i]) || utf8[i] == ' ') {
|
||||
line += "=" + utf8.mid(i, 1).toHex().toUpper();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
line += utf8[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -290,81 +274,67 @@ QByteArray QxtMailMessage::rfc2822() const
|
|||
// Use base64 if requested
|
||||
bool useBase64 = (extraHeader("Content-Transfer-Encoding").toLower() == "base64");
|
||||
// Check to see if plain text is ASCII-clean; assume it isn't if QP or base64 was requested
|
||||
QTextCodec* latin1 = QTextCodec::codecForName("latin1");
|
||||
QTextCodec *latin1 = QTextCodec::codecForName("latin1");
|
||||
bool bodyIsAscii = latin1->canEncode(body()) && !useQuotedPrintable && !useBase64;
|
||||
|
||||
QHash<QString, QxtMailAttachment> attach = attachments();
|
||||
QByteArray rv;
|
||||
|
||||
if (!sender().isEmpty() && !hasExtraHeader("From"))
|
||||
{
|
||||
if (!sender().isEmpty() && !hasExtraHeader("From")) {
|
||||
rv += qxt_fold_mime_header("From", sender(), latin1);
|
||||
}
|
||||
|
||||
if (!qxt_d->rcptTo.isEmpty())
|
||||
{
|
||||
if (!qxt_d->rcptTo.isEmpty()) {
|
||||
rv += qxt_fold_mime_header("To", qxt_d->rcptTo.join(", "), latin1);
|
||||
}
|
||||
|
||||
if (!qxt_d->rcptCc.isEmpty())
|
||||
{
|
||||
if (!qxt_d->rcptCc.isEmpty()) {
|
||||
rv += qxt_fold_mime_header("Cc", qxt_d->rcptCc.join(", "), latin1);
|
||||
}
|
||||
|
||||
if (!subject().isEmpty())
|
||||
{
|
||||
if (!subject().isEmpty()) {
|
||||
rv += qxt_fold_mime_header("Subject", subject(), latin1);
|
||||
}
|
||||
|
||||
if (!bodyIsAscii)
|
||||
{
|
||||
if (!bodyIsAscii) {
|
||||
if (!hasExtraHeader("MIME-Version") && !attach.count())
|
||||
rv += "MIME-Version: 1.0\r\n";
|
||||
|
||||
// If no transfer encoding has been requested, guess.
|
||||
// Heuristic: If >20% of the first 100 characters aren't
|
||||
// 7-bit clean, use base64, otherwise use Q-P.
|
||||
if(!bodyIsAscii && !useQuotedPrintable && !useBase64)
|
||||
{
|
||||
if (!bodyIsAscii && !useQuotedPrintable && !useBase64) {
|
||||
QString b = body();
|
||||
int nonAscii = 0;
|
||||
int ct = b.length();
|
||||
for (int i = 0; i < ct && i < 100; i++)
|
||||
{
|
||||
if (QXT_MUST_QP(b[i])) nonAscii++;
|
||||
for (int i = 0; i < ct && i < 100; i++) {
|
||||
if (QXT_MUST_QP(b[i]))
|
||||
nonAscii++;
|
||||
}
|
||||
useQuotedPrintable = !(nonAscii > 20);
|
||||
useBase64 = !useQuotedPrintable;
|
||||
}
|
||||
}
|
||||
|
||||
if (attach.count())
|
||||
{
|
||||
if (attach.count()) {
|
||||
if (qxt_d->boundary.isEmpty())
|
||||
qxt_d->boundary = QUuid::createUuid().toString().toLatin1().replace("{", "").replace("}", "");
|
||||
if (!hasExtraHeader("MIME-Version"))
|
||||
rv += "MIME-Version: 1.0\r\n";
|
||||
if (!hasExtraHeader("Content-Type"))
|
||||
rv += "Content-Type: multipart/mixed; boundary=" + qxt_d->boundary + "\r\n";
|
||||
}
|
||||
else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding"))
|
||||
{
|
||||
if (!useQuotedPrintable)
|
||||
{
|
||||
} else if (!bodyIsAscii && !hasExtraHeader("Content-Transfer-Encoding")) {
|
||||
if (!useQuotedPrintable) {
|
||||
// base64
|
||||
rv += "Content-Transfer-Encoding: base64\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// quoted-printable
|
||||
rv += "Content-Transfer-Encoding: quoted-printable\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach(const QString& r, qxt_d->extraHeaders.keys())
|
||||
{
|
||||
if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count())
|
||||
{
|
||||
foreach (const QString &r, qxt_d->extraHeaders.keys()) {
|
||||
if ((r.toLower() == "content-type" || r.toLower() == "content-transfer-encoding") && attach.count()) {
|
||||
// Since we're in multipart mode, we'll be outputting this later
|
||||
continue;
|
||||
}
|
||||
|
|
@ -373,8 +343,7 @@ QByteArray QxtMailMessage::rfc2822() const
|
|||
|
||||
rv += "\r\n";
|
||||
|
||||
if (attach.count())
|
||||
{
|
||||
if (attach.count()) {
|
||||
// we're going to have attachments, so output the lead-in for the message body
|
||||
rv += "This is a message with multiple parts in MIME format.\r\n";
|
||||
rv += "--" + qxt_d->boundary + "\r\nContent-Type: ";
|
||||
|
|
@ -382,19 +351,13 @@ QByteArray QxtMailMessage::rfc2822() const
|
|||
rv += extraHeader("Content-Type") + "\r\n";
|
||||
else
|
||||
rv += "text/plain; charset=UTF-8\r\n";
|
||||
if (hasExtraHeader("Content-Transfer-Encoding"))
|
||||
{
|
||||
if (hasExtraHeader("Content-Transfer-Encoding")) {
|
||||
rv += "Content-Transfer-Encoding: " + extraHeader("Content-Transfer-Encoding") + "\r\n";
|
||||
}
|
||||
else if (!bodyIsAscii)
|
||||
{
|
||||
if (!useQuotedPrintable)
|
||||
{
|
||||
} else if (!bodyIsAscii) {
|
||||
if (!useQuotedPrintable) {
|
||||
// base64
|
||||
rv += "Content-Transfer-Encoding: base64\r\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// quoted-printable
|
||||
rv += "Content-Transfer-Encoding: quoted-printable\r\n";
|
||||
}
|
||||
|
|
@ -402,132 +365,100 @@ QByteArray QxtMailMessage::rfc2822() const
|
|||
rv += "\r\n";
|
||||
}
|
||||
|
||||
if (bodyIsAscii)
|
||||
{
|
||||
if (bodyIsAscii) {
|
||||
QByteArray b = latin1->fromUnicode(body());
|
||||
int len = b.length();
|
||||
QByteArray line = "";
|
||||
QByteArray word = "";
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (b[i] == '\n' || b[i] == '\r')
|
||||
{
|
||||
if (line.isEmpty())
|
||||
{
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (b[i] == '\n' || b[i] == '\r') {
|
||||
if (line.isEmpty()) {
|
||||
line = word;
|
||||
word = "";
|
||||
}
|
||||
else if (line.length() + word.length() + 1 <= 78)
|
||||
{
|
||||
} else if (line.length() + word.length() + 1 <= 78) {
|
||||
line = line + ' ' + word;
|
||||
word = "";
|
||||
}
|
||||
if(line[0] == '.')
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1])
|
||||
{
|
||||
if ((b[i + 1] == '\n' || b[i + 1] == '\r') && b[i] != b[i + 1]) {
|
||||
// If we're looking at a CRLF pair, skip the second half
|
||||
i++;
|
||||
}
|
||||
line = word;
|
||||
}
|
||||
else if (b[i] == ' ')
|
||||
{
|
||||
if (line.length() + word.length() + 1 > 78)
|
||||
{
|
||||
if(line[0] == '.')
|
||||
} else if (b[i] == ' ') {
|
||||
if (line.length() + word.length() + 1 > 78) {
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
line = word;
|
||||
}
|
||||
else if (line.isEmpty())
|
||||
{
|
||||
} else if (line.isEmpty()) {
|
||||
line = word;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
line = line + ' ' + word;
|
||||
}
|
||||
word = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
word += b[i];
|
||||
}
|
||||
}
|
||||
if (line.length() + word.length() + 1 > 78)
|
||||
{
|
||||
if(line[0] == '.')
|
||||
if (line.length() + word.length() + 1 > 78) {
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
line = word;
|
||||
}
|
||||
else if (!word.isEmpty())
|
||||
{
|
||||
} else if (!word.isEmpty()) {
|
||||
line += ' ' + word;
|
||||
}
|
||||
if(!line.isEmpty()) {
|
||||
if(line[0] == '.')
|
||||
if (!line.isEmpty()) {
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
}
|
||||
}
|
||||
else if (useQuotedPrintable)
|
||||
{
|
||||
} else if (useQuotedPrintable) {
|
||||
QByteArray b = body().toUtf8();
|
||||
int ct = b.length();
|
||||
QByteArray line;
|
||||
for (int i = 0; i < ct; i++)
|
||||
{
|
||||
if(b[i] == '\n' || b[i] == '\r')
|
||||
{
|
||||
if(line[0] == '.')
|
||||
for (int i = 0; i < ct; i++) {
|
||||
if (b[i] == '\n' || b[i] == '\r') {
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
line = "";
|
||||
if ((b[i+1] == '\n' || b[i+1] == '\r') && b[i] != b[i+1])
|
||||
{
|
||||
if ((b[i + 1] == '\n' || b[i + 1] == '\r') && b[i] != b[i + 1]) {
|
||||
// If we're looking at a CRLF pair, skip the second half
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (line.length() > 74)
|
||||
{
|
||||
} else if (line.length() > 74) {
|
||||
rv += line + "=\r\n";
|
||||
line = "";
|
||||
}
|
||||
if (QXT_MUST_QP(b[i]))
|
||||
{
|
||||
if (QXT_MUST_QP(b[i])) {
|
||||
line += "=" + b.mid(i, 1).toHex().toUpper();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
line += b[i];
|
||||
}
|
||||
}
|
||||
if(!line.isEmpty()) {
|
||||
if(line[0] == '.')
|
||||
if (!line.isEmpty()) {
|
||||
if (line[0] == '.')
|
||||
rv += ".";
|
||||
rv += line + "\r\n";
|
||||
}
|
||||
}
|
||||
else /* base64 */
|
||||
} else /* base64 */
|
||||
{
|
||||
QByteArray b = body().toUtf8().toBase64();
|
||||
int ct = b.length();
|
||||
for (int i = 0; i < ct; i += 78)
|
||||
{
|
||||
for (int i = 0; i < ct; i += 78) {
|
||||
rv += b.mid(i, 78) + "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (attach.count())
|
||||
{
|
||||
foreach(const QString& filename, attach.keys())
|
||||
{
|
||||
if (attach.count()) {
|
||||
foreach (const QString &filename, attach.keys()) {
|
||||
rv += "--" + qxt_d->boundary + "\r\n";
|
||||
rv += qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), latin1, "attachment; filename=");
|
||||
rv +=
|
||||
qxt_fold_mime_header("Content-Disposition", QDir(filename).dirName(), latin1, "attachment; filename=");
|
||||
rv += attach[filename].mimeData();
|
||||
}
|
||||
rv += "--" + qxt_d->boundary + "--\r\n";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue