Prechádzať zdrojové kódy

Move to new Build System

JDierkse 5 rokov pred
rodič
commit
1a464d85cf

+ 13 - 2
.gitignore

@@ -1,3 +1,14 @@
-*.o
+*.o.*
+*.d.*
+*.a.*
 .*.swp
-AlarmServer
+.AppleDouble
+.debug
+.gdbinit*
+gdb*
+core-*
+*-core
+core.*
+*.core
+AlarmServer*
+!AlarmServer.cc

+ 3 - 0
.gitmodules

@@ -0,0 +1,3 @@
+[submodule "Makefiles"]
+	path = Makefiles
+	url = https://gogs.dierkse.nl/JDierkse/Makefiles.git

+ 5 - 5
main.cpp → Application/AlarmServer.cc

@@ -1,8 +1,9 @@
-#include <iostream>
+#include "MailServer/Server.h"
+#include <asio.hpp>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include "server.h"
+#include <iostream>
 
 
 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;
 	}
 }
-

+ 1 - 0
Application/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 30 - 26
inputImage.cpp → 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<char>& inputImage::data() const
+const std::vector<char>& 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<source_mgr_ptr>(pCinfo->src);
+	SourceMgrPtr pSrc = reinterpret_cast<SourceMgrPtr>(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<source_mgr_ptr>(pCinfo->src);
-	if (pSrc->pub.bytes_in_buffer < numBytes)
+	SourceMgrPtr pSrc = reinterpret_cast<SourceMgrPtr>(pCinfo->src);
+	if (pSrc->pub.bytes_in_buffer < static_cast<size_t>(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<j_common_ptr>(pCinfo), JPOOL_PERMANENT, sizeof(source_mgr));
+		pCinfo->src = (struct jpeg_source_mgr *)(*pCinfo->mem->alloc_small)(reinterpret_cast<j_common_ptr>(pCinfo), JPOOL_PERMANENT, sizeof(SourceMgr));
 
-	pSrc = reinterpret_cast<source_mgr_ptr>(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<SourceMgrPtr>(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
+

+ 54 - 0
Image/JpegImage.h

@@ -0,0 +1,54 @@
+#ifndef JPEGIMAGE_H
+#define JPEGIMAGE_H
+
+#include <string>
+#include <vector>
+#include <jpeglib.h>
+
+
+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<char>& 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<char> m_data;
+};
+
+} // namespace Image
+
+#endif // JPEGIMAGE_H

+ 13 - 10
outputImage.cpp → Image/JpegImageCollection.cpp

@@ -1,8 +1,10 @@
 #include <stdio.h>
-#include "outputImage.h"
+#include "JpegImageCollection.h"
 
 
-outputImage::outputImage(const std::vector<inputImage>& images) :
+namespace Image {
+
+JpegImageCollection::JpegImageCollection(const std::vector<JpegImage>& images) :
 	m_width(0),
 	m_height(0),
 	m_strideX(0),
@@ -11,23 +13,23 @@ outputImage::outputImage(const std::vector<inputImage>& 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<char>::iterator outIterator = m_imageData.begin();
-	for (std::vector<inputImage>::const_iterator it = images.begin(); it != images.end(); ++it)
-		outIterator = std::copy(it->data().begin(), it->data().end(), outIterator);
+	for (std::vector<JpegImage>::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

+ 8 - 5
outputImage.h → Image/JpegImageCollection.h

@@ -3,15 +3,17 @@
 
 #include <string>
 #include <vector>
-#include "inputImage.h"
+#include "JpegImage.h"
 
 
-class outputImage
+namespace Image {
+
+class JpegImageCollection
 {
 public:
-	outputImage(const std::vector<inputImage>& images);
+	JpegImageCollection(const std::vector<JpegImage>& 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<char> m_imageData;
 };
 
-#endif // OUTPUTIMAGE_H
+} // namespace Image
 
+#endif // OUTPUTIMAGE_H

+ 1 - 0
Image/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 34 - 0
Image/MimeImage.cpp

@@ -0,0 +1,34 @@
+#include "MimeImage.h"
+#include "Util/Base64.h"
+#include <StringAlgorithm.h>
+#include <fstream>
+
+
+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
+

+ 26 - 0
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

+ 1 - 0
Libraries/Http

@@ -0,0 +1 @@
+../../Libraries/Http

+ 1 - 0
Libraries/Logging

@@ -0,0 +1 @@
+../../Libraries/Logging

+ 1 - 0
Libraries/Utilities

@@ -0,0 +1 @@
+../../Libraries/Utilities

+ 1 - 0
Libraries/asio

@@ -0,0 +1 @@
+../../Libraries/asio

+ 1 - 0
Libraries/filesystem

@@ -0,0 +1 @@
+../../Libraries/filesystem

+ 61 - 0
MailServer/Connection.cpp

@@ -0,0 +1,61 @@
+#include "Connection.h"
+#include <functional>
+#include <sstream>
+
+
+namespace MailServer {
+
+Connection::Connection(asio::io_service& ioService, const Util::Context& context) :
+	m_ioService(ioService),
+	m_socket(ioService),
+	m_protocol(context)
+{
+}
+
+asio::ip::tcp::socket& Connection::Socket()
+{
+	return m_socket;
+}
+
+void Connection::Start()
+{
+	StartWrite(m_protocol.OpenConnection(m_socket.remote_endpoint().address().to_string()).second);
+}
+
+void Connection::StartRead()
+{
+	m_socket.async_read_some(asio::buffer(m_data), std::bind(&Connection::HandleRead, this, std::placeholders::_1, std::placeholders::_2));
+}
+
+void Connection::HandleRead(const asio::error_code& ec, std::size_t length)
+{
+	if (!ec)
+	{
+		Protocol::Protocol::Result result = m_protocol.ProcessMessage(std::string(m_data.begin(), m_data.begin() + length));
+		if (result.second.size() > 0)
+			StartWrite(result.second);
+		else
+			StartRead();
+
+		if (result.first == Protocol::Protocol::State::Disconnected)
+			m_socket.close();
+	}
+}
+
+void Connection::StartWrite(const std::string& message)
+{
+	std::copy(message.begin(), message.end(), m_data.data());
+
+	size_t length = message.size() + 1;
+	m_data[message.size()] = '\n';
+
+	asio::async_write(m_socket, asio::buffer(m_data, length), std::bind(&Connection::HandleWrite, this, std::placeholders::_1));
+}
+
+void Connection::HandleWrite(const asio::error_code& ec)
+{
+	if (!ec)
+		StartRead();
+}
+
+} // namespace MailServer

+ 40 - 0
MailServer/Connection.h

@@ -0,0 +1,40 @@
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include "Protocol/Protocol.h"
+#include "Util/Util.h"
+#include <asio.hpp>
+#include <string>
+
+
+namespace MailServer {
+
+class Connection
+{
+public:
+	Connection(asio::io_service& ioService, const Util::Context& context);
+
+	asio::ip::tcp::socket& Socket();
+
+	void Start();
+
+private:
+	Connection(const Connection&) = delete;
+	Connection& operator=(const Connection&) = delete;
+
+private:
+	void StartRead();
+	void HandleRead(const asio::error_code& ec, std::size_t length);
+	void StartWrite(const std::string& message);
+	void HandleWrite(const asio::error_code& ec);
+
+	asio::io_service& m_ioService;
+	asio::ip::tcp::socket m_socket;
+	std::array<char, 1024> m_data;
+
+	Protocol::Protocol m_protocol;
+};
+
+} // namespace MailServer
+
+#endif // CONNECTION_H

+ 1 - 0
MailServer/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 219 - 0
MailServer/Message.cpp

@@ -0,0 +1,219 @@
+#include "Message.h"
+#include "Image/JpegImageCollection.h"
+#include "Util/Base64.h"
+#include <Logging.h>
+#include <StringAlgorithm.h>
+#include <filesystem/path.h>
+#include <sstream>
+
+
+namespace MailServer {
+
+Message::Message(const std::string& address, const Util::Context& context, const std::string& message) :
+	m_address(address),
+	m_context(context),
+	m_mimeBoundary("-_-InvalidBoundary-_-"),
+	m_contentEncoding("none"),
+	m_currentImage(0)
+{
+	ProcessMessage(message);
+}
+
+std::string Decode(const std::string& data)
+{
+	std::string result;
+	if (StringAlgorithm::istarts_with(data, "=?utf-8?"))
+		result = Util::Base64::Decode(std::string(data.begin() + 10, data.begin() + data.find_last_of('?')));
+	else
+		result = Util::Base64::Decode(data);
+
+	return result;
+}
+
+void Message::ProcessMessage(const std::string& message)
+{
+	try
+	{
+		Message::State::type state = Message::State::Header;
+		std::string line;
+		std::istringstream stream(message);
+		while (std::getline(stream, line, '\r'))
+		{
+			StringAlgorithm::erase_all(line, '\n');
+			switch (state)
+			{
+			case State::Header:
+				state = ProcessHeaderLine(line);
+				break;
+			case State::Data:
+				state = ProcessDataLine(line);
+				break;
+			case State::MimeBoundary:
+				state = ProcessMimeBoundary(line);
+				break;
+			case State::Image:
+				state = ProcessImage(line);
+				break;
+			}
+		}
+
+		if (StringAlgorithm::contains(m_contentEncoding, "base64"))
+		{
+			std::string decoded = Decode(m_subject);
+			if (decoded.size() > 0)
+				m_subject = decoded;
+		}
+
+		std::stringstream ss;
+		ss << "From: " << m_sender << std::endl;
+		Logging::Log(Logging::Severity::Debug, ss.str());
+
+		ss.str("");
+		ss << "To: " << m_receiver << std::endl;
+		Logging::Log(Logging::Severity::Debug, ss.str());
+
+		ss.str("");
+		ss << "Subject: " << m_subject << std::endl;
+		Logging::Log(Logging::Severity::Debug, ss.str());
+
+		ss.str("");
+		ss << "Received " << m_images.size() << " Images" << std::endl;
+		Logging::Log(Logging::Severity::Info, ss.str());
+
+		Image::JpegImageCollection imageCollection(m_images);
+
+		filesystem::path path(m_context.path);
+		path = path.make_absolute();
+		path = path/filesystem::path(m_address);
+
+		if (!path.exists())
+			filesystem::create_directory(path);
+
+		std::string fileName(Util::getTimeString());
+		fileName.append(".jpg");
+		path = path/filesystem::path(fileName);
+
+		imageCollection.Save(path.str());
+
+		ss.str("");
+		ss << "Images Stored as " << path.str() << std::endl;
+		Logging::Log(Logging::Severity::Info, ss.str());
+
+		if (!m_context.url.empty())
+		{
+			std::stringstream url;
+			url << m_context.url << "?ip=" << m_address << "&file=" << fileName;
+			m_context.client.GetUrlContents(url.str());
+		}
+	}
+	catch (std::exception& e)
+	{
+		std::stringstream ss;
+		ss << "Message::ProcessMessage() - Error: " << e.what() << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+	}
+}
+
+Message::State::type Message::ProcessHeaderLine(const std::string& line)
+{
+	if (line.empty())
+		return Message::State::Data;
+
+	if (StringAlgorithm::istarts_with(line, "from:"))
+	{
+		m_sender = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "to:"))
+	{
+		m_receiver = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "subject:"))
+	{
+		m_subject = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "date"))
+	{
+		m_date = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "content-type:"))
+	{
+		m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "boundary") || StringAlgorithm::istarts_with(line, "\tboundary") || StringAlgorithm::istarts_with(line, "  boundary"))
+	{
+		m_mimeBoundary = std::string("--").append(std::string(line.begin() + line.find_first_of('"') + 1, line.begin() + line.find_last_of('"')));
+		return Message::State::Header;
+	}
+	if (StringAlgorithm::istarts_with(line, "content-transfer-encoding:"))
+	{
+		m_contentEncoding = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::Header;
+	}
+
+	return Message::State::Header;
+}
+
+Message::State::type Message::ProcessDataLine(const std::string& line)
+{
+	if (StringAlgorithm::istarts_with(line, m_mimeBoundary))
+	{
+		m_contentType = "";
+		return Message::State::MimeBoundary;
+	}
+
+	return Message::State::Data;
+}
+
+Message::State::type Message::ProcessMimeBoundary(const std::string& line)
+{
+	if (line.empty())
+	{
+		if (StringAlgorithm::istarts_with(m_contentType, "image/jpeg") || StringAlgorithm::istarts_with(m_contentType, "image/jpg"))
+		{
+			m_mimeImages.push_back(Image::MimeImage(m_imageName));
+			m_imageName = "";
+			return Message::State::Image;
+		}
+		return Message::State::Data;
+	}
+	if (StringAlgorithm::istarts_with(line, "content-type:"))
+	{
+		m_contentType = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::MimeBoundary;
+	}
+	if (StringAlgorithm::istarts_with(line, "name") || StringAlgorithm::istarts_with(line, "\tname"))
+	{
+		m_imageName = "image.jpg";
+		//m_imageName = Decode(std::string(line.begin() + line.find_first_of('"') + 1, line.begin() + line.find_last_of('"')));
+		return Message::State::MimeBoundary;
+	}
+	if (StringAlgorithm::istarts_with(line, "content-transfer-encoding:"))
+	{
+		m_contentEncoding = std::string(line.begin() + line.find_first_of(' ') + 1, line.end());
+		return Message::State::MimeBoundary;
+	}
+
+	return Message::State::MimeBoundary;
+}
+
+Message::State::type Message::ProcessImage(const std::string& line)
+{
+	if (StringAlgorithm::istarts_with(line, m_mimeBoundary))
+	{
+		m_images.push_back(m_mimeImages[m_currentImage].DecodeImageData());
+		++m_currentImage;
+		m_contentType = "";
+		return Message::State::MimeBoundary;
+	}
+
+	m_mimeImages[m_currentImage].AppendData(line);
+
+	return Message::State::Image;
+}
+
+} // namespace MailServer

+ 60 - 0
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 <string>
+#include <vector>
+
+
+namespace MailServer {
+
+class Message
+{
+public:
+	Message(const std::string& address, const Util::Context& context, const std::string& message);
+
+private:
+	class State
+	{
+	public:
+		enum type
+		{
+			Header,
+			Data,
+			MimeBoundary,
+			Image
+		};
+	};
+
+private:
+	void ProcessMessage(const std::string& message);
+	State::type ProcessHeaderLine(const std::string& line);
+	State::type ProcessDataLine(const std::string& line);
+	State::type ProcessMimeBoundary(const std::string& line);
+	State::type ProcessImage(const std::string& line);
+
+private:
+	std::string m_address;
+	const Util::Context& m_context;
+
+	std::string m_sender;
+	std::string m_receiver;
+	std::string m_subject;
+	std::string m_date;
+	std::string m_mimeBoundary;
+
+	std::string m_contentType;
+	std::string m_contentEncoding;
+
+	size_t m_currentImage;
+	std::string m_imageName;
+
+	std::vector<Image::MimeImage> m_mimeImages;
+	std::vector<Image::JpegImage> m_images;
+};
+
+} // namespace MailServer
+
+#endif // MESSAGE_H

+ 81 - 0
MailServer/Server.cpp

@@ -0,0 +1,81 @@
+#include "Server.h"
+#include <Logging.h>
+#include <functional>
+#include <sstream>
+#include <sys/wait.h>
+
+
+namespace MailServer {
+
+Server::Server(asio::io_service& ioService, unsigned short port, const std::string& path, const std::string& url) :
+	m_ioService(ioService),
+	m_signal(ioService, SIGCHLD),
+	m_acceptor(ioService, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)),
+	m_socket(ioService)
+{
+	m_context.path = path;
+	m_context.url = url;
+
+	StartSignalWait();
+	StartAccept();
+}
+
+void Server::StartSignalWait()
+{
+	m_signal.async_wait(std::bind(&Server::HandleSignalWait, this));
+}
+
+void Server::HandleSignalWait()
+{
+	// Only the parent process should check for this signal. We can determine
+	// whether we are in the parent by checking if the acceptor is still open.
+	if (m_acceptor.is_open())
+	{
+		// Reap completed child processes so that we don't end up with zombies.
+		int status = 0;
+		while (waitpid(-1, &status, WNOHANG) > 0) {}
+
+		StartSignalWait();
+	}
+}
+
+void Server::StartAccept()
+{
+	m_connection.reset(new Connection(m_ioService, m_context));
+	m_acceptor.async_accept(m_connection->Socket(), std::bind(&Server::HandleAccept, this, std::placeholders::_1));
+}
+
+void Server::HandleAccept(const asio::error_code& ec)
+{
+	if (!ec)
+	{
+		m_ioService.notify_fork(asio::io_service::fork_prepare);
+
+		if (fork() == 0)
+		{
+			m_ioService.notify_fork(asio::io_service::fork_child);
+			m_acceptor.close();
+			m_signal.cancel();
+
+			m_connection->Start();
+		}
+		else
+		{
+			m_ioService.notify_fork(asio::io_service::fork_parent);
+
+			m_connection.reset();
+			m_socket.close();
+			StartAccept();
+		}
+	}
+	else
+	{
+		std::stringstream ss;
+		ss << "Server::HandleAccept() - Error" << std::endl;
+		Logging::Log(Logging::Severity::Error, ss.str());
+
+		StartAccept();
+	}
+}
+
+} // namespace MailServer

