Add SMTP client code and email verification functions

This commit is contained in:
relesgoe 2020-05-21 10:42:57 -07:00
parent 1ee1068ec0
commit 8daa602a68
19 changed files with 4485 additions and 12 deletions

View file

@ -4,7 +4,8 @@ set(OUTPUT_CONFS ad.json anongame_infos.conf address_translation.conf
bnissue.txt bnmaps.conf bnxpcalc.conf
bnxplevel.conf channel.conf command_groups.conf realm.conf
sql_DB_layout.conf supportfile.conf topics.conf
tournament.conf versioncheck.json icons.conf)
tournament.conf versioncheck.json icons.conf
email_verification.conf cacert.pem)
foreach(CONF ${OUTPUT_CONFS})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${CONF}.in ${CMAKE_CURRENT_BINARY_DIR}/${CONF} @ONLY)
endforeach(CONF)
@ -20,7 +21,8 @@ if(WITH_BNETD)
bnetd_default_user.plain bnissue.txt bnmaps.conf
bnxpcalc.conf bnxplevel.conf channel.conf command_groups.conf
realm.conf sql_DB_layout.conf supportfile.conf topics.conf
tournament.conf versioncheck.json icons.conf)
tournament.conf versioncheck.json icons.conf
email_verification.conf cacert.pem)
endif(WITH_BNETD)
if(WITH_D2CS)

View file

@ -97,6 +97,7 @@ DBlayoutfile = "${SYSCONFDIR}/sql_DB_layout.conf"
supportfile = "${SYSCONFDIR}/supportfile.conf"
transfile = "${SYSCONFDIR}/address_translation.conf"
customicons_file = "${SYSCONFDIR}/icons.conf"
email_verification_file = "${SYSCONFDIR}/email_verification.conf"
# #
@ -370,6 +371,18 @@ passfail_bantime = 300
# Max users limit in private channels (0 = unlimited)
maxusers_per_channel = 0
# Verify account email address
# Upon account registration, sends an email to the user with a code that will
# be used to verify the registered email address.
# Set smtp_server_url, smtp_port, smtp_username, and smtp_password before enabling.
verify_account_email = false
# Number of minutes before an account email verification code expires
verify_account_email_expiration = 10
# Email address to use for the 'From' field in email verification emails.
verify_account_email_from_address = ""
# #
##############################################################################
@ -645,3 +658,37 @@ log_command_groups = 2345678
# set empty to log all commands
log_command_list = ""
# #
##############################################################################
###############################################################################
# SMTP #
#-----------------------------------------------------------------------------#
# Path to a CA certificate store file in PEM format, relative to ${SYSCONFDIR},
# containing X.509 certificates of public certificate authorities.
# See https://curl.haxx.se/docs/caextract.html
smtp_ca_cert_store = "${SYSCONFDIR}/cacert.pem"
# URL to download a new CA certificate store file.
smtp_ca_cert_store_remote_url = "https://curl.haxx.se/ca/cacert.pem"
# Number of days to wait before downloading a new cacert.pem
# Recommended value: 30
# Set to 0 to disable
smtp_ca_cert_store_fetch_interval = 30
# smtp_server_url = "smtp.gmail.com"
smtp_server_url = ""
smtp_port = 587
smtp_username = ""
# If your account has two-factor authentication (2FA) enabled, you will need to
# create a token through your SMTP provider.
smtp_password = ""
# #
##############################################################################

View file

@ -81,6 +81,7 @@ anongame_infos_file = conf\anongame_infos.conf
DBlayoutfile = conf\sql_DB_layout.conf
supportfile = conf\supportfile.conf
customicons_file = conf\icons.conf
email_verification_file = "conf\email_verification.conf"
# #
@ -353,6 +354,18 @@ passfail_bantime = 300
# Max users limit in private channels (0 = unlimited)
maxusers_per_channel = 0
# Verify account email address
# Upon account registration, sends an email to the user with a code that will
# be used to verify the registered email address.
# Set smtp_server_url, smtp_port, smtp_username, and smtp_password before enabling.
verify_account_email = false
# Number of minutes before an account email verification code expires
verify_account_email_expiration = 10
# Email address to use for the 'From' field in email verification emails.
verify_account_email_from_address = ""
# #
##############################################################################
@ -632,3 +645,37 @@ log_command_groups = 2345678
# set empty to log all commands
log_command_list = ""
# #
##############################################################################
###############################################################################
# SMTP #
#-----------------------------------------------------------------------------#
# Path to a CA certificate store file in PEM format, relative to ${SYSCONFDIR},
# containing X.509 certificates of public certificate authorities.
# See https://curl.haxx.se/docs/caextract.html
smtp_ca_cert_store = "conf\cacert.pem"
# URL to download a new CA certificate store file.
smtp_ca_cert_store_remote_url = "https://curl.haxx.se/ca/cacert.pem"
# Number of days to wait before downloading a new cacert.pem
# Recommended value: 30
# Set to 0 to disable
smtp_ca_cert_store_fetch_interval = 30
# smtp_server_url = "smtp.gmail.com"
smtp_server_url = ""
smtp_port = 587
smtp_username = ""
# If your account has two-factor authentication (2FA) enabled, you will need to
# create a token through your SMTP provider.
smtp_password = ""
# #
##############################################################################

3466
conf/cacert.pem.in Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
Hello {{account_name}},
Your verification code is {{account_email_verification_code}}.
This code will expire in {{account_email_verification_expiration}} minutes.
To verify your email address, enter /email verify {{account_email_verification_code}} in the PvPGN server.
{pvpgn_server_name}

