diff --git a/.gitignore b/.gitignore index c097947..ecc4dea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,14 @@ -*.o +*.o.* +*.d.* +*.a.* .*.swp -AlarmServer +.AppleDouble +.debug +.gdbinit* +gdb* +core-* +*-core +core.* +*.core +AlarmServer* +!AlarmServer.cc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7da2786 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Makefiles"] + path = Makefiles + url = https://gogs.dierkse.nl/JDierkse/Makefiles.git diff --git a/main.cpp b/Application/AlarmServer.cc similarity index 84% rename from main.cpp rename to Application/AlarmServer.cc index 529ad14..b4ecfbe 100644 --- a/main.cpp +++ b/Application/AlarmServer.cc @@ -1,8 +1,9 @@ -#include +#include "MailServer/Server.h" +#include #include #include #include -#include "server.h" +#include int main(int argc, char* argv[]) @@ -35,9 +36,9 @@ int main(int argc, char* argv[]) return 1; } - boost::asio::io_service io_service; + asio::io_service io_service; - server s(io_service, port, path, url); + MailServer::Server s(io_service, port, path, url); io_service.run(); } @@ -46,4 +47,3 @@ int main(int argc, char* argv[]) std::cerr << "Exception: " << e.what() << std::endl; } } - diff --git a/Application/Makefile b/Application/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Application/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/inputImage.cpp b/Image/JpegImage.cpp similarity index 53% rename from inputImage.cpp rename to Image/JpegImage.cpp index 1c2b9eb..7f94d6b 100644 --- a/inputImage.cpp +++ b/Image/JpegImage.cpp @@ -1,58 +1,60 @@ -#include "inputImage.h" +#include "JpegImage.h" +namespace Image { + const static JOCTET EOI_BUFFER[1] = { JPEG_EOI }; -inputImage::inputImage() +JpegImage::JpegImage() { } -void inputImage::setImageData(const std::string& data) +void JpegImage::SetImageData(const std::string& data) { - decompressImage(data.c_str(), data.size()); + DecompressImage(data.c_str(), data.size()); } -unsigned int inputImage::width() const +unsigned int JpegImage::Width() const { return m_width; } -unsigned int inputImage::height() const +unsigned int JpegImage::Height() const { return m_height; } -unsigned int inputImage::strideX() const +unsigned int JpegImage::StrideX() const { return m_strideX; } -unsigned int inputImage::strideY() const +unsigned int JpegImage::StrideY() const { return m_strideY; } -const std::vector& inputImage::data() const +const std::vector& JpegImage::Data() const { return m_data; } -void inputImage::init_source(j_decompress_ptr pCinfo) +void JpegImage::InitSource(j_decompress_ptr pCinfo) { } -boolean inputImage::fill_input_buffer(j_decompress_ptr pCinfo) +boolean JpegImage::FillInputBuffer(j_decompress_ptr pCinfo) { - source_mgr_ptr pSrc = reinterpret_cast(pCinfo->src); + SourceMgrPtr pSrc = reinterpret_cast(pCinfo->src); pSrc->pub.next_input_byte = EOI_BUFFER; pSrc->pub.bytes_in_buffer = 1; return TRUE; } -void inputImage::skip_input_data(j_decompress_ptr pCinfo, long numBytes) +void JpegImage::SkipInputData(j_decompress_ptr pCinfo, long numBytes) { - source_mgr_ptr pSrc = reinterpret_cast(pCinfo->src); - if (pSrc->pub.bytes_in_buffer < numBytes) + SourceMgrPtr pSrc = reinterpret_cast(pCinfo->src); + if (pSrc->pub.bytes_in_buffer < static_cast(numBytes)) { pSrc->pub.next_input_byte = EOI_BUFFER; pSrc->pub.bytes_in_buffer = 1; @@ -64,22 +66,22 @@ void inputImage::skip_input_data(j_decompress_ptr pCinfo, long numBytes) } } -void inputImage::term_source(j_decompress_ptr pCinfo) +void JpegImage::TermSource(j_decompress_ptr pCinfo) { } -void inputImage::setSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t len) const +void JpegImage::SetSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t len) const { - source_mgr_ptr pSrc; + SourceMgrPtr pSrc; if (pCinfo->src == 0) - pCinfo->src = (struct jpeg_source_mgr *)(*pCinfo->mem->alloc_small)(reinterpret_cast(pCinfo), JPOOL_PERMANENT, sizeof(source_mgr)); + pCinfo->src = (struct jpeg_source_mgr *)(*pCinfo->mem->alloc_small)(reinterpret_cast(pCinfo), JPOOL_PERMANENT, sizeof(SourceMgr)); - pSrc = reinterpret_cast(pCinfo->src); - pSrc->pub.init_source = inputImage::init_source; - pSrc->pub.fill_input_buffer = inputImage::fill_input_buffer; - pSrc->pub.skip_input_data = inputImage::skip_input_data; + pSrc = reinterpret_cast(pCinfo->src); + pSrc->pub.init_source = JpegImage::InitSource; + pSrc->pub.fill_input_buffer = JpegImage::FillInputBuffer; + pSrc->pub.skip_input_data = JpegImage::SkipInputData; pSrc->pub.resync_to_restart = jpeg_resync_to_restart; - pSrc->pub.term_source = inputImage::term_source; + pSrc->pub.term_source = JpegImage::TermSource; pSrc->data = (const JOCTET *)pData; pSrc->len = len; @@ -87,7 +89,7 @@ void inputImage::setSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t pSrc->pub.next_input_byte = pSrc->data; } -void inputImage::decompressImage(const char* pData, size_t len) +void JpegImage::DecompressImage(const char* pData, size_t len) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; @@ -95,7 +97,7 @@ void inputImage::decompressImage(const char* pData, size_t len) jerr.trace_level = 0; cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); - setSourceMgr(&cinfo, pData, len); + SetSourceMgr(&cinfo, pData, len); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); @@ -118,3 +120,5 @@ void inputImage::decompressImage(const char* pData, size_t len) jpeg_destroy_decompress(&cinfo); } +} // namespace Image + diff --git a/Image/JpegImage.h b/Image/JpegImage.h new file mode 100644 index 0000000..bfc2726 --- /dev/null +++ b/Image/JpegImage.h @@ -0,0 +1,54 @@ +#ifndef JPEGIMAGE_H +#define JPEGIMAGE_H + +#include +#include +#include + + +namespace Image { + +class JpegImage +{ +public: + JpegImage(); + + void SetImageData(const std::string& data); + + unsigned int Width() const; + unsigned int Height() const; + unsigned int StrideX() const; + unsigned int StrideY() const; + const std::vector& Data() const; + +private: + struct SourceMgr + { + struct jpeg_source_mgr pub; + const JOCTET *data; + size_t len; + }; + typedef SourceMgr* SourceMgrPtr; + +private: + static void InitSource(j_decompress_ptr pCinfo); + static boolean FillInputBuffer(j_decompress_ptr pCinfo); + static void SkipInputData(j_decompress_ptr pCinfo, long numBytes); + static void TermSource(j_decompress_ptr pCinfo); + + void SetSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t len) const; + + void DecompressImage(const char* pData, size_t len); + +private: + unsigned int m_width; + unsigned int m_height; + unsigned int m_strideX; + unsigned int m_strideY; + + std::vector m_data; +}; + +} // namespace Image + +#endif // JPEGIMAGE_H diff --git a/outputImage.cpp b/Image/JpegImageCollection.cpp similarity index 69% rename from outputImage.cpp rename to Image/JpegImageCollection.cpp index 1776c54..6b1fa0e 100644 --- a/outputImage.cpp +++ b/Image/JpegImageCollection.cpp @@ -1,8 +1,10 @@ #include -#include "outputImage.h" +#include "JpegImageCollection.h" -outputImage::outputImage(const std::vector& images) : +namespace Image { + +JpegImageCollection::JpegImageCollection(const std::vector& images) : m_width(0), m_height(0), m_strideX(0), @@ -11,23 +13,23 @@ outputImage::outputImage(const std::vector& images) : if (images.size() == 0) return; - m_width = images[0].width(); - m_strideX = images[0].strideX(); - m_strideY = images[0].strideY(); + m_width = images[0].Width(); + m_strideX = images[0].StrideX(); + m_strideY = images[0].StrideY(); - size_t height = images[0].height(); + size_t height = images[0].Height(); m_height = height * images.size(); m_imageData.resize(m_strideY * m_height); std::vector::iterator outIterator = m_imageData.begin(); - for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) - outIterator = std::copy(it->data().begin(), it->data().end(), outIterator); + for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) + outIterator = std::copy(it->Data().begin(), it->Data().end(), outIterator); } -void outputImage::save(const std::string& fileName) +void JpegImageCollection::Save(const std::string& fileName) { if (m_width == 0 || m_height == 0 || m_strideX == 0 || m_strideY == 0) - return; + return; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -62,3 +64,4 @@ void outputImage::save(const std::string& fileName) fclose(outFile); } +} // namespace Image diff --git a/outputImage.h b/Image/JpegImageCollection.h similarity index 55% rename from outputImage.h rename to Image/JpegImageCollection.h index 446f9f3..9f01f26 100644 --- a/outputImage.h +++ b/Image/JpegImageCollection.h @@ -3,15 +3,17 @@ #include #include -#include "inputImage.h" +#include "JpegImage.h" -class outputImage +namespace Image { + +class JpegImageCollection { public: - outputImage(const std::vector& images); + JpegImageCollection(const std::vector& images); - void save(const std::string& fileName); + void Save(const std::string& fileName); private: size_t m_width; @@ -22,5 +24,6 @@ private: std::vector m_imageData; }; -#endif // OUTPUTIMAGE_H +} // namespace Image +#endif // OUTPUTIMAGE_H diff --git a/Image/Makefile b/Image/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Image/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/Image/MimeImage.cpp b/Image/MimeImage.cpp new file mode 100644 index 0000000..4290325 --- /dev/null +++ b/Image/MimeImage.cpp @@ -0,0 +1,34 @@ +#include "MimeImage.h" +#include "Util/Base64.h" +#include +#include + + +namespace Image { + +MimeImage::MimeImage() +{ +} + +MimeImage::MimeImage(const std::string& name) : + m_name(name) +{ +} + +void MimeImage::AppendData(const std::string& data) +{ + if (data.empty()) + return; + + m_imageData.append(data); +} + +const JpegImage& MimeImage::DecodeImageData() +{ + StringAlgorithm::erase_all(m_imageData, '\r'); + m_jpegImage.SetImageData(Util::Base64::Decode(m_imageData)); + return m_jpegImage; +} + +} // namespace Image + diff --git a/Image/MimeImage.h b/Image/MimeImage.h new file mode 100644 index 0000000..56c1932 --- /dev/null +++ b/Image/MimeImage.h @@ -0,0 +1,26 @@ +#ifndef MIMEIMAGE_H +#define MIMEIMAGE_H + +#include "JpegImage.h" + + +namespace Image { + +class MimeImage +{ +public: + MimeImage(); + MimeImage(const std::string& name); + + void AppendData(const std::string& data); + const JpegImage& DecodeImageData(); + +private: + std::string m_name; + std::string m_imageData; + JpegImage m_jpegImage; +}; + +} // namespace Image + +#endif // MIMEIMAGE_H diff --git a/Libraries/Http b/Libraries/Http new file mode 120000 index 0000000..5fe3319 --- /dev/null +++ b/Libraries/Http @@ -0,0 +1 @@ +../../Libraries/Http \ No newline at end of file diff --git a/Libraries/Logging b/Libraries/Logging new file mode 120000 index 0000000..2b4f659 --- /dev/null +++ b/Libraries/Logging @@ -0,0 +1 @@ +../../Libraries/Logging \ No newline at end of file diff --git a/Libraries/Utilities b/Libraries/Utilities new file mode 120000 index 0000000..88f16bc --- /dev/null +++ b/Libraries/Utilities @@ -0,0 +1 @@ +../../Libraries/Utilities \ No newline at end of file diff --git a/Libraries/asio b/Libraries/asio new file mode 120000 index 0000000..98b68c2 --- /dev/null +++ b/Libraries/asio @@ -0,0 +1 @@ +../../Libraries/asio \ No newline at end of file diff --git a/Libraries/filesystem b/Libraries/filesystem new file mode 120000 index 0000000..81d9630 --- /dev/null +++ b/Libraries/filesystem @@ -0,0 +1 @@ +../../Libraries/filesystem \ No newline at end of file diff --git a/MailServer/Connection.cpp b/MailServer/Connection.cpp new file mode 100644 index 0000000..3a9487e --- /dev/null +++ b/MailServer/Connection.cpp @@ -0,0 +1,61 @@ +#include "Connection.h" +#include +#include + + +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 diff --git a/MailServer/Connection.h b/MailServer/Connection.h new file mode 100644 index 0000000..f547852 --- /dev/null +++ b/MailServer/Connection.h @@ -0,0 +1,40 @@ +#ifndef CONNECTION_H +#define CONNECTION_H + +#include "Protocol/Protocol.h" +#include "Util/Util.h" +#include +#include + + +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 m_data; + + Protocol::Protocol m_protocol; +}; + +} // namespace MailServer + +#endif // CONNECTION_H diff --git a/MailServer/Makefile b/MailServer/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/MailServer/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/MailServer/Message.cpp b/MailServer/Message.cpp new file mode 100644 index 0000000..5d0a93c --- /dev/null +++ b/MailServer/Message.cpp @@ -0,0 +1,219 @@ +#include "Message.h" +#include "Image/JpegImageCollection.h" +#include "Util/Base64.h" +#include +#include +#include +#include + + +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 diff --git a/MailServer/Message.h b/MailServer/Message.h new file mode 100644 index 0000000..7d35709 --- /dev/null +++ b/MailServer/Message.h @@ -0,0 +1,60 @@ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include "Image/MimeImage.h" +#include "Image/JpegImage.h" +#include "Util/Util.h" +#include +#include + + +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 m_mimeImages; + std::vector m_images; +}; + +} // namespace MailServer + +#endif // MESSAGE_H diff --git a/MailServer/Server.cpp b/MailServer/Server.cpp new file mode 100644 index 0000000..1a27590 --- /dev/null +++ b/MailServer/Server.cpp @@ -0,0 +1,81 @@ +#include "Server.h" +#include +#include +#include +#include + + +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 diff --git a/MailServer/Server.h b/MailServer/Server.h new file mode 100644 index 0000000..c20aadd --- /dev/null +++ b/MailServer/Server.h @@ -0,0 +1,35 @@ +#ifndef SERVER_H +#define SERVER_H + +#include "Connection.h" +#include "Util/Util.h" +#include +#include +#include + + +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 m_connection; + + Util::Context m_context; +}; + +} // namespace MailServer + +#endif // SERVER_H diff --git a/Makefile b/Makefile deleted file mode 100644 index 01bc8a6..0000000 --- a/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -CC := g++ -CFLAGS := -O3 -LFLAGS := -L/opt/lib -lpthread -lcrypto -lcurl -lboost_regex -lboost_filesystem -lboost_system -ljpeg - -ifeq ($(BUILD),debug) -# Debug flags -CFLAGS += -O0 -g -endif - -ifeq ($(BUILD),release) -# Optimization flags valid for Intel Atom (DS1813+) -CFLAGS += -march=prescott -mtune=pentium -mfpmath=sse -O3 -s -DNDEBUG -endif - -%.o: %.cpp - $(CC) -c $(CFLAGS) $< -o $@ - -AlarmServer: main.cpp server.o connection.o protocol.o message.o mimeImage.o outputImage.o inputImage.o util.o httpClient.o base64.o - g++ -o AlarmServer main.cpp server.o connection.o protocol.o message.o mimeImage.o outputImage.o inputImage.o util.o httpClient.o base64.o $(LFLAGS) $(LDFLAGS) $(CFLAGS) - -server.o: server.cpp server.h connection.h protocol.h util.h httpClient.h - -connection.o: connection.cpp connection.h protocol.h util.h httpClient.h - -protocol.o: protocol.cpp protocol.h util.h httpClient.h - -message.o: message.cpp message.h mimeImage.h outputImage.h inputImage.h util.h httpClient.h base64.h - -mimeImage.o: mimeImage.cpp mimeImage.h outputImage.h inputImage.h base64.h - -outputImage.o: outputImage.cpp outputImage.h inputImage.h - -inputImage.o: inputImage.cpp inputImage.h - -util.o: util.cpp util.h httpClient.h - -httpClient.o: httpClient.cpp httpClient.h - -base64.o: base64.cpp base64.h - -all: AlarmServer - -debug: - make "BUILD=debug" - -release: - make "BUILD=release" - -clean: - -rm *.o AlarmServer - diff --git a/Makefile b/Makefile new file mode 120000 index 0000000..6aa5187 --- /dev/null +++ b/Makefile @@ -0,0 +1 @@ +Makefiles/Makefile \ No newline at end of file diff --git a/Makefile.conf b/Makefile.conf new file mode 100644 index 0000000..158143e --- /dev/null +++ b/Makefile.conf @@ -0,0 +1,22 @@ +# +# Makefile.conf +# + +LFLAGS += -lHttp +LFLAGS += -L$(ROOTPATH)/Libraries/Http/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Http/include + +LFLAGS += -lLogging +LFLAGS += -L$(ROOTPATH)/Libraries/Logging/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Logging/include + +LFLAGS += -lUtilities +LFLAGS += -L$(ROOTPATH)/Libraries/Utilities/lib/$(ARCH) +CFLAGS += -I$(ROOTPATH)/Libraries/Utilities/include + +CFLAGS += -I$(ROOTPATH)/Libraries/asio/asio/include +CFLAGS += -I$(ROOTPATH)/Libraries/filesystem + +LFLAGS += -lpthread -lcrypto -lcurl -ljpeg +CFLAGS += -I$(ROOTPATH) -I$(ROOTPATH)/Libraries +DEBUGDIR := .debug diff --git a/Makefile.target b/Makefile.target new file mode 100644 index 0000000..1e2ccbd --- /dev/null +++ b/Makefile.target @@ -0,0 +1,13 @@ +# +# Makefile.target +# + +AlarmServer.$(ARCH): $(OBJECTS) Application/AlarmServer.o.$(ARCH) + $(call build_target_arch,$@,$^) + +AlarmServer: + $(call build_target,$@) + +.DEFAULT_GOAL := AlarmServer + +TARGETS += AlarmServer diff --git a/Makefiles b/Makefiles new file mode 160000 index 0000000..5e11355 --- /dev/null +++ b/Makefiles @@ -0,0 +1 @@ +Subproject commit 5e113555ebd4e049b287b3c94c4ef33e151ea4e6 diff --git a/Protocol/Makefile b/Protocol/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Protocol/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/Protocol/Protocol.cpp b/Protocol/Protocol.cpp new file mode 100644 index 0000000..7cab784 --- /dev/null +++ b/Protocol/Protocol.cpp @@ -0,0 +1,113 @@ +#include "Protocol.h" +#include "MailServer/Message.h" +#include +#include +#include + + +namespace Protocol { + +Protocol::Protocol(const Util::Context& context) : + m_internalState(InternalState::None), + m_context(context) +{ +} + +Protocol::Result Protocol::OpenConnection(const std::string& address) +{ + m_address = address; + m_internalState = InternalState::Connect; + + std::stringstream ss; + ss << "Connection from " << address << std::endl; + Logging::Log(Logging::Severity::Info, ss.str()); + + return Answer(Protocol::State::Connected, "220 AlarmServer"); +} + +Protocol::Result Protocol::ProcessMessage(const std::string& message) +{ + if (m_internalState == InternalState::Data) + { + AppendMessage(message); + + if (StringAlgorithm::iends_with(message, ".\r\n")) + { + MailServer::Message msg(m_address, m_context, m_message); + m_internalState = InternalState::Helo; + return Answer(Protocol::State::Connected, "250 Ok"); + } + + return Answer(Protocol::State::Connected, ""); + } + + if (m_internalState == InternalState::Auth_login_user) + { + m_internalState = InternalState::Auth_login_pass; + return Answer(Protocol::State::Connected, "334 UGFzc3dvcmQ6"); + } + if (m_internalState == InternalState::Auth_login_pass) + { + m_internalState = InternalState::Helo; + return Answer(Protocol::State::Connected, "235 2.7.0 Authentication successful"); + } + + if (StringAlgorithm::iequals(message, "quit\r\n")) + { + m_internalState = InternalState::Disconnect; + return Answer(Protocol::State::Disconnected, "221 Bye"); + } + if (StringAlgorithm::istarts_with(message, "helo")) + { + m_internalState = InternalState::Helo; + return Answer(Protocol::State::Connected, "250-AlarmServer\r\n250 AUTH LOGIN"); + } + if (StringAlgorithm::istarts_with(message, "ehlo")) + { + m_internalState = InternalState::Helo; + return Answer(Protocol::State::Connected, "250-AlarmServer\r\n250 AUTH LOGIN"); + } + if (StringAlgorithm::istarts_with(message, "auth login")) + { + if (m_internalState != InternalState::Helo) + return Answer(Protocol::State::Connected, "503 Bad sequence of commands"); + m_internalState = InternalState::Auth_login_user; + return Answer(Protocol::State::Connected, "334 VXNlcm5hbWU6"); + } + if (StringAlgorithm::istarts_with(message, "mail from:")) + { + if (m_internalState != InternalState::Helo) + return Answer(Protocol::State::Connected, "503 Bad sequence of commands"); + m_internalState = InternalState::Mail_from; + return Answer(Protocol::State::Connected, "250 Ok"); + } + if (StringAlgorithm::istarts_with(message, "rcpt to:")) + { + if (m_internalState != InternalState::Mail_from) + return Answer(Protocol::State::Connected, "503 Bad sequence of commands"); + m_internalState = InternalState::Rcpt_to; + return Answer(Protocol::State::Connected, "250 Ok"); + } + if (StringAlgorithm::iequals(message, "data\r\n")) + { + if (m_internalState != InternalState::Rcpt_to) + return Answer(Protocol::State::Connected, "503 Bad sequence of commands"); + m_internalState = InternalState::Data; + return Answer(Protocol::State::Connected, "354 Go ahead"); + } + + return Answer(Protocol::State::Connected, "502 Error"); +} + +Protocol::Result Protocol::Answer(Protocol::State::type state, std::string answer) const +{ + return Result(state, answer); +} + +void Protocol::AppendMessage(const std::string& message) +{ + m_message.append(message); + m_message.append("\n"); +} + +} // namespace Protocol diff --git a/Protocol/Protocol.h b/Protocol/Protocol.h new file mode 100644 index 0000000..72cb75c --- /dev/null +++ b/Protocol/Protocol.h @@ -0,0 +1,66 @@ +#ifndef PROTOCOL_H +#define PROTOCOL_H + +#include "Util/Util.h" +#include +#include + + +namespace Protocol { + +class Protocol +{ +public: + class State + { + public: + enum type + { + Connected, + Disconnected + }; + }; + + typedef std::pair Result; + +public: + explicit Protocol(const Util::Context& context); + + Result OpenConnection(const std::string& address); + Result ProcessMessage(const std::string& message); + +private: + class InternalState + { + public: + enum type + { + None, + Connect, + Helo, + Auth_login_user, + Auth_login_pass, + Mail_from, + Rcpt_to, + Data, + Mime_boundary, + Image_data, + Disconnect + }; + }; + +private: + Result Answer(State::type state, std::string answer) const; + void AppendMessage(const std::string& message); + +private: + InternalState::type m_internalState; + std::string m_address; + std::string m_message; + + const Util::Context& m_context; +}; + +} // namespace Protocol + +#endif // PROTOCOL_H diff --git a/Util/Base64.cpp b/Util/Base64.cpp new file mode 100644 index 0000000..7329fc1 --- /dev/null +++ b/Util/Base64.cpp @@ -0,0 +1,136 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "Base64.h" + + +namespace Util { +namespace Base64 { + +static const std::string Base64Chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +static inline bool IsBase64(unsigned char c) +{ + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string Encode(unsigned char const* bytes_to_encode, unsigned int in_len) +{ + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) + { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) + { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += Base64Chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += Base64Chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + } + + return ret; +} + +std::string Decode(std::string const& encoded_string) +{ + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && IsBase64(encoded_string[in_])) + { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) + { + for (i = 0; i <4; i++) + char_array_4[i] = Base64Chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) + { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = Base64Chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) + ret += char_array_3[j]; + } + + return ret; +} + +} // namespace Base64 +} // namespace Util + diff --git a/Util/Base64.h b/Util/Base64.h new file mode 100644 index 0000000..bd98905 --- /dev/null +++ b/Util/Base64.h @@ -0,0 +1,16 @@ +#ifndef UTIL_BASE64_H +#define UTIL_BASE64_H + +#include + + +namespace Util { +namespace Base64 { + +std::string Encode(unsigned char const* , unsigned int len); +std::string Decode(std::string const& s); + +} // namespace Base64 +} // namespace Util + +#endif // UTIL_BASE64_H diff --git a/Util/Makefile b/Util/Makefile new file mode 120000 index 0000000..d0b0e8e --- /dev/null +++ b/Util/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/util.cpp b/Util/Util.cpp similarity index 81% rename from util.cpp rename to Util/Util.cpp index facea1a..c87a444 100644 --- a/util.cpp +++ b/Util/Util.cpp @@ -1,6 +1,9 @@ +#include "Util.h" #include #include -#include "util.h" + + +namespace Util { std::string getTimeString() { @@ -15,3 +18,5 @@ std::string getTimeString() return std::string(buffer); } +} // namespace Util + diff --git a/Util/Util.h b/Util/Util.h new file mode 100644 index 0000000..e430890 --- /dev/null +++ b/Util/Util.h @@ -0,0 +1,22 @@ +#ifndef UTIL_UTIL_H +#define UTIL_UTIL_H + +#include +#include + + +namespace Util { + +struct Context +{ +public: + std::string path; + Http::HttpClient client; + std::string url; +}; + +std::string getTimeString(); + +} // namespace Util + +#endif // UTIL_UTIL_H diff --git a/base64.cpp b/base64.cpp deleted file mode 100644 index f0efe95..0000000 --- a/base64.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - base64.cpp and base64.h - - Copyright (C) 2004-2008 René Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - René Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ - -#include "base64.h" - -static const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); -} - -std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for(i = 0; (i <4) ; i++) - ret += base64_chars[char_array_4[i]]; - i = 0; - } - } - - if (i) - { - for(j = i; j < 3; j++) - char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (j = 0; (j < i + 1); j++) - ret += base64_chars[char_array_4[j]]; - - while((i++ < 3)) - ret += '='; - - } - - return ret; - -} - -std::string base64_decode(std::string const& encoded_string) { - int in_len = encoded_string.size(); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; in_++; - if (i ==4) { - for (i = 0; i <4; i++) - char_array_4[i] = base64_chars.find(char_array_4[i]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) - ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j <4; j++) - char_array_4[j] = 0; - - for (j = 0; j <4; j++) - char_array_4[j] = base64_chars.find(char_array_4[j]); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; -} - diff --git a/base64.h b/base64.h deleted file mode 100644 index 5f87aec..0000000 --- a/base64.h +++ /dev/null @@ -1,5 +0,0 @@ -#include - -std::string base64_encode(unsigned char const* , unsigned int len); -std::string base64_decode(std::string const& s); - diff --git a/connection.cpp b/connection.cpp deleted file mode 100644 index 1dd46e4..0000000 --- a/connection.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include - -#include "connection.h" - - -connection::connection(boost::asio::io_service& ioService, const context& context) : - m_ioService(ioService), - m_socket(ioService), - m_protocol(context) -{ -} - -boost::asio::ip::tcp::socket& connection::socket() -{ - return m_socket; -} - -void connection::start() -{ - start_write(m_protocol.openConnection(m_socket.remote_endpoint().address().to_string()).second); -} - -void connection::start_read() -{ - m_socket.async_read_some(boost::asio::buffer(m_data), boost::bind(&connection::handle_read, this, _1, _2)); -} - -void connection::handle_read(const boost::system::error_code& ec, std::size_t length) -{ - if (!ec) - { - protocol::result result = m_protocol.processMessage(std::string(m_data.begin(), m_data.begin() + length)); - if (result.second.size() > 0) - start_write(result.second); - else - start_read(); - - if (result.first == protocol::state::disconnected) - m_socket.close(); - } -} - -void connection::start_write(const std::string& message) -{ - std::copy(message.begin(), message.end(), m_data.data()); - - size_t length = message.size() + 1; - m_data[message.size()] = '\n'; - - boost::asio::async_write(m_socket, boost::asio::buffer(m_data, length), boost::bind(&connection::handle_write, this, _1)); -} - -void connection::handle_write(const boost::system::error_code& ec) -{ - if (!ec) - start_read(); -} - diff --git a/connection.h b/connection.h deleted file mode 100644 index 83c07a4..0000000 --- a/connection.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CONNECTION_H -#define CONNECTION_H - -#include -#include -#include -#include -#include -#include -#include "protocol.h" -#include "util.h" - - -class connection : private boost::noncopyable -{ -public: - connection(boost::asio::io_service& ioService, const context& context); - - boost::asio::ip::tcp::socket& socket(); - - void start(); - -private: - void start_read(); - void handle_read(const boost::system::error_code& ec, std::size_t length); - void start_write(const std::string& message); - void handle_write(const boost::system::error_code& ec); - - boost::asio::io_service& m_ioService; - boost::asio::ip::tcp::socket m_socket; - boost::array m_data; - - protocol m_protocol; -}; - -#endif // CONNECTION_H - diff --git a/httpClient.cpp b/httpClient.cpp deleted file mode 100644 index a1a6c9e..0000000 --- a/httpClient.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "httpClient.h" - - -httpClient::httpClient() -{ - curl_global_init(CURL_GLOBAL_ALL); - - m_userAgents.push_back("Opera/9.80 (X11; Linux i686; U; en) Presto/2.7.62 Version/11.00"); - - initialize_locks(); -} - -httpClient::~httpClient() -{ - free_locks(); - curl_global_cleanup(); -} - -std::string httpClient::GetUrlContents(const std::string& url) const -{ - std::string buffer; - - CURL* curl = curl_easy_init(); - - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); - curl_easy_setopt(curl, CURLOPT_USERAGENT, m_userAgents[rand() % m_userAgents.size()].c_str()); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - - int code = 0; - curl_easy_perform(curl); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - curl_easy_cleanup(curl); - - if (code != 200) - { - std::stringstream error; - error << "Error encountered while retrieving " << url; - throw std::runtime_error(error.str()); - } - - return buffer; -} - -size_t httpClient::WriteCallback(char* data, size_t size, size_t nmemb, std::string* writerData) -{ - if (writerData == NULL) - return 0; - - writerData->append(data, size * nmemb); - return size * nmemb; -} - diff --git a/httpClient.h b/httpClient.h deleted file mode 100644 index dda065c..0000000 --- a/httpClient.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef HTTPCLIENT_H -#define HTTPCLIENT_H - -#include -#include -#include -#include -#include -#include -#include - - -static std::vector > g_mutexes; - -static void lock_callback(int mode, int type, const char *file, int line) -{ - if (mode & CRYPTO_LOCK) - g_mutexes[type]->lock(); - else - g_mutexes[type]->unlock(); -} - -static unsigned long thread_id(void) -{ - return (unsigned long)pthread_self(); -} - -static void initialize_locks(void) -{ - g_mutexes.resize(CRYPTO_num_locks()); - for (int i = 0; i < CRYPTO_num_locks(); ++i) - g_mutexes[i] = boost::shared_ptr(new boost::mutex()); - - CRYPTO_set_id_callback(thread_id); - CRYPTO_set_locking_callback(lock_callback); -} - -static void free_locks(void) -{ - CRYPTO_set_locking_callback(NULL); -} - -class httpClient : boost::noncopyable -{ -public: - httpClient(); - ~httpClient(); - - std::string GetUrlContents(const std::string& url) const; - -private: - static size_t WriteCallback(char* data, size_t size, size_t nmemb, std::string* writerData); - -private: - std::vector m_userAgents; -}; - -#endif // HTTPCLIENT_H - diff --git a/inputImage.h b/inputImage.h deleted file mode 100644 index b813a1d..0000000 --- a/inputImage.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef INPUTIMAGE_H -#define INPUTIMAGE_H - -#include -#include -#include - -class inputImage -{ -public: - inputImage(); - - void setImageData(const std::string& data); - - unsigned int width() const; - unsigned int height() const; - unsigned int strideX() const; - unsigned int strideY() const; - const std::vector& data() const; - -private: - struct source_mgr - { - struct jpeg_source_mgr pub; - const JOCTET *data; - size_t len; - }; - typedef source_mgr* source_mgr_ptr; - -private: - static void init_source(j_decompress_ptr pCinfo); - static boolean fill_input_buffer(j_decompress_ptr pCinfo); - static void skip_input_data(j_decompress_ptr pCinfo, long numBytes); - static void term_source(j_decompress_ptr pCinfo); - - void setSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t len) const; - - void decompressImage(const char* pData, size_t len); - -private: - unsigned int m_width; - unsigned int m_height; - unsigned int m_strideX; - unsigned int m_strideY; - - std::vector m_data; -}; - -#endif // INPUTIMAGE_H - diff --git a/message.cpp b/message.cpp deleted file mode 100644 index 1e908cc..0000000 --- a/message.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include -#include -#include -#include -#include -#include "outputImage.h" -#include "base64.h" -#include "message.h" - - -message::message(const std::string& address, const 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 (boost::istarts_with(data, "=?utf-8?")) - result = base64_decode(std::string(data.begin() + 10, data.begin() + data.find_last_of('?'))); - else - result = base64_decode(data); - - return result; -} - -void message::processMessage(const std::string& message) -{ - try - { - std::cout << "message::processMessage enter" << std::endl; - message::state::type state = message::state::header; - std::string line; - std::istringstream stream(message); - while (std::getline(stream, line, '\r')) - { - boost::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 (boost::algorithm::contains(m_contentEncoding, "base64")) - { - std::string decoded = Decode(m_subject); - if (decoded.size() > 0) - m_subject = decoded; - } - - outputImage image(m_images); - - boost::filesystem::path path(m_context.path); - path = boost::filesystem::canonical(path); - path /= m_address; - - if (!boost::filesystem::exists(path)) - boost::filesystem::create_directory(path); - - std::string fileName(getTimeString()); - fileName.append(".jpg"); - path /= fileName; - - image.save(path.string()); - - 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::cout << "Exception: " << e.what() << std::endl; - std::cerr << "Exception: " << e.what() << std::endl; - } - - std::cout << "message::processMessage exit" << std::endl; -} - -message::state::type message::processHeaderLine(const std::string& line) -{ - if (line.empty()) - return message::state::data; - - if (boost::istarts_with(line, "from:")) - { - m_sender = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::header; - } - if (boost::istarts_with(line, "to:")) - { - m_receiver = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::header; - } - if (boost::istarts_with(line, "subject:")) - { - m_subject = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::header; - } - if (boost::istarts_with(line, "date")) - { - m_date = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::header; - } - if (boost::istarts_with(line, "content-type:")) - { - m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::header; - } - if (boost::istarts_with(line, "boundary") || boost::istarts_with(line, "\tboundary") || boost::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 (boost::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 (boost::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 (boost::istarts_with(m_contentType, "image/jpeg") || boost::istarts_with(m_contentType, "image/jpg")) - { - m_mimeImages.push_back(mimeImage(m_imageName)); - m_imageName = ""; - return message::state::image; - } - return message::state::data; - } - if (boost::istarts_with(line, "content-type:")) - { - m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end()); - return message::state::mimeBoundary; - } - if (boost::istarts_with(line, "name") || boost::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 (boost::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 (boost::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; -} - diff --git a/message.h b/message.h deleted file mode 100644 index fddbc19..0000000 --- a/message.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef MESSAGE_H -#define MESSAGE_H - -#include -#include -#include "mimeImage.h" -#include "inputImage.h" -#include "util.h" - - -class message -{ -public: - message(const std::string& address, const 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 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 m_mimeImages; - std::vector m_images; -}; - -#endif // MESSAGE_H - diff --git a/mimeImage.cpp b/mimeImage.cpp deleted file mode 100644 index e115ee1..0000000 --- a/mimeImage.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include "base64.h" -#include "mimeImage.h" - - -mimeImage::mimeImage() -{ -} - -mimeImage::mimeImage(const std::string& name) : - m_name(name) -{ -} - -void mimeImage::appendData(const std::string& data) -{ - if (data.empty()) - return; - - m_imageData.append(data); -} - -const inputImage& mimeImage::decodeImageData() -{ - boost::erase_all(m_imageData, "\r"); - m_inputImage.setImageData(base64_decode(m_imageData)); - return m_inputImage; -} - diff --git a/mimeImage.h b/mimeImage.h deleted file mode 100644 index 10a1ad6..0000000 --- a/mimeImage.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef MIMEIMAGE_H -#define MIMEIMAGE_H - -#include "inputImage.h" - - -class mimeImage -{ -public: - mimeImage(); - mimeImage(const std::string& name); - - void appendData(const std::string& data); - const inputImage& decodeImageData(); - -private: - std::string m_name; - std::string m_imageData; - inputImage m_inputImage; -}; - -#endif // MIMEIMAGE_H - diff --git a/protocol.cpp b/protocol.cpp deleted file mode 100644 index 4c56e01..0000000 --- a/protocol.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include "message.h" -#include "protocol.h" - -protocol::protocol(const context& context) : - m_internalState(internalState::none), - m_context(context) -{ -} - -protocol::result protocol::openConnection(const std::string& address) -{ - m_address = address; - m_internalState = internalState::connect; - std::cout << "Connection from " << address << std::endl; - return answer(protocol::state::connected, "220 AlarmServer"); -} - -protocol::result protocol::processMessage(const std::string& message) -{ - if (m_internalState == internalState::data) - { - appendMessage(message); - - if (boost::iends_with(message, ".\r\n")) - { - std::cout << "-> " << " . " << std::endl; - ::message msg(m_address, m_context, m_message); - std::cout << "-> " << " . " << std::endl; - m_internalState = internalState::helo; - std::cout << "-> " << " . " << std::endl; - return answer(protocol::state::connected, "250 Ok"); - } - - return answer(protocol::state::connected, ""); - } - - std::cout << "-> " << message << std::endl; - - if (m_internalState == internalState::auth_login_user) - { - m_internalState = internalState::auth_login_pass; - return answer(protocol::state::connected, "334 UGFzc3dvcmQ6"); - } - if (m_internalState == internalState::auth_login_pass) - { - m_internalState = internalState::helo; - return answer(protocol::state::connected, "235 2.7.0 Authentication successful"); - } - - if (boost::iequals(message, "quit\r\n")) - { - m_internalState = internalState::disconnect; - return answer(protocol::state::disconnected, "221 Bye"); - } - if (boost::istarts_with(message, "helo")) - { - m_internalState = internalState::helo; - return answer(protocol::state::connected, "250-AlarmServer\r\n250 AUTH LOGIN"); - } - if (boost::istarts_with(message, "ehlo")) - { - m_internalState = internalState::helo; - return answer(protocol::state::connected, "250-AlarmServer\r\n250 AUTH LOGIN"); - } - if (boost::istarts_with(message, "auth login")) - { - if (m_internalState != internalState::helo) - return answer(protocol::state::connected, "503 Bad sequence of commands"); - m_internalState = internalState::auth_login_user; - return answer(protocol::state::connected, "334 VXNlcm5hbWU6"); - } - if (boost::istarts_with(message, "mail from:")) - { - if (m_internalState != internalState::helo) - return answer(protocol::state::connected, "503 Bad sequence of commands"); - m_internalState = internalState::mail_from; - return answer(protocol::state::connected, "250 Ok"); - } - if (boost::istarts_with(message, "rcpt to:")) - { - if (m_internalState != internalState::mail_from) - return answer(protocol::state::connected, "503 Bad sequence of commands"); - m_internalState = internalState::rcpt_to; - return answer(protocol::state::connected, "250 Ok"); - } - if (boost::iequals(message, "data\r\n")) - { - if (m_internalState != internalState::rcpt_to) - return answer(protocol::state::connected, "503 Bad sequence of commands"); - m_internalState = internalState::data; - return answer(protocol::state::connected, "354 Go ahead"); - } - - return answer(protocol::state::connected, "502 Error"); -} - -protocol::result protocol::answer(protocol::state::type state, std::string answer) const -{ - if (answer.size() > 0) - std::cout << "<- " << answer << std::endl; - - return result(state, answer); -} - -void protocol::appendMessage(const std::string& message) -{ - m_message.append(message); - m_message.append("\n"); -} - diff --git a/protocol.h b/protocol.h deleted file mode 100644 index 4b2b55e..0000000 --- a/protocol.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef PROTOCOL_H -#define PROTOCOL_H - -#include -#include -#include "util.h" - - -class protocol -{ -public: - class state - { - public: - enum type - { - connected, - disconnected - }; - }; - - typedef std::pair result; - -public: - explicit protocol(const context& context); - - result openConnection(const std::string& address); - result processMessage(const std::string& message); - -private: - class internalState - { - public: - enum type - { - none, - connect, - helo, - auth_login_user, - auth_login_pass, - mail_from, - rcpt_to, - data, - mime_oundary, - image_data, - disconnect - }; - }; - -private: - result answer(state::type state, std::string answer) const; - void appendMessage(const std::string& message); - -private: - internalState::type m_internalState; - std::string m_address; - std::string m_message; - - const context& m_context; -}; - -#endif // PROTOCOL_H - diff --git a/server.cpp b/server.cpp deleted file mode 100644 index e7b4e97..0000000 --- a/server.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include - -#include "server.h" - -server::server(boost::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, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), - m_socket(ioService) -{ - m_context.path = path; - m_context.url = url; - - start_signal_wait(); - start_accept(); -} - -void server::start_signal_wait() -{ - m_signal.async_wait(boost::bind(&server::handle_signal_wait, this)); -} - -void server::handle_signal_wait() -{ - // 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) {} - - start_signal_wait(); - } -} - -void server::start_accept() -{ - m_connection.reset(new connection(m_ioService, m_context)); - m_acceptor.async_accept(m_connection->socket(), boost::bind(&server::handle_accept, this, _1)); -} - -void server::handle_accept(const boost::system::error_code& ec) -{ - if (!ec) - { - m_ioService.notify_fork(boost::asio::io_service::fork_prepare); - - if (fork() == 0) - { - m_ioService.notify_fork(boost::asio::io_service::fork_child); - m_acceptor.close(); - m_signal.cancel(); - - m_connection->start(); - } - else - { - m_ioService.notify_fork(boost::asio::io_service::fork_parent); - - m_connection.reset(); - m_socket.close(); - start_accept(); - } - } - else - { - std::cout << "Accept error: " << ec.message() << std::endl; - start_accept(); - } -} - diff --git a/server.h b/server.h deleted file mode 100644 index f448406..0000000 --- a/server.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include -#include -#include -#include -#include -#include "connection.h" -#include "util.h" - - -class server -{ -public: - server(boost::asio::io_service& ioService, unsigned short port, const std::string& path, const std::string& url); - -private: - void start_signal_wait(); - void handle_signal_wait(); - void start_accept(); - void handle_accept(const boost::system::error_code& ec); - - boost::asio::io_service& m_ioService; - boost::asio::signal_set m_signal; - boost::asio::ip::tcp::acceptor m_acceptor; - boost::asio::ip::tcp::socket m_socket; - boost::shared_ptr m_connection; - - context m_context; -}; - -#endif // SERVER_H - diff --git a/util.h b/util.h deleted file mode 100644 index eefe826..0000000 --- a/util.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#include -#include "httpClient.h" - -struct context -{ -public: - std::string path; - httpClient client; - std::string url; -}; - -std::string getTimeString(); - -#endif // UTIL_H -