Răsfoiți Sursa

Initial commit

JDierkse 8 ani în urmă
comite
37fe983738
23 a modificat fișierele cu 1319 adăugiri și 0 ștergeri
  1. 3 0
      .gitignore
  2. 51 0
      Makefile
  3. 123 0
      base64.cpp
  4. 5 0
      base64.h
  5. 59 0
      connection.cpp
  6. 37 0
      connection.h
  7. 57 0
      httpClient.cpp
  8. 59 0
      httpClient.h
  9. 120 0
      inputImage.cpp
  10. 50 0
      inputImage.h
  11. 49 0
      main.cpp
  12. 188 0
      message.cpp
  13. 57 0
      message.h
  14. 30 0
      mimeImage.cpp
  15. 23 0
      mimeImage.h
  16. 64 0
      outputImage.cpp
  17. 26 0
      outputImage.h
  18. 111 0
      protocol.cpp
  19. 63 0
      protocol.h
  20. 75 0
      server.cpp
  21. 34 0
      server.h
  22. 17 0
      util.cpp
  23. 18 0
      util.h

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+*.o
+.*.swp
+AlarmServer

+ 51 - 0
Makefile

@@ -0,0 +1,51 @@
+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
+

+ 123 - 0
base64.cpp

@@ -0,0 +1,123 @@
+/* 
+   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;
+}
+

+ 5 - 0
base64.h

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

+ 59 - 0
connection.cpp

@@ -0,0 +1,59 @@
+#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();
+}
+

+ 37 - 0
connection.h

@@ -0,0 +1,37 @@
+#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
+

+ 57 - 0
httpClient.cpp

@@ -0,0 +1,57 @@
+#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;
+}
+

+ 59 - 0
httpClient.h

@@ -0,0 +1,59 @@
+#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
+

+ 120 - 0
inputImage.cpp

@@ -0,0 +1,120 @@
+#include "inputImage.h"
+
+
+const static JOCTET EOI_BUFFER[1] = { JPEG_EOI };
+
+inputImage::inputImage()
+{
+}
+
+void inputImage::setImageData(const std::string& data)
+{
+	decompressImage(data.c_str(), data.size());
+}
+
+unsigned int inputImage::width() const
+{
+	return m_width;
+}
+
+unsigned int inputImage::height() const
+{
+	return m_height;
+}
+
+unsigned int inputImage::strideX() const
+{
+	return m_strideX;
+}
+
+unsigned int inputImage::strideY() const
+{
+	return m_strideY;
+}
+
+const std::vector<char>& inputImage::data() const
+{
+	return m_data;
+}
+
+void inputImage::init_source(j_decompress_ptr pCinfo)
+{
+}
+
+boolean inputImage::fill_input_buffer(j_decompress_ptr pCinfo)
+{
+	source_mgr_ptr pSrc = reinterpret_cast<source_mgr_ptr>(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)
+{
+	source_mgr_ptr pSrc = reinterpret_cast<source_mgr_ptr>(pCinfo->src);
+	if (pSrc->pub.bytes_in_buffer < numBytes)
+	{
+		pSrc->pub.next_input_byte = EOI_BUFFER;
+		pSrc->pub.bytes_in_buffer = 1;
+	}
+	else
+	{
+		pSrc->pub.next_input_byte += numBytes;
+		pSrc->pub.bytes_in_buffer -= numBytes;
+	}
+}
+
+void inputImage::term_source(j_decompress_ptr pCinfo)
+{
+}
+
+void inputImage::setSourceMgr(j_decompress_ptr pCinfo, const char* pData, size_t len) const
+{
+	source_mgr_ptr 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));
+
+	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->pub.resync_to_restart = jpeg_resync_to_restart;
+	pSrc->pub.term_source = inputImage::term_source;
+
+	pSrc->data = (const JOCTET *)pData;
+	pSrc->len = len;
+	pSrc->pub.bytes_in_buffer = len;
+	pSrc->pub.next_input_byte = pSrc->data;
+}
+
+void inputImage::decompressImage(const char* pData, size_t len)
+{
+	struct jpeg_decompress_struct cinfo; 
+	struct jpeg_error_mgr jerr;
+
+	jerr.trace_level = 0;
+	cinfo.err = jpeg_std_error(&jerr); 
+	jpeg_create_decompress(&cinfo); 
+	setSourceMgr(&cinfo, pData, len);
+
+	jpeg_read_header(&cinfo, TRUE);
+	jpeg_start_decompress(&cinfo);
+
+	m_width = cinfo.image_width;
+	m_height = cinfo.image_height;
+	m_strideX = cinfo.num_components;
+	m_strideY = m_width * cinfo.num_components;
+
+	m_data.resize(m_height * m_strideY);
+
+	JSAMPROW scanlines[1];
+	while (cinfo.output_scanline < cinfo.image_height)
+	{
+		scanlines[0] = reinterpret_cast<JSAMPROW>(&m_data[cinfo.output_scanline * m_strideY]);
+		jpeg_read_scanlines(&cinfo, scanlines, 1);
+	}
+
+	jpeg_finish_decompress(&cinfo);
+	jpeg_destroy_decompress(&cinfo); 
+}
+

+ 50 - 0
inputImage.h

@@ -0,0 +1,50 @@
+#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
+

+ 49 - 0
main.cpp

@@ -0,0 +1,49 @@
+#include <iostream>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "server.h"
+
+
+int main(int argc, char* argv[])
+{
+	try
+	{
+		/* error checking elided for brevity */
+		int fd = ::open("/dev/null", O_WRONLY);
+		::dup2(fd, 2);
+		::close(fd);
+
+		unsigned int port;
+		std::string path;
+		std::string url;
+
+		if (argc == 3)
+		{
+			port = std::atoi(argv[1]);
+			path = std::string(argv[2]);
+		}
+		else if (argc == 4)
+		{
+			port = std::atoi(argv[1]);
+			path = std::string(argv[2]);
+			url = std::string(argv[3]);
+		}
+		else
+		{
+			std::cout << "Usage: AlarmServer <port> <path> [url]\n";
+			return 1;
+		}
+
+		boost::asio::io_service io_service;
+
+		server s(io_service, port, path, url);
+
+		io_service.run();
+	}
+	catch (std::exception& e)
+	{
+		std::cerr << "Exception: " << e.what() << std::endl;
+	}
+}
+