+ 35 - 0
MailServer/Server.h

@@ -0,0 +1,35 @@
+#ifndef SERVER_H
+#define SERVER_H
+
+#include "Connection.h"
+#include "Util/Util.h"
+#include <asio.hpp>
+#include <memory>
+#include <string>
+
+
+namespace MailServer {
+
+class Server
+{
+public:
+	Server(asio::io_service& ioService, unsigned short port, const std::string& path, const std::string& url);
+
+private:
+	void StartSignalWait();
+	void HandleSignalWait();
+	void StartAccept();
+	void HandleAccept(const asio::error_code& ec);
+
+	asio::io_service& m_ioService;
+	asio::signal_set m_signal;
+	asio::ip::tcp::acceptor m_acceptor;
+	asio::ip::tcp::socket m_socket;
+	std::shared_ptr<Connection> m_connection;
+
+	Util::Context m_context;
+};
+
+} // namespace MailServer
+
+#endif // SERVER_H

+ 0 - 51
Makefile

@@ -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
-

+ 1 - 0
Makefile

@@ -0,0 +1 @@
+Makefiles/Makefile

+ 22 - 0
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

+ 13 - 0
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

+ 1 - 0
Makefiles

@@ -0,0 +1 @@
+Subproject commit 5e113555ebd4e049b287b3c94c4ef33e151ea4e6