View file

@ -19,6 +19,7 @@
"acct_userid int","NULL"
"acct_passhash1 varchar(40)","NULL"
"acct_email varchar(128)","NULL"
"acct_email_verified varchar(6)","'false'"
"auth_admin varchar(6)","'false'"
"auth_normallogin varchar(6)","'true'"
"auth_changepass varchar(6)","'true'"
@ -39,6 +40,10 @@
"acct_lastlogin_ip varchar(16)","NULL"
:"DROP INDEX username"
:"CREATE UNIQUE INDEX username2 ON ${prefix}BNET (username)"
[${prefix}email_verification]
"uid int NOT NULL PRIMARY KEY","'0'"
code varchar(64)","NULL"
expiration int", "'0'"
[${prefix}WOL]
"uid int NOT NULL PRIMARY KEY","'0'"
"auth_apgar varchar(8)","NULL"

View file

@ -1,5 +1,6 @@
set(BNETD_SOURCES
account.cpp account.h account_wrap.cpp account_wrap.h adbanner.cpp
account.cpp account.h account_email_verification.cpp account_email_verification.h
account_wrap.cpp account_wrap.h adbanner.cpp
adbanner.h alias_command.cpp alias_command.h anongame.cpp
anongame_gameresult.cpp anongame_gameresult.h anongame.h
anongame_infos.cpp anongame_infos.h anongame_maplists.cpp
@ -7,8 +8,8 @@ set(BNETD_SOURCES
attrlayer.h autoupdate.cpp autoupdate.h channel_conv.cpp channel_conv.h
channel.cpp channel.h character.cpp character.h clan.cpp clan.h
cmdline.cpp cmdline.h command.cpp command_groups.cpp command_groups.h
command.h connection.cpp connection.h file.cpp file.h file_plain.cpp
file_plain.h friends.cpp friends.h game_conv.cpp
command.h connection.cpp connection.h file.cpp file.h
file_plain.cpp file_plain.h friends.cpp friends.h game_conv.cpp
game_conv.h game.cpp game.h handle_anongame.cpp handle_anongame.h
handle_apireg.cpp handle_apireg.h handle_bnet.cpp handle_bnet.h
handle_bot.cpp handle_bot.h handle_d2cs.cpp handle_d2cs.h
@ -20,9 +21,9 @@ set(BNETD_SOURCES
ipban.cpp ipban.h irc.cpp irc.h ladder_calc.cpp ladder_calc.h ladder.cpp
ladder.h mail.cpp mail.h main.cpp message.cpp message.h news.cpp news.h
output.cpp output.h prefs.cpp prefs.h quota.h realm.cpp realm.h
runprog.cpp runprog.h server.cpp server.h sql_common.cpp sql_common.h
sql_dbcreator.cpp sql_dbcreator.h sql_mysql.cpp sql_mysql.h sql_odbc.cpp
sql_odbc.h sql_pgsql.cpp sql_pgsql.h sql_sqlite3.cpp sql_sqlite3.h
runprog.cpp runprog.h server.cpp server.h smtp.cpp smtp.h sql_common.cpp
sql_common.h sql_dbcreator.cpp sql_dbcreator.h sql_mysql.cpp sql_mysql.h
sql_odbc.cpp sql_odbc.h sql_pgsql.cpp sql_pgsql.h sql_sqlite3.cpp sql_sqlite3.h
storage.cpp storage_file.cpp storage_file.h storage.h
storage_sql.cpp storage_sql.h support.cpp support.h
team.cpp team.h tick.cpp tick.h timer.cpp timer.h topic.cpp topic.h
@ -51,6 +52,7 @@ endif(WITH_WIN32_GUI)
target_include_directories(bnetd
PRIVATE
${ZLIB_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${LUA_INCLUDE_DIR}
${MYSQL_INCLUDE_DIR}
${SQLITE3_INCLUDE_DIR}
@ -58,7 +60,7 @@ target_include_directories(bnetd
${ODBC_INCLUDE_DIR}
)
target_link_libraries(bnetd PRIVATE common compat fmt::fmt win32 ${NETWORK_LIBRARIES} ZLIB::ZLIB ${MYSQL_CLIENT_LIBS} ${SQLITE3_LIBRARIES} ${PGSQL_LIBRARIES} ${ODBC_LIBRARIES} ${LUA_LIBRARIES})
target_link_libraries(bnetd PRIVATE common compat fmt::fmt win32 ${NETWORK_LIBRARIES} ZLIB::ZLIB ${CURL_LIBRARIES} ${MYSQL_CLIENT_LIBS} ${SQLITE3_LIBRARIES} ${PGSQL_LIBRARIES} ${ODBC_LIBRARIES} ${LUA_LIBRARIES})
install(TARGETS bnetd DESTINATION ${SBINDIR})
if(WIN32 AND MSVC)

View file

@ -0,0 +1,186 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "common/setup_before.h"
#include "account_email_verification.h"
#include <fstream>
#include <iterator>
#include <limits>
#include <random>
#include <string>
#include <curl.h>
#include "common/eventlog.h"
#include "account.h"
#include "account_wrap.h"
#include "prefs.h"
#include "server.h"
#include "smtp.h"
#include "common/setup_after.h"
namespace pvpgn
{
namespace bnetd
{
static std::string message;
static std::string verify_account_email_from_address;
bool account_email_verification_load(const char* filepath, const char* prefs_servername, const char* prefs_verify_account_email_from_address)
{
if (filepath == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL filepath");
return false;
}
if (prefs_servername == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL prefs_servername");
return false;
}
if (prefs_verify_account_email_from_address == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL prefs_verify_account_email_from_address");
return false;
}
std::ifstream infile{ filepath };
if (!infile.is_open())
{
eventlog(eventlog_level_error, __FUNCTION__, "Could not open email verification message file ({})", filepath);
return false;
}
try
{
std::string raw_message{ std::istreambuf_iterator<char>(infile), std::istreambuf_iterator<char>() };
message = fmt::format(raw_message, fmt::arg("pvpgn_server_name", prefs_servername));
eventlog(eventlog_level_info, __FUNCTION__, "Succesfully loaded email verification message ({} bytes)", message.length());
}
catch (const std::exception& e)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to load email verification message ({})", e.what());
return false;
}
verify_account_email_from_address = prefs_verify_account_email_from_address;
return true;
}
void account_email_verification_unload()
{
message.clear();
verify_account_email_from_address.clear();
}
/**
* If successful, sets "BNET\\acct\\email\\verified" attribute to true and sets "email_verification\\code"
* attribute and "email_verification\\expiration" attribute to 0 for the given account.
*/
AccountVerifyEmailStatus account_verify_email(t_account* account, const std::string& code)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return AccountVerifyEmailStatus::FailureOther;
}
const char* account_code = account_get_email_verification_code(account);
if (account_code == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "Could not retrieve email verification code for account uid {}", account_get_uid(account));
return AccountVerifyEmailStatus::FailureOther;
}
unsigned int account_expiration = account_get_email_verification_expiration(account);
if (account_expiration == 0)
{
eventlog(eventlog_level_error, __FUNCTION__, "Could not retrieve email verification expiration date for account uid {}", account_get_uid(account));
return AccountVerifyEmailStatus::FailureOther;
}
if (now >= account_expiration)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to verify email for account uid {} (Email verification code expired)", account_get_uid(account));
return AccountVerifyEmailStatus::FailureCodeExpired;
}
if (code.compare(account_code) != 0)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to verify email for account uid {} (Incorrect email verification code)", account_get_uid(account));
return AccountVerifyEmailStatus::FailureCodeIncorrect;
}
int verified = account_set_email_verified(account, true);
if (verified != 1)
{
eventlog(eventlog_level_error, __FUNCTION__, "Could not verify email code for account uid {}", account_get_uid(account));
return AccountVerifyEmailStatus::FailureOther;
}
account_set_email_verification_code(account, 0);
account_set_email_verification_expiration(account, 0);
eventlog(eventlog_level_info, __FUNCTION__, "Succesfully verified email address ({}) account uid {}", account_get_email(account), account_get_uid(account));
return AccountVerifyEmailStatus::Success;
}
/**
* Generates an email verification code and saves it in the "email_verification\\code" attribute for the given account. Sets the "email_verification\\expiration" attribute
* to X minutes from current time, where X is the value of 'verify_account_email_expiration' in bnetd.conf.
* Sends an email containing the email verification code to the registered email address of the account.
*/
bool account_generate_email_verification_code(t_account* account)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return false;
}
static std::random_device rdevice;
static std::default_random_engine rengine(rdevice());
static std::uniform_int_distribution<unsigned long long> uniform_dist(std::numeric_limits<unsigned long long>::min(), std::numeric_limits<unsigned long long>::max());
std::time_t expiration = now + (60ull * prefs_get_verify_account_email_expiration());
std::string code = fmt::to_string(uniform_dist(rengine));
int a = account_set_email_verification_expiration(account, expiration);
int b = account_set_email_verification_code(account, code.c_str());
if (!(a == 0 && b == 0))
{
eventlog(eventlog_level_error, __FUNCTION__, "Could not generate an email verification code for account uid {}", account_get_uid(account));
return false;
}
std::string personalized_message = fmt::format(message, fmt::arg("account_name", account_get_name(account)), fmt::arg("account_email_verification_code", code), fmt::arg("account_email_verification_expiration", expiration));
eventlog(eventlog_level_debug, __FUNCTION__, "Sending email verification code to {} for account uid {}", account_get_email(account), account_get_uid(account));
smtp_send_email(account_get_email(account), verify_account_email_from_address, "Email Verification", personalized_message);
return true;
}
}
}

