From 1a464d85cfc794674305d27f19634669daace8fe Mon Sep 17 00:00:00 2001 From: JDierkse Date: Fri, 1 May 2020 10:21:46 +0200 Subject: [PATCH] Move to new Build System --- .gitignore | 15 +- .gitmodules | 3 + main.cpp => Application/AlarmServer.cc | 10 +- Application/Makefile | 1 + inputImage.cpp => Image/JpegImage.cpp | 56 ++--- Image/JpegImage.h | 54 +++++ .../JpegImageCollection.cpp | 23 +- outputImage.h => Image/JpegImageCollection.h | 13 +- Image/Makefile | 1 + Image/MimeImage.cpp | 34 +++ Image/MimeImage.h | 26 +++ Libraries/Http | 1 + Libraries/Logging | 1 + Libraries/Utilities | 1 + Libraries/asio | 1 + Libraries/filesystem | 1 + MailServer/Connection.cpp | 61 +++++ MailServer/Connection.h | 40 ++++ MailServer/Makefile | 1 + MailServer/Message.cpp | 219 ++++++++++++++++++ MailServer/Message.h | 60 +++++ MailServer/Server.cpp | 81 +++++++ MailServer/Server.h | 35 +++ Makefile | 52 +---- Makefile.conf | 22 ++ Makefile.target | 13 ++ Makefiles | 1 + Protocol/Makefile | 1 + Protocol/Protocol.cpp | 113 +++++++++ Protocol/Protocol.h | 66 ++++++ Util/Base64.cpp | 136 +++++++++++ Util/Base64.h | 16 ++ Util/Makefile | 1 + util.cpp => Util/Util.cpp | 7 +- Util/Util.h | 22 ++ base64.cpp | 123 ---------- base64.h | 5 - connection.cpp | 59 ----- connection.h | 37 --- httpClient.cpp | 57 ----- httpClient.h | 59 ----- inputImage.h | 50 ---- message.cpp | 199 ---------------- message.h | 57 ----- mimeImage.cpp | 30 --- mimeImage.h | 23 -- protocol.cpp | 112 --------- protocol.h | 63 ----- server.cpp | 75 ------ server.h | 34 --- util.h | 18 -- 51 files changed, 1088 insertions(+), 1101 deletions(-) create mode 100644 .gitmodules rename main.cpp => Application/AlarmServer.cc (84%) create mode 120000 Application/Makefile rename inputImage.cpp => Image/JpegImage.cpp (53%) create mode 100644 Image/JpegImage.h rename outputImage.cpp => Image/JpegImageCollection.cpp (69%) rename outputImage.h => Image/JpegImageCollection.h (55%) create mode 120000 Image/Makefile create mode 100644 Image/MimeImage.cpp create mode 100644 Image/MimeImage.h create mode 120000 Libraries/Http create mode 120000 Libraries/Logging create mode 120000 Libraries/Utilities create mode 120000 Libraries/asio create mode 120000 Libraries/filesystem create mode 100644 MailServer/Connection.cpp create mode 100644 MailServer/Connection.h create mode 120000 MailServer/Makefile create mode 100644 MailServer/Message.cpp create mode 100644 MailServer/Message.h create mode 100644 MailServer/Server.cpp create mode 100644 MailServer/Server.h mode change 100644 => 120000 Makefile create mode 100644 Makefile.conf create mode 100644 Makefile.target create mode 160000 Makefiles create mode 120000 Protocol/Makefile create mode 100644 Protocol/Protocol.cpp create mode 100644 Protocol/Protocol.h create mode 100644 Util/Base64.cpp create mode 100644 Util/Base64.h create mode 120000 Util/Makefile rename util.cpp => Util/Util.cpp (81%) create mode 100644 Util/Util.h delete mode 100644 base64.cpp delete mode 100644 base64.h delete mode 100644 connection.cpp delete mode 100644 connection.h delete mode 100644 httpClient.cpp delete mode 100644 httpClient.h delete mode 100644 inputImage.h delete mode 100644 message.cpp delete mode 100644 message.h delete mode 100644 mimeImage.cpp delete mode 100644 mimeImage.h delete mode 100644 protocol.cpp delete mode 100644 protocol.h delete mode 100644 server.cpp delete mode 100644 server.h delete mode 100644 util.h 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 -