From 471f6371b5aea2dabd404decca4e8258a103c39a Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 24 May 2015 23:02:51 +0200 Subject: [PATCH] More work * Refactored code out of common/ into servatrice/ * added smtp client library * disable registration when connected * validate email address * send activation token via email --- cockatrice/src/window_main.cpp | 6 +- common/server.cpp | 58 --- common/server.h | 17 - common/server_protocolhandler.cpp | 61 --- common/server_protocolhandler.h | 3 +- servatrice/CMakeLists.txt | 12 + servatrice/servatrice.ini.example | 33 ++ servatrice/src/servatrice.cpp | 4 +- .../src/servatrice_database_interface.cpp | 4 +- .../src/servatrice_database_interface.h | 6 +- servatrice/src/serversocketinterface.cpp | 178 +++++++ servatrice/src/serversocketinterface.h | 5 + servatrice/src/smtp/SmtpMime | 31 ++ servatrice/src/smtp/emailaddress.cpp | 60 +++ servatrice/src/smtp/emailaddress.h | 61 +++ servatrice/src/smtp/mimeattachment.cpp | 50 ++ servatrice/src/smtp/mimeattachment.h | 51 ++ servatrice/src/smtp/mimecontentformatter.cpp | 66 +++ servatrice/src/smtp/mimecontentformatter.h | 43 ++ servatrice/src/smtp/mimefile.cpp | 70 +++ servatrice/src/smtp/mimefile.h | 62 +++ servatrice/src/smtp/mimehtml.cpp | 57 +++ servatrice/src/smtp/mimehtml.h | 61 +++ servatrice/src/smtp/mimeinlinefile.cpp | 52 ++ servatrice/src/smtp/mimeinlinefile.h | 56 ++ servatrice/src/smtp/mimemessage.cpp | 257 ++++++++++ servatrice/src/smtp/mimemessage.h | 92 ++++ servatrice/src/smtp/mimemultipart.cpp | 78 +++ servatrice/src/smtp/mimemultipart.h | 75 +++ servatrice/src/smtp/mimepart.cpp | 214 ++++++++ servatrice/src/smtp/mimepart.h | 114 +++++ servatrice/src/smtp/mimetext.cpp | 62 +++ servatrice/src/smtp/mimetext.h | 62 +++ servatrice/src/smtp/quotedprintable.cpp | 69 +++ servatrice/src/smtp/quotedprintable.h | 39 ++ servatrice/src/smtp/smtpclient.cpp | 482 ++++++++++++++++++ servatrice/src/smtp/smtpclient.h | 184 +++++++ servatrice/src/smtp/smtpexports.h | 10 + 38 files changed, 2699 insertions(+), 146 deletions(-) create mode 100644 servatrice/src/smtp/SmtpMime create mode 100644 servatrice/src/smtp/emailaddress.cpp create mode 100644 servatrice/src/smtp/emailaddress.h create mode 100644 servatrice/src/smtp/mimeattachment.cpp create mode 100644 servatrice/src/smtp/mimeattachment.h create mode 100644 servatrice/src/smtp/mimecontentformatter.cpp create mode 100644 servatrice/src/smtp/mimecontentformatter.h create mode 100644 servatrice/src/smtp/mimefile.cpp create mode 100644 servatrice/src/smtp/mimefile.h create mode 100644 servatrice/src/smtp/mimehtml.cpp create mode 100644 servatrice/src/smtp/mimehtml.h create mode 100644 servatrice/src/smtp/mimeinlinefile.cpp create mode 100644 servatrice/src/smtp/mimeinlinefile.h create mode 100644 servatrice/src/smtp/mimemessage.cpp create mode 100644 servatrice/src/smtp/mimemessage.h create mode 100644 servatrice/src/smtp/mimemultipart.cpp create mode 100644 servatrice/src/smtp/mimemultipart.h create mode 100644 servatrice/src/smtp/mimepart.cpp create mode 100644 servatrice/src/smtp/mimepart.h create mode 100644 servatrice/src/smtp/mimetext.cpp create mode 100644 servatrice/src/smtp/mimetext.h create mode 100644 servatrice/src/smtp/quotedprintable.cpp create mode 100644 servatrice/src/smtp/quotedprintable.h create mode 100644 servatrice/src/smtp/smtpclient.cpp create mode 100644 servatrice/src/smtp/smtpclient.h create mode 100644 servatrice/src/smtp/smtpexports.h diff --git a/cockatrice/src/window_main.cpp b/cockatrice/src/window_main.cpp index 896d7821..5b640046 100644 --- a/cockatrice/src/window_main.cpp +++ b/cockatrice/src/window_main.cpp @@ -104,11 +104,13 @@ void MainWindow::statusChanged(ClientStatus _status) tabSupervisor->stop(); aSinglePlayer->setEnabled(true); aConnect->setEnabled(true); + aRegister->setEnabled(true); aDisconnect->setEnabled(false); break; case StatusLoggingIn: aSinglePlayer->setEnabled(false); aConnect->setEnabled(false); + aRegister->setEnabled(false); aDisconnect->setEnabled(true); break; case StatusConnecting: @@ -179,6 +181,7 @@ void MainWindow::actSinglePlayer() return; aConnect->setEnabled(false); + aRegister->setEnabled(false); aSinglePlayer->setEnabled(false); localServer = new LocalServer(this); @@ -226,6 +229,7 @@ void MainWindow::localGameEnded() localServer = 0; aConnect->setEnabled(true); + aRegister->setEnabled(true); aSinglePlayer->setEnabled(true); } @@ -336,7 +340,7 @@ void MainWindow::registerError(Response::ResponseCode r, QString reasonStr, quin QMessageBox::critical(this, tr("Registration denied"), tr("There is already an existing account with the same user name.")); break; case Response::RespEmailRequiredToRegister: - QMessageBox::critical(this, tr("Registration denied"), tr("It's mandatory to specify an email when registering.")); + QMessageBox::critical(this, tr("Registration denied"), tr("It's mandatory to specify a valid email address when registering.")); break; case Response::RespTooManyRequests: QMessageBox::critical(this, tr("Registration denied"), tr("Too many registration attempts from your IP address.")); diff --git a/common/server.cpp b/common/server.cpp index 1e3991c3..3c2c9718 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -175,64 +175,6 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString return authState; } -RegistrationResult Server::registerUserAccount(const QString &ipAddress, const Command_Register &cmd, QString &banReason, int &banSecondsRemaining) -{ - if (!registrationEnabled) - return RegistrationDisabled; - - QString emailAddress = QString::fromStdString(cmd.email()); - if (requireEmailForRegistration && emailAddress.isEmpty()) - return EmailRequired; - - Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); - - // TODO: Move this method outside of the db interface - QString userName = QString::fromStdString(cmd.user_name()); - if (!databaseInterface->usernameIsValid(userName)) - return InvalidUsername; - - if(databaseInterface->userExists(userName)) - return UserAlreadyExists; - - if (databaseInterface->checkUserIsBanned(ipAddress, userName, banReason, banSecondsRemaining)) - return ClientIsBanned; - - if (tooManyRegistrationAttempts(ipAddress)) - return TooManyRequests; - - QString realName = QString::fromStdString(cmd.real_name()); - ServerInfo_User_Gender gender = cmd.gender(); - QString country = QString::fromStdString(cmd.country()); - QString password = QString::fromStdString(cmd.password()); - - if(password.length() < 6) - return PasswordTooShort; - - bool regSucceeded = databaseInterface->registerUser(userName, realName, gender, password, emailAddress, country, !requireEmailForRegistration); - - if(regSucceeded) - return requireEmailForRegistration ? AcceptedNeedsActivation : Accepted; - else - return Failed; -} - -bool Server::activateUserAccount(const Command_Activate &cmd) -{ - QString userName = QString::fromStdString(cmd.user_name()); - QString token = QString::fromStdString(cmd.token()); - - Server_DatabaseInterface *databaseInterface = getDatabaseInterface(); - - return databaseInterface->activateUser(userName, token); -} - -bool Server::tooManyRegistrationAttempts(const QString &ipAddress) -{ - // TODO: implement - Q_UNUSED(ipAddress); - return false; -} - void Server::addPersistentPlayer(const QString &userName, int roomId, int gameId, int playerId) { QWriteLocker locker(&persistentPlayersLock); diff --git a/common/server.h b/common/server.h index 827b38f4..ad17bc53 100644 --- a/common/server.h +++ b/common/server.h @@ -29,7 +29,6 @@ class CommandContainer; class Command_JoinGame; enum AuthenticationResult { NotLoggedIn, PasswordRight, UnknownUser, WouldOverwriteOldSession, UserIsBanned, UsernameInvalid, RegistrationRequired, UserIsInactive }; -enum RegistrationResult { Accepted, UserAlreadyExists, EmailRequired, TooManyRequests, InvalidUsername, ClientIsBanned, RegistrationDisabled, Failed, PasswordTooShort, AcceptedNeedsActivation }; class Server : public QObject { @@ -47,19 +46,6 @@ public: void setThreaded(bool _threaded) { threaded = _threaded; } AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft); - /** - * Registers a user account. - * @param ipAddress The address of the connection from the user - * @param userName The username to attempt to register - * @param emailAddress The email address to associate with the new account (and to use for activation) - * @param banReason If the client is banned, the reason for the ban will be included in this string. - * @param banSecondsRemaining If the client is banned, the time left will be included in this. 0 if the ban is permanent. - * @return RegistrationResult member indicating whether it succeeded or failed. - */ - RegistrationResult registerUserAccount(const QString &ipAddress, const Command_Register &cmd, QString &banReason, int &banSecondsRemaining); - bool activateUserAccount(const Command_Activate &cmd); - - bool tooManyRegistrationAttempts(const QString &ipAddress); const QMap &getRooms() { return rooms; } Server_AbstractUserInterface *findUser(const QString &userName) const; @@ -131,9 +117,6 @@ protected: int getUsersCount() const; int getGamesCount() const; void addRoom(Server_Room *newRoom); - - bool registrationEnabled; - bool requireEmailForRegistration; }; #endif diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index fb38f2b6..46e2bb83 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -9,7 +9,6 @@ #include "pb/commands.pb.h" #include "pb/response.pb.h" #include "pb/response_login.pb.h" -#include "pb/response_register.pb.h" #include "pb/response_list_users.pb.h" #include "pb/response_get_games_of_user.pb.h" #include "pb/response_get_user_info.pb.h" @@ -145,8 +144,6 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co switch ((SessionCommand::SessionCommandType) num) { case SessionCommand::PING: resp = cmdPing(sc.GetExtension(Command_Ping::ext), rc); break; case SessionCommand::LOGIN: resp = cmdLogin(sc.GetExtension(Command_Login::ext), rc); break; - case SessionCommand::REGISTER: resp = cmdRegisterAccount(sc.GetExtension(Command_Register::ext), rc); break; - case SessionCommand::ACTIVATE: resp = cmdActivateAccount(sc.GetExtension(Command_Activate::ext), rc); break; case SessionCommand::MESSAGE: resp = cmdMessage(sc.GetExtension(Command_Message::ext), rc); break; case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break; case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break; @@ -421,64 +418,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd return Response::RespOk; } -Response::ResponseCode Server_ProtocolHandler::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) -{ - qDebug() << "Got register command: " << QString::fromStdString(cmd.user_name()); - - QString banReason; - int banSecondsRemaining; - RegistrationResult result = - server->registerUserAccount( - this->getAddress(), - cmd, - banReason, - banSecondsRemaining); - qDebug() << "Register command result:" << result; - - switch (result) { - case RegistrationDisabled: - return Response::RespRegistrationDisabled; - case Accepted: - return Response::RespRegistrationAccepted; - case AcceptedNeedsActivation: - // TODO SEND EMAIL WITH TOKEN TO THE USER - return Response::RespRegistrationAcceptedNeedsActivation; - case UserAlreadyExists: - return Response::RespUserAlreadyExists; - case EmailRequired: - return Response::RespEmailRequiredToRegister; - case TooManyRequests: - return Response::RespTooManyRequests; - case PasswordTooShort: - return Response::RespPasswordTooShort; - case InvalidUsername: - return Response::RespUsernameInvalid; - case Failed: - return Response::RespRegistrationFailed; - case ClientIsBanned: - Response_Register *re = new Response_Register; - re->set_denied_reason_str(banReason.toStdString()); - if (banSecondsRemaining != 0) - re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t()); - rc.setResponseExtension(re); - return Response::RespUserIsBanned; - } - - return Response::RespInvalidCommand; -} - -Response::ResponseCode Server_ProtocolHandler::cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /*rc*/) -{ - if(server->activateUserAccount(cmd)) - { - qDebug() << "Accepted activation for user" << QString::fromStdString(cmd.user_name()); - return Response::RespActivationAccepted; - } else { - qDebug() << "Failed activation for user" << QString::fromStdString(cmd.user_name()); - return Response::RespActivationFailed; - } -} - Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message &cmd, ResponseContainer &rc) { if (authState == NotLoggedIn) diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index b285ae99..bf9d1940 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -60,8 +60,6 @@ private: Response::ResponseCode cmdPing(const Command_Ping &cmd, ResponseContainer &rc); Response::ResponseCode cmdLogin(const Command_Login &cmd, ResponseContainer &rc); - Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); - Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode cmdMessage(const Command_Message &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); @@ -101,6 +99,7 @@ public: void sendProtocolItem(const SessionEvent &item); void sendProtocolItem(const GameEventContainer &item); void sendProtocolItem(const RoomEvent &item); + }; #endif diff --git a/servatrice/CMakeLists.txt b/servatrice/CMakeLists.txt index e249fdcd..2fe46e00 100644 --- a/servatrice/CMakeLists.txt +++ b/servatrice/CMakeLists.txt @@ -15,6 +15,18 @@ SET(servatrice_SOURCES src/settingscache.cpp src/isl_interface.cpp ${VERSION_STRING_CPP} + src/smtp/emailaddress.cpp + src/smtp/mimeattachment.cpp + src/smtp/mimecontentformatter.cpp + src/smtp/mimefile.cpp + src/smtp/mimehtml.cpp + src/smtp/mimeinlinefile.cpp + src/smtp/mimemessage.cpp + src/smtp/mimemultipart.cpp + src/smtp/mimepart.cpp + src/smtp/mimetext.cpp + src/smtp/quotedprintable.cpp + src/smtp/smtpclient.cpp ) set(servatrice_RESOURCES servatrice.qrc) diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 27a3e2f1..31483afe 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -65,6 +65,39 @@ regonly=0 ; to get their account activated. Default true. ;requireemail=true +[smtp] + +; Connectin type: currently supported method are "tcp", "ssl" and "tls" +connection=tcp + +; Auth type: currently supported method are "plain" and "login" +auth=plain + +; Hostname or IP addres of the smtp server +host=localhost + +; Smtp port number of the smtp server. Usual values are 25 or 587 for tcp, 465 for ssl +port=25 + +; Username: this typically matches the "from" email address +username=root@localhost + +; Password for the username +password=foobar + +; Sender email address: the "from" email address +email=root@localhost + +; Sender email name +name="Cockatrice server" + +; Email subject +subject="Cockatrice server account activation token" + +; Email body. You can use these tags here: %username %token +; They will be substituted with the actual values in the email +; +body="Hi %username, thank our for registering on our Cockatrice server\r\nHere's the activation token you need to supply for activatin your account:\r\n\r\n%token\r\n\r\nHappy gaming!" [database] diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index e4430912..ad6f6ada 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -160,8 +160,8 @@ bool Servatrice::initServer() authenticationMethod = AuthenticationNone; } - registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); - requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); + bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); qDebug() << "Registration enabled: " << registrationEnabled; if (registrationEnabled) diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index e5ff7d1e..b2027709 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -111,13 +111,13 @@ bool Servatrice_DatabaseInterface::getRequireRegistration() return settingsCache->value("authentication/regonly", 0).toBool(); } -bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, bool active) +bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active) { if (!checkSql()) return false; QString passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt()); - QString token = PasswordHasher::generateActivationToken(); + token = active ? QString() : PasswordHasher::generateActivationToken(); QSqlQuery *query = prepareQuery("insert into {prefix}_users " "(name, realname, gender, password_sha512, email, country, registrationDate, active, token) " diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index 9bb111ff..d7715544 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -25,9 +25,7 @@ private: bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining); QChar getGenderChar(ServerInfo_User_Gender const &gender); protected: - bool usernameIsValid(const QString &user); AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft); - bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining); public slots: void initDatabase(const QSqlDatabase &_sqlDatabase); public: @@ -62,9 +60,11 @@ public: void lockSessionTables(); void unlockSessionTables(); bool userSessionExists(const QString &userName); + bool usernameIsValid(const QString &user); + bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining); bool getRequireRegistration(); - bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, bool active = false); + bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false); bool activateUser(const QString &userName, const QString &token); void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage, LogMessage_TargetType targetType, const int targetId, const QString &targetName); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 86485661..a2fa26b2 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -51,12 +51,15 @@ #include "pb/response_deck_list.pb.h" #include "pb/response_deck_download.pb.h" #include "pb/response_deck_upload.pb.h" +#include "pb/response_register.pb.h" #include "pb/response_replay_list.pb.h" #include "pb/response_replay_download.pb.h" #include "pb/serverinfo_replay.pb.h" #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_deckstorage.pb.h" +#include "smtp/SmtpMime" + #include "version_string.h" #include #include @@ -267,6 +270,8 @@ Response::ResponseCode ServerSocketInterface::processExtendedSessionCommand(int case SessionCommand::REPLAY_DOWNLOAD: return cmdReplayDownload(cmd.GetExtension(Command_ReplayDownload::ext), rc); case SessionCommand::REPLAY_MODIFY_MATCH: return cmdReplayModifyMatch(cmd.GetExtension(Command_ReplayModifyMatch::ext), rc); case SessionCommand::REPLAY_DELETE_MATCH: return cmdReplayDeleteMatch(cmd.GetExtension(Command_ReplayDeleteMatch::ext), rc); + case SessionCommand::REGISTER: return cmdRegisterAccount(cmd.GetExtension(Command_Register::ext), rc); break; + case SessionCommand::ACTIVATE: return cmdActivateAccount(cmd.GetExtension(Command_Activate::ext), rc); break; default: return Response::RespFunctionNotAllowed; } } @@ -764,6 +769,179 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban return Response::RespOk; } +Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc) +{ + QString userName = QString::fromStdString(cmd.user_name()); + qDebug() << "Got register command: " << userName; + + bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool(); + if (!registrationEnabled) + return Response::RespRegistrationDisabled; + + QString emailAddress = QString::fromStdString(cmd.email()); + bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool(); + if (requireEmailForRegistration) + { + QRegExp rx("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b"); + if(emailAddress.isEmpty() || !rx.exactMatch(emailAddress)) + return Response::RespEmailRequiredToRegister; + } + + // TODO: Move this method outside of the db interface + if (!sqlInterface->usernameIsValid(userName)) + return Response::RespUsernameInvalid; + + if(sqlInterface->userExists(userName)) + return Response::RespUserAlreadyExists; + + QString banReason; + int banSecondsRemaining; + if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining)) + { + Response_Register *re = new Response_Register; + re->set_denied_reason_str(banReason.toStdString()); + if (banSecondsRemaining != 0) + re->set_denied_end_time(QDateTime::currentDateTime().addSecs(banSecondsRemaining).toTime_t()); + rc.setResponseExtension(re); + return Response::RespUserIsBanned; + } + + if (tooManyRegistrationAttempts(this->getAddress())) + return Response::RespTooManyRequests; + + QString realName = QString::fromStdString(cmd.real_name()); + ServerInfo_User_Gender gender = cmd.gender(); + QString country = QString::fromStdString(cmd.country()); + QString password = QString::fromStdString(cmd.password()); + + // TODO make this configurable? + if(password.length() < 6) + return Response::RespPasswordTooShort; + + QString token; + bool regSucceeded = sqlInterface->registerUser(userName, realName, gender, password, emailAddress, country, token, !requireEmailForRegistration); + + if(regSucceeded) + { + qDebug() << "Accepted register command for user: " << userName; + if(requireEmailForRegistration) + { + // TODO call a slot on another thread to send email + sendActivationTokenMail(userName, emailAddress, token); + return Response::RespRegistrationAcceptedNeedsActivation; + } else { + return Response::RespRegistrationAccepted; + } + } else { + return Response::RespRegistrationFailed; + } +} + +bool ServerSocketInterface::sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token) +{ + QString tmp = settingsCache->value("smtp/connection", "tcp").toString(); + SmtpClient::ConnectionType connection = SmtpClient::TcpConnection; + if(tmp == "ssl") + connection = SmtpClient::SslConnection; + else if(tmp == "tls") + connection = SmtpClient::TlsConnection; + + tmp = settingsCache->value("smtp/auth", "plain").toString(); + SmtpClient::AuthMethod auth = SmtpClient::AuthPlain; + if(tmp == "login") + auth = SmtpClient::AuthLogin; + + QString host = settingsCache->value("smtp/host", "localhost").toString(); + int port = settingsCache->value("smtp/port", 25).toInt(); + QString username = settingsCache->value("smtp/username", "").toString(); + QString password = settingsCache->value("smtp/password", "").toString(); + QString email = settingsCache->value("smtp/email", "").toString(); + QString name = settingsCache->value("smtp/name", "").toString(); + QString subject = settingsCache->value("smtp/subject", "").toString(); + QString body = settingsCache->value("smtp/body", "").toString(); + + if(email.isEmpty()) + { + qDebug() << "[MAIL] Missing email field" << endl; + return false; + } + if(body.isEmpty()) + { + qDebug() << "[MAIL] Missing body field" << endl; + return false; + } + if(recipient.isEmpty()) + { + qDebug() << "[MAIL] Missing recipient field" << endl; + return false; + } + if(token.isEmpty()) + { + qDebug() << "[MAIL] Missing token field" << endl; + return false; + } + + SmtpClient smtp(host, port, connection); + smtp.setUser(username); + smtp.setPassword(password); + smtp.setAuthMethod(auth); + + MimeMessage message; + EmailAddress sender(email, name); + message.setSender(&sender); + EmailAddress to(recipient, nickname); + message.addRecipient(&to); + message.setSubject(subject); + + MimeText text; + text.setText(body.replace("%username", nickname).replace("%token", token)); + message.addPart(&text); + + // Now we can send the mail + + if (!smtp.connectToHost()) { + qDebug() << "[MAIL] Failed to connect to host" << host << "on port" << port; + return false; + } + + if (!smtp.login()) { + qDebug() << "[MAIL] Failed to login as " << username; + return false; + } + + if (!smtp.sendMail(message)) { + qDebug() << "[MAIL] Failed to send mail to " << recipient; + return false; + } + + smtp.quit(); + qDebug() << "[MAIL] Sent activation email to " << recipient << " for nickname " << nickname; + return true; +} + +bool ServerSocketInterface::tooManyRegistrationAttempts(const QString &ipAddress) +{ + // TODO: implement + Q_UNUSED(ipAddress); + return false; +} + +Response::ResponseCode ServerSocketInterface::cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /*rc*/) +{ + QString userName = QString::fromStdString(cmd.user_name()); + QString token = QString::fromStdString(cmd.token()); + + if(sqlInterface->activateUser(userName, token)) + { + qDebug() << "Accepted activation for user" << QString::fromStdString(cmd.user_name()); + return Response::RespActivationAccepted; + } else { + qDebug() << "Failed activation for user" << QString::fromStdString(cmd.user_name()); + return Response::RespActivationFailed; + } +} + + // ADMIN FUNCTIONS. // Permission is checked by the calling function. diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 6a2a41f8..c89709d3 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -59,6 +59,7 @@ signals: void outputQueueChanged(); protected: void logDebugMessage(const QString &message); + bool tooManyRegistrationAttempts(const QString &ipAddress); private: QMutex outputQueueMutex; Servatrice *servatrice; @@ -91,10 +92,14 @@ private: Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); + Response::ResponseCode cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc); + Response::ResponseCode cmdActivateAccount(const Command_Activate &cmd, ResponseContainer & /* rc */); Response::ResponseCode processExtendedSessionCommand(int cmdType, const SessionCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedModeratorCommand(int cmdType, const ModeratorCommand &cmd, ResponseContainer &rc); Response::ResponseCode processExtendedAdminCommand(int cmdType, const AdminCommand &cmd, ResponseContainer &rc); + + bool sendActivationTokenMail(const QString &nickname, const QString &recipient, const QString &token); public: ServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent = 0); ~ServerSocketInterface(); diff --git a/servatrice/src/smtp/SmtpMime b/servatrice/src/smtp/SmtpMime new file mode 100644 index 00000000..940996b8 --- /dev/null +++ b/servatrice/src/smtp/SmtpMime @@ -0,0 +1,31 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef SMTPMIME_H +#define SMTPMIME_H + +#include "smtpclient.h" +#include "mimepart.h" +#include "mimehtml.h" +#include "mimeattachment.h" +#include "mimemessage.h" +#include "mimetext.h" +#include "mimeinlinefile.h" +#include "mimefile.h" + +#endif // SMTPMIME_H diff --git a/servatrice/src/smtp/emailaddress.cpp b/servatrice/src/smtp/emailaddress.cpp new file mode 100644 index 00000000..9480e5d4 --- /dev/null +++ b/servatrice/src/smtp/emailaddress.cpp @@ -0,0 +1,60 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "emailaddress.h" + +/* [1] Constructors and Destructors */ + +EmailAddress::EmailAddress(const QString & address, const QString & name) +{ + this->address = address; + this->name = name; +} + +EmailAddress::~EmailAddress() +{ +} + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +void EmailAddress::setName(const QString & name) +{ + this->name = name; + +} + +void EmailAddress::setAddress(const QString & address) +{ + this->address = address; +} + +const QString & EmailAddress::getName() const +{ + return name; +} + +const QString & EmailAddress::getAddress() const +{ + return address; +} + +/* [2] --- */ + diff --git a/servatrice/src/smtp/emailaddress.h b/servatrice/src/smtp/emailaddress.h new file mode 100644 index 00000000..4ecf6efc --- /dev/null +++ b/servatrice/src/smtp/emailaddress.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef EMAILADDRESS_H +#define EMAILADDRESS_H + +#include + +#include "smtpexports.h" + +class SMTP_EXPORT EmailAddress : public QObject +{ + Q_OBJECT +public: + + /* [1] Constructors and Destructors */ + + EmailAddress(); + EmailAddress(const QString & address, const QString & name=""); + + ~EmailAddress(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + void setName(const QString & name); + void setAddress(const QString & address); + + const QString & getName() const; + const QString & getAddress() const; + + /* [2] --- */ + + +private: + + /* [3] Private members */ + + QString name; + QString address; + + /* [3] --- */ +}; + +#endif // EMAILADDRESS_H diff --git a/servatrice/src/smtp/mimeattachment.cpp b/servatrice/src/smtp/mimeattachment.cpp new file mode 100644 index 00000000..0dea6222 --- /dev/null +++ b/servatrice/src/smtp/mimeattachment.cpp @@ -0,0 +1,50 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimeattachment.h" +#include + +/* [1] Constructors and Destructors */ + +MimeAttachment::MimeAttachment(QFile *file) + : MimeFile(file) +{ +} +MimeAttachment::MimeAttachment(const QByteArray& stream, const QString& fileName): MimeFile(stream, fileName) +{ + +} + +MimeAttachment::~MimeAttachment() +{ +} + +/* [1] --- */ + + +/* [2] Protected methods */ + +void MimeAttachment::prepare() +{ + this->header += "Content-disposition: attachment\r\n"; + + /* !!! IMPORTANT !!! */ + MimeFile::prepare(); +} + +/* [2] --- */ diff --git a/servatrice/src/smtp/mimeattachment.h b/servatrice/src/smtp/mimeattachment.h new file mode 100644 index 00000000..4a92e9a1 --- /dev/null +++ b/servatrice/src/smtp/mimeattachment.h @@ -0,0 +1,51 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEATTACHMENT_H +#define MIMEATTACHMENT_H + +#include +#include "mimepart.h" +#include "mimefile.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimeAttachment : public MimeFile +{ + Q_OBJECT +public: + + /* [1] Constructors and Destructors */ + + MimeAttachment(QFile* file); + MimeAttachment(const QByteArray& stream, const QString& fileName); + + ~MimeAttachment(); + + /* [1] --- */ + +protected: + + /* [2] Protected methods */ + + virtual void prepare(); + + /* [2] --- */ +}; + +#endif // MIMEATTACHMENT_H diff --git a/servatrice/src/smtp/mimecontentformatter.cpp b/servatrice/src/smtp/mimecontentformatter.cpp new file mode 100644 index 00000000..7f5a6e43 --- /dev/null +++ b/servatrice/src/smtp/mimecontentformatter.cpp @@ -0,0 +1,66 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimecontentformatter.h" + +MimeContentFormatter::MimeContentFormatter(int max_length) : + max_length(max_length) +{} + +QString MimeContentFormatter::format(const QString &content, bool quotedPrintable) const { + + QString out; + + int chars = 0; + for (int i = 0; i < content.length() ; ++i) { + chars++; + if (!quotedPrintable) { + if (chars > max_length) { + out.append("\r\n"); + chars = 1; + } + } + else { + if (content[i] == '\n') { // new line + out.append(content[i]); + chars = 0; + continue; + } + + if ((chars > max_length - 1) + || ((content[i] == '=') && (chars > max_length - 3) )) { + out.append('='); + out.append("\r\n"); + chars = 1; + } + + } + out.append(content[i]); + } + + return out; + +} + +void MimeContentFormatter::setMaxLength(int l) { + max_length = l; +} + +int MimeContentFormatter::getMaxLength() const { + return max_length; +} diff --git a/servatrice/src/smtp/mimecontentformatter.h b/servatrice/src/smtp/mimecontentformatter.h new file mode 100644 index 00000000..bf751588 --- /dev/null +++ b/servatrice/src/smtp/mimecontentformatter.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMECONTENTFORMATTER_H +#define MIMECONTENTFORMATTER_H + +#include +#include + +#include "smtpexports.h" + +class SMTP_EXPORT MimeContentFormatter : public QObject +{ + Q_OBJECT +public: + MimeContentFormatter (int max_length = 76); + + void setMaxLength(int l); + int getMaxLength() const; + + QString format(const QString &content, bool quotedPrintable = false) const; + +protected: + int max_length; + +}; + +#endif // MIMECONTENTFORMATTER_H diff --git a/servatrice/src/smtp/mimefile.cpp b/servatrice/src/smtp/mimefile.cpp new file mode 100644 index 00000000..c6f313a8 --- /dev/null +++ b/servatrice/src/smtp/mimefile.cpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimefile.h" +#include + +/* [1] Constructors and Destructors */ + +MimeFile::MimeFile(QFile *file) +{ + this->file = file; + this->cType = "application/octet-stream"; + this->cName = QFileInfo(*file).fileName(); + this->cEncoding = Base64; +} + +MimeFile::MimeFile(const QByteArray& stream, const QString& fileName) +{ + this->cEncoding = Base64; + this->cType = "application/octet-stream"; + this->file = 0; + this->cName = fileName; + this->content = stream; +} + +MimeFile::~MimeFile() +{ + if (file) + delete file; +} + +/* [1] --- */ + + +/* [2] Getters and setters */ + +/* [2] --- */ + + +/* [3] Protected methods */ + +void MimeFile::prepare() +{ + if (this->file) + { + file->open(QIODevice::ReadOnly); + this->content = file->readAll(); + file->close(); + } + /* !!! IMPORTANT !!!! */ + MimePart::prepare(); +} + +/* [3] --- */ + diff --git a/servatrice/src/smtp/mimefile.h b/servatrice/src/smtp/mimefile.h new file mode 100644 index 00000000..13f444de --- /dev/null +++ b/servatrice/src/smtp/mimefile.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEFILE_H +#define MIMEFILE_H + +#include "mimepart.h" +#include + +#include "smtpexports.h" + +class SMTP_EXPORT MimeFile : public MimePart +{ + Q_OBJECT +public: + + /* [1] Constructors and Destructors */ + + MimeFile(const QByteArray& stream, const QString& fileName); + MimeFile(QFile *f); + ~MimeFile(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + /* [2] --- */ + +protected: + + /* [3] Protected members */ + + QFile* file; + + /* [3] --- */ + + + /* [4] Protected methods */ + + virtual void prepare(); + + /* [4] --- */ + +}; + +#endif // MIMEFILE_H diff --git a/servatrice/src/smtp/mimehtml.cpp b/servatrice/src/smtp/mimehtml.cpp new file mode 100644 index 00000000..5594d3a4 --- /dev/null +++ b/servatrice/src/smtp/mimehtml.cpp @@ -0,0 +1,57 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimehtml.h" + +/* [1] Constructors and Destructors */ + +MimeHtml::MimeHtml(const QString &html) : MimeText(html) +{ + this->cType = "text/html"; +} + +MimeHtml::~MimeHtml() {} + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +void MimeHtml::setHtml(const QString & html) +{ + this->text = html; +} + +const QString & MimeHtml::getHtml() const +{ + return text; +} + + +/* [2] --- */ + + +/* [3] Protected methods */ + +void MimeHtml::prepare() +{ + /* !!! IMPORTANT !!! */ + MimeText::prepare(); +} + +/* [3] --- */ diff --git a/servatrice/src/smtp/mimehtml.h b/servatrice/src/smtp/mimehtml.h new file mode 100644 index 00000000..f3f54300 --- /dev/null +++ b/servatrice/src/smtp/mimehtml.h @@ -0,0 +1,61 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEHTML_H +#define MIMEHTML_H + +#include "mimetext.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimeHtml : public MimeText +{ + Q_OBJECT +public: + + /* [1] Constructors and Destructors */ + + MimeHtml(const QString &html = ""); + ~MimeHtml(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + void setHtml(const QString & html); + + const QString& getHtml() const; + + /* [2] --- */ + +protected: + + /* [3] Protected members */ + + /* [3] --- */ + + + /* [4] Protected methods */ + + virtual void prepare(); + + /* [4] --- */ +}; + +#endif // MIMEHTML_H diff --git a/servatrice/src/smtp/mimeinlinefile.cpp b/servatrice/src/smtp/mimeinlinefile.cpp new file mode 100644 index 00000000..0823b0db --- /dev/null +++ b/servatrice/src/smtp/mimeinlinefile.cpp @@ -0,0 +1,52 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimeinlinefile.h" + +/* [1] Constructors and Destructors */ + +MimeInlineFile::MimeInlineFile(QFile *f) + : MimeFile(f) +{ +} + +MimeInlineFile::~MimeInlineFile() +{} + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +/* [2] --- */ + + +/* [3] Protected methods */ + +void MimeInlineFile::prepare() +{ + this->header += "Content-Disposition: inline\r\n"; + + /* !!! IMPORTANT !!! */ + MimeFile::prepare(); +} + +/* [3] --- */ + + + diff --git a/servatrice/src/smtp/mimeinlinefile.h b/servatrice/src/smtp/mimeinlinefile.h new file mode 100644 index 00000000..98fe3cab --- /dev/null +++ b/servatrice/src/smtp/mimeinlinefile.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEINLINEFILE_H +#define MIMEINLINEFILE_H + +#include "mimefile.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimeInlineFile : public MimeFile +{ +public: + + /* [1] Constructors and Destructors */ + + MimeInlineFile(QFile *f); + ~MimeInlineFile(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + /* [2] --- */ + +protected: + + /* [3] Protected members */ + + /* [3] --- */ + + + /* [4] Protected methods */ + + virtual void prepare(); + + /* [4] --- */ +}; + +#endif // MIMEINLINEFILE_H diff --git a/servatrice/src/smtp/mimemessage.cpp b/servatrice/src/smtp/mimemessage.cpp new file mode 100644 index 00000000..0c4ebc2d --- /dev/null +++ b/servatrice/src/smtp/mimemessage.cpp @@ -0,0 +1,257 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimemessage.h" + +#include +#include "quotedprintable.h" +#include + +/* [1] Constructors and Destructors */ +MimeMessage::MimeMessage(bool createAutoMimeContent) : + hEncoding(MimePart::_8Bit) +{ + if (createAutoMimeContent) + this->content = new MimeMultiPart(); + + autoMimeContentCreated = createAutoMimeContent; +} + +MimeMessage::~MimeMessage() +{ + if (this->autoMimeContentCreated) + { + this->autoMimeContentCreated = false; + delete (this->content); + } +} + +/* [1] --- */ + + +/* [2] Getters and Setters */ +MimePart& MimeMessage::getContent() { + return *content; +} + +void MimeMessage::setContent(MimePart *content) { + if (this->autoMimeContentCreated) + { + this->autoMimeContentCreated = false; + delete (this->content); + } + this->content = content; +} + +void MimeMessage::setSender(EmailAddress* e) +{ + this->sender = e; +} + +void MimeMessage::addRecipient(EmailAddress* rcpt, RecipientType type) +{ + switch (type) + { + case To: + recipientsTo << rcpt; + break; + case Cc: + recipientsCc << rcpt; + break; + case Bcc: + recipientsBcc << rcpt; + break; + } +} + +void MimeMessage::addTo(EmailAddress* rcpt) { + this->recipientsTo << rcpt; +} + +void MimeMessage::addCc(EmailAddress* rcpt) { + this->recipientsCc << rcpt; +} + +void MimeMessage::addBcc(EmailAddress* rcpt) { + this->recipientsBcc << rcpt; +} + +void MimeMessage::setSubject(const QString & subject) +{ + this->subject = subject; +} + +void MimeMessage::addPart(MimePart *part) +{ + if (typeid(*content) == typeid(MimeMultiPart)) { + ((MimeMultiPart*) content)->addPart(part); + }; +} + +void MimeMessage::setHeaderEncoding(MimePart::Encoding hEnc) +{ + this->hEncoding = hEnc; +} + +const EmailAddress & MimeMessage::getSender() const +{ + return *sender; +} + +const QList & MimeMessage::getRecipients(RecipientType type) const +{ + switch (type) + { + default: + case To: + return recipientsTo; + case Cc: + return recipientsCc; + case Bcc: + return recipientsBcc; + } +} + +const QString & MimeMessage::getSubject() const +{ + return subject; +} + +const QList & MimeMessage::getParts() const +{ + if (typeid(*content) == typeid(MimeMultiPart)) { + return ((MimeMultiPart*) content)->getParts(); + } + else { + QList *res = new QList(); + res->append(content); + return *res; + } +} + +/* [2] --- */ + + +/* [3] Public Methods */ + +QString MimeMessage::toString() +{ + QString mime; + + /* =========== MIME HEADER ============ */ + + /* ---------- Sender / From ----------- */ + mime = "From:"; + if (sender->getName() != "") + { + switch (hEncoding) + { + case MimePart::Base64: + mime += " =?utf-8?B?" + QByteArray().append(sender->getName()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(sender->getName())).replace(' ', "_").replace(':',"=3A") + "?="; + break; + default: + mime += " " + sender->getName(); + } + } + mime += " <" + sender->getAddress() + ">\r\n"; + /* ---------------------------------- */ + + + /* ------- Recipients / To ---------- */ + mime += "To:"; + QList::iterator it; int i; + for (i = 0, it = recipientsTo.begin(); it != recipientsTo.end(); ++it, ++i) + { + if (i != 0) { mime += ","; } + + if ((*it)->getName() != "") + { + switch (hEncoding) + { + case MimePart::Base64: + mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + mime += "\r\n"; + /* ---------------------------------- */ + + /* ------- Recipients / Cc ---------- */ + if (recipientsCc.size() != 0) { + mime += "Cc:"; + } + for (i = 0, it = recipientsCc.begin(); it != recipientsCc.end(); ++it, ++i) + { + if (i != 0) { mime += ","; } + + if ((*it)->getName() != "") + { + switch (hEncoding) + { + case MimePart::Base64: + mime += " =?utf-8?B?" + QByteArray().append((*it)->getName()).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += " =?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append((*it)->getName())).replace(' ', "_").replace(':',"=3A") + "?="; + break; + default: + mime += " " + (*it)->getName(); + } + } + mime += " <" + (*it)->getAddress() + ">"; + } + if (recipientsCc.size() != 0) { + mime += "\r\n"; + } + /* ---------------------------------- */ + + /* ------------ Subject ------------- */ + mime += "Subject: "; + + + switch (hEncoding) + { + case MimePart::Base64: + mime += "=?utf-8?B?" + QByteArray().append(subject).toBase64() + "?="; + break; + case MimePart::QuotedPrintable: + mime += "=?utf-8?Q?" + QuotedPrintable::encode(QByteArray().append(subject)).replace(' ', "_").replace(':',"=3A") + "?="; + break; + default: + mime += subject; + } + /* ---------------------------------- */ + + mime += "\r\n"; + mime += "MIME-Version: 1.0\r\n"; + + mime += content->toString(); + return mime; +} + +/* [3] --- */ diff --git a/servatrice/src/smtp/mimemessage.h b/servatrice/src/smtp/mimemessage.h new file mode 100644 index 00000000..18b4ec3b --- /dev/null +++ b/servatrice/src/smtp/mimemessage.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEMESSAGE_H +#define MIMEMESSAGE_H + +#include "mimepart.h" +#include "mimemultipart.h" +#include "emailaddress.h" +#include + +#include "smtpexports.h" + +class SMTP_EXPORT MimeMessage : public QObject +{ +public: + + enum RecipientType { + To, // primary + Cc, // carbon copy + Bcc // blind carbon copy + }; + + /* [1] Constructors and Destructors */ + + MimeMessage(bool createAutoMimeConent = true); + ~MimeMessage(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + void setSender(EmailAddress* e); + void addRecipient(EmailAddress* rcpt, RecipientType type = To); + void addTo(EmailAddress* rcpt); + void addCc(EmailAddress* rcpt); + void addBcc(EmailAddress* rcpt); + void setSubject(const QString & subject); + void addPart(MimePart* part); + + void setHeaderEncoding(MimePart::Encoding); + + const EmailAddress & getSender() const; + const QList & getRecipients(RecipientType type = To) const; + const QString & getSubject() const; + const QList & getParts() const; + + MimePart& getContent(); + void setContent(MimePart *content); + /* [2] --- */ + + + /* [3] Public methods */ + + virtual QString toString(); + + /* [3] --- */ + +protected: + + /* [4] Protected members */ + + EmailAddress* sender; + QList recipientsTo, recipientsCc, recipientsBcc; + QString subject; + MimePart *content; + bool autoMimeContentCreated; + + MimePart::Encoding hEncoding; + + /* [4] --- */ + + +}; + +#endif // MIMEMESSAGE_H diff --git a/servatrice/src/smtp/mimemultipart.cpp b/servatrice/src/smtp/mimemultipart.cpp new file mode 100644 index 00000000..19d47297 --- /dev/null +++ b/servatrice/src/smtp/mimemultipart.cpp @@ -0,0 +1,78 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimemultipart.h" +#include +#include + +const QString MULTI_PART_NAMES[] = { + "multipart/mixed", // Mixed + "multipart/digest", // Digest + "multipart/alternative", // Alternative + "multipart/related", // Related + "multipart/report", // Report + "multipart/signed", // Signed + "multipart/encrypted" // Encrypted +}; + +MimeMultiPart::MimeMultiPart(MultiPartType type) +{ + this->type = type; + this->cType = MULTI_PART_NAMES[this->type]; + this->cEncoding = _8Bit; + + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(QByteArray().append(qrand())); + cBoundary = md5.result().toHex(); +} + +MimeMultiPart::~MimeMultiPart() { + +} + +void MimeMultiPart::addPart(MimePart *part) { + parts.append(part); +} + +const QList & MimeMultiPart::getParts() const { + return parts; +} + +void MimeMultiPart::prepare() { + QList::iterator it; + + content = ""; + for (it = parts.begin(); it != parts.end(); it++) { + content += "--" + cBoundary + "\r\n"; + (*it)->prepare(); + content += (*it)->toString(); + }; + + content += "--" + cBoundary + "--\r\n"; + + MimePart::prepare(); +} + +void MimeMultiPart::setMimeType(const MultiPartType type) { + this->type = type; + this->cType = MULTI_PART_NAMES[type]; +} + +MimeMultiPart::MultiPartType MimeMultiPart::getMimeType() const { + return type; +} diff --git a/servatrice/src/smtp/mimemultipart.h b/servatrice/src/smtp/mimemultipart.h new file mode 100644 index 00000000..a8e9f599 --- /dev/null +++ b/servatrice/src/smtp/mimemultipart.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEMULTIPART_H +#define MIMEMULTIPART_H + +#include "mimepart.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimeMultiPart : public MimePart +{ + Q_OBJECT +public: + + /* [0] Enums */ + enum MultiPartType { + Mixed = 0, // RFC 2046, section 5.1.3 + Digest = 1, // RFC 2046, section 5.1.5 + Alternative = 2, // RFC 2046, section 5.1.4 + Related = 3, // RFC 2387 + Report = 4, // RFC 6522 + Signed = 5, // RFC 1847, section 2.1 + Encrypted = 6 // RFC 1847, section 2.2 + }; + + /* [0] --- */ + + /* [1] Constructors and Destructors */ + MimeMultiPart(const MultiPartType type = Related); + + ~MimeMultiPart(); + + /* [1] --- */ + + /* [2] Getters and Setters */ + + void setMimeType(const MultiPartType type); + MultiPartType getMimeType() const; + + const QList & getParts() const; + + /* [2] --- */ + + /* [3] Public methods */ + + void addPart(MimePart *part); + + virtual void prepare(); + + /* [3] --- */ + +protected: + QList< MimePart* > parts; + + MultiPartType type; + +}; + +#endif // MIMEMULTIPART_H diff --git a/servatrice/src/smtp/mimepart.cpp b/servatrice/src/smtp/mimepart.cpp new file mode 100644 index 00000000..443f863e --- /dev/null +++ b/servatrice/src/smtp/mimepart.cpp @@ -0,0 +1,214 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimepart.h" +#include "quotedprintable.h" + +/* [1] Constructors and Destructors */ + +MimePart::MimePart() +{ + cEncoding = _7Bit; + prepared = false; + cBoundary = ""; +} + +MimePart::~MimePart() +{ + return; +} + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +void MimePart::setContent(const QByteArray & content) +{ + this->content = content; +} + +void MimePart::setHeader(const QString & header) +{ + this->header = header; +} + +void MimePart::addHeaderLine(const QString & line) +{ + this->header += line + "\r\n"; +} + +const QString& MimePart::getHeader() const +{ + return header; +} + +const QByteArray& MimePart::getContent() const +{ + return content; +} + +void MimePart::setContentId(const QString & cId) +{ + this->cId = cId; +} + +const QString & MimePart::getContentId() const +{ + return this->cId; +} + +void MimePart::setContentName(const QString & cName) +{ + this->cName = cName; +} + +const QString & MimePart::getContentName() const +{ + return this->cName; +} + +void MimePart::setContentType(const QString & cType) +{ + this->cType = cType; +} + +const QString & MimePart::getContentType() const +{ + return this->cType; +} + +void MimePart::setCharset(const QString & charset) +{ + this->cCharset = charset; +} + +const QString & MimePart::getCharset() const +{ + return this->cCharset; +} + +void MimePart::setEncoding(Encoding enc) +{ + this->cEncoding = enc; +} + +MimePart::Encoding MimePart::getEncoding() const +{ + return this->cEncoding; +} + +MimeContentFormatter& MimePart::getContentFormatter() +{ + return this->formatter; +} + +/* [2] --- */ + + +/* [3] Public methods */ + +QString MimePart::toString() +{ + if (!prepared) + prepare(); + + return mimeString; +} + +/* [3] --- */ + + +/* [4] Protected methods */ + +void MimePart::prepare() +{ + mimeString = QString(); + + /* === Header Prepare === */ + + /* Content-Type */ + mimeString.append("Content-Type: ").append(cType); + + if (cName != "") + mimeString.append("; name=\"").append(cName).append("\""); + + if (cCharset != "") + mimeString.append("; charset=").append(cCharset); + + if (cBoundary != "") + mimeString.append("; boundary=").append(cBoundary); + + mimeString.append("\r\n"); + /* ------------ */ + + /* Content-Transfer-Encoding */ + mimeString.append("Content-Transfer-Encoding: "); + switch (cEncoding) + { + case _7Bit: + mimeString.append("7bit\r\n"); + break; + case _8Bit: + mimeString.append("8bit\r\n"); + break; + case Base64: + mimeString.append("base64\r\n"); + break; + case QuotedPrintable: + mimeString.append("quoted-printable\r\n"); + break; + } + /* ------------------------ */ + + /* Content-Id */ + if (cId != NULL) + mimeString.append("Content-ID: <").append(cId).append(">\r\n"); + /* ---------- */ + + /* Addition header lines */ + + mimeString.append(header).append("\r\n"); + + /* ------------------------- */ + + /* === End of Header Prepare === */ + + /* === Content === */ + switch (cEncoding) + { + case _7Bit: + mimeString.append(QString(content).toLatin1()); + break; + case _8Bit: + mimeString.append(content); + break; + case Base64: + mimeString.append(formatter.format(content.toBase64())); + break; + case QuotedPrintable: + mimeString.append(formatter.format(QuotedPrintable::encode(content), true)); + break; + } + mimeString.append("\r\n"); + /* === End of Content === */ + + prepared = true; +} + +/* [4] --- */ diff --git a/servatrice/src/smtp/mimepart.h b/servatrice/src/smtp/mimepart.h new file mode 100644 index 00000000..8636035d --- /dev/null +++ b/servatrice/src/smtp/mimepart.h @@ -0,0 +1,114 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMEPART_H +#define MIMEPART_H + +#include +#include "mimecontentformatter.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimePart : public QObject +{ + Q_OBJECT +public: + + /* [0] Enumerations */ + enum Encoding { + _7Bit, + _8Bit, + Base64, + QuotedPrintable + }; + + + /* [0] --- */ + + + /* [1] Constructors and Destructors */ + + MimePart(); + ~MimePart(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + const QString& getHeader() const; + const QByteArray& getContent() const; + + void setContent(const QByteArray & content); + void setHeader(const QString & header); + + void addHeaderLine(const QString & line); + + void setContentId(const QString & cId); + const QString & getContentId() const; + + void setContentName(const QString & cName); + const QString & getContentName() const; + + void setContentType(const QString & cType); + const QString & getContentType() const; + + void setCharset(const QString & charset); + const QString & getCharset() const; + + void setEncoding(Encoding enc); + Encoding getEncoding() const; + + MimeContentFormatter& getContentFormatter(); + + /* [2] --- */ + + + /* [3] Public methods */ + + virtual QString toString(); + + virtual void prepare(); + + /* [3] --- */ + + + +protected: + + /* [4] Protected members */ + + QString header; + QByteArray content; + + QString cId; + QString cName; + QString cType; + QString cCharset; + QString cBoundary; + Encoding cEncoding; + + QString mimeString; + bool prepared; + + MimeContentFormatter formatter; + + /* [4] --- */ +}; + +#endif // MIMEPART_H diff --git a/servatrice/src/smtp/mimetext.cpp b/servatrice/src/smtp/mimetext.cpp new file mode 100644 index 00000000..20b1b0a0 --- /dev/null +++ b/servatrice/src/smtp/mimetext.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "mimetext.h" + +/* [1] Constructors and Destructors */ + +MimeText::MimeText(const QString &txt) +{ + this->text = txt; + this->cType = "text/plain"; + this->cCharset = "utf-8"; + this->cEncoding = _8Bit; +} + +MimeText::~MimeText() { } + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +void MimeText::setText(const QString & text) +{ + this->text = text; +} + +const QString & MimeText::getText() const +{ + return text; +} + +/* [2] --- */ + + +/* [3] Protected Methods */ + +void MimeText::prepare() +{ + this->content.clear(); + this->content.append(text); + + /* !!! IMPORTANT !!! */ + MimePart::prepare(); +} + +/* [3] --- */ diff --git a/servatrice/src/smtp/mimetext.h b/servatrice/src/smtp/mimetext.h new file mode 100644 index 00000000..c0d29835 --- /dev/null +++ b/servatrice/src/smtp/mimetext.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef MIMETEXT_H +#define MIMETEXT_H + +#include "mimepart.h" + +#include "smtpexports.h" + +class SMTP_EXPORT MimeText : public MimePart +{ +public: + + /* [1] Constructors and Destructors */ + + MimeText(const QString &text = ""); + ~MimeText(); + + /* [1] --- */ + + + /* [2] Getters and Setters*/ + + void setText(const QString & text); + + const QString & getText() const; + + /* [2] --- */ + +protected: + + /* [3] Protected members */ + + QString text; + /* [3] --- */ + + + /* [4] Protected methods */ + + void prepare(); + + /* [4] --- */ + +}; + +#endif // MIMETEXT_H diff --git a/servatrice/src/smtp/quotedprintable.cpp b/servatrice/src/smtp/quotedprintable.cpp new file mode 100644 index 00000000..aa3eda6a --- /dev/null +++ b/servatrice/src/smtp/quotedprintable.cpp @@ -0,0 +1,69 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "quotedprintable.h" + +QString QuotedPrintable::encode(const QByteArray &input) +{ + QString output; + + char byte; + const char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + for (int i = 0; i < input.length() ; ++i) + { + byte = input[i]; + + if ((byte == 0x20) || ((byte >= 33) && (byte <= 126) && (byte != 61))) + { + output.append(byte); + } + else + { + output.append('='); + output.append(hex[((byte >> 4) & 0x0F)]); + output.append(hex[(byte & 0x0F)]); + } + } + + return output; +} + + +QByteArray QuotedPrintable::decode(const QString &input) +{ + // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F + const int hexVal[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15}; + + QByteArray output; + + for (int i = 0; i < input.length(); ++i) + { + if (input.at(i).toLatin1() == '=') + { + output.append((hexVal[input.at(i + 1).toLatin1() - '0'] << 4) + hexVal[input.at(i + 2).toLatin1() - '0']); + i += 2; + } + else + { + output.append(input.at(i).toLatin1()); + } + } + + return output; +} diff --git a/servatrice/src/smtp/quotedprintable.h b/servatrice/src/smtp/quotedprintable.h new file mode 100644 index 00000000..03d3acf8 --- /dev/null +++ b/servatrice/src/smtp/quotedprintable.h @@ -0,0 +1,39 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef QUOTEDPRINTABLE_H +#define QUOTEDPRINTABLE_H + +#include +#include + +#include "smtpexports.h" + +class SMTP_EXPORT QuotedPrintable : public QObject +{ + Q_OBJECT +public: + + static QString encode(const QByteArray &input); + static QByteArray decode(const QString &input); + +private: + QuotedPrintable(); +}; + +#endif // QUOTEDPRINTABLE_H diff --git a/servatrice/src/smtp/smtpclient.cpp b/servatrice/src/smtp/smtpclient.cpp new file mode 100644 index 00000000..aaa59095 --- /dev/null +++ b/servatrice/src/smtp/smtpclient.cpp @@ -0,0 +1,482 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#include "smtpclient.h" + +#include +#include + + +/* [1] Constructors and destructors */ + +SmtpClient::SmtpClient(const QString & host, int port, ConnectionType connectionType) : + name("localhost"), + authMethod(AuthPlain), + connectionTimeout(5000), + responseTimeout(5000), + sendMessageTimeout(60000) +{ + setConnectionType(connectionType); + + this->host = host; + this->port = port; + + connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), + this, SLOT(socketStateChanged(QAbstractSocket::SocketState))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(socketError(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(readyRead()), + this, SLOT(socketReadyRead())); +} + +SmtpClient::~SmtpClient() {} + +/* [1] --- */ + + +/* [2] Getters and Setters */ + +void SmtpClient::setUser(const QString &user) +{ + this->user = user; +} + +void SmtpClient::setPassword(const QString &password) +{ + this->password = password; +} + +void SmtpClient::setAuthMethod(AuthMethod method) +{ + this->authMethod = method; +} + +void SmtpClient::setHost(const QString &host) +{ + this->host = host; +} + +void SmtpClient::setPort(int port) +{ + this->port = port; +} + +void SmtpClient::setConnectionType(ConnectionType ct) +{ + this->connectionType = ct; + + switch (connectionType) + { + case TcpConnection: + socket = new QTcpSocket(this); + break; + case SslConnection: + case TlsConnection: + socket = new QSslSocket(this); + } +} + +const QString& SmtpClient::getHost() const +{ + return this->host; +} + +const QString& SmtpClient::getUser() const +{ + return this->user; +} + +const QString& SmtpClient::getPassword() const +{ + return this->password; +} + +SmtpClient::AuthMethod SmtpClient::getAuthMethod() const +{ + return this->authMethod; +} + +int SmtpClient::getPort() const +{ + return this->port; +} + +SmtpClient::ConnectionType SmtpClient::getConnectionType() const +{ + return connectionType; +} + +const QString& SmtpClient::getName() const +{ + return this->name; +} + +void SmtpClient::setName(const QString &name) +{ + this->name = name; +} + +const QString & SmtpClient::getResponseText() const +{ + return responseText; +} + +int SmtpClient::getResponseCode() const +{ + return responseCode; +} + +QTcpSocket* SmtpClient::getSocket() { + return socket; +} + +int SmtpClient::getConnectionTimeout() const +{ + return connectionTimeout; +} + +void SmtpClient::setConnectionTimeout(int msec) +{ + connectionTimeout = msec; +} + +int SmtpClient::getResponseTimeout() const +{ + return responseTimeout; +} + +void SmtpClient::setResponseTimeout(int msec) +{ + responseTimeout = msec; +} +int SmtpClient::getSendMessageTimeout() const +{ + return sendMessageTimeout; +} +void SmtpClient::setSendMessageTimeout(int msec) +{ + sendMessageTimeout = msec; +} + +/* [2] --- */ + + +/* [3] Public methods */ + +bool SmtpClient::connectToHost() +{ + switch (connectionType) + { + case TlsConnection: + case TcpConnection: + socket->connectToHost(host, port); + break; + case SslConnection: + ((QSslSocket*) socket)->connectToHostEncrypted(host, port); + break; + + } + + // Tries to connect to server + if (!socket->waitForConnected(connectionTimeout)) + { + emit smtpError(ConnectionTimeoutError); + return false; + } + + try + { + // Wait for the server's response + waitForResponse(); + + // If the response code is not 220 (Service ready) + // means that is something wrong with the server + if (responseCode != 220) + { + emit smtpError(ServerError); + return false; + } + + // Send a EHLO/HELO message to the server + // The client's first command must be EHLO/HELO + sendMessage("EHLO " + name); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 250. + if (responseCode != 250) { + emit smtpError(ServerError); + return false; + } + + if (connectionType == TlsConnection) { + // send a request to start TLS handshake + sendMessage("STARTTLS"); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 220. + if (responseCode != 220) { + emit smtpError(ServerError); + return false; + }; + + ((QSslSocket*) socket)->startClientEncryption(); + + if (!((QSslSocket*) socket)->waitForEncrypted(connectionTimeout)) { + qDebug() << ((QSslSocket*) socket)->errorString(); + emit smtpError(ConnectionTimeoutError); + return false; + } + + // Send ELHO one more time + sendMessage("EHLO " + name); + + // Wait for the server's response + waitForResponse(); + + // The response code needs to be 250. + if (responseCode != 250) { + emit smtpError(ServerError); + return false; + } + } + } + catch (ResponseTimeoutException) + { + return false; + } + catch (SendMessageTimeoutException) + { + return false; + } + + // If no errors occured the function returns true. + return true; +} + +bool SmtpClient::login() +{ + return login(user, password, authMethod); +} + +bool SmtpClient::login(const QString &user, const QString &password, AuthMethod method) +{ + try { + if (method == AuthPlain) + { + // Sending command: AUTH PLAIN base64('\0' + username + '\0' + password) + sendMessage("AUTH PLAIN " + QByteArray().append((char) 0).append(user).append((char) 0).append(password).toBase64()); + + // Wait for the server's response + waitForResponse(); + + // If the response is not 235 then the authentication was faild + if (responseCode != 235) + { + emit smtpError(AuthenticationFailedError); + return false; + } + } + else if (method == AuthLogin) + { + // Sending command: AUTH LOGIN + sendMessage("AUTH LOGIN"); + + // Wait for 334 response code + waitForResponse(); + if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; } + + // Send the username in base64 + sendMessage(QByteArray().append(user).toBase64()); + + // Wait for 334 + waitForResponse(); + if (responseCode != 334) { emit smtpError(AuthenticationFailedError); return false; } + + // Send the password in base64 + sendMessage(QByteArray().append(password).toBase64()); + + // Wait for the server's responce + waitForResponse(); + + // If the response is not 235 then the authentication was faild + if (responseCode != 235) + { + emit smtpError(AuthenticationFailedError); + return false; + } + } + } + catch (ResponseTimeoutException e) + { + // Responce Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } + catch (SendMessageTimeoutException) + { + // Send Timeout exceeded + emit smtpError(AuthenticationFailedError); + return false; + } + + return true; +} + +bool SmtpClient::sendMail(MimeMessage& email) +{ + try + { + // Send the MAIL command with the sender + sendMessage("MAIL FROM: <" + email.getSender().getAddress() + ">"); + + waitForResponse(); + + if (responseCode != 250) return false; + + // Send RCPT command for each recipient + QList::const_iterator it, itEnd; + // To (primary recipients) + for (it = email.getRecipients().begin(), itEnd = email.getRecipients().end(); + it != itEnd; ++it) + { + sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) return false; + } + + // Cc (carbon copy) + for (it = email.getRecipients(MimeMessage::Cc).begin(), itEnd = email.getRecipients(MimeMessage::Cc).end(); + it != itEnd; ++it) + { + sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) return false; + } + + // Bcc (blind carbon copy) + for (it = email.getRecipients(MimeMessage::Bcc).begin(), itEnd = email.getRecipients(MimeMessage::Bcc).end(); + it != itEnd; ++it) + { + sendMessage("RCPT TO: <" + (*it)->getAddress() + ">"); + waitForResponse(); + + if (responseCode != 250) return false; + } + + // Send DATA command + sendMessage("DATA"); + waitForResponse(); + + if (responseCode != 354) return false; + + sendMessage(email.toString()); + + // Send \r\n.\r\n to end the mail data + sendMessage("."); + + waitForResponse(); + + if (responseCode != 250) return false; + } + catch (ResponseTimeoutException) + { + return false; + } + catch (SendMessageTimeoutException) + { + return false; + } + + return true; +} + +void SmtpClient::quit() +{ + sendMessage("QUIT"); +} + +/* [3] --- */ + + +/* [4] Protected methods */ + +void SmtpClient::waitForResponse() throw (ResponseTimeoutException) +{ + do { + if (!socket->waitForReadyRead(responseTimeout)) + { + emit smtpError(ResponseTimeoutError); + throw ResponseTimeoutException(); + } + + while (socket->canReadLine()) { + // Save the server's response + responseText = socket->readLine(); + + // Extract the respose code from the server's responce (first 3 digits) + responseCode = responseText.left(3).toInt(); + + if (responseCode / 100 == 4) + emit smtpError(ServerError); + + if (responseCode / 100 == 5) + emit smtpError(ClientError); + + if (responseText[3] == ' ') { return; } + } + } while (true); +} + +void SmtpClient::sendMessage(const QString &text) throw (SendMessageTimeoutException) +{ + socket->write(text.toUtf8() + "\r\n"); + if (! socket->waitForBytesWritten(sendMessageTimeout)) + { + emit smtpError(SendDataTimeoutError); + throw SendMessageTimeoutException(); + } +} + +/* [4] --- */ + + +/* [5] Slots for the socket's signals */ + +void SmtpClient::socketStateChanged(QAbstractSocket::SocketState state) +{ +} + +void SmtpClient::socketError(QAbstractSocket::SocketError socketError) +{ +} + +void SmtpClient::socketReadyRead() +{ +} + +/* [5] --- */ + + + + diff --git a/servatrice/src/smtp/smtpclient.h b/servatrice/src/smtp/smtpclient.h new file mode 100644 index 00000000..f734aad6 --- /dev/null +++ b/servatrice/src/smtp/smtpclient.h @@ -0,0 +1,184 @@ +/* + Copyright (c) 2011-2012 - Tőkés Attila + + This file is part of SmtpClient for Qt. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + See the LICENSE file for more details. +*/ + +#ifndef SMTPCLIENT_H +#define SMTPCLIENT_H + +#include +#include + +#include "mimemessage.h" +#include "smtpexports.h" + +class SMTP_EXPORT SmtpClient : public QObject +{ + Q_OBJECT +public: + + /* [0] Enumerations */ + + enum AuthMethod + { + AuthPlain, + AuthLogin + }; + + enum SmtpError + { + ConnectionTimeoutError, + ResponseTimeoutError, + SendDataTimeoutError, + AuthenticationFailedError, + ServerError, // 4xx smtp error + ClientError // 5xx smtp error + }; + + enum ConnectionType + { + TcpConnection, + SslConnection, + TlsConnection // STARTTLS + }; + + /* [0] --- */ + + + /* [1] Constructors and Destructors */ + + SmtpClient(const QString & host = "localhost", int port = 25, ConnectionType ct = TcpConnection); + + ~SmtpClient(); + + /* [1] --- */ + + + /* [2] Getters and Setters */ + + const QString& getHost() const; + void setHost(const QString &host); + + int getPort() const; + void setPort(int port); + + const QString& getName() const; + void setName(const QString &name); + + ConnectionType getConnectionType() const; + void setConnectionType(ConnectionType ct); + + const QString & getUser() const; + void setUser(const QString &user); + + const QString & getPassword() const; + void setPassword(const QString &password); + + SmtpClient::AuthMethod getAuthMethod() const; + void setAuthMethod(AuthMethod method); + + const QString & getResponseText() const; + int getResponseCode() const; + + int getConnectionTimeout() const; + void setConnectionTimeout(int msec); + + int getResponseTimeout() const; + void setResponseTimeout(int msec); + + int getSendMessageTimeout() const; + void setSendMessageTimeout(int msec); + + QTcpSocket* getSocket(); + + + /* [2] --- */ + + + /* [3] Public methods */ + + bool connectToHost(); + + bool login(); + bool login(const QString &user, const QString &password, AuthMethod method = AuthLogin); + + bool sendMail(MimeMessage& email); + + void quit(); + + + /* [3] --- */ + +protected: + + /* [4] Protected members */ + + QTcpSocket *socket; + + QString host; + int port; + ConnectionType connectionType; + QString name; + + QString user; + QString password; + AuthMethod authMethod; + + int connectionTimeout; + int responseTimeout; + int sendMessageTimeout; + + + QString responseText; + int responseCode; + + + class ResponseTimeoutException {}; + class SendMessageTimeoutException {}; + + /* [4] --- */ + + + /* [5] Protected methods */ + + void waitForResponse() throw (ResponseTimeoutException); + + void sendMessage(const QString &text) throw (SendMessageTimeoutException); + + /* [5] --- */ + +protected slots: + + /* [6] Protected slots */ + + void socketStateChanged(QAbstractSocket::SocketState state); + void socketError(QAbstractSocket::SocketError error); + void socketReadyRead(); + + /* [6] --- */ + + +signals: + + /* [7] Signals */ + + void smtpError(SmtpClient::SmtpError e); + + /* [7] --- */ + +}; + +#endif // SMTPCLIENT_H diff --git a/servatrice/src/smtp/smtpexports.h b/servatrice/src/smtp/smtpexports.h new file mode 100644 index 00000000..0c4919f3 --- /dev/null +++ b/servatrice/src/smtp/smtpexports.h @@ -0,0 +1,10 @@ +#ifndef SMTPEXPORTS_H +#define SMTPEXPORTS_H + +#ifdef SMTP_BUILD +#define SMTP_EXPORT Q_DECL_EXPORT +#else +#define SMTP_EXPORT Q_DECL_IMPORT +#endif + +#endif // SMTPEXPORTS_H