Move to new Build System
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
#include "Connection.h"
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
Connection::Connection(asio::io_service& ioService, const Util::Context& context) :
|
||||
m_ioService(ioService),
|
||||
m_socket(ioService),
|
||||
m_protocol(context)
|
||||
{
|
||||
}
|
||||
|
||||
asio::ip::tcp::socket& Connection::Socket()
|
||||
{
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
void Connection::Start()
|
||||
{
|
||||
StartWrite(m_protocol.OpenConnection(m_socket.remote_endpoint().address().to_string()).second);
|
||||
}
|
||||
|
||||
void Connection::StartRead()
|
||||
{
|
||||
m_socket.async_read_some(asio::buffer(m_data), std::bind(&Connection::HandleRead, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void Connection::HandleRead(const asio::error_code& ec, std::size_t length)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
Protocol::Protocol::Result result = m_protocol.ProcessMessage(std::string(m_data.begin(), m_data.begin() + length));
|
||||
if (result.second.size() > 0)
|
||||
StartWrite(result.second);
|
||||
else
|
||||
StartRead();
|
||||
|
||||
if (result.first == Protocol::Protocol::State::Disconnected)
|
||||
m_socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::StartWrite(const std::string& message)
|
||||
{
|
||||
std::copy(message.begin(), message.end(), m_data.data());
|
||||
|
||||
size_t length = message.size() + 1;
|
||||
m_data[message.size()] = '\n';
|
||||
|
||||
asio::async_write(m_socket, asio::buffer(m_data, length), std::bind(&Connection::HandleWrite, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Connection::HandleWrite(const asio::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
StartRead();
|
||||
}
|
||||
|
||||
} // namespace MailServer
|
||||
@@ -0,0 +1,40 @@
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "Protocol/Protocol.h"
|
||||
#include "Util/Util.h"
|
||||
#include <asio.hpp>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
Connection(asio::io_service& ioService, const Util::Context& context);
|
||||
|
||||
asio::ip::tcp::socket& Socket();
|
||||
|
||||
void Start();
|
||||
|
||||
private:
|
||||
Connection(const Connection&) = delete;
|
||||
Connection& operator=(const Connection&) = delete;
|
||||
|
||||
private:
|
||||
void StartRead();
|
||||
void HandleRead(const asio::error_code& ec, std::size_t length);
|
||||
void StartWrite(const std::string& message);
|
||||
void HandleWrite(const asio::error_code& ec);
|
||||
|
||||
asio::io_service& m_ioService;
|
||||
asio::ip::tcp::socket m_socket;
|
||||
std::array<char, 1024> m_data;
|
||||
|
||||
Protocol::Protocol m_protocol;
|
||||
};
|
||||
|
||||
} // namespace MailServer
|
||||
|
||||
#endif // CONNECTION_H
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../Makefile
|
||||
@@ -0,0 +1,219 @@
|
||||
#include "Message.h"
|
||||
#include "Image/JpegImageCollection.h"
|
||||
#include "Util/Base64.h"
|
||||
#include <Logging.h>
|
||||
#include <StringAlgorithm.h>
|
||||
#include <filesystem/path.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
Message::Message(const std::string& address, const Util::Context& context, const std::string& message) :
|
||||
m_address(address),
|
||||
m_context(context),
|
||||
m_mimeBoundary("-_-InvalidBoundary-_-"),
|
||||
m_contentEncoding("none"),
|
||||
m_currentImage(0)
|
||||
{
|
||||
ProcessMessage(message);
|
||||
}
|
||||
|
||||
std::string Decode(const std::string& data)
|
||||
{
|
||||
std::string result;
|
||||
if (StringAlgorithm::istarts_with(data, "=?utf-8?"))
|
||||
result = Util::Base64::Decode(std::string(data.begin() + 10, data.begin() + data.find_last_of('?')));
|
||||
else
|
||||
result = Util::Base64::Decode(data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Message::ProcessMessage(const std::string& message)
|
||||
{
|
||||
try
|
||||
{
|
||||
Message::State::type state = Message::State::Header;
|
||||
std::string line;
|
||||
std::istringstream stream(message);
|
||||
while (std::getline(stream, line, '\r'))
|
||||
{
|
||||
StringAlgorithm::erase_all(line, '\n');
|
||||
switch (state)
|
||||
{
|
||||
case State::Header:
|
||||
state = ProcessHeaderLine(line);
|
||||
break;
|
||||
case State::Data:
|
||||
state = ProcessDataLine(line);
|
||||
break;
|
||||
case State::MimeBoundary:
|
||||
state = ProcessMimeBoundary(line);
|
||||
break;
|
||||
case State::Image:
|
||||
state = ProcessImage(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringAlgorithm::contains(m_contentEncoding, "base64"))
|
||||
{
|
||||
std::string decoded = Decode(m_subject);
|
||||
if (decoded.size() > 0)
|
||||
m_subject = decoded;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "From: " << m_sender << std::endl;
|
||||
Logging::Log(Logging::Severity::Debug, ss.str());
|
||||
|
||||
ss.str("");
|
||||
ss << "To: " << m_receiver << std::endl;
|
||||
Logging::Log(Logging::Severity::Debug, ss.str());
|
||||
|
||||
ss.str("");
|
||||
ss << "Subject: " << m_subject << std::endl;
|
||||
Logging::Log(Logging::Severity::Debug, ss.str());
|
||||
|
||||
ss.str("");
|
||||
ss << "Received " << m_images.size() << " Images" << std::endl;
|
||||
Logging::Log(Logging::Severity::Info, ss.str());
|
||||
|
||||
Image::JpegImageCollection imageCollection(m_images);
|
||||
|
||||
filesystem::path path(m_context.path);
|
||||
path = path.make_absolute();
|
||||
path = path/filesystem::path(m_address);
|
||||
|
||||
if (!path.exists())
|
||||
filesystem::create_directory(path);
|
||||
|
||||
std::string fileName(Util::getTimeString());
|
||||
fileName.append(".jpg");
|
||||
path = path/filesystem::path(fileName);
|
||||
|
||||
imageCollection.Save(path.str());
|
||||
|
||||
ss.str("");
|
||||
ss << "Images Stored as " << path.str() << std::endl;
|
||||
Logging::Log(Logging::Severity::Info, ss.str());
|
||||
|
||||
if (!m_context.url.empty())
|
||||
{
|
||||
std::stringstream url;
|
||||
url << m_context.url << "?ip=" << m_address << "&file=" << fileName;
|
||||
m_context.client.GetUrlContents(url.str());
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Message::ProcessMessage() - Error: " << e.what() << std::endl;
|
||||
Logging::Log(Logging::Severity::Error, ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
Message::State::type Message::ProcessHeaderLine(const std::string& line)
|
||||
{
|
||||
if (line.empty())
|
||||
return Message::State::Data;
|
||||
|
||||
if (StringAlgorithm::istarts_with(line, "from:"))
|
||||
{
|
||||
m_sender = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "to:"))
|
||||
{
|
||||
m_receiver = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "subject:"))
|
||||
{
|
||||
m_subject = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "date"))
|
||||
{
|
||||
m_date = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "content-type:"))
|
||||
{
|
||||
m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "boundary") || StringAlgorithm::istarts_with(line, "\tboundary") || StringAlgorithm::istarts_with(line, " boundary"))
|
||||
{
|
||||
m_mimeBoundary = std::string("--").append(std::string(line.begin() + line.find_first_of('"') + 1, line.begin() + line.find_last_of('"')));
|
||||
return Message::State::Header;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "content-transfer-encoding:"))
|
||||
{
|
||||
m_contentEncoding = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::Header;
|
||||
}
|
||||
|
||||
return Message::State::Header;
|
||||
}
|
||||
|
||||
Message::State::type Message::ProcessDataLine(const std::string& line)
|
||||
{
|
||||
if (StringAlgorithm::istarts_with(line, m_mimeBoundary))
|
||||
{
|
||||
m_contentType = "";
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
|
||||
return Message::State::Data;
|
||||
}
|
||||
|
||||
Message::State::type Message::ProcessMimeBoundary(const std::string& line)
|
||||
{
|
||||
if (line.empty())
|
||||
{
|
||||
if (StringAlgorithm::istarts_with(m_contentType, "image/jpeg") || StringAlgorithm::istarts_with(m_contentType, "image/jpg"))
|
||||
{
|
||||
m_mimeImages.push_back(Image::MimeImage(m_imageName));
|
||||
m_imageName = "";
|
||||
return Message::State::Image;
|
||||
}
|
||||
return Message::State::Data;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "content-type:"))
|
||||
{
|
||||
m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "name") || StringAlgorithm::istarts_with(line, "\tname"))
|
||||
{
|
||||
m_imageName = "image.jpg";
|
||||
//m_imageName = Decode(std::string(line.begin() + line.find_first_of('"') + 1, line.begin() + line.find_last_of('"')));
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
if (StringAlgorithm::istarts_with(line, "content-transfer-encoding:"))
|
||||
{
|
||||
m_contentEncoding = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
|
||||
Message::State::type Message::ProcessImage(const std::string& line)
|
||||
{
|
||||
if (StringAlgorithm::istarts_with(line, m_mimeBoundary))
|
||||
{
|
||||
m_images.push_back(m_mimeImages[m_currentImage].DecodeImageData());
|
||||
++m_currentImage;
|
||||
m_contentType = "";
|
||||
return Message::State::MimeBoundary;
|
||||
}
|
||||
|
||||
m_mimeImages[m_currentImage].AppendData(line);
|
||||
|
||||
return Message::State::Image;
|
||||
}
|
||||
|
||||
} // namespace MailServer
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef MESSAGE_H
|
||||
#define MESSAGE_H
|
||||
|
||||
#include "Image/MimeImage.h"
|
||||
#include "Image/JpegImage.h"
|
||||
#include "Util/Util.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
Message(const std::string& address, const Util::Context& context, const std::string& message);
|
||||
|
||||
private:
|
||||
class State
|
||||
{
|
||||
public:
|
||||
enum type
|
||||
{
|
||||
Header,
|
||||
Data,
|
||||
MimeBoundary,
|
||||
Image
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
void ProcessMessage(const std::string& message);
|
||||
State::type ProcessHeaderLine(const std::string& line);
|
||||
State::type ProcessDataLine(const std::string& line);
|
||||
State::type ProcessMimeBoundary(const std::string& line);
|
||||
State::type ProcessImage(const std::string& line);
|
||||
|
||||
private:
|
||||
std::string m_address;
|
||||
const Util::Context& m_context;
|
||||
|
||||
std::string m_sender;
|
||||
std::string m_receiver;
|
||||
std::string m_subject;
|
||||
std::string m_date;
|
||||
std::string m_mimeBoundary;
|
||||
|
||||
std::string m_contentType;
|
||||
std::string m_contentEncoding;
|
||||
|
||||
size_t m_currentImage;
|
||||
std::string m_imageName;
|
||||
|
||||
std::vector<Image::MimeImage> m_mimeImages;
|
||||
std::vector<Image::JpegImage> m_images;
|
||||
};
|
||||
|
||||
} // namespace MailServer
|
||||
|
||||
#endif // MESSAGE_H
|
||||
@@ -0,0 +1,81 @@
|
||||
#include "Server.h"
|
||||
#include <Logging.h>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <sys/wait.h>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
Server::Server(asio::io_service& ioService, unsigned short port, const std::string& path, const std::string& url) :
|
||||
m_ioService(ioService),
|
||||
m_signal(ioService, SIGCHLD),
|
||||
m_acceptor(ioService, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
|
||||
m_socket(ioService)
|
||||
{
|
||||
m_context.path = path;
|
||||
m_context.url = url;
|
||||
|
||||
StartSignalWait();
|
||||
StartAccept();
|
||||
}
|
||||
|
||||
void Server::StartSignalWait()
|
||||
{
|
||||
m_signal.async_wait(std::bind(&Server::HandleSignalWait, this));
|
||||
}
|
||||
|
||||
void Server::HandleSignalWait()
|
||||
{
|
||||
// Only the parent process should check for this signal. We can determine
|
||||
// whether we are in the parent by checking if the acceptor is still open.
|
||||
if (m_acceptor.is_open())
|
||||
{
|
||||
// Reap completed child processes so that we don't end up with zombies.
|
||||
int status = 0;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0) {}
|
||||
|
||||
StartSignalWait();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::StartAccept()
|
||||
{
|
||||
m_connection.reset(new Connection(m_ioService, m_context));
|
||||
m_acceptor.async_accept(m_connection->Socket(), std::bind(&Server::HandleAccept, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Server::HandleAccept(const asio::error_code& ec)
|
||||
{
|
||||
if (!ec)
|
||||
{
|
||||
m_ioService.notify_fork(asio::io_service::fork_prepare);
|
||||
|
||||
if (fork() == 0)
|
||||
{
|
||||
m_ioService.notify_fork(asio::io_service::fork_child);
|
||||
m_acceptor.close();
|
||||
m_signal.cancel();
|
||||
|
||||
m_connection->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ioService.notify_fork(asio::io_service::fork_parent);
|
||||
|
||||
m_connection.reset();
|
||||
m_socket.close();
|
||||
StartAccept();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Server::HandleAccept() - Error" << std::endl;
|
||||
Logging::Log(Logging::Severity::Error, ss.str());
|
||||
|
||||
StartAccept();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MailServer
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "Connection.h"
|
||||
#include "Util/Util.h"
|
||||
#include <asio.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace MailServer {
|
||||
|
||||
class Server
|
||||
{
|
||||
public:
|
||||
Server(asio::io_service& ioService, unsigned short port, const std::string& path, const std::string& url);
|
||||
|
||||
private:
|
||||
void StartSignalWait();
|
||||
void HandleSignalWait();
|
||||
void StartAccept();
|
||||
void HandleAccept(const asio::error_code& ec);
|
||||
|
||||
asio::io_service& m_ioService;
|
||||
asio::signal_set m_signal;
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
asio::ip::tcp::socket m_socket;
|
||||
std::shared_ptr<Connection> m_connection;
|
||||
|
||||
Util::Context m_context;
|
||||
};
|
||||
|
||||
} // namespace MailServer
|
||||
|
||||
#endif // SERVER_H
|
||||
Reference in New Issue
Block a user