#include "email_sender_file.h"
#include <atomic>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fcntl.h>
#include <string>
#include <system_error>
#include <unistd.h>
FileEmailSender::FileEmailSender(std::filesystem::path link_file)
: link_file_(std::move(link_file))
{}
mw::E<void> FileEmailSender::send(const AuthenticationEmail& email)
{
static std::atomic<std::uint64_t> next_temporary_id = 0;
const std::uint64_t suffix = next_temporary_id.fetch_add(
1, std::memory_order_relaxed);
std::filesystem::path temporary = link_file_;
temporary += ".tmp-" + std::to_string(::getpid()) + '-' +
std::to_string(suffix);
const int file = ::open(
temporary.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600);
if(file < 0)
{
return std::unexpected(mw::runtimeError(
"Failed to write authentication link"));
}
const std::string contents = email.confirmation_url.str() + "\n";
std::size_t written = 0;
while(written < contents.size())
{
const ssize_t count = ::write(
file, contents.data() + written, contents.size() - written);
if(count < 0 && errno == EINTR)
{
continue;
}
if(count <= 0)
{
::close(file);
std::error_code ignored;
std::filesystem::remove(temporary, ignored);
return std::unexpected(mw::runtimeError(
"Failed to write authentication link"));
}
written += static_cast<std::size_t>(count);
}
if(::fsync(file) != 0 || ::close(file) != 0)
{
std::error_code ignored;
std::filesystem::remove(temporary, ignored);
return std::unexpected(mw::runtimeError(
"Failed to write authentication link"));
}
std::error_code error;
std::filesystem::rename(temporary, link_file_, error);
if(error)
{
std::error_code ignored;
std::filesystem::remove(temporary, ignored);
return std::unexpected(mw::runtimeError(
"Failed to publish authentication link"));
}
return {};
}
bool FileEmailSender::usesGlobalQuota() const
{
return false;
}