+ 1 - 0
Protocol/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 113 - 0
Protocol/Protocol.cpp

@@ -0,0 +1,113 @@
+#include "Protocol.h"
+#include "MailServer/Message.h"
+#include <Logging.h>
+#include <StringAlgorithm.h>
+#include <sstream>
+
+
+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

+ 66 - 0
Protocol/Protocol.h

@@ -0,0 +1,66 @@
+#ifndef PROTOCOL_H
+#define PROTOCOL_H
+
+#include "Util/Util.h"
+#include <string>
+#include <utility>
+
+
+namespace Protocol {
+
+class Protocol
+{
+public:
+	class State
+	{
+	public:
+		enum type
+		{
+			Connected,
+			Disconnected
+		};
+	};
+
+	typedef std::pair<State::type, std::string> 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

+ 136 - 0
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
+

+ 16 - 0
Util/Base64.h

@@ -0,0 +1,16 @@
+#ifndef UTIL_BASE64_H
+#define UTIL_BASE64_H
+
+#include <string>
+
+
+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

+ 1 - 0
Util/Makefile

@@ -0,0 +1 @@
+../Makefile

+ 6 - 1
util.cpp → Util/Util.cpp

@@ -1,6 +1,9 @@
+#include "Util.h"
 #include <ctime>
 #include <string>
-#include "util.h"
+
+
+namespace Util {
 
 std::string getTimeString()
 {
@@ -15,3 +18,5 @@ std::string getTimeString()
 	return std::string(buffer);
 }
 
+} // namespace Util
+

+ 22 - 0
Util/Util.h

@@ -0,0 +1,22 @@
+#ifndef UTIL_UTIL_H
+#define UTIL_UTIL_H
+
+#include <HttpClient.h>
+#include <string>
+
+
+namespace Util {
+
+struct Context
+{
+public:
+	std::string path;
+	Http::HttpClient client;
+	std::string url;
+};
+
+std::string getTimeString();
+
+} // namespace Util
+
+#endif // UTIL_UTIL_H

+ 0 - 123
base64.cpp

@@ -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;
-}
-

+ 0 - 5
base64.h

@@ -1,5 +0,0 @@
-#include <string>
-
-std::string base64_encode(unsigned char const* , unsigned int len);
-std::string base64_decode(std::string const& s);
-

+ 0 - 59
connection.cpp

@@ -1,59 +0,0 @@
-#include <boost/asio/write.hpp>
-#include <boost/bind.hpp>
-
-#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();
-}
-