View file

@ -0,0 +1,54 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef INCLUDED_EMAIL_VERIFICATION_H
#define INCLUDED_EMAIL_VERIFICATION_H
#ifndef JUST_NEED_TYPES
#define JUST_NEED_TYPES
#include "account.h"
#undef JUST_NEED_TYPES
#endif
#include <string>
namespace pvpgn
{
namespace bnetd
{
enum class AccountVerifyEmailStatus
{
Success,
FailureCodeExpired,
FailureCodeIncorrect,
FailureOther
};
bool account_email_verification_load(const char* filepath, const char* prefs_servername, const char* prefs_verify_account_email_from_address);
void account_email_verification_unload();
AccountVerifyEmailStatus account_verify_email(t_account* account, const std::string& code);
bool account_generate_email_verification_code(t_account* account);
}
}
#endif

View file

@ -23,6 +23,8 @@
#include <memory>
#include <string>
#include <fmt/format.h>
#include "compat/strcasecmp.h"
#include "common/bnet_protocol.h"
@ -2641,6 +2643,72 @@ namespace pvpgn
return account_set_strattr(account, "BNET\\acct\\email", email.c_str());
}
extern int account_get_email_verified(t_account* account)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return -1;
}
return account_get_boolattr(account, "BNET\\acct\\email\\verified");
}
extern int account_set_email_verified(t_account* account, bool is_verified)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return -1;
}
return account_set_boolattr(account, "BNET\\acct\\email\\verified", is_verified ? 1 : 0);
}
extern char const * account_get_email_verification_code(t_account* account)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return nullptr;
}
return account_get_strattr(account, "email_verification\\code");
}
extern int account_set_email_verification_code(t_account* account, char const * expiration_date)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return -1;
}
return account_set_strattr(account, "email_verification\\code", fmt::to_string(expiration_date).c_str());
}
extern unsigned int account_get_email_verification_expiration(t_account* account)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return -1;
}
return account_get_numattr(account, "email_verification\\expiration");
}
extern int account_set_email_verification_expiration(t_account* account, unsigned int expiration_date)
{
if (account == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "got NULL account");
return -1;
}
return account_set_numattr(account, "email_verification\\expiration", expiration_date);
}
extern int account_set_userlang(t_account * account, const char * lang)
{
if (lang)

View file

@ -264,6 +264,12 @@ namespace pvpgn
extern int account_set_email(t_account * account, std::string email);
extern char const * account_get_email(t_account * account);
extern int account_get_email_verified(t_account* account);
extern int account_set_email_verified(t_account* account, bool is_verified);
extern char const* account_get_email_verification_code(t_account* account);
extern int account_set_email_verification_code(t_account* account, char const * expiration_date);
extern unsigned int account_get_email_verification_expiration(t_account* account);
extern int account_set_email_verification_expiration(t_account* account, unsigned int expiration_date);
extern int account_set_userlang(t_account * account, const char * lang);
extern int account_set_userlang(t_account * account, std::string lang);

View file

@ -88,6 +88,8 @@
#include "handle_apireg.h"
#include "i18n.h"
#include "userlog.h"
#include "account_email_verification.h"
#include "smtp.h"
#ifdef WIN32
#include "win32/windump.h"
#endif
@ -373,10 +375,10 @@ int pre_server_startup(void)
{
AdBannerList.unload();
}
AdBannerList.load(prefs_get_adfile());
}
catch (const std::exception& e)
catch (const std::exception & e)
{
eventlog(eventlog_level_error, __FUNCTION__, "{}", e.what());
}
@ -420,6 +422,26 @@ int pre_server_startup(void)
eventlog(eventlog_level_error, __FUNCTION__, "could not load realm list");
//topiclist_load(std::string(prefs_get_topicfile()));
userlog_init();
if (prefs_get_verify_account_email() == 1)
{
if (!smtp_init(prefs_get_smtp_ca_cert_store(), prefs_get_smtp_server_url(), prefs_get_smtp_port(), prefs_get_smtp_username(), prefs_get_smtp_password()))
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to initialize SMTP client");
eventlog(eventlog_level_error, __FUNCTION__, "Disabling account email verification");
prefs_set_verify_account_email(false);
}
if (!account_email_verification_load(prefs_get_email_verification_file(), prefs_get_servername(), prefs_get_verify_account_email_from_address()))
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to load email verification message");
eventlog(eventlog_level_error, __FUNCTION__, "Disabling account email verification");
prefs_set_verify_account_email(false);
}
}
else
{
eventlog(eventlog_level_debug, __FUNCTION__, "Config option 'verify_account_email' is false");
}
#ifdef WITH_LUA
lua_load(prefs_get_scriptdir());
@ -435,6 +457,9 @@ void post_server_shutdown(int status)
{
case 0:
//topiclist_unload();
account_email_verification_unload();
smtp_cleanup();
account_email_verification_unload();
realmlist_destroy();
teamlist_unload();
clanlist_unload();

View file

@ -144,6 +144,7 @@ namespace pvpgn
char const * command_groups_file;
char const * tournament_file;
char const * customicons_file;
char const * email_verification_file;
char const * scriptdir;
char const * aliasfile;
char const * anongame_infos_file;
@ -156,6 +157,9 @@ namespace pvpgn
unsigned int passfail_count;
unsigned int passfail_bantime;
unsigned int maxusers_per_channel;
unsigned int verify_account_email;
unsigned int verify_account_email_expiration;
char const* verify_account_email_from_address;
char const * supportfile;
char const * allowed_clients;
char const * ladder_games;
@ -168,6 +172,13 @@ namespace pvpgn
unsigned int log_commands;
char const * log_command_groups;
char const * log_command_list;
char const * smtp_ca_cert_store;
char const * smtp_ca_cert_store_remote_url;
unsigned int smtp_ca_cert_store_fetch_interval;
char const * smtp_server_url;
unsigned int smtp_port;
char const * smtp_username;
char const * smtp_password;
char const * apiregaddrs;
char const * wolv1addrs;
@ -589,6 +600,10 @@ namespace pvpgn
static const char *conf_get_customicons_file(void);
static int conf_setdef_customicons_file(void);
static int conf_set_email_verification_file(const char* valstr);
static const char* conf_get_email_verification_file(void);
static int conf_setdef_email_verification_file(void);
static int conf_set_scriptdir(const char *valstr);
static const char *conf_get_scriptdir(void);
static int conf_setdef_scriptdir(void);
@ -637,6 +652,18 @@ namespace pvpgn
static const char *conf_get_maxusers_per_channel(void);
static int conf_setdef_maxusers_per_channel(void);
static int conf_set_verify_account_email(const char* valstr);
static const char* conf_get_verify_account_email(void);
static int conf_setdef_verify_account_email(void);
static int conf_set_verify_account_email_expiration(const char* valstr);
static const char* conf_get_verify_account_email_expiration(void);
static int conf_setdef_verify_account_email_expiration(void);
static int conf_set_verify_account_email_from_address(const char* valstr);
static const char* conf_get_verify_account_email_from_address(void);
static int conf_setdef_verify_account_email_from_address(void);
static int conf_set_allowed_clients(const char *valstr);
static const char *conf_get_allowed_clients(void);
static int conf_setdef_allowed_clients(void);
@ -681,6 +708,34 @@ namespace pvpgn
static const char *conf_get_log_command_list(void);
static int conf_setdef_log_command_list(void);
static int conf_set_smtp_ca_cert_store(const char* valstr);
static const char* conf_get_smtp_ca_cert_store(void);
static int conf_setdef_smtp_ca_cert_store(void);
static int conf_set_smtp_ca_cert_store_remote_url(const char* valstr);
static const char* conf_get_smtp_ca_cert_store_remote_url(void);
static int conf_setdef_smtp_ca_cert_store_remote_url(void);
static int conf_set_smtp_ca_cert_store_fetch_interval(const char* valstr);
static const char* conf_get_smtp_ca_cert_store_fetch_interval(void);
static int conf_setdef_smtp_ca_cert_store_fetch_interval(void);
static int conf_set_smtp_server_url(const char* valstr);
static const char* conf_get_smtp_server_url(void);
static int conf_setdef_smtp_server_url(void);
static int conf_set_smtp_port(const char* valstr);
static const char* conf_get_smtp_port(void);
static int conf_setdef_smtp_port(void);
static int conf_set_smtp_username(const char* valstr);
static const char* conf_get_smtp_username(void);
static int conf_setdef_smtp_username(void);
static int conf_set_smtp_password(const char* valstr);
static const char* conf_get_smtp_password(void);
static int conf_setdef_smtp_password(void);
static int conf_setdef_apireg_addrs(void);
static int conf_set_apireg_addrs(const char *valstr);
@ -827,6 +882,7 @@ namespace pvpgn
{ "command_groups_file", conf_set_command_groups_file, conf_get_command_groups_file, conf_setdef_command_groups_file },
{ "tournament_file", conf_set_tournament_file, conf_get_tournament_file, conf_setdef_tournament_file },
{ "customicons_file", conf_set_customicons_file, conf_get_customicons_file, conf_setdef_customicons_file },
{ "email_verification_file", conf_set_email_verification_file, conf_get_email_verification_file, conf_setdef_email_verification_file },
{ "scriptdir", conf_set_scriptdir, conf_get_scriptdir, conf_setdef_scriptdir },
{ "aliasfile", conf_set_aliasfile, conf_get_aliasfile, conf_setdef_aliasfile },
{ "anongame_infos_file", conf_set_anongame_infos_file, conf_get_anongame_infos_file, conf_setdef_anongame_infos_file },
@ -839,6 +895,9 @@ namespace pvpgn
{ "passfail_count", conf_set_passfail_count, conf_get_passfail_count, conf_setdef_passfail_count },
{ "passfail_bantime", conf_set_passfail_bantime, conf_get_passfail_bantime, conf_setdef_passfail_bantime },
{ "maxusers_per_channel", conf_set_maxusers_per_channel, conf_get_maxusers_per_channel, conf_setdef_maxusers_per_channel },
{ "verify_account_email", conf_set_verify_account_email, conf_get_verify_account_email, conf_setdef_verify_account_email },
{ "verify_account_email_expiration", conf_set_verify_account_email_expiration, conf_get_verify_account_email_expiration, conf_setdef_verify_account_email_expiration },
{ "verify_account_email_from_address", conf_set_verify_account_email_from_address, conf_get_verify_account_email_from_address, conf_setdef_verify_account_email_from_address },
{ "allowed_clients", conf_set_allowed_clients, conf_get_allowed_clients, conf_setdef_allowed_clients },
{ "ladder_games", conf_set_ladder_games, conf_get_ladder_games, conf_setdef_ladder_games },
{ "max_connections", conf_set_max_connections, conf_get_max_connections, conf_setdef_max_connections },
@ -850,6 +909,13 @@ namespace pvpgn
{ "log_commands", conf_set_log_commands, conf_get_log_commands, conf_setdef_log_commands },
{ "log_command_groups", conf_set_log_command_groups, conf_get_log_command_groups, conf_setdef_log_command_groups },
{ "log_command_list", conf_set_log_command_list, conf_get_log_command_list, conf_setdef_log_command_list },
{ "smtp_ca_cert_store", conf_set_smtp_ca_cert_store, conf_get_smtp_ca_cert_store, conf_setdef_smtp_ca_cert_store },
{ "smtp_ca_cert_store_remote_url", conf_set_smtp_ca_cert_store_remote_url, conf_get_smtp_ca_cert_store_remote_url, conf_setdef_smtp_ca_cert_store_remote_url },
{ "smtp_ca_cert_store_fetch_interval", conf_set_smtp_ca_cert_store_fetch_interval, conf_get_smtp_ca_cert_store_fetch_interval, conf_setdef_smtp_ca_cert_store_fetch_interval },
{ "smtp_server_url", conf_set_smtp_server_url, conf_get_smtp_server_url, conf_setdef_smtp_server_url, },
{ "smtp_port", conf_set_smtp_port, conf_get_smtp_port, conf_setdef_smtp_port },
{ "smtp_username", conf_set_smtp_username, conf_get_smtp_username, conf_setdef_smtp_username },
{ "smtp_password", conf_set_smtp_password, conf_get_smtp_password, conf_setdef_smtp_password },
{ "apiregaddrs", conf_set_apireg_addrs, conf_get_apireg_addrs, conf_setdef_apireg_addrs },
{ "wgameresaddrs", conf_set_wgameres_addrs, conf_get_wgameres_addrs, conf_setdef_wgameres_addrs },
@ -3050,6 +3116,28 @@ namespace pvpgn
return prefs_runtime_config.customicons_file;
}
extern char const* prefs_get_email_verification_file(void)
{
return prefs_runtime_config.email_verification_file;
}
static int conf_set_email_verification_file(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.email_verification_file, valstr, NULL);
}
static const char* conf_get_email_verification_file(void)
{
return prefs_runtime_config.email_verification_file;
}
static int conf_setdef_email_verification_file(void)
{
return conf_set_str(&prefs_runtime_config.email_verification_file, NULL, BNETD_EMAIL_VERIFICATION_FILE);
}
extern char const * prefs_get_scriptdir(void)
{
return prefs_runtime_config.scriptdir;
@ -3312,6 +3400,74 @@ namespace pvpgn
}
extern unsigned int prefs_get_verify_account_email(void)
{
return prefs_runtime_config.verify_account_email;
}
extern void prefs_set_verify_account_email(bool enable)
{
prefs_runtime_config.verify_account_email = enable ? 1 : 0;
}
static int conf_set_verify_account_email(const char* valstr)
{
return conf_set_bool(&prefs_runtime_config.verify_account_email, valstr, 0);
}
static const char* conf_get_verify_account_email(void)
{
return conf_get_bool(prefs_runtime_config.verify_account_email);
}
static int conf_setdef_verify_account_email(void)
{
return conf_set_bool(&prefs_runtime_config.verify_account_email, nullptr, 0);
}
extern unsigned int prefs_get_verify_account_email_expiration(void)
{
return prefs_runtime_config.verify_account_email_expiration;
}
static int conf_set_verify_account_email_expiration(const char* valstr)
{
return conf_set_int(&prefs_runtime_config.verify_account_email_expiration, valstr, 0);
}
static const char* conf_get_verify_account_email_expiration(void)
{
return conf_get_int(prefs_runtime_config.verify_account_email_expiration);
}
static int conf_setdef_verify_account_email_expiration(void)
{
return conf_set_int(&prefs_runtime_config.verify_account_email_expiration, nullptr, 10);
}
extern char const* prefs_get_verify_account_email_from_address(void)
{
return prefs_runtime_config.verify_account_email_from_address;
}
static int conf_set_verify_account_email_from_address(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.verify_account_email_from_address, valstr, NULL);
}
static const char* conf_get_verify_account_email_from_address(void)
{
return prefs_runtime_config.verify_account_email_from_address;
}
static int conf_setdef_verify_account_email_from_address(void)
{
return conf_set_str(&prefs_runtime_config.verify_account_email_from_address, NULL, NULL);
}
extern char const * prefs_get_supportfile(void)
{
return prefs_runtime_config.supportfile;
@ -3563,6 +3719,152 @@ namespace pvpgn
}
extern char const * prefs_get_smtp_ca_cert_store(void)
{
return prefs_runtime_config.smtp_ca_cert_store;
}
static int conf_set_smtp_ca_cert_store(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.smtp_ca_cert_store, valstr, NULL);
}
static const char* conf_get_smtp_ca_cert_store(void)
{
return prefs_runtime_config.smtp_ca_cert_store;
}
static int conf_setdef_smtp_ca_cert_store(void)
{
return conf_set_str(&prefs_runtime_config.smtp_ca_cert_store, NULL, NULL);
}
extern char const * prefs_get_smtp_ca_cert_store_remote_url(void)
{
return prefs_runtime_config.smtp_ca_cert_store_remote_url;
}
static int conf_set_smtp_ca_cert_store_remote_url(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.smtp_ca_cert_store_remote_url, valstr, NULL);
}
static const char* conf_get_smtp_ca_cert_store_remote_url(void)
{
return prefs_runtime_config.smtp_ca_cert_store_remote_url;
}
static int conf_setdef_smtp_ca_cert_store_remote_url(void)
{
return conf_set_str(&prefs_runtime_config.smtp_ca_cert_store_remote_url, NULL, NULL);
}
extern unsigned int prefs_get_smtp_ca_cert_store_fetch_interval(void)
{
return prefs_runtime_config.smtp_ca_cert_store_fetch_interval;
}
static int conf_set_smtp_ca_cert_store_fetch_interval(const char* valstr)
{
return conf_set_int(&prefs_runtime_config.smtp_ca_cert_store_fetch_interval, valstr, NULL);
}
static const char* conf_get_smtp_ca_cert_store_fetch_interval(void)
{
return conf_get_int(prefs_runtime_config.smtp_ca_cert_store_fetch_interval);
}
static int conf_setdef_smtp_ca_cert_store_fetch_interval(void)
{
return conf_set_int(&prefs_runtime_config.smtp_ca_cert_store_fetch_interval, NULL, 30);
}
extern char const * prefs_get_smtp_server_url(void)
{
return prefs_runtime_config.smtp_server_url;
}
static int conf_set_smtp_server_url(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.smtp_server_url, valstr, NULL);
}
static const char* conf_get_smtp_server_url(void)
{
return prefs_runtime_config.smtp_server_url;
}
static int conf_setdef_smtp_server_url(void)
{
return conf_set_str(&prefs_runtime_config.smtp_server_url, NULL, NULL);
}
extern unsigned int prefs_get_smtp_port(void)
{
return prefs_runtime_config.smtp_port;
}
static int conf_set_smtp_port(const char* valstr)
{
return conf_set_int(&prefs_runtime_config.smtp_port, valstr, NULL);
}
static const char* conf_get_smtp_port(void)
{
return conf_get_int(prefs_runtime_config.smtp_port);
}
static int conf_setdef_smtp_port(void)
{
return conf_set_int(&prefs_runtime_config.smtp_port, NULL, 587);
}
extern char const * prefs_get_smtp_username(void)
{
return prefs_runtime_config.smtp_username;
}
static int conf_set_smtp_username(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.smtp_username, valstr, NULL);
}
static const char* conf_get_smtp_username(void)
{
return prefs_runtime_config.smtp_username;
}
static int conf_setdef_smtp_username(void)
{
return conf_set_str(&prefs_runtime_config.smtp_username, NULL, NULL);
}
extern char const * prefs_get_smtp_password(void)
{
return prefs_runtime_config.smtp_password;
}
static int conf_set_smtp_password(const char* valstr)
{
return conf_set_str(&prefs_runtime_config.smtp_password, valstr, NULL);
}
static const char* conf_get_smtp_password(void)
{
return prefs_runtime_config.smtp_password;
}
static int conf_setdef_smtp_password(void)
{
return conf_set_str(&prefs_runtime_config.smtp_password, NULL, NULL);
}
/**
* Westwood Online Extensions

View file

@ -153,6 +153,7 @@ namespace pvpgn
extern char const * prefs_get_command_groups_file(void);
extern char const * prefs_get_tournament_file(void);
extern char const * prefs_get_customicons_file(void);
extern char const* prefs_get_email_verification_file(void);
extern char const * prefs_get_scriptdir(void);
extern char const * prefs_get_aliasfile(void);
@ -170,6 +171,10 @@ namespace pvpgn
extern unsigned int prefs_get_passfail_count(void);
extern unsigned int prefs_get_passfail_bantime(void);
extern unsigned int prefs_get_maxusers_per_channel(void);
extern unsigned int prefs_get_verify_account_email(void);
extern void prefs_set_verify_account_email(bool enable);
extern unsigned int prefs_get_verify_account_email_expiration(void);
extern char const* prefs_get_verify_account_email_from_address(void);
extern char const * prefs_get_supportfile(void);
extern char const * prefs_get_allowed_clients(void);
extern char const * prefs_get_ladder_games(void);
@ -182,6 +187,14 @@ namespace pvpgn
extern unsigned int prefs_get_log_commands(void);
extern char const * prefs_get_log_command_groups(void);
extern char const * prefs_get_log_command_list(void);
extern char const * prefs_get_smtp_ca_cert_store(void);
extern char const * prefs_get_smtp_ca_cert_store_remote_url(void);
extern unsigned int prefs_get_smtp_ca_cert_store_fetch_interval(void);
extern char const * prefs_get_smtp_server_url(void);
extern unsigned int prefs_get_smtp_port(void);
extern char const * prefs_get_smtp_username(void);
extern char const * prefs_get_smtp_password(void);
/**
* Westwood Online Extensions

View file

@ -87,6 +87,8 @@
#include "anongame_infos.h"
#include "topic.h"
#include "i18n.h"
#include "smtp.h"
#include "account_email_verification.h"
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
@ -1506,6 +1508,32 @@ namespace pvpgn
}
#endif
if (prefs_get_verify_account_email() == 1)
{
if (do_restart == restart_mode_all || do_restart == restart_mode_smtp)
{
smtp_cleanup();
if (!smtp_init(prefs_get_smtp_ca_cert_store(), prefs_get_smtp_server_url(), prefs_get_smtp_port(), prefs_get_smtp_username(), prefs_get_smtp_password()))
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to initialize SMTP client");
eventlog(eventlog_level_error, __FUNCTION__, "Disabling account email verification");
prefs_set_verify_account_email(false);
}
}
if (do_restart == restart_mode_all || do_restart == restart_mode_accountemailverification)
{
account_email_verification_unload();
if (!account_email_verification_load(prefs_get_email_verification_file(), prefs_get_servername(), prefs_get_verify_account_email_from_address()))
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to load email verification message");
eventlog(eventlog_level_error, __FUNCTION__, "Disabling account email verification");
prefs_set_verify_account_email(false);
}
}
}
eventlog(eventlog_level_info, __FUNCTION__, "done reconfiguring");
do_restart = 0;

View file

@ -92,7 +92,9 @@ namespace pvpgn
restart_mode_tournament,
restart_mode_icons,
restart_mode_anongame,
restart_mode_lua
restart_mode_lua,
restart_mode_smtp,
restart_mode_accountemailverification
};
extern unsigned int server_get_uptime(void);

179
src/bnetd/smtp.cpp Normal file
View file

@ -0,0 +1,179 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "common/setup_before.h"
#include "smtp.h"
#include <ctime>
#include <string>
#include <curl.h>
#include <fmt/core.h>
#include <fmt/chrono.h>
#include "common/eventlog.h"
#include "server.h"
#include "common/setup_after.h"
namespace pvpgn
{
namespace bnetd
{
static bool is_curl_initialized = false;
static std::string smtp_ca_cert_store;
static char smtp_server_url[512] = {};
static long smtp_port;
static std::string smtp_username;
static std::string smtp_password;
static int debug_callback(CURL* handle, curl_infotype type, char* data, size_t size, void* userptr)
{
char* info = (char*)std::malloc(size + 1);
info[size] = '\0';
std::memcpy(info, data, size);
eventlog(eventlog_level_debug, __FUNCTION__, "DEBUG: {}", info);
return 0;
}
static std::size_t read_callback(char* buffer, std::size_t size, std::size_t nitems, void* message)
{
std::size_t buffer_size = size * nitems;
std::size_t message_size = std::strlen(reinterpret_cast<const char*>(message)) + 1;
if (message_size <= buffer_size)
{
std::memcpy(buffer, message, message_size);
eventlog(eventlog_level_trace, __FUNCTION__, "buffer: {}", buffer);
return 0;
}
else
{
std::memcpy(buffer, message, buffer_size);
return buffer_size;
}
}
/**
* Initializes libcurl's global context if it hasn't already been initialized.
* Initializes smtp_server_url, smtp_port, smtp_username, and smtp_password from the four function parameters.
*
* On success, returns true.
* On failure, returns false. Will fail if libcurl couldn't initialize global context or if prefs_smtp_port is greater than 65535.
*/
bool smtp_init(const char* prefs_smtp_ca_cert_store, const char* prefs_smtp_server_url, unsigned int prefs_smtp_port, const char* prefs_smtp_username, const char* prefs_smtp_password)
{
if (!is_curl_initialized)
{
if (curl_global_init(CURL_GLOBAL_NOTHING) != 0)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to initialize libcurl");
return false;
}
else
{
eventlog(eventlog_level_debug, __FUNCTION__, "Succesfully initialized libcurl");
is_curl_initialized = true;
}
}
smtp_ca_cert_store = prefs_smtp_ca_cert_store;
std::snprintf(smtp_server_url, sizeof(smtp_server_url), "smtps://%s", prefs_smtp_server_url);
if (prefs_smtp_port > 65535)
{
eventlog(eventlog_level_error, __FUNCTION__, "Received out-of-range port number ({})", prefs_smtp_port);
return false;
}
smtp_port = prefs_smtp_port;
smtp_username = prefs_smtp_username;
smtp_password = prefs_smtp_password;
eventlog(eventlog_level_info, __FUNCTION__, "Succesfully initialized SMTP client");
return true;
}
void smtp_cleanup()
{
if (is_curl_initialized)
{
curl_global_cleanup();
is_curl_initialized = false;
}
}
void smtp_send_email(const std::string& to_address, const std::string& from_address, const std::string& subject, std::string message)
{
CURL* curl = curl_easy_init();
if (curl == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to initialize CURL easy handle");
return;
}
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_CAINFO, smtp_ca_cert_store.c_str());
curl_easy_setopt(curl, CURLOPT_URL, smtp_server_url);
curl_easy_setopt(curl, CURLOPT_PORT, smtp_port);
curl_easy_setopt(curl, CURLOPT_USERNAME, smtp_username.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, smtp_password.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, fmt::format("<{}>", from_address).c_str());
struct curl_slist* recipient = nullptr;
{
struct curl_slist* recipient_temp = curl_slist_append(recipient, fmt::format("<{}>", to_address).c_str());
if (recipient_temp == nullptr)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to append recipient address to recipient list");
return;
}
recipient = recipient_temp;
}
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipient);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
// \r\nContent-Type: text/plain; charset=\"UTF-8\"
message.insert(0, fmt::format("Date: {:%a, %d %b %Y %T %z}\r\nFrom: <{}>\r\nTo: <{}>\r\nSubject: {}\r\n\r\n", *std::localtime(&now), from_address, to_address, subject));
message.append("\r\n.\r\n");
curl_easy_setopt(curl, CURLOPT_READDATA, reinterpret_cast<void *>(const_cast<char*>(message.c_str())));
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_callback);
CURLcode result = curl_easy_perform(curl);
if (result != CURLE_OK)
{
eventlog(eventlog_level_error, __FUNCTION__, "Failed to send email ({})", curl_easy_strerror(result));
}
curl_slist_free_all(recipient);
curl_easy_cleanup(curl);
}
}
}

31
src/bnetd/smtp.h Normal file
View file

@ -0,0 +1,31 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <string>
namespace pvpgn
{
namespace bnetd
{
bool smtp_init(const char* prefs_smtp_ca_cert_store, const char* prefs_smtp_server_url, unsigned int prefs_smtp_port, const char* prefs_smtp_username, const char* prefs_smtp_password);
void smtp_cleanup();
void smtp_send_email(const std::string& to_address, const std::string& from_address, const std::string& subject, std::string message);
}
}

View file

@ -158,6 +158,7 @@ const char * const BNETD_SUPPORT_FILE = "conf/supportfile.conf";
const char * const BNETD_COMMAND_GROUPS_FILE = "conf/command_groups.conf";
const char * const BNETD_TOURNAMENT_FILE = "conf/tournament.conf";
const char * const BNETD_CUSTOMICONS_FILE = "conf/icons.conf";
const char * const BNETD_EMAIL_VERIFICATION_FILE = "conf/email_verification.conf";
const char * const BNETD_ALIASFILE = "conf/bnalias.conf";
/* time limit for new member as newer(whom cannot be promoted) in clan, (hrs) */
const unsigned CLAN_NEWER_TIME = 168;