+ 188 - 0
message.cpp

@@ -0,0 +1,188 @@
+#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)
+{
+	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());
+	}
+}
+
+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;	
+}
+

+ 57 - 0
message.h

@@ -0,0 +1,57 @@
+#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
+

+ 30 - 0
mimeImage.cpp

@@ -0,0 +1,30 @@
+#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;
+}
+

+ 23 - 0
mimeImage.h

@@ -0,0 +1,23 @@
+#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
+

+ 64 - 0
outputImage.cpp

@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include "outputImage.h"
+
+
+outputImage::outputImage(const std::vector<inputImage>& images) :
+	m_width(0),
+	m_height(0),
+	m_strideX(0),
+	m_strideY(0)
+{
+	if (images.size() == 0)
+		return;
+
+	m_width = images[0].width();
+	m_strideX = images[0].strideX();
+	m_strideY = images[0].strideY();
+
+	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);
+}
+
+void outputImage::save(const std::string& fileName)
+{
+	if (m_width == 0 || m_height == 0 || m_strideX == 0 || m_strideY == 0)
+                return;
+
+	struct jpeg_compress_struct cinfo;
+	struct jpeg_error_mgr jerr;
+
+	FILE* outFile = fopen(fileName.c_str(), "wb");
+
+	cinfo.err = jpeg_std_error(&jerr);
+	jpeg_create_compress(&cinfo);
+	jpeg_stdio_dest(&cinfo, outFile);
+
+	cinfo.image_width = m_width;
+	cinfo.image_height = m_height;
+	cinfo.input_components = m_strideX;
+	cinfo.in_color_space = JCS_RGB;
+
+	jpeg_set_defaults( &cinfo );
+	cinfo.num_components = 3;
+	cinfo.dct_method = JDCT_FLOAT;
+	jpeg_set_quality(&cinfo, 80, TRUE);
+
+	jpeg_start_compress( &cinfo, TRUE );
+
+	JSAMPROW scanlines[1];
+	while (cinfo.next_scanline < cinfo.image_height)
+	{
+		scanlines[0] = reinterpret_cast<JSAMPROW>(&m_imageData[cinfo.next_scanline * m_strideY]);
+		jpeg_write_scanlines(&cinfo, scanlines, 1);
+	}
+
+	jpeg_finish_compress(&cinfo);
+	jpeg_destroy_compress(&cinfo);
+	fclose(outFile);
+}
+

+ 26 - 0
outputImage.h

@@ -0,0 +1,26 @@
+#ifndef OUTPUTIMAGE_H
+#define OUTPUTIMAGE_H
+
+#include <string>
+#include <vector>
+#include "inputImage.h"
+
+
+class outputImage
+{
+public:
+	outputImage(const std::vector<inputImage>& images);
+
+	void save(const std::string& fileName);
+
+private:
+	size_t m_width;
+	size_t m_height;
+	size_t m_strideX;
+	size_t m_strideY;
+
+	std::vector<char> m_imageData;
+};
+
+#endif // OUTPUTIMAGE_H
+

+ 111 - 0
protocol.cpp

@@ -0,0 +1,111 @@
+#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"))
+		{
+			::message msg(m_address, m_context, m_message);
+			m_internalState = internalState::helo;
+			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");
+}
+

+ 63 - 0
protocol.h

@@ -0,0 +1,63 @@
+#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
+

+ 75 - 0
server.cpp

@@ -0,0 +1,75 @@
+#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();
+	}
+}
+

+ 34 - 0
server.h

@@ -0,0 +1,34 @@
+#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
+

+ 17 - 0
util.cpp

@@ -0,0 +1,17 @@
+#include <ctime>
+#include <string>
+#include "util.h"
+
+std::string getTimeString()
+{
+	time_t rawtime;
+	struct tm* timeinfo;
+	char buffer[80];
+
+	time (&rawtime);
+	timeinfo = localtime(&rawtime);
+
+	strftime(buffer, 80, "%Y%m%d-%H%M%S", timeinfo);
+	return std::string(buffer);
+}
+

+ 18 - 0
util.h

@@ -0,0 +1,18 @@
+#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
+