+ 0 - 37
connection.h

@@ -1,37 +0,0 @@
-#ifndef CONNECTION_H
-#define CONNECTION_H
-
-#include <string>
-#include <boost/noncopyable.hpp>
-#include <boost/asio/io_service.hpp>
-#include <boost/asio/ip/tcp.hpp>
-#include <boost/asio/signal_set.hpp>
-#include <boost/array.hpp>
-#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<char, 1024> m_data;
-
-	protocol m_protocol;
-};
-
-#endif // CONNECTION_H
-

+ 0 - 57
httpClient.cpp

@@ -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;
-}
-

+ 0 - 59
httpClient.h

@@ -1,59 +0,0 @@
-#ifndef HTTPCLIENT_H
-#define HTTPCLIENT_H
-
-#include <string>
-#include <vector>
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/thread.hpp>
-#include <openssl/crypto.h>
-#include <curl/curl.h>
-
-
-static std::vector<boost::shared_ptr<boost::mutex> > 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<boost::mutex>(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<std::string> m_userAgents;
-};
-
-#endif // HTTPCLIENT_H
-

+ 0 - 50
inputImage.h

@@ -1,50 +0,0 @@
-#ifndef INPUTIMAGE_H
-#define INPUTIMAGE_H
-
-#include <string>
-#include <vector>
-#include <jpeglib.h>
-
-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<char>& 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<char> m_data;
-};
-
-#endif // INPUTIMAGE_H
-

+ 0 - 199
message.cpp

@@ -1,199 +0,0 @@
-#include <sstream>
-#include <boost/lexical_cast.hpp>
-#include <boost/algorithm/string/predicate.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem.hpp>
-#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;	
-}
-

+ 0 - 57
message.h

@@ -1,57 +0,0 @@
-#ifndef MESSAGE_H
-#define MESSAGE_H
-
-#include <string>
-#include <vector>
-#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<mimeImage> m_mimeImages;
-	std::vector<inputImage> m_images;
-};
-
-#endif // MESSAGE_H
-

+ 0 - 30
mimeImage.cpp

@@ -1,30 +0,0 @@
-#include <fstream>
-#include <boost/algorithm/string.hpp>
-#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;
-}
-

+ 0 - 23
mimeImage.h

@@ -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
-

+ 0 - 112
protocol.cpp

@@ -1,112 +0,0 @@
-#include <iostream>
-#include <boost/algorithm/string/predicate.hpp>
-#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");
-}
-

+ 0 - 63
protocol.h

@@ -1,63 +0,0 @@
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
-
-#include <string>
-#include <utility>
-#include "util.h"
-
-
-class protocol
-{
-public:
-	class state
-	{
-	public:
-		enum type
-		{
-			connected,
-			disconnected
-		};
-	};
-
-	typedef std::pair<state::type, std::string> 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
-

+ 0 - 75
server.cpp

@@ -1,75 +0,0 @@
-#include <boost/bind.hpp>
-#include <boost/make_shared.hpp>
-#include <iostream>
-#include <sys/wait.h>
-
-#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();
-	}
-}
-

+ 0 - 34
server.h

@@ -1,34 +0,0 @@
-#ifndef SERVER_H
-#define SERVER_H
-
-#include <string>
-#include <boost/asio/io_service.hpp>
-#include <boost/asio/ip/tcp.hpp>
-#include <boost/asio/signal_set.hpp>
-#include <boost/shared_ptr.hpp>
-#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<connection> m_connection;
-
-	context m_context;
-};
-
-#endif // SERVER_H
-

+ 0 - 18
util.h

@@ -1,18 +0,0 @@
-#ifndef UTIL_H
-#define UTIL_H
-
-#include <string>
-#include "httpClient.h"
-
-struct context
-{
-public:
-	std::string path;
-	httpClient client;
-	std::string url;
-};
-
-std::string getTimeString();
-
-#endif // UTIL_H
-