aboutsummaryrefslogtreecommitdiff
path: root/contrib/epee/include/net
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--contrib/epee/include/net/abstract_tcp_server.h316
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.h276
-rw-r--r--contrib/epee/include/net/abstract_tcp_server2.inl811
-rw-r--r--contrib/epee/include/net/abstract_tcp_server_cp.h233
-rw-r--r--contrib/epee/include/net/abstract_tcp_server_cp.inl605
-rw-r--r--contrib/epee/include/net/http_base.h184
-rw-r--r--contrib/epee/include/net/http_client.h875
-rw-r--r--contrib/epee/include/net/http_client_abstract_invoke.h98
-rw-r--r--contrib/epee/include/net/http_client_base.h73
-rw-r--r--contrib/epee/include/net/http_client_via_api_helper.h177
-rw-r--r--contrib/epee/include/net/http_protocol_handler.h209
-rw-r--r--contrib/epee/include/net/http_protocol_handler.inl664
-rw-r--r--contrib/epee/include/net/http_server_cp.h48
-rw-r--r--contrib/epee/include/net/http_server_cp2.h47
-rw-r--r--contrib/epee/include/net/http_server_handlers_map2.h260
-rw-r--r--contrib/epee/include/net/http_server_impl_base.h112
-rw-r--r--contrib/epee/include/net/http_server_thread_per_connect.h48
-rw-r--r--contrib/epee/include/net/levin_base.h125
-rw-r--r--contrib/epee/include/net/levin_client.h89
-rw-r--r--contrib/epee/include/net/levin_client.inl194
-rw-r--r--contrib/epee/include/net/levin_client_async.h577
-rw-r--r--contrib/epee/include/net/levin_client_async.inl0
-rw-r--r--contrib/epee/include/net/levin_helper.h137
-rw-r--r--contrib/epee/include/net/levin_protocol_handler.h178
-rw-r--r--contrib/epee/include/net/levin_protocol_handler_async.h778
-rw-r--r--contrib/epee/include/net/levin_server_cp.h47
-rw-r--r--contrib/epee/include/net/levin_server_cp2.h49
-rw-r--r--contrib/epee/include/net/local_ip.h72
-rw-r--r--contrib/epee/include/net/multiprotocols_server.h47
-rw-r--r--contrib/epee/include/net/munin_connection_handler.h376
-rw-r--r--contrib/epee/include/net/munin_node_server.h49
-rw-r--r--contrib/epee/include/net/net_helper.h683
-rw-r--r--contrib/epee/include/net/net_parse_helpers.h168
-rw-r--r--contrib/epee/include/net/net_utils_base.h150
-rw-r--r--contrib/epee/include/net/protocol_switcher.h121
-rw-r--r--contrib/epee/include/net/rpc_method_name.h31
-rw-r--r--contrib/epee/include/net/smtp.h181
-rw-r--r--contrib/epee/include/net/smtp.inl1569
-rw-r--r--contrib/epee/include/net/smtp_helper.h88
39 files changed, 10745 insertions, 0 deletions
diff --git a/contrib/epee/include/net/abstract_tcp_server.h b/contrib/epee/include/net/abstract_tcp_server.h
new file mode 100644
index 000000000..c74444c8e
--- /dev/null
+++ b/contrib/epee/include/net/abstract_tcp_server.h
@@ -0,0 +1,316 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _ABSTRACT_TCP_SERVER_H_
+#define _ABSTRACT_TCP_SERVER_H_
+
+#include <process.h>
+#include <list>
+#include <winsock2.h>
+#include "winobj.h"
+//#include "threads_helper.h"
+#include "net_utils_base.h"
+
+#pragma comment(lib, "Ws2_32.lib")
+
+namespace epee
+{
+namespace net_utils
+{
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class soket_sender: public i_service_endpoint
+ {
+ public:
+ soket_sender(SOCKET sock):m_sock(sock){}
+ private:
+ virtual bool handle_send(const void* ptr, size_t cb)
+ {
+ if(cb != send(m_sock, (char*)ptr, (int)cb, 0))
+ {
+ int sock_err = WSAGetLastError();
+ LOG_ERROR("soket_sender: Failed to send " << cb << " bytes, Error=" << sock_err);
+ return false;
+ }
+ return true;
+
+ }
+
+ SOCKET m_sock;
+ };
+
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ template<class THandler>
+ class abstract_tcp_server
+ {
+ public:
+ abstract_tcp_server();
+
+ bool init_server(int port_no);
+ bool deinit_server();
+ bool run_server();
+ bool send_stop_signal();
+
+ typename THandler::config_type& get_config_object(){return m_config;}
+
+ private:
+ bool invoke_connection(SOCKET hnew_sock, long ip_from, int post_from);
+ static unsigned __stdcall ConnectionHandlerProc(void* lpParameter);
+
+ class thread_context;
+ typedef std::list<thread_context> connections_container;
+ typedef typename connections_container::iterator connections_iterator;
+
+ struct thread_context
+ {
+ HANDLE m_htread;
+ SOCKET m_socket;
+ abstract_tcp_server* powner;
+ connection_context m_context;
+ typename connections_iterator m_self_it;
+ };
+
+ SOCKET m_listen_socket;
+ int m_port;
+ bool m_initialized;
+ volatile LONG m_stop_server;
+ volatile LONG m_threads_count;
+ typename THandler::config_type m_config;
+ connections_container m_connections;
+ critical_section m_connections_lock;
+ };
+
+ template<class THandler>
+ unsigned __stdcall abstract_tcp_server<THandler>::ConnectionHandlerProc(void* lpParameter)
+ {
+
+ thread_context* pthread_context = (thread_context*)lpParameter;
+ if(!pthread_context)
+ return 0;
+ abstract_tcp_server<THandler>* pthis = pthread_context->powner;
+
+ ::InterlockedIncrement(&pthis->m_threads_count);
+
+ ::CoInitialize(NULL);
+
+
+ LOG_PRINT("Handler thread STARTED with socket=" << pthread_context->m_socket, LOG_LEVEL_2);
+ int res = 0;
+
+ soket_sender sndr(pthread_context->m_socket);
+ THandler srv(&sndr, pthread_context->powner->m_config, pthread_context->m_context);
+
+
+ srv.after_init_connection();
+
+ char buff[1000] = {0};
+ std::string ansver;
+ while ( (res = recv(pthread_context->m_socket, (char*)buff, 1000, 0)) > 0)
+ {
+ LOG_PRINT("Data in, " << res << " bytes", LOG_LEVEL_3);
+ if(!srv.handle_recv(buff, res))
+ break;
+ }
+ shutdown(pthread_context->m_socket, SD_BOTH);
+ closesocket(pthread_context->m_socket);
+
+ abstract_tcp_server* powner = pthread_context->powner;
+ LOG_PRINT("Handler thread with socket=" << pthread_context->m_socket << " STOPPED", LOG_LEVEL_2);
+ powner->m_connections_lock.lock();
+ ::CloseHandle(pthread_context->m_htread);
+ pthread_context->powner->m_connections.erase(pthread_context->m_self_it);
+ powner->m_connections_lock.unlock();
+ CoUninitialize();
+ ::InterlockedDecrement(&pthis->m_threads_count);
+ return 1;
+ }
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ abstract_tcp_server<THandler>::abstract_tcp_server():m_listen_socket(INVALID_SOCKET),
+ m_initialized(false),
+ m_stop_server(0), m_port(0), m_threads_count(0)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ bool abstract_tcp_server<THandler>::init_server(int port_no)
+ {
+ m_port = port_no;
+ WSADATA wsad = {0};
+ int err = ::WSAStartup(MAKEWORD(2,2), &wsad);
+ if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 )
+ {
+ LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+ m_initialized = true;
+
+ m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
+ if(INVALID_SOCKET == m_listen_socket)
+ {
+ err = ::WSAGetLastError();
+ LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+ int opt = 1;
+ setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int));
+
+ sockaddr_in adr = {0};
+ adr.sin_family = AF_INET;
+ adr.sin_addr.s_addr = htonl(INADDR_ANY);
+ adr.sin_port = (u_short)htons(port_no);
+
+ err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr ));
+ if(SOCKET_ERROR == err )
+ {
+ err = ::WSAGetLastError();
+ LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
+ deinit_server();
+ return false;
+ }
+
+ ::InterlockedExchange(&m_stop_server, 0);
+
+ return true;
+ }
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ bool abstract_tcp_server<THandler>::deinit_server()
+ {
+
+ if(!m_initialized)
+ return true;
+
+ if(INVALID_SOCKET != m_listen_socket)
+ {
+ shutdown(m_listen_socket, SD_BOTH);
+ int res = closesocket(m_listen_socket);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ }
+ m_listen_socket = INVALID_SOCKET;
+ }
+
+ int res = ::WSACleanup();
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ }
+ m_initialized = false;
+
+ return true;
+ }
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ bool abstract_tcp_server<THandler>::send_stop_signal()
+ {
+ InterlockedExchange(&m_stop_server, 1);
+ return true;
+ }
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ bool abstract_tcp_server<THandler>::run_server()
+ {
+ int err = listen(m_listen_socket, 10000);
+ if(SOCKET_ERROR == err )
+ {
+ err = ::WSAGetLastError();
+ LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+ LOG_PRINT("Listening port "<< m_port << "...." , LOG_LEVEL_2);
+
+ while(!m_stop_server)
+ {
+ sockaddr_in adr_from = {0};
+ int adr_len = sizeof(adr_from);
+ fd_set read_fs = {0};
+ read_fs.fd_count = 1;
+ read_fs.fd_array[0] = m_listen_socket;
+ TIMEVAL tv = {0};
+ tv.tv_usec = 100;
+ int select_res = select(0, &read_fs, NULL, NULL, &tv);
+ if(!select_res)
+ continue;
+ SOCKET new_sock = WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, NULL, NULL);
+ LOG_PRINT("Accepted connection on socket=" << new_sock, LOG_LEVEL_2);
+ invoke_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port);
+ }
+
+ deinit_server();
+
+#define ABSTR_TCP_SRV_WAIT_COUNT_MAX 5000
+#define ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL 1000
+
+ int wait_count = 0;
+
+ while(m_threads_count && wait_count*1000 < ABSTR_TCP_SRV_WAIT_COUNT_MAX)
+ {
+ ::Sleep(ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL);
+ wait_count++;
+ }
+ LOG_PRINT("abstract_tcp_server exit with wait count=" << wait_count*ABSTR_TCP_SRV_WAIT_COUNT_INTERVAL << "(max=" << ABSTR_TCP_SRV_WAIT_COUNT_MAX <<")", LOG_LEVEL_0);
+
+ return true;
+ }
+ //----------------------------------------------------------------------------------------
+ template<class THandler>
+ bool abstract_tcp_server<THandler>::invoke_connection(SOCKET hnew_sock, long ip_from, int post_from)
+ {
+ m_connections_lock.lock();
+ m_connections.push_back(thread_context());
+ m_connections_lock.unlock();
+ m_connections.back().m_socket = hnew_sock;
+ m_connections.back().powner = this;
+ m_connections.back().m_self_it = --m_connections.end();
+ m_connections.back().m_context.m_remote_ip = ip_from;
+ m_connections.back().m_context.m_remote_port = post_from;
+ m_connections.back().m_htread = threads_helper::create_thread(ConnectionHandlerProc, &m_connections.back());
+
+ return true;
+ }
+ //----------------------------------------------------------------------------------------
+
+ //----------------------------------------------------------------------------------------
+ //----------------------------------------------------------------------------------------
+}
+}
+#endif //_ABSTRACT_TCP_SERVER_H_
diff --git a/contrib/epee/include/net/abstract_tcp_server2.h b/contrib/epee/include/net/abstract_tcp_server2.h
new file mode 100644
index 000000000..d49b8f864
--- /dev/null
+++ b/contrib/epee/include/net/abstract_tcp_server2.h
@@ -0,0 +1,276 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _ABSTRACT_TCP_SERVER2_H_
+#define _ABSTRACT_TCP_SERVER2_H_
+
+
+#include <boost/asio.hpp>
+#include <string>
+#include <vector>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <atomic>
+
+#include <boost/asio.hpp>
+#include <boost/array.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include <boost/thread/thread.hpp>
+#include "net_utils_base.h"
+#include "syncobj.h"
+
+
+#define ABSTRACT_SERVER_SEND_QUE_MAX_COUNT 100
+
+namespace epee
+{
+namespace net_utils
+{
+
+ struct i_connection_filter
+ {
+ virtual bool is_remote_ip_allowed(boost::uint32_t adress)=0;
+ protected:
+ virtual ~i_connection_filter(){}
+ };
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ /// Represents a single connection from a client.
+ template<class t_protocol_handler>
+ class connection
+ : public boost::enable_shared_from_this<connection<t_protocol_handler> >,
+ private boost::noncopyable,
+ public i_service_endpoint
+ {
+ public:
+ typedef typename t_protocol_handler::connection_context t_connection_context;
+ /// Construct a connection with the given io_service.
+ explicit connection(boost::asio::io_service& io_service,
+ typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter * &pfilter);
+
+ virtual ~connection();
+ /// Get the socket associated with the connection.
+ boost::asio::ip::tcp::socket& socket();
+
+ /// Start the first asynchronous operation for the connection.
+ bool start(bool is_income, bool is_multithreaded);
+
+ void get_context(t_connection_context& context_){context_ = context;}
+
+ void call_back_starter();
+ private:
+ //----------------- i_service_endpoint ---------------------
+ virtual bool do_send(const void* ptr, size_t cb);
+ virtual bool close();
+ virtual bool call_run_once_service_io();
+ virtual bool request_callback();
+ virtual boost::asio::io_service& get_io_service();
+ virtual bool add_ref();
+ virtual bool release();
+ //------------------------------------------------------
+ boost::shared_ptr<connection<t_protocol_handler> > safe_shared_from_this();
+ bool shutdown();
+ /// Handle completion of a read operation.
+ void handle_read(const boost::system::error_code& e,
+ std::size_t bytes_transferred);
+
+ /// Handle completion of a write operation.
+ void handle_write(const boost::system::error_code& e, size_t cb);
+
+ /// Strand to ensure the connection's handlers are not called concurrently.
+ boost::asio::io_service::strand strand_;
+
+ /// Socket for the connection.
+ boost::asio::ip::tcp::socket socket_;
+
+ /// Buffer for incoming data.
+ boost::array<char, 8192> buffer_;
+
+ t_connection_context context;
+ volatile boost::uint32_t m_want_close_connection;
+ std::atomic<bool> m_was_shutdown;
+ critical_section m_send_que_lock;
+ std::list<std::string> m_send_que;
+ volatile boost::uint32_t& m_ref_sockets_count;
+ i_connection_filter* &m_pfilter;
+ volatile bool m_is_multithreaded;
+
+ //this should be the last one, because it could be wait on destructor, while other activities possible on other threads
+ t_protocol_handler m_protocol_handler;
+ //typename t_protocol_handler::config_type m_dummy_config;
+ std::list<boost::shared_ptr<connection<t_protocol_handler> > > m_self_refs; // add_ref/release support
+ critical_section m_self_refs_lock;
+ };
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ template<class t_protocol_handler>
+ class boosted_tcp_server
+ : private boost::noncopyable
+ {
+ public:
+ typedef boost::shared_ptr<connection<t_protocol_handler> > connection_ptr;
+ typedef typename t_protocol_handler::connection_context t_connection_context;
+ /// Construct the server to listen on the specified TCP address and port, and
+ /// serve up files from the given directory.
+ boosted_tcp_server();
+ explicit boosted_tcp_server(boost::asio::io_service& external_io_service);
+ ~boosted_tcp_server();
+
+ bool init_server(uint32_t port, const std::string address = "0.0.0.0");
+ bool init_server(const std::string port, const std::string& address = "0.0.0.0");
+
+ /// Run the server's io_service loop.
+ bool run_server(size_t threads_count, bool wait = true);
+
+ /// wait for service workers stop
+ bool timed_wait_server_stop(boost::uint64_t wait_mseconds);
+
+ /// Stop the server.
+ void send_stop_signal();
+
+ bool is_stop_signal_sent();
+
+ void set_threads_prefix(const std::string& prefix_name);
+
+ bool deinit_server(){return true;}
+
+ size_t get_threads_count(){return m_threads_count;}
+
+ void set_connection_filter(i_connection_filter* pfilter);
+
+ bool connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_connection_context& cn, const std::string& bind_ip = "0.0.0.0");
+ template<class t_callback>
+ bool connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeot, t_callback cb, const std::string& bind_ip = "0.0.0.0");
+
+ typename t_protocol_handler::config_type& get_config_object(){return m_config;}
+
+ int get_binded_port(){return m_port;}
+
+ boost::asio::io_service& get_io_service(){return io_service_;}
+
+ struct idle_callback_conext_base
+ {
+ virtual ~idle_callback_conext_base(){}
+
+ virtual bool call_handler(){return true;}
+
+ idle_callback_conext_base(boost::asio::io_service& io_serice):
+ m_timer(io_serice)
+ {}
+ boost::asio::deadline_timer m_timer;
+ boost::uint64_t m_period;
+ };
+
+ template <class t_handler>
+ struct idle_callback_conext: public idle_callback_conext_base
+ {
+ idle_callback_conext(boost::asio::io_service& io_serice, t_handler& h, boost::uint64_t period):
+ idle_callback_conext_base(io_serice),
+ m_handler(h)
+ {this->m_period = period;}
+
+ t_handler m_handler;
+ virtual bool call_handler()
+ {
+ return m_handler();
+ }
+ };
+
+ template<class t_handler>
+ bool add_idle_handler(t_handler t_callback, boost::uint64_t timeout_ms)
+ {
+ boost::shared_ptr<idle_callback_conext_base> ptr(new idle_callback_conext<t_handler>(io_service_, t_callback, timeout_ms));
+ //needed call handler here ?...
+ ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
+ ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr));
+ return true;
+ }
+
+ bool global_timer_handler(/*const boost::system::error_code& err, */boost::shared_ptr<idle_callback_conext_base> ptr)
+ {
+ //if handler return false - he don't want to be called anymore
+ if(!ptr->call_handler())
+ return true;
+ ptr->m_timer.expires_from_now(boost::posix_time::milliseconds(ptr->m_period));
+ ptr->m_timer.async_wait(boost::bind(&boosted_tcp_server<t_protocol_handler>::global_timer_handler, this, ptr));
+ return true;
+ }
+
+ template<class t_handler>
+ bool async_call(t_handler t_callback)
+ {
+ io_service_.post(t_callback);
+ return true;
+ }
+
+ protected:
+ typename t_protocol_handler::config_type m_config;
+
+ private:
+ /// Run the server's io_service loop.
+ bool worker_thread();
+ /// Handle completion of an asynchronous accept operation.
+ void handle_accept(const boost::system::error_code& e);
+
+ bool is_thread_worker();
+
+ /// The io_service used to perform asynchronous operations.
+ std::unique_ptr<boost::asio::io_service> m_io_service_local_instance;
+ boost::asio::io_service& io_service_;
+
+ /// Acceptor used to listen for incoming connections.
+ boost::asio::ip::tcp::acceptor acceptor_;
+
+ /// The next connection to be accepted.
+ connection_ptr new_connection_;
+ std::atomic<bool> m_stop_signal_sent;
+ uint32_t m_port;
+ volatile boost::uint32_t m_sockets_count;
+ std::string m_address;
+ std::string m_thread_name_prefix;
+ size_t m_threads_count;
+ i_connection_filter* m_pfilter;
+ std::vector<boost::shared_ptr<boost::thread> > m_threads;
+ boost::thread::id m_main_thread_id;
+ critical_section m_threads_lock;
+ volatile uint32_t m_thread_index;
+ };
+}
+}
+
+#include "abstract_tcp_server2.inl"
+
+#endif \ No newline at end of file
diff --git a/contrib/epee/include/net/abstract_tcp_server2.inl b/contrib/epee/include/net/abstract_tcp_server2.inl
new file mode 100644
index 000000000..1d6a1662f
--- /dev/null
+++ b/contrib/epee/include/net/abstract_tcp_server2.inl
@@ -0,0 +1,811 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#include "net_utils_base.h"
+#include <boost/lambda/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/uuid/random_generator.hpp>
+#include <boost/chrono.hpp>
+#include <boost/utility/value_init.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include "misc_language.h"
+#include "pragma_comp_defs.h"
+
+PRAGMA_WARNING_PUSH
+namespace epee
+{
+namespace net_utils
+{
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+PRAGMA_WARNING_DISABLE_VS(4355)
+
+ template<class t_protocol_handler>
+ connection<t_protocol_handler>::connection(boost::asio::io_service& io_service,
+ typename t_protocol_handler::config_type& config, volatile boost::uint32_t& sock_count, i_connection_filter* &pfilter)
+ : strand_(io_service),
+ socket_(io_service),
+ //context(typename boost::value_initialized<t_connection_context>())
+ m_protocol_handler(this, config, context),
+ m_want_close_connection(0),
+ m_was_shutdown(0),
+ m_ref_sockets_count(sock_count),
+ m_pfilter(pfilter)
+ {
+ boost::interprocess::ipcdetail::atomic_inc32(&m_ref_sockets_count);
+ }
+PRAGMA_WARNING_DISABLE_VS(4355)
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ connection<t_protocol_handler>::~connection()
+ {
+ if(!m_was_shutdown)
+ {
+ LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed without shutdown.");
+ shutdown();
+ }
+
+ LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Socket destroyed");
+ boost::interprocess::ipcdetail::atomic_dec32(&m_ref_sockets_count);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ boost::asio::ip::tcp::socket& connection<t_protocol_handler>::socket()
+ {
+ return socket_;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ boost::shared_ptr<connection<t_protocol_handler> > connection<t_protocol_handler>::safe_shared_from_this()
+ {
+ try
+ {
+ return connection<t_protocol_handler>::shared_from_this();
+ }
+ catch (const boost::bad_weak_ptr&)
+ {
+ // It happens when the connection is being deleted
+ return boost::shared_ptr<connection<t_protocol_handler> >();
+ }
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::start(bool is_income, bool is_multithreaded)
+ {
+ TRY_ENTRY();
+
+ // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
+ auto self = safe_shared_from_this();
+ if(!self)
+ return false;
+
+ m_is_multithreaded = is_multithreaded;
+
+ boost::system::error_code ec;
+ auto remote_ep = socket_.remote_endpoint(ec);
+ CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get remote endpoint: " << ec.message() << ':' << ec.value());
+
+ auto local_ep = socket_.local_endpoint(ec);
+ CHECK_AND_NO_ASSERT_MES(!ec, false, "Failed to get local endpoint: " << ec.message() << ':' << ec.value());
+
+ context = boost::value_initialized<t_connection_context>();
+ long ip_ = boost::asio::detail::socket_ops::host_to_network_long(remote_ep.address().to_v4().to_ulong());
+
+ context.set_details(boost::uuids::random_generator()(), ip_, remote_ep.port(), is_income);
+ LOG_PRINT_L3("[sock " << socket_.native_handle() << "] new connection from " << print_connection_context_short(context) <<
+ " to " << local_ep.address().to_string() << ':' << local_ep.port() <<
+ ", total sockets objects " << m_ref_sockets_count);
+
+ if(m_pfilter && !m_pfilter->is_remote_ip_allowed(context.m_remote_ip))
+ {
+ LOG_PRINT_L2("[sock " << socket_.native_handle() << "] ip denied " << string_tools::get_ip_string_from_int32(context.m_remote_ip) << ", shutdowning connection");
+ close();
+ return false;
+ }
+
+ m_protocol_handler.after_init_connection();
+
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ strand_.wrap(
+ boost::bind(&connection<t_protocol_handler>::handle_read, self,
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred)));
+
+ return true;
+
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::start()", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::request_callback()
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L2("[" << print_connection_context_short(context) << "] request_callback");
+ // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
+ auto self = safe_shared_from_this();
+ if(!self)
+ return false;
+
+ strand_.post(boost::bind(&connection<t_protocol_handler>::call_back_starter, self));
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::request_callback()", false);
+ return true;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ boost::asio::io_service& connection<t_protocol_handler>::get_io_service()
+ {
+ return socket_.get_io_service();
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::add_ref()
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] add_ref");
+ CRITICAL_REGION_LOCAL(m_self_refs_lock);
+
+ // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
+ auto self = safe_shared_from_this();
+ if(!self)
+ return false;
+ if(m_was_shutdown)
+ return false;
+ m_self_refs.push_back(self);
+ return true;
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::add_ref()", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::release()
+ {
+ TRY_ENTRY();
+ boost::shared_ptr<connection<t_protocol_handler> > back_connection_copy;
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] release");
+ CRITICAL_REGION_BEGIN(m_self_refs_lock);
+ CHECK_AND_ASSERT_MES(m_self_refs.size(), false, "[sock " << socket_.native_handle() << "] m_self_refs empty at connection<t_protocol_handler>::release() call");
+ //erasing from container without additional copy can cause start deleting object, including m_self_refs
+ back_connection_copy = m_self_refs.back();
+ m_self_refs.pop_back();
+ CRITICAL_REGION_END();
+ return true;
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::release()", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void connection<t_protocol_handler>::call_back_starter()
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L2("[" << print_connection_context_short(context) << "] fired_callback");
+ m_protocol_handler.handle_qued_callback();
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::call_back_starter()", void());
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void connection<t_protocol_handler>::handle_read(const boost::system::error_code& e,
+ std::size_t bytes_transferred)
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async read calledback.");
+
+ if (!e)
+ {
+ LOG_PRINT("[sock " << socket_.native_handle() << "] RECV " << bytes_transferred, LOG_LEVEL_4);
+ bool recv_res = m_protocol_handler.handle_recv(buffer_.data(), bytes_transferred);
+ if(!recv_res)
+ {
+ LOG_PRINT("[sock " << socket_.native_handle() << "] protocol_want_close", LOG_LEVEL_4);
+
+ //some error in protocol, protocol handler ask to close connection
+ boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1);
+ bool do_shutdown = false;
+ CRITICAL_REGION_BEGIN(m_send_que_lock);
+ if(!m_send_que.size())
+ do_shutdown = true;
+ CRITICAL_REGION_END();
+ if(do_shutdown)
+ shutdown();
+ }else
+ {
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ strand_.wrap(
+ boost::bind(&connection<t_protocol_handler>::handle_read, connection<t_protocol_handler>::shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred)));
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "]Async read requested.");
+ }
+ }else
+ {
+ LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some not success at read: " << e.message() << ':' << e.value());
+ if(e.value() != 2)
+ {
+ LOG_PRINT_L3("[sock " << socket_.native_handle() << "] Some problems at read: " << e.message() << ':' << e.value());
+ shutdown();
+ }
+ }
+ // If an error occurs then no new asynchronous operations are started. This
+ // means that all shared_ptr references to the connection object will
+ // disappear and the object will be destroyed automatically after this
+ // handler returns. The connection class's destructor closes the socket.
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_read", void());
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::call_run_once_service_io()
+ {
+ TRY_ENTRY();
+ if(!m_is_multithreaded)
+ {
+ //single thread model, we can wait in blocked call
+ size_t cnt = socket_.get_io_service().run_one();
+ if(!cnt)//service is going to quit
+ return false;
+ }else
+ {
+ //multi thread model, we can't(!) wait in blocked call
+ //so we make non blocking call and releasing CPU by calling sleep(0);
+ //if no handlers were called
+ //TODO: Maybe we need to have have critical section + event + callback to upper protocol to
+ //ask it inside(!) critical region if we still able to go in event wait...
+ size_t cnt = socket_.get_io_service().poll_one();
+ if(!cnt)
+ misc_utils::sleep_no_w(0);
+ }
+
+ return true;
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::call_run_once_service_io", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::do_send(const void* ptr, size_t cb)
+ {
+ TRY_ENTRY();
+ // Use safe_shared_from_this, because of this is public method and it can be called on the object being deleted
+ auto self = safe_shared_from_this();
+ if(!self)
+ return false;
+ if(m_was_shutdown)
+ return false;
+
+ LOG_PRINT("[sock " << socket_.native_handle() << "] SEND " << cb, LOG_LEVEL_4);
+ //some data should be wrote to stream
+ //request complete
+
+ epee::critical_region_t<decltype(m_send_que_lock)> send_guard(m_send_que_lock);
+ if(m_send_que.size() > ABSTRACT_SERVER_SEND_QUE_MAX_COUNT)
+ {
+ send_guard.unlock();
+ LOG_ERROR("send que size is more than ABSTRACT_SERVER_SEND_QUE_MAX_COUNT(" << ABSTRACT_SERVER_SEND_QUE_MAX_COUNT << "), shutting down connection");
+ close();
+ return false;
+ }
+
+ m_send_que.resize(m_send_que.size()+1);
+ m_send_que.back().assign((const char*)ptr, cb);
+
+ if(m_send_que.size() > 1)
+ {
+ //active operation should be in progress, nothing to do, just wait last operation callback
+ }else
+ {
+ //no active operation
+ if(m_send_que.size()!=1)
+ {
+ LOG_ERROR("Looks like no active operations, but send que size != 1!!");
+ return false;
+ }
+
+ boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()),
+ //strand_.wrap(
+ boost::bind(&connection<t_protocol_handler>::handle_write, self, _1, _2)
+ //)
+ );
+
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send requested " << m_send_que.front().size());
+ }
+
+ return true;
+
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::do_send", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::shutdown()
+ {
+ // Initiate graceful connection closure.
+ boost::system::error_code ignored_ec;
+ socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ m_was_shutdown = true;
+ m_protocol_handler.release_protocol();
+ return true;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool connection<t_protocol_handler>::close()
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Que Shutdown called.");
+ size_t send_que_size = 0;
+ CRITICAL_REGION_BEGIN(m_send_que_lock);
+ send_que_size = m_send_que.size();
+ CRITICAL_REGION_END();
+ boost::interprocess::ipcdetail::atomic_write32(&m_want_close_connection, 1);
+ if(!send_que_size)
+ {
+ shutdown();
+ }
+
+ return true;
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::close", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void connection<t_protocol_handler>::handle_write(const boost::system::error_code& e, size_t cb)
+ {
+ TRY_ENTRY();
+ LOG_PRINT_L4("[sock " << socket_.native_handle() << "] Async send calledback " << cb);
+
+ if (e)
+ {
+ LOG_PRINT_L0("[sock " << socket_.native_handle() << "] Some problems at write: " << e.message() << ':' << e.value());
+ shutdown();
+ return;
+ }
+
+ bool do_shutdown = false;
+ CRITICAL_REGION_BEGIN(m_send_que_lock);
+ if(m_send_que.empty())
+ {
+ LOG_ERROR("[sock " << socket_.native_handle() << "] m_send_que.size() == 0 at handle_write!");
+ return;
+ }
+
+ m_send_que.pop_front();
+ if(m_send_que.empty())
+ {
+ if(boost::interprocess::ipcdetail::atomic_read32(&m_want_close_connection))
+ {
+ do_shutdown = true;
+ }
+ }else
+ {
+ //have more data to send
+ boost::asio::async_write(socket_, boost::asio::buffer(m_send_que.front().data(), m_send_que.front().size()),
+ //strand_.wrap(
+ boost::bind(&connection<t_protocol_handler>::handle_write, connection<t_protocol_handler>::shared_from_this(), _1, _2));
+ //);
+ }
+ CRITICAL_REGION_END();
+
+ if(do_shutdown)
+ {
+ shutdown();
+ }
+ CATCH_ENTRY_L0("connection<t_protocol_handler>::handle_write", void());
+ }
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ template<class t_protocol_handler>
+ boosted_tcp_server<t_protocol_handler>::boosted_tcp_server():
+ m_io_service_local_instance(new boost::asio::io_service()),
+ io_service_(*m_io_service_local_instance.get()),
+ acceptor_(io_service_),
+ new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)),
+ m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0)
+ {
+ m_thread_name_prefix = "NET";
+ }
+
+ template<class t_protocol_handler>
+ boosted_tcp_server<t_protocol_handler>::boosted_tcp_server(boost::asio::io_service& extarnal_io_service):
+ io_service_(extarnal_io_service),
+ acceptor_(io_service_),
+ new_connection_(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter)),
+ m_stop_signal_sent(false), m_port(0), m_sockets_count(0), m_threads_count(0), m_pfilter(NULL), m_thread_index(0)
+ {
+ m_thread_name_prefix = "NET";
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ boosted_tcp_server<t_protocol_handler>::~boosted_tcp_server()
+ {
+ this->send_stop_signal();
+ timed_wait_server_stop(10000);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::init_server(uint32_t port, const std::string address)
+ {
+ TRY_ENTRY();
+ m_stop_signal_sent = false;
+ m_port = port;
+ m_address = address;
+ // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(address, boost::lexical_cast<std::string>(port));
+ boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
+ acceptor_.open(endpoint.protocol());
+ acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
+ acceptor_.bind(endpoint);
+ acceptor_.listen();
+ boost::asio::ip::tcp::endpoint binded_endpoint = acceptor_.local_endpoint();
+ m_port = binded_endpoint.port();
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
+ boost::asio::placeholders::error));
+
+ return true;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::init_server", false);
+ }
+ //-----------------------------------------------------------------------------
+PUSH_WARNINGS
+DISABLE_GCC_WARNING(maybe-uninitialized)
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::init_server(const std::string port, const std::string& address)
+ {
+ uint32_t p = 0;
+
+ if (port.size() && !string_tools::get_xtype_from_string(p, port)) {
+ return false;
+ LOG_ERROR("Failed to convert port no = port");
+ }
+ return this->init_server(p, address);
+ }
+POP_WARNINGS
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::worker_thread()
+ {
+ TRY_ENTRY();
+ uint32_t local_thr_index = boost::interprocess::ipcdetail::atomic_inc32(&m_thread_index);
+ std::string thread_name = std::string("[") + m_thread_name_prefix;
+ thread_name += boost::to_string(local_thr_index) + "]";
+ log_space::log_singletone::set_thread_log_prefix(thread_name);
+ while(!m_stop_signal_sent)
+ {
+ try
+ {
+ io_service_.run();
+ }
+ catch(const std::exception& ex)
+ {
+ LOG_ERROR("Exception at server worker thread, what=" << ex.what());
+ }
+ catch(...)
+ {
+ LOG_ERROR("Exception at server worker thread, unknown execption");
+ }
+ }
+ LOG_PRINT_L4("Worker thread finished");
+ return true;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::worker_thread", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::set_threads_prefix(const std::string& prefix_name)
+ {
+ m_thread_name_prefix = prefix_name;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::set_connection_filter(i_connection_filter* pfilter)
+ {
+ m_pfilter = pfilter;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::run_server(size_t threads_count, bool wait)
+ {
+ TRY_ENTRY();
+ m_threads_count = threads_count;
+ m_main_thread_id = boost::this_thread::get_id();
+ log_space::log_singletone::set_thread_log_prefix("[SRV_MAIN]");
+ while(!m_stop_signal_sent)
+ {
+
+ // Create a pool of threads to run all of the io_services.
+ CRITICAL_REGION_BEGIN(m_threads_lock);
+ for (std::size_t i = 0; i < threads_count; ++i)
+ {
+ boost::shared_ptr<boost::thread> thread(new boost::thread(
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::worker_thread, this)));
+ m_threads.push_back(thread);
+ }
+ CRITICAL_REGION_END();
+ // Wait for all threads in the pool to exit.
+ if(wait)
+ {
+ for (std::size_t i = 0; i < m_threads.size(); ++i)
+ m_threads[i]->join();
+ m_threads.clear();
+
+ }else
+ {
+ return true;
+ }
+
+ if(wait && !m_stop_signal_sent)
+ {
+ //some problems with the listening socket ?..
+ LOG_PRINT_L0("Net service stopped without stop request, restarting...");
+ if(!this->init_server(m_port, m_address))
+ {
+ LOG_PRINT_L0("Reiniting service failed, exit.");
+ return false;
+ }else
+ {
+ LOG_PRINT_L0("Reiniting OK.");
+ }
+ }
+ }
+ return true;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::run_server", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::is_thread_worker()
+ {
+ TRY_ENTRY();
+ CRITICAL_REGION_LOCAL(m_threads_lock);
+ BOOST_FOREACH(boost::shared_ptr<boost::thread>& thp, m_threads)
+ {
+ if(thp->get_id() == boost::this_thread::get_id())
+ return true;
+ }
+ if(m_threads_count == 1 && boost::this_thread::get_id() == m_main_thread_id)
+ return true;
+ return false;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::is_thread_worker", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop(boost::uint64_t wait_mseconds)
+ {
+ TRY_ENTRY();
+ boost::chrono::milliseconds ms(wait_mseconds);
+ for (std::size_t i = 0; i < m_threads.size(); ++i)
+ {
+ if(m_threads[i]->joinable() && !m_threads[i]->try_join_for(ms))
+ {
+ LOG_PRINT_L0("Interrupting thread " << m_threads[i]->native_handle());
+ m_threads[i]->interrupt();
+ }
+ }
+ return true;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::timed_wait_server_stop", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::send_stop_signal()
+ {
+ m_stop_signal_sent = true;
+ TRY_ENTRY();
+ io_service_.stop();
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::send_stop_signal()", void());
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::is_stop_signal_sent()
+ {
+ return m_stop_signal_sent;
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ void boosted_tcp_server<t_protocol_handler>::handle_accept(const boost::system::error_code& e)
+ {
+ TRY_ENTRY();
+ if (!e)
+ {
+ connection_ptr conn(std::move(new_connection_));
+
+ new_connection_.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter));
+ acceptor_.async_accept(new_connection_->socket(),
+ boost::bind(&boosted_tcp_server<t_protocol_handler>::handle_accept, this,
+ boost::asio::placeholders::error));
+
+ bool r = conn->start(true, 1 < m_threads_count);
+ if (!r)
+ LOG_ERROR("[sock " << conn->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count);
+ }else
+ {
+ LOG_ERROR("Some problems at accept: " << e.message() << ", connections_count = " << m_sockets_count);
+ }
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::handle_accept", void());
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler>
+ bool boosted_tcp_server<t_protocol_handler>::connect(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_connection_context& conn_context, const std::string& bind_ip)
+ {
+ TRY_ENTRY();
+
+ connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) );
+ boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
+
+ //////////////////////////////////////////////////////////////////////////
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::ip::tcp::resolver::iterator end;
+ if(iterator == end)
+ {
+ LOG_ERROR("Failed to resolve " << adr);
+ return false;
+ }
+ //////////////////////////////////////////////////////////////////////////
+
+
+ //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
+ boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
+
+ sock_.open(remote_endpoint.protocol());
+ if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
+ {
+ boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0);
+ sock_.bind(local_endpoint);
+ }
+
+ /*
+ NOTICE: be careful to make sync connection from event handler: in case if all threads suddenly do sync connect, there will be no thread to dispatch events from io service.
+ */
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ //have another free thread(s), work in wait mode, without event handling
+ struct local_async_context
+ {
+ boost::system::error_code ec;
+ boost::mutex connect_mut;
+ boost::condition_variable cond;
+ };
+
+ boost::shared_ptr<local_async_context> local_shared_context(new local_async_context());
+ local_shared_context->ec = boost::asio::error::would_block;
+ boost::unique_lock<boost::mutex> lock(local_shared_context->connect_mut);
+ auto connect_callback = [](boost::system::error_code ec_, boost::shared_ptr<local_async_context> shared_context)
+ {
+ shared_context->connect_mut.lock(); shared_context->ec = ec_; shared_context->connect_mut.unlock(); shared_context->cond.notify_one();
+ };
+
+ sock_.async_connect(remote_endpoint, boost::bind<void>(connect_callback, _1, local_shared_context));
+ while(local_shared_context->ec == boost::asio::error::would_block)
+ {
+ bool r = local_shared_context->cond.timed_wait(lock, boost::get_system_time() + boost::posix_time::milliseconds(conn_timeout));
+ if(local_shared_context->ec == boost::asio::error::would_block && !r)
+ {
+ //timeout
+ sock_.close();
+ LOG_PRINT_L3("Failed to connect to " << adr << ":" << port << ", because of timeout (" << conn_timeout << ")");
+ return false;
+ }
+ }
+ ec = local_shared_context->ec;
+
+ if (ec || !sock_.is_open())
+ {
+ LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
+ return false;
+ }
+
+ LOG_PRINT_L3("Connected success to " << adr << ':' << port);
+
+ bool r = new_connection_l->start(false, 1 < m_threads_count);
+ if (r)
+ {
+ new_connection_l->get_context(conn_context);
+ //new_connection_l.reset(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter));
+ }
+ else
+ {
+ LOG_ERROR("[sock " << new_connection_->socket().native_handle() << "] Failed to start connection, connections_count = " << m_sockets_count);
+ }
+
+ return r;
+
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect", false);
+ }
+ //---------------------------------------------------------------------------------
+ template<class t_protocol_handler> template<class t_callback>
+ bool boosted_tcp_server<t_protocol_handler>::connect_async(const std::string& adr, const std::string& port, boost::uint32_t conn_timeout, t_callback cb, const std::string& bind_ip)
+ {
+ TRY_ENTRY();
+ connection_ptr new_connection_l(new connection<t_protocol_handler>(io_service_, m_config, m_sockets_count, m_pfilter) );
+ boost::asio::ip::tcp::socket& sock_ = new_connection_l->socket();
+
+ //////////////////////////////////////////////////////////////////////////
+ boost::asio::ip::tcp::resolver resolver(io_service_);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), adr, port);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::ip::tcp::resolver::iterator end;
+ if(iterator == end)
+ {
+ LOG_ERROR("Failed to resolve " << adr);
+ return false;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
+
+ sock_.open(remote_endpoint.protocol());
+ if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
+ {
+ boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(adr.c_str()), 0);
+ sock_.bind(local_endpoint);
+ }
+
+ boost::shared_ptr<boost::asio::deadline_timer> sh_deadline(new boost::asio::deadline_timer(io_service_));
+ //start deadline
+ sh_deadline->expires_from_now(boost::posix_time::milliseconds(conn_timeout));
+ sh_deadline->async_wait([=](const boost::system::error_code& error)
+ {
+ if(error != boost::asio::error::operation_aborted)
+ {
+ LOG_PRINT_L3("Failed to connect to " << adr << ':' << port << ", because of timeout (" << conn_timeout << ")");
+ new_connection_l->socket().close();
+ }
+ });
+ //start async connect
+ sock_.async_connect(remote_endpoint, [=](const boost::system::error_code& ec_)
+ {
+ t_connection_context conn_context = AUTO_VAL_INIT(conn_context);
+ boost::system::error_code ignored_ec;
+ boost::asio::ip::tcp::socket::endpoint_type lep = new_connection_l->socket().local_endpoint(ignored_ec);
+ if(!ec_)
+ {//success
+ if(!sh_deadline->cancel())
+ {
+ cb(conn_context, boost::asio::error::operation_aborted);//this mean that deadline timer already queued callback with cancel operation, rare situation
+ }else
+ {
+ LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Connected success to " << adr << ':' << port <<
+ " from " << lep.address().to_string() << ':' << lep.port());
+ bool r = new_connection_l->start(false, 1 < m_threads_count);
+ if (r)
+ {
+ new_connection_l->get_context(conn_context);
+ cb(conn_context, ec_);
+ }
+ else
+ {
+ LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to start connection to " << adr << ':' << port);
+ cb(conn_context, boost::asio::error::fault);
+ }
+ }
+ }else
+ {
+ LOG_PRINT_L3("[sock " << new_connection_l->socket().native_handle() << "] Failed to connect to " << adr << ':' << port <<
+ " from " << lep.address().to_string() << ':' << lep.port() << ": " << ec_.message() << ':' << ec_.value());
+ cb(conn_context, ec_);
+ }
+ });
+ return true;
+ CATCH_ENTRY_L0("boosted_tcp_server<t_protocol_handler>::connect_async", false);
+ }
+}
+}
+PRAGMA_WARNING_POP
diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.h b/contrib/epee/include/net/abstract_tcp_server_cp.h
new file mode 100644
index 000000000..b6410e120
--- /dev/null
+++ b/contrib/epee/include/net/abstract_tcp_server_cp.h
@@ -0,0 +1,233 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#ifndef _LEVIN_CP_SERVER_H_
+#define _LEVIN_CP_SERVER_H_
+
+#include <winsock2.h>
+#include <rpc.h>
+#include <string>
+#include <map>
+#include <boost/shared_ptr.hpp>
+
+#include "misc_log_ex.h"
+//#include "threads_helper.h"
+#include "syncobj.h"
+#define ENABLE_PROFILING
+#include "profile_tools.h"
+#include "net_utils_base.h"
+#include "pragma_comp_defs.h"
+
+#define LEVIN_DEFAULT_DATA_BUFF_SIZE 2000
+
+namespace epee
+{
+namespace net_utils
+{
+
+ template<class TProtocol>
+ class cp_server_impl//: public abstract_handler
+ {
+ public:
+ cp_server_impl(/*abstract_handler* phandler = NULL*/);
+ virtual ~cp_server_impl();
+
+ bool init_server(int port_no);
+ bool deinit_server();
+ bool run_server(int threads_count = 0);
+ bool send_stop_signal();
+ bool is_stop_signal();
+ virtual bool on_net_idle(){return true;}
+ size_t get_active_connections_num();
+ typename TProtocol::config_type& get_config_object(){return m_config;}
+ private:
+ enum overlapped_operation_type
+ {
+ op_type_recv,
+ op_type_send,
+ op_type_stop
+ };
+
+ struct io_data_base
+ {
+ OVERLAPPED m_overlapped;
+ WSABUF DataBuf;
+ overlapped_operation_type m_op_type;
+ DWORD TotalBuffBytes;
+ volatile LONG m_is_in_use;
+ char Buffer[1];
+ };
+
+PRAGMA_WARNING_PUSH
+PRAGMA_WARNING_DISABLE_VS(4355)
+ template<class TProtocol>
+ struct connection: public net_utils::i_service_endpoint
+ {
+ connection(typename TProtocol::config_type& ref_config):m_sock(INVALID_SOCKET), m_tprotocol_handler(this, ref_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0)
+ {
+ }
+
+ //connection():m_sock(INVALID_SOCKET), m_tprotocol_handler(this, m_dummy_config, context), m_psend_data(NULL), m_precv_data(NULL), m_asked_to_shutdown(0), m_connection_shutwoned(0)
+ //{
+ //}
+
+ connection<TProtocol>& operator=(const connection<TProtocol>& obj)
+ {
+ return *this;
+ }
+
+ bool init_buffers()
+ {
+ m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1];
+ m_psend_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
+ m_precv_data = (io_data_base*)new char[sizeof(io_data_base) + LEVIN_DEFAULT_DATA_BUFF_SIZE-1];
+ m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
+ return true;
+ }
+
+ bool query_shutdown()
+ {
+ if(!::InterlockedCompareExchange(&m_asked_to_shutdown, 1, 0))
+ {
+ m_psend_data->m_op_type = op_type_stop;
+ ::PostQueuedCompletionStatus(m_completion_port, 0, (ULONG_PTR)this, &m_psend_data->m_overlapped);
+ }
+ return true;
+ }
+
+ //bool set_config(typename TProtocol::config_type& config)
+ //{
+ // this->~connection();
+ // new(this) connection<TProtocol>(config);
+ // return true;
+ //}
+ ~connection()
+ {
+ if(m_psend_data)
+ delete m_psend_data;
+
+ if(m_precv_data)
+ delete m_precv_data;
+ }
+ virtual bool handle_send(const void* ptr, size_t cb)
+ {
+ PROFILE_FUNC("[handle_send]");
+ if(m_psend_data->TotalBuffBytes < cb)
+ resize_send_buff((DWORD)cb);
+
+ ZeroMemory(&m_psend_data->m_overlapped, sizeof(OVERLAPPED));
+ m_psend_data->DataBuf.len = (u_long)cb;//m_psend_data->TotalBuffBytes;
+ m_psend_data->DataBuf.buf = m_psend_data->Buffer;
+ memcpy(m_psend_data->DataBuf.buf, ptr, cb);
+ m_psend_data->m_op_type = op_type_send;
+ InterlockedExchange(&m_psend_data->m_is_in_use, 1);
+ DWORD bytes_sent = 0;
+ DWORD flags = 0;
+ int res = 0;
+ {
+ PROFILE_FUNC("[handle_send] ::WSASend");
+ res = ::WSASend(m_sock, &(m_psend_data->DataBuf), 1, &bytes_sent, flags, &(m_psend_data->m_overlapped), NULL);
+ }
+
+ if(res == SOCKET_ERROR )
+ {
+ int err = ::WSAGetLastError();
+ if(WSA_IO_PENDING == err )
+ return true;
+ }
+ LOG_ERROR("BIG FAIL: WSASend error code not correct, res=" << res << " last_err=" << err);
+ ::InterlockedExchange(&m_psend_data->m_is_in_use, 0);
+ query_shutdown();
+ //closesocket(m_psend_data);
+ return false;
+ }else if(0 == res)
+ {
+ ::InterlockedExchange(&m_psend_data->m_is_in_use, 0);
+ if(!bytes_sent || bytes_sent != cb)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("BIG FAIL: WSASend immediatly complete? but bad results, res=" << res << " last_err=" << err);
+ query_shutdown();
+ return false;
+ }else
+ {
+ return true;
+ }
+ }
+
+ return true;
+ }
+ bool resize_send_buff(DWORD new_size)
+ {
+ if(m_psend_data->TotalBuffBytes >= new_size)
+ return true;
+
+ delete m_psend_data;
+ m_psend_data = (io_data_base*)new char[sizeof(io_data_base) + new_size-1];
+ m_psend_data->TotalBuffBytes = new_size;
+ LOG_PRINT("Connection buffer resized up to " << new_size, LOG_LEVEL_3);
+ return true;
+ }
+
+
+ SOCKET m_sock;
+ net_utils::connection_context_base context;
+ TProtocol m_tprotocol_handler;
+ typename TProtocol::config_type m_dummy_config;
+ io_data_base* m_precv_data;
+ io_data_base* m_psend_data;
+ HANDLE m_completion_port;
+ volatile LONG m_asked_to_shutdown;
+ volatile LONG m_connection_shutwoned;
+ };
+PRAGMA_WARNING_POP
+
+ bool worker_thread_member();
+ static unsigned CALLBACK worker_thread(void* param);
+
+ bool add_new_connection(SOCKET new_sock, long ip_from, int port_from);
+ bool shutdown_connection(connection<TProtocol>* pconn);
+
+
+ typedef std::map<SOCKET, boost::shared_ptr<connection<TProtocol> > > connections_container;
+ SOCKET m_listen_socket;
+ HANDLE m_completion_port;
+ connections_container m_connections;
+ critical_section m_connections_lock;
+ int m_port;
+ volatile LONG m_stop;
+ //abstract_handler* m_phandler;
+ bool m_initialized;
+ volatile LONG m_worker_thread_counter;
+ typename TProtocol::config_type m_config;
+ };
+}
+}
+#include "abstract_tcp_server_cp.inl"
+
+
+#endif //_LEVIN_SERVER_H_
diff --git a/contrib/epee/include/net/abstract_tcp_server_cp.inl b/contrib/epee/include/net/abstract_tcp_server_cp.inl
new file mode 100644
index 000000000..5673c50be
--- /dev/null
+++ b/contrib/epee/include/net/abstract_tcp_server_cp.inl
@@ -0,0 +1,605 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#pragma comment(lib, "Ws2_32.lib")
+
+namespace epee
+{
+namespace net_utils
+{
+template<class TProtocol>
+cp_server_impl<TProtocol>::cp_server_impl():
+ m_port(0), m_stop(false),
+ m_worker_thread_counter(0), m_listen_socket(INVALID_SOCKET)
+{
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+cp_server_impl<TProtocol>::~cp_server_impl()
+{
+ deinit_server();
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::init_server(int port_no)
+{
+ m_port = port_no;
+
+ WSADATA wsad = {0};
+ int err = ::WSAStartup(MAKEWORD(2,2), &wsad);
+ if ( err != 0 || LOBYTE( wsad.wVersion ) != 2 || HIBYTE( wsad.wVersion ) != 2 )
+ {
+ LOG_ERROR("Could not find a usable WinSock DLL, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+ m_initialized = true;
+
+ m_listen_socket = ::WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
+ if(INVALID_SOCKET == m_listen_socket)
+ {
+ err = ::WSAGetLastError();
+ LOG_ERROR("Failed to create socket, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+
+ int opt = 1;
+ err = setsockopt (m_listen_socket, SOL_SOCKET,SO_REUSEADDR, reinterpret_cast<char*>(&opt), sizeof(int));
+ if(SOCKET_ERROR == err )
+ {
+ err = ::WSAGetLastError();
+ LOG_PRINT("Failed to setsockopt(SO_REUSEADDR), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
+ deinit_server();
+ return false;
+ }
+
+
+ sockaddr_in adr = {0};
+ adr.sin_family = AF_INET;
+ adr.sin_addr.s_addr = htonl(INADDR_ANY);
+ adr.sin_port = (u_short)htons(m_port);
+
+ //binding
+ err = bind(m_listen_socket, (const sockaddr*)&adr, sizeof(adr ));
+ if(SOCKET_ERROR == err )
+ {
+ err = ::WSAGetLastError();
+ LOG_PRINT("Failed to Bind, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
+ deinit_server();
+ return false;
+ }
+
+
+ m_completion_port = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if(INVALID_HANDLE_VALUE == m_completion_port)
+ {
+ err = ::WSAGetLastError();
+ LOG_PRINT("Failed to CreateIoCompletionPort, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_1);
+ deinit_server();
+ return false;
+ }
+
+
+ return true;
+}
+//-------------------------------------------------------------
+
+//-------------------------------------------------------------
+static int CALLBACK CPConditionFunc(
+ IN LPWSABUF lpCallerId,
+ IN LPWSABUF lpCallerData,
+ IN OUT LPQOS lpSQOS,
+ IN OUT LPQOS lpGQOS,
+ IN LPWSABUF lpCalleeId,
+ OUT LPWSABUF lpCalleeData,
+ OUT GROUP FAR *g,
+ IN DWORD_PTR dwCallbackData
+ )
+{
+
+ /*cp_server_impl* pthis = (cp_server_impl*)dwCallbackData;
+ if(!pthis)
+ return CF_REJECT;*/
+ /*if(pthis->get_active_connections_num()>=FD_SETSIZE-1)
+ {
+ LOG_PRINT("Maximum connections count overfull.", LOG_LEVEL_2);
+ return CF_REJECT;
+ }*/
+
+ return CF_ACCEPT;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+size_t cp_server_impl<TProtocol>::get_active_connections_num()
+{
+ return m_connections.size();
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+unsigned CALLBACK cp_server_impl<TProtocol>::worker_thread(void* param)
+{
+ if(!param)
+ return 0;
+
+ cp_server_impl<TProtocol>* pthis = (cp_server_impl<TProtocol>*)param;
+ pthis->worker_thread_member();
+ return 1;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::worker_thread_member()
+{
+ LOG_PRINT("Worker thread STARTED", LOG_LEVEL_1);
+ bool stop_handling = false;
+ while(!stop_handling)
+ {
+ PROFILE_FUNC("[worker_thread]Worker Loop");
+ DWORD bytes_transfered = 0;
+ connection<TProtocol>* pconnection = 0;
+ io_data_base* pio_data = 0;
+
+ {
+ PROFILE_FUNC("[worker_thread]GetQueuedCompletionStatus");
+ BOOL res = ::GetQueuedCompletionStatus (m_completion_port, &bytes_transfered , (PULONG_PTR)&pconnection, (LPOVERLAPPED *)&pio_data, INFINITE);
+ if (res == 0)
+ {
+ // check return code for error
+ int err = GetLastError();
+ LOG_PRINT("GetQueuedCompletionStatus failed with error " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_1);
+
+ if(pio_data)
+ ::InterlockedExchange(&pio_data->m_is_in_use, 0);
+
+
+ continue;
+ }
+ }
+
+ if(pio_data)
+ ::InterlockedExchange(&pio_data->m_is_in_use, 0);
+
+
+
+ if(!bytes_transfered && !pconnection && !pio_data)
+ {
+ //signal to stop
+ break;
+ }
+ if(!pconnection || !pio_data)
+ {
+ LOG_PRINT("BIG FAIL: pconnection or pio_data is empty: pconnection=" << pconnection << " pio_data=" << pio_data, LOG_LEVEL_0);
+ break;
+ }
+
+
+
+ if(::InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0))
+ {
+ LOG_ERROR("InterlockedCompareExchange(&pconnection->m_connection_shutwoned, 0, 0)");
+ //DebugBreak();
+ }
+
+ if(pio_data->m_op_type == op_type_stop)
+ {
+ if(!pconnection)
+ {
+ LOG_ERROR("op_type=op_type_stop, but pconnection is empty!!!");
+ continue;
+ }
+ shutdown_connection(pconnection);
+ continue;//
+ }
+ else if(pio_data->m_op_type == op_type_send)
+ {
+ continue;
+ //do nothing, just queuing request
+ }else if(pio_data->m_op_type == op_type_recv)
+ {
+ PROFILE_FUNC("[worker_thread]m_tprotocol_handler.handle_recv");
+ if(bytes_transfered)
+ {
+ bool res = pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_transfered);
+ if(!res)
+ pconnection->query_shutdown();
+ }
+ else
+ {
+ pconnection->query_shutdown();
+ continue;
+ }
+
+ }
+
+ //preparing new request,
+
+ {
+ PROFILE_FUNC("[worker_thread]RECV Request small loop");
+ int res = 0;
+ while(true)
+ {
+ LOG_PRINT("Prepearing data for WSARecv....", LOG_LEVEL_3);
+ ZeroMemory(&pio_data->m_overlapped, sizeof(OVERLAPPED));
+ pio_data->DataBuf.len = pio_data->TotalBuffBytes;
+ pio_data->DataBuf.buf = pio_data->Buffer;
+ pio_data->m_op_type = op_type_recv;
+ //calling WSARecv() and go to completion waiting
+ DWORD bytes_recvd = 0;
+ DWORD flags = 0;
+
+ LOG_PRINT("Calling WSARecv....", LOG_LEVEL_3);
+ ::InterlockedExchange(&pio_data->m_is_in_use, 1);
+ res = WSARecv(pconnection->m_sock, &(pio_data->DataBuf), 1, &bytes_recvd , &flags, &(pio_data->m_overlapped), NULL);
+ if(res == SOCKET_ERROR )
+ {
+ int err = ::WSAGetLastError();
+ if(WSA_IO_PENDING == err )
+ {//go pending, ok
+ LOG_PRINT("WSARecv return WSA_IO_PENDING", LOG_LEVEL_3);
+ break;
+ }
+ LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err);
+ ::InterlockedExchange(&pio_data->m_is_in_use, 0);
+ pconnection->query_shutdown();
+ break;
+ }
+ break;
+ /*else if(0 == res)
+ {
+ if(!bytes_recvd)
+ {
+ ::InterlockedExchange(&pio_data->m_is_in_use, 0);
+ LOG_PRINT("WSARecv return 0, bytes_recvd=0, graceful close.", LOG_LEVEL_3);
+ int err = ::WSAGetLastError();
+ //LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err);
+ //pconnection->query_shutdown();
+ break;
+ }else
+ {
+ LOG_PRINT("WSARecv return immediatily 0, bytes_recvd=" << bytes_recvd, LOG_LEVEL_3);
+ //pconnection->m_tprotocol_handler.handle_recv(pio_data->Buffer, bytes_recvd);
+ }
+ }*/
+ }
+ }
+ }
+
+
+ LOG_PRINT("Worker thread STOPED", LOG_LEVEL_1);
+ ::InterlockedDecrement(&m_worker_thread_counter);
+ return true;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::shutdown_connection(connection<TProtocol>* pconn)
+{
+ PROFILE_FUNC("[shutdown_connection]");
+
+ if(!pconn)
+ {
+ LOG_ERROR("Attempt to remove null pptr connection!");
+ return false;
+ }
+ else
+ {
+ LOG_PRINT("Shutting down connection ("<< pconn << ")", LOG_LEVEL_3);
+ }
+ m_connections_lock.lock();
+ connections_container::iterator it = m_connections.find(pconn->m_sock);
+ m_connections_lock.unlock();
+ if(it == m_connections.end())
+ {
+ LOG_ERROR("Failed to find closing socket=" << pconn->m_sock);
+ return false;
+ }
+ SOCKET sock = it->second->m_sock;
+ {
+ PROFILE_FUNC("[shutdown_connection] shutdown, close");
+ ::shutdown(it->second->m_sock, SD_SEND );
+ }
+ size_t close_sock_wait_count = 0;
+ {
+ LOG_PRINT("Entered to 'in_use wait zone'", LOG_LEVEL_3);
+ PROFILE_FUNC("[shutdown_connection] wait for in_use");
+ while(::InterlockedCompareExchange(&it->second->m_precv_data->m_is_in_use, 1, 1))
+ {
+
+ Sleep(100);
+ close_sock_wait_count++;
+ }
+ LOG_PRINT("First step to 'in_use wait zone'", LOG_LEVEL_3);
+
+
+ while(::InterlockedCompareExchange(&it->second->m_psend_data->m_is_in_use, 1, 1))
+ {
+ Sleep(100);
+ close_sock_wait_count++;
+ }
+ LOG_PRINT("Leaved 'in_use wait zone'", LOG_LEVEL_3);
+ }
+
+ ::closesocket(it->second->m_sock);
+
+ ::InterlockedExchange(&it->second->m_connection_shutwoned, 1);
+ m_connections_lock.lock();
+ m_connections.erase(it);
+ m_connections_lock.unlock();
+ LOG_PRINT("Socked " << sock << " closed, wait_count=" << close_sock_wait_count, LOG_LEVEL_2);
+ return true;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::run_server(int threads_count = 0)
+{
+ int err = listen(m_listen_socket, 100);
+ if(SOCKET_ERROR == err )
+ {
+ err = ::WSAGetLastError();
+ LOG_ERROR("Failed to listen, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+
+ if(!threads_count)
+ {
+ SYSTEM_INFO si = {0};
+ ::GetSystemInfo(&si);
+ threads_count = si.dwNumberOfProcessors + 2;
+ }
+ for(int i = 0; i != threads_count; i++)
+ {
+ boost::thread(boost::bind(&cp_server_impl::worker_thread_member, this));
+ //HANDLE h_thread = threads_helper::create_thread(worker_thread, this);
+ InterlockedIncrement(&m_worker_thread_counter);
+ //::CloseHandle(h_thread);
+ }
+
+ LOG_PRINT("Numbers of worker threads started: " << threads_count, LOG_LEVEL_1);
+
+ m_stop = false;
+ while(!m_stop)
+ {
+ PROFILE_FUNC("[run_server] main_loop");
+ TIMEVAL tv = {0};
+ tv.tv_sec = 0;
+ tv.tv_usec = 100;
+ fd_set sock_set;
+ sock_set.fd_count = 1;
+ sock_set.fd_array[0] = m_listen_socket;
+ int select_res = 0;
+ {
+ PROFILE_FUNC("[run_server] select");
+ select_res = select(0, &sock_set, &sock_set, NULL, &tv);
+ }
+
+ if(SOCKET_ERROR == select_res)
+ {
+ err = ::WSAGetLastError();
+ LOG_ERROR("Failed to select, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ return false;
+ }
+ if(!select_res)
+ {
+ on_net_idle();
+ continue;
+ }
+ else
+ {
+ sockaddr_in adr_from = {0};
+ int adr_len = sizeof(adr_from);
+ SOCKET new_sock = INVALID_SOCKET;
+ {
+ PROFILE_FUNC("[run_server] WSAAccept");
+ new_sock = ::WSAAccept(m_listen_socket, (sockaddr *)&adr_from, &adr_len, CPConditionFunc, (DWORD_PTR)this);
+ }
+
+ if(INVALID_SOCKET == new_sock)
+ {
+ if(m_stop)
+ break;
+ int err = ::WSAGetLastError();
+ LOG_PRINT("Failed to WSAAccept, err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
+ continue;
+ }
+ LOG_PRINT("Accepted connection (new socket=" << new_sock << ")", LOG_LEVEL_2);
+ {
+ PROFILE_FUNC("[run_server] Add new connection");
+ add_new_connection(new_sock, adr_from.sin_addr.s_addr, adr_from.sin_port);
+ }
+
+ }
+
+ }
+ LOG_PRINT("Closing connections("<< m_connections.size() << ") and waiting...", LOG_LEVEL_2);
+ m_connections_lock.lock();
+ for(connections_container::iterator it = m_connections.begin(); it != m_connections.end(); it++)
+ {
+ ::shutdown(it->second->m_sock, SD_BOTH);
+ ::closesocket(it->second->m_sock);
+ }
+ m_connections_lock.unlock();
+ size_t wait_count = 0;
+ while(m_connections.size() && wait_count < 100)
+ {
+ ::Sleep(100);
+ wait_count++;
+ }
+ LOG_PRINT("Connections closed OK (wait_count=" << wait_count << ")", LOG_LEVEL_2);
+
+
+ LOG_PRINT("Stopping worker threads("<< m_worker_thread_counter << ").", LOG_LEVEL_2);
+ for(int i = 0; i<m_worker_thread_counter; i++)
+ {
+ ::PostQueuedCompletionStatus(m_completion_port, 0, 0, 0);
+ }
+
+ wait_count = 0;
+ while(InterlockedCompareExchange(&m_worker_thread_counter, 0, 0) && wait_count < 100)
+ {
+ Sleep(100);
+ wait_count++;
+ }
+
+ LOG_PRINT("Net Server STOPPED, wait_count = " << wait_count, LOG_LEVEL_1);
+ return true;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::add_new_connection(SOCKET new_sock, long ip_from, int port_from)
+{
+ PROFILE_FUNC("[add_new_connection]");
+
+ LOG_PRINT("Add new connection zone: entering lock", LOG_LEVEL_3);
+ m_connections_lock.lock();
+
+ boost::shared_ptr<connection<TProtocol> > ptr;
+ ptr.reset(new connection<TProtocol>(m_config));
+
+ connection<TProtocol>& conn = *ptr.get();
+ m_connections[new_sock] = ptr;
+ LOG_PRINT("Add new connection zone: leaving lock", LOG_LEVEL_3);
+ m_connections_lock.unlock();
+ conn.init_buffers();
+ conn.m_sock = new_sock;
+ conn.context.m_remote_ip = ip_from;
+ conn.context.m_remote_port = port_from;
+ conn.m_completion_port = m_completion_port;
+ {
+ PROFILE_FUNC("[add_new_connection] CreateIoCompletionPort");
+ ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0);
+ }
+
+ //if(NULL == ::CreateIoCompletionPort((HANDLE)new_sock, m_completion_port, (ULONG_PTR)&conn, 0))
+ //{
+ // int err = ::GetLastError();
+ // LOG_PRINT("Failed to CreateIoCompletionPort(associate socket and completion port), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"", LOG_LEVEL_2);
+ // return false;
+ //}
+
+ conn.m_tprotocol_handler.after_init_connection();
+ {
+ PROFILE_FUNC("[add_new_connection] starting loop");
+ int res = 0;
+ while(true)//res!=SOCKET_ERROR)
+ {
+ PROFILE_FUNC("[add_new_connection] in loop time");
+ conn.m_precv_data->TotalBuffBytes = LEVIN_DEFAULT_DATA_BUFF_SIZE;
+ ZeroMemory(&conn.m_precv_data->m_overlapped, sizeof(OVERLAPPED));
+ conn.m_precv_data->DataBuf.len = conn.m_precv_data->TotalBuffBytes;
+ conn.m_precv_data->DataBuf.buf = conn.m_precv_data->Buffer;
+ conn.m_precv_data->m_op_type = op_type_recv;
+ InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1);
+ DWORD bytes_recvd = 0;
+ DWORD flags = 0;
+
+ ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 1);
+ {
+ PROFILE_FUNC("[add_new_connection] ::WSARecv");
+ res = ::WSARecv(conn.m_sock, &(conn.m_precv_data->DataBuf), 1, &bytes_recvd , &flags, &(conn.m_precv_data->m_overlapped), NULL);
+ }
+ if(res == SOCKET_ERROR )
+ {
+ int err = ::WSAGetLastError();
+ if(WSA_IO_PENDING == err )
+ {
+ break;
+ }
+ LOG_ERROR("BIG FAIL: WSARecv error code not correct, res=" << res << " last_err=" << err << " " << log_space::get_win32_err_descr(err));
+ ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0);
+ conn.query_shutdown();
+ //shutdown_connection(&conn);
+ break;
+ }
+
+
+ break;
+ /*else if(0 == res)
+ {
+ if(!bytes_recvd)
+ {
+ PROFILE_FUNC("[add_new_connection] shutdown_connection");
+ ::InterlockedExchange(&conn.m_precv_data->m_is_in_use, 0);
+ conn.query_shutdown();
+ //shutdown_connection(&conn);
+ break;
+ }else
+ {
+ PROFILE_FUNC("[add_new_connection] handle_recv");
+ }
+ }*/
+ }
+ }
+
+
+
+ return true;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::deinit_server()
+{
+ if(!m_initialized)
+ return true;
+
+ if(INVALID_SOCKET != m_listen_socket)
+ {
+ shutdown(m_listen_socket, SD_BOTH);
+ int res = closesocket(m_listen_socket);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to closesocket(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ }
+ m_listen_socket = INVALID_SOCKET;
+ }
+
+ int res = ::WSACleanup();
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to WSACleanup(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ }
+ m_initialized = false;
+
+ return true;
+}
+
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::send_stop_signal()
+{
+ ::InterlockedExchange(&m_stop, 1);
+ return true;
+}
+//-------------------------------------------------------------
+template<class TProtocol>
+bool cp_server_impl<TProtocol>::is_stop_signal()
+{
+ return m_stop?true:false;
+}
+//-------------------------------------------------------------
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_base.h b/contrib/epee/include/net/http_base.h
new file mode 100644
index 000000000..6de537a4c
--- /dev/null
+++ b/contrib/epee/include/net/http_base.h
@@ -0,0 +1,184 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#pragma once
+#include <boost/lexical_cast.hpp>
+#include <boost/regex.hpp>
+
+#include "string_tools.h"
+namespace epee
+{
+namespace net_utils
+{
+ namespace http
+ {
+
+ enum http_method{
+ http_method_get,
+ http_method_post,
+ http_method_put,
+ http_method_head,
+ http_method_etc,
+ http_method_unknown
+ };
+
+ enum http_content_type
+ {
+ http_content_type_text_html,
+ http_content_type_image_gif,
+ http_content_type_other,
+ http_content_type_not_set
+ };
+
+ typedef std::list<std::pair<std::string, std::string> > fields_list;
+
+ inline
+ std::string get_value_from_fields_list(const std::string& param_name, const net_utils::http::fields_list& fields)
+ {
+ fields_list::const_iterator it = fields.begin();
+ for(; it != fields.end(); it++)
+ if(!string_tools::compare_no_case(param_name, it->first))
+ break;
+
+ if(it==fields.end())
+ return std::string();
+
+ return it->second;
+ }
+
+
+ inline
+ std::string get_value_from_uri_line(const std::string& param_name, const std::string& uri)
+ {
+ std::string buff = "([\\?|&])";
+ buff += param_name + "=([^&]*)";
+ boost::regex match_param(buff.c_str(), boost::regex::icase | boost::regex::normal);
+ boost::smatch result;
+ if(boost::regex_search(uri, result, match_param, boost::match_default) && result[0].matched)
+ {
+ return result[2];
+ }
+ return std::string();
+ }
+
+
+
+ struct http_header_info
+ {
+ std::string m_connection; //"Connection:"
+ std::string m_referer; //"Referer:"
+ std::string m_content_length; //"Content-Length:"
+ std::string m_content_type; //"Content-Type:"
+ std::string m_transfer_encoding;//"Transfer-Encoding:"
+ std::string m_content_encoding; //"Content-Encoding:"
+ std::string m_host; //"Host:"
+ std::string m_cookie; //"Cookie:"
+ fields_list m_etc_fields;
+
+ void clear()
+ {
+ m_connection.clear();
+ m_referer.clear();
+ m_content_length.clear();
+ m_content_type.clear();
+ m_transfer_encoding.clear();
+ m_content_encoding.clear();
+ m_host.clear();
+ m_cookie.clear();
+ m_etc_fields.clear();
+ }
+ };
+
+ struct uri_content
+ {
+ std::string m_path;
+ std::string m_query;
+ std::string m_fragment;
+ std::list<std::pair<std::string, std::string> > m_query_params;
+ };
+
+ struct url_content
+ {
+ std::string schema;
+ std::string host;
+ std::string uri;
+ boost::uint64_t port;
+ uri_content m_uri_content;
+ };
+
+
+ struct http_request_info
+ {
+ http_request_info():m_http_method(http_method_unknown),
+ m_http_ver_hi(0),
+ m_http_ver_lo(0),
+ m_have_to_block(false)
+ {}
+
+ http_method m_http_method;
+ std::string m_URI;
+ std::string m_http_method_str;
+ std::string m_full_request_str;
+ std::string m_replace_html;
+ std::string m_request_head;
+ int m_http_ver_hi;
+ int m_http_ver_lo;
+ bool m_have_to_block;
+ http_header_info m_header_info;
+ uri_content m_uri_content;
+ size_t m_full_request_buf_size;
+ std::string m_body;
+
+ void clear()
+ {
+ this->~http_request_info();
+ new(this) http_request_info();
+ }
+ };
+
+
+ struct http_response_info
+ {
+ int m_response_code;
+ std::string m_response_comment;
+ fields_list m_additional_fields;
+ std::string m_body;
+ std::string m_mime_tipe;
+ http_header_info m_header_info;
+ int m_http_ver_hi;// OUT paramter only
+ int m_http_ver_lo;// OUT paramter only
+
+ void clear()
+ {
+ this->~http_response_info();
+ new(this) http_response_info();
+ }
+ };
+ }
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_client.h b/contrib/epee/include/net/http_client.h
new file mode 100644
index 000000000..4a88c06ef
--- /dev/null
+++ b/contrib/epee/include/net/http_client.h
@@ -0,0 +1,875 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#pragma once
+#include <boost/shared_ptr.hpp>
+#include <boost/regex.hpp>
+#include <boost/lexical_cast.hpp>
+//#include <mbstring.h>
+#include <algorithm>
+#include <cctype>
+#include <functional>
+
+#include "net_helper.h"
+#include "http_client_base.h"
+
+#ifdef HTTP_ENABLE_GZIP
+#include "gzip_encoding.h"
+#endif
+
+#include "string_tools.h"
+#include "reg_exp_definer.h"
+#include "http_base.h"
+#include "to_nonconst_iterator.h"
+#include "net_parse_helpers.h"
+
+//#include "shlwapi.h"
+
+//#pragma comment(lib, "shlwapi.lib")
+
+extern epee::critical_section gregexp_lock;
+
+
+namespace epee
+{
+namespace net_utils
+{
+
+using namespace std;
+
+ /*struct url
+ {
+ public:
+ void parse(const std::string& url_s)
+ {
+ const string prot_end("://");
+ string::const_iterator prot_i = search(url_s.begin(), url_s.end(),
+ prot_end.begin(), prot_end.end());
+ protocol_.reserve(distance(url_s.begin(), prot_i));
+ transform(url_s.begin(), prot_i,
+ back_inserter(protocol_),
+ ptr_fun<int,int>(tolower)); // protocol is icase
+ if( prot_i == url_s.end() )
+ return;
+ advance(prot_i, prot_end.length());
+ string::const_iterator path_i = find(prot_i, url_s.end(), '/');
+ host_.reserve(distance(prot_i, path_i));
+ transform(prot_i, path_i,
+ back_inserter(host_),
+ ptr_fun<int,int>(tolower)); // host is icase
+ string::const_iterator query_i = find(path_i, url_s.end(), '?');
+ path_.assign(path_i, query_i);
+ if( query_i != url_s.end() )
+ ++query_i;
+ query_.assign(query_i, url_s.end());
+ }
+
+ std::string protocol_;
+ std::string host_;
+ std::string path_;
+ std::string query_;
+ };*/
+
+
+
+
+ //---------------------------------------------------------------------------
+ static inline const char* get_hex_vals()
+ {
+ static char hexVals[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ return hexVals;
+ }
+
+ static inline const char* get_unsave_chars()
+ {
+ //static char unsave_chars[] = "\"<>%\\^[]`+$,@:;/!#?=&";
+ static char unsave_chars[] = "\"<>%\\^[]`+$,@:;!#&";
+ return unsave_chars;
+ }
+
+ static inline bool is_unsafe(unsigned char compare_char)
+ {
+ if(compare_char <= 32 || compare_char >= 123)
+ return true;
+
+ const char* punsave = get_unsave_chars();
+
+ for(int ichar_pos = 0; 0!=punsave[ichar_pos] ;ichar_pos++)
+ if(compare_char == punsave[ichar_pos])
+ return true;
+
+ return false;
+ }
+
+ static inline
+ std::string dec_to_hex(char num, int radix)
+ {
+ int temp=0;
+ std::string csTmp;
+ int num_char;
+
+ num_char = (int) num;
+ if (num_char < 0)
+ num_char = 256 + num_char;
+
+ while (num_char >= radix)
+ {
+ temp = num_char % radix;
+ num_char = (int)floor((float)num_char / (float)radix);
+ csTmp = get_hex_vals()[temp];
+ }
+
+ csTmp += get_hex_vals()[num_char];
+
+ if(csTmp.size() < 2)
+ {
+ csTmp += '0';
+ }
+
+ std::reverse(csTmp.begin(), csTmp.end());
+ //_mbsrev((unsigned char*)csTmp.data());
+
+ return csTmp;
+ }
+
+ static inline std::string convert(char val)
+ {
+ std::string csRet;
+ csRet += "%";
+ csRet += dec_to_hex(val, 16);
+ return csRet;
+ }
+ static inline std::string conver_to_url_format(const std::string& uri)
+ {
+
+ std::string result;
+
+ for(size_t i = 0; i!= uri.size(); i++)
+ {
+ if(is_unsafe(uri[i]))
+ result += convert(uri[i]);
+ else
+ result += uri[i];
+
+ }
+
+ return result;
+ }
+
+ static inline std::string convert_to_url_format_force_all(const std::string& uri)
+ {
+
+ std::string result;
+
+ for(size_t i = 0; i!= uri.size(); i++)
+ {
+ result += convert(uri[i]);
+ }
+
+ return result;
+ }
+
+
+
+
+
+ namespace http
+ {
+
+ class http_simple_client: public i_target_handler
+ {
+ public:
+
+
+ private:
+ enum reciev_machine_state
+ {
+ reciev_machine_state_header,
+ reciev_machine_state_body_content_len,
+ reciev_machine_state_body_connection_close,
+ reciev_machine_state_body_chunked,
+ reciev_machine_state_done,
+ reciev_machine_state_error
+ };
+
+
+
+ enum chunked_state{
+ http_chunked_state_chunk_head,
+ http_chunked_state_chunk_body,
+ http_chunked_state_done,
+ http_chunked_state_undefined
+ };
+
+
+ blocked_mode_client m_net_client;
+ std::string m_host_buff;
+ std::string m_port;
+ unsigned int m_timeout;
+ std::string m_header_cache;
+ http_response_info m_response_info;
+ size_t m_len_in_summary;
+ size_t m_len_in_remain;
+ //std::string* m_ptarget_buffer;
+ boost::shared_ptr<i_sub_handler> m_pcontent_encoding_handler;
+ reciev_machine_state m_state;
+ chunked_state m_chunked_state;
+ std::string m_chunked_cache;
+ critical_section m_lock;
+
+ public:
+ void set_host_name(const std::string& name)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_host_buff = name;
+ }
+ bool connect(const std::string& host, int port, unsigned int timeout)
+ {
+ return connect(host, std::to_string(port), timeout);
+ }
+ bool connect(const std::string& host, const std::string& port, unsigned int timeout)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_host_buff = host;
+ m_port = port;
+ m_timeout = timeout;
+
+ return m_net_client.connect(host, port, timeout, timeout);
+ }
+ //---------------------------------------------------------------------------
+ bool disconnect()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return m_net_client.disconnect();
+ }
+ //---------------------------------------------------------------------------
+ bool is_connected()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return m_net_client.is_connected();
+ }
+ //---------------------------------------------------------------------------
+ virtual bool handle_target_data(std::string& piece_of_transfer)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ m_response_info.m_body += piece_of_transfer;
+ piece_of_transfer.clear();
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool invoke_get(const std::string& uri, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return invoke(uri, "GET", body, ppresponse_info, additional_params);
+ }
+
+ //---------------------------------------------------------------------------
+ inline bool invoke(const std::string& uri, const std::string& method, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if(!is_connected())
+ {
+ LOG_PRINT("Reconnecting...", LOG_LEVEL_3);
+ if(!connect(m_host_buff, m_port, m_timeout))
+ {
+ LOG_PRINT("Failed to connect to " << m_host_buff << ":" << m_port, LOG_LEVEL_3);
+ return false;
+ }
+ }
+ m_response_info.clear();
+ std::string req_buff = method + " ";
+ req_buff += uri + " HTTP/1.1\r\n" +
+ "Host: "+ m_host_buff +"\r\n" + "Content-Length: " + boost::lexical_cast<std::string>(body.size()) + "\r\n";
+
+
+ //handle "additional_params"
+ for(fields_list::const_iterator it = additional_params.begin(); it!=additional_params.end(); it++)
+ req_buff += it->first + ": " + it->second + "\r\n";
+ req_buff += "\r\n";
+ //--
+
+ bool res = m_net_client.send(req_buff);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+ if(body.size())
+ res = m_net_client.send(body);
+ CHECK_AND_ASSERT_MES(res, false, "HTTP_CLIENT: Failed to SEND");
+
+ if(ppresponse_info)
+ *ppresponse_info = &m_response_info;
+
+ m_state = reciev_machine_state_header;
+ return handle_reciev();
+ }
+ //---------------------------------------------------------------------------
+ inline bool invoke_post(const std::string& uri, const std::string& body, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list())
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ return invoke(uri, "POST", body, ppresponse_info, additional_params);
+ }
+ private:
+ //---------------------------------------------------------------------------
+ inline bool handle_reciev()
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ bool keep_handling = true;
+ bool need_more_data = true;
+ std::string recv_buffer;
+ while(keep_handling)
+ {
+ if(need_more_data)
+ {
+ if(!m_net_client.recv(recv_buffer))
+ {
+ LOG_PRINT("Unexpected reciec fail", LOG_LEVEL_3);
+ m_state = reciev_machine_state_error;
+ }
+ if(!recv_buffer.size())
+ {
+ //connection is going to be closed
+ if(reciev_machine_state_body_connection_close != m_state)
+ {
+ m_state = reciev_machine_state_error;
+ }
+ }
+ need_more_data = false;
+ }
+ switch(m_state)
+ {
+ case reciev_machine_state_header:
+ keep_handling = handle_header(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_content_len:
+ keep_handling = handle_body_content_len(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_connection_close:
+ keep_handling = handle_body_connection_close(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_body_chunked:
+ keep_handling = handle_body_body_chunked(recv_buffer, need_more_data);
+ break;
+ case reciev_machine_state_done:
+ keep_handling = false;
+ break;
+ case reciev_machine_state_error:
+ keep_handling = false;
+ break;
+ }
+
+ }
+ m_header_cache.clear();
+ if(m_state != reciev_machine_state_error)
+ {
+ if(m_response_info.m_header_info.m_connection.size() && !string_tools::compare_no_case("close", m_response_info.m_header_info.m_connection))
+ disconnect();
+
+ return true;
+ }
+ else
+ {
+ LOG_PRINT_L3("Returning false because of wrong state machine. state: " << m_state);
+ return false;
+ }
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_header(std::string& recv_buff, bool& need_more_data)
+ {
+
+ CRITICAL_REGION_LOCAL(m_lock);
+ if(!recv_buff.size())
+ {
+ LOG_ERROR("Connection closed at handle_header");
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+
+ m_header_cache += recv_buff;
+ recv_buff.clear();
+ std::string::size_type pos = m_header_cache.find("\r\n\r\n");
+ if(pos != std::string::npos)
+ {
+ recv_buff.assign(m_header_cache.begin()+pos+4, m_header_cache.end());
+ m_header_cache.erase(m_header_cache.begin()+pos+4, m_header_cache.end());
+
+ analize_cached_header_and_invoke_state();
+ m_header_cache.clear();
+ if(!recv_buff.size() && (m_state != reciev_machine_state_error && m_state != reciev_machine_state_done))
+ need_more_data = true;
+
+ return true;
+ }else
+ need_more_data = true;
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_content_len(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if(!recv_buff.size())
+ {
+ LOG_PRINT("Warning: Content-Len mode, but connection unexpectedly closed", LOG_LEVEL_3);
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ CHECK_AND_ASSERT_MES(m_len_in_remain >= recv_buff.size(), false, "m_len_in_remain >= recv_buff.size()");
+ m_len_in_remain -= recv_buff.size();
+ m_pcontent_encoding_handler->update_in(recv_buff);
+
+ if(m_len_in_remain == 0)
+ m_state = reciev_machine_state_done;
+ else
+ need_more_data = true;
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_connection_close(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if(!recv_buff.size())
+ {
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ need_more_data = true;
+ m_pcontent_encoding_handler->update_in(recv_buff);
+
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline bool is_hex_symbol(char ch)
+ {
+
+ if( (ch >= '0' && ch <='9')||(ch >= 'A' && ch <='F')||(ch >= 'a' && ch <='f'))
+ return true;
+ else
+ return false;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool get_len_from_chunk_head(const std::string &chunk_head, size_t& result_size)
+ {
+ std::stringstream str_stream;
+ str_stream << std::hex;
+ if(!(str_stream << chunk_head && str_stream >> result_size))
+ return false;
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool get_chunk_head(std::string& buff, size_t& chunk_size, bool& is_matched)
+ {
+ is_matched = false;
+ size_t offset = 0;
+ for(std::string::iterator it = buff.begin(); it!= buff.end(); it++, offset++)
+ {
+ if(!is_hex_symbol(*it))
+ {
+ if(*it == '\r' || *it == ' ' )
+ {
+ offset--;
+ continue;
+ }
+ else if(*it == '\n')
+ {
+ std::string chunk_head = buff.substr(0, offset);
+ if(!get_len_from_chunk_head(chunk_head, chunk_size))
+ return false;
+
+ if(0 == chunk_size)
+ {
+ //Here is a small confusion
+ //In breif - if the chunk is the last one we need to get terminating sequence
+ //along with the cipher, generally in the "ddd\r\n\r\n" form
+
+ for(it++;it != buff.end(); it++)
+ {
+ if('\r' == *it)
+ continue;
+ else if('\n' == *it)
+ break;
+ else
+ {
+ LOG_ERROR("http_stream_filter: Wrong last chunk terminator");
+ return false;
+ }
+ }
+
+ if(it == buff.end())
+ return true;
+ }
+
+ buff.erase(buff.begin(), ++it);
+
+ is_matched = true;
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool handle_body_body_chunked(std::string& recv_buff, bool& need_more_data)
+ {
+ CRITICAL_REGION_LOCAL(m_lock);
+ if(!recv_buff.size())
+ {
+ LOG_PRINT("Warning: CHUNKED mode, but connection unexpectedly closed", LOG_LEVEL_3);
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ m_chunked_cache += recv_buff;
+ recv_buff.clear();
+ bool is_matched = false;
+
+ while(true)
+ {
+ if(!m_chunked_cache.size())
+ {
+ need_more_data = true;
+ break;
+ }
+
+ switch(m_chunked_state)
+ {
+ case http_chunked_state_chunk_head:
+ if(m_chunked_cache[0] == '\n' || m_chunked_cache[0] == '\r')
+ {
+ //optimize a bit
+ if(m_chunked_cache[0] == '\r' && m_chunked_cache.size()>1 && m_chunked_cache[1] == '\n')
+ m_chunked_cache.erase(0, 2);
+ else
+ m_chunked_cache.erase(0, 1);
+ break;
+ }
+ if(!get_chunk_head(m_chunked_cache, m_len_in_remain, is_matched))
+ {
+ LOG_ERROR("http_stream_filter::handle_chunked(*) Failed to get length from chunked head:" << m_chunked_cache);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+
+ if(!is_matched)
+ {
+ need_more_data = true;
+ return true;
+ }else
+ {
+ m_chunked_state = http_chunked_state_chunk_body;
+ if(m_len_in_remain == 0)
+ {//last chunk, let stop the stream and fix the chunk queue.
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ m_chunked_state = http_chunked_state_chunk_body;
+ break;
+ }
+ break;
+ case http_chunked_state_chunk_body:
+ {
+ std::string chunk_body;
+ if(m_len_in_remain >= m_chunked_cache.size())
+ {
+ m_len_in_remain -= m_chunked_cache.size();
+ chunk_body.swap(m_chunked_cache);
+ }else
+ {
+ chunk_body.assign(m_chunked_cache, 0, m_len_in_remain);
+ m_chunked_cache.erase(0, m_len_in_remain);
+ m_len_in_remain = 0;
+ }
+
+ m_pcontent_encoding_handler->update_in(chunk_body);
+
+ if(!m_len_in_remain)
+ m_chunked_state = http_chunked_state_chunk_head;
+ }
+ break;
+ case http_chunked_state_done:
+ m_state = reciev_machine_state_done;
+ return true;
+ case http_chunked_state_undefined:
+ default:
+ LOG_ERROR("http_stream_filter::handle_chunked(): Wrong state" << m_chunked_state);
+ return false;
+ }
+ }
+
+ return true;
+ }
+ //---------------------------------------------------------------------------
+ inline
+ bool parse_header(http_header_info& body_info, const std::string& m_cache_to_process)
+ {
+ LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_4);
+
+ STATIC_REGEXP_EXPR_1(rexp_mach_field,
+ "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)"
+ // 12 3 4 5 6 7 8 9
+ "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
+ //10 1112 13
+ boost::regex::icase | boost::regex::normal);
+
+ boost::smatch result;
+ std::string::const_iterator it_current_bound = m_cache_to_process.begin();
+ std::string::const_iterator it_end_bound = m_cache_to_process.end();
+
+
+
+ //lookup all fields and fill well-known fields
+ while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
+ {
+ const size_t field_val = 12;
+ //const size_t field_etc_name = 10;
+
+ int i = 2; //start position = 2
+ if(result[i++].matched)//"Connection"
+ body_info.m_connection = result[field_val];
+ else if(result[i++].matched)//"Referrer"
+ body_info.m_referer = result[field_val];
+ else if(result[i++].matched)//"Content-Length"
+ body_info.m_content_length = result[field_val];
+ else if(result[i++].matched)//"Content-Type"
+ body_info.m_content_type = result[field_val];
+ else if(result[i++].matched)//"Transfer-Encoding"
+ body_info.m_transfer_encoding = result[field_val];
+ else if(result[i++].matched)//"Content-Encoding"
+ body_info.m_content_encoding = result[field_val];
+ else if(result[i++].matched)//"Host"
+ { body_info.m_host = result[field_val];
+ string_tools::trim(body_info.m_host);
+ }
+ else if(result[i++].matched)//"Cookie"
+ body_info.m_cookie = result[field_val];
+ else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
+ {;}
+ else
+ {CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);}
+
+ it_current_bound = result[(int)result.size()-1]. first;
+ }
+ return true;
+
+ }
+ inline
+ bool analize_first_response_line()
+ {
+
+ //First line response, look like this: "HTTP/1.1 200 OK"
+ STATIC_REGEXP_EXPR_1(rexp_match_first_response_line, "^HTTP/(\\d+).(\\d+) ((\\d)\\d{2})( [^\n]*)?\r?\n", boost::regex::icase | boost::regex::normal);
+ // 1 2 34 5
+ //size_t match_len = 0;
+ boost::smatch result;
+ if(boost::regex_search( m_header_cache, result, rexp_match_first_response_line, boost::match_default) && result[0].matched)
+ {
+ CHECK_AND_ASSERT_MES(result[1].matched&&result[2].matched, false, "http_stream_filter::handle_invoke_reply_line() assert failed...");
+ m_response_info.m_http_ver_hi = boost::lexical_cast<int>(result[1]);
+ m_response_info.m_http_ver_lo = boost::lexical_cast<int>(result[2]);
+ m_response_info.m_response_code = boost::lexical_cast<int>(result[3]);
+
+ m_header_cache.erase(to_nonsonst_iterator(m_header_cache, result[0].first), to_nonsonst_iterator(m_header_cache, result[0].second));
+ return true;
+ }else
+ {
+ LOG_ERROR("http_stream_filter::handle_invoke_reply_line(): Failed to match first response line:" << m_header_cache);
+ return false;
+ }
+
+ }
+ inline
+ bool set_reply_content_encoder()
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_gzip, "^.*?((gzip)|(deflate))", boost::regex::icase | boost::regex::normal);
+ boost::smatch result; // 12 3
+ if(boost::regex_search( m_response_info.m_header_info.m_content_encoding, result, rexp_match_gzip, boost::match_default) && result[0].matched)
+ {
+#ifdef HTTP_ENABLE_GZIP
+ m_pcontent_encoding_handler.reset(new content_encoding_gzip(this, result[3].matched));
+#else
+ m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
+ LOG_ERROR("GZIP encoding not supported in this build, please add zlib to your project and define HTTP_ENABLE_GZIP");
+ return false;
+#endif
+ }
+ else
+ {
+ m_pcontent_encoding_handler.reset(new do_nothing_sub_handler(this));
+ }
+
+ return true;
+ }
+ inline
+ bool analize_cached_header_and_invoke_state()
+ {
+ m_response_info.clear();
+ analize_first_response_line();
+ std::string fake_str; //gcc error workaround
+
+ bool res = parse_header(m_response_info.m_header_info, m_header_cache);
+ CHECK_AND_ASSERT_MES(res, false, "http_stream_filter::analize_cached_reply_header_and_invoke_state(): failed to anilize reply header: " << m_header_cache);
+
+ set_reply_content_encoder();
+
+ m_len_in_summary = 0;
+ bool content_len_valid = false;
+ if(m_response_info.m_header_info.m_content_length.size())
+ content_len_valid = string_tools::get_xtype_from_string(m_len_in_summary, m_response_info.m_header_info.m_content_length);
+
+
+
+ if(!m_len_in_summary && ((m_response_info.m_response_code>=100&&m_response_info.m_response_code<200)
+ || 204 == m_response_info.m_response_code
+ || 304 == m_response_info.m_response_code) )
+ {//There will be no response body, server will display the local page with error
+ m_state = reciev_machine_state_done;
+ return true;
+ }else if(m_response_info.m_header_info.m_transfer_encoding.size())
+ {
+ string_tools::trim(m_response_info.m_header_info.m_transfer_encoding);
+ if(string_tools::compare_no_case(m_response_info.m_header_info.m_transfer_encoding, "chunked"))
+ {
+ LOG_ERROR("Wrong Transfer-Encoding:" << m_response_info.m_header_info.m_transfer_encoding);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+ m_state = reciev_machine_state_body_chunked;
+ m_chunked_state = http_chunked_state_chunk_head;
+ return true;
+ }
+ else if(!m_response_info.m_header_info.m_content_length.empty())
+ {
+ //In the response header the length was specified
+ if(!content_len_valid)
+ {
+ LOG_ERROR("http_stream_filter::analize_cached_reply_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_response_info.m_header_info.m_content_length);
+ m_state = reciev_machine_state_error;
+ return false;
+ }
+ if(!m_len_in_summary)
+ {
+ m_state = reciev_machine_state_done;
+ return true;
+ }
+ else
+ {
+ m_len_in_remain = m_len_in_summary;
+ m_state = reciev_machine_state_body_content_len;
+ return true;
+ }
+ }else if(!m_response_info.m_header_info.m_connection.empty() && is_connection_close_field(m_response_info.m_header_info.m_connection))
+ { //By indirect signs we suspect that data transfer will end with a connection break
+ m_state = reciev_machine_state_body_connection_close;
+ }else if(is_multipart_body(m_response_info.m_header_info, fake_str))
+ {
+ m_state = reciev_machine_state_error;
+ LOG_ERROR("Unsupported MULTIPART BODY.");
+ return false;
+ }else
+ { //Apparently there are no signs of the form of transfer, will receive data until the connection is closed
+ m_state = reciev_machine_state_error;
+ LOG_PRINT("Undefinded transfer type, consider http_body_transfer_connection_close method.", LOG_LEVEL_2);
+ return false;
+ }
+ return false;
+ }
+ inline
+ bool is_connection_close_field(const std::string& str)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_close, "^\\s*close", boost::regex::icase | boost::regex::normal);
+ boost::smatch result;
+ if(boost::regex_search( str, result, rexp_match_close, boost::match_default) && result[0].matched)
+ return true;
+ else
+ return false;
+ }
+ inline
+ bool is_multipart_body(const http_header_info& head_info, OUT std::string& boundary)
+ {
+ //Check whether this is multi part - if yes, capture boundary immediately
+ STATIC_REGEXP_EXPR_1(rexp_match_multipart_type, "^\\s*multipart/([\\w\\-]+); boundary=((\"(.*?)\")|(\\\\\"(.*?)\\\\\")|([^\\s;]*))", boost::regex::icase | boost::regex::normal);
+ boost::smatch result;
+ if(boost::regex_search(head_info.m_content_type, result, rexp_match_multipart_type, boost::match_default) && result[0].matched)
+ {
+ if(result[4].matched)
+ boundary = result[4];
+ else if(result[6].matched)
+ boundary = result[6];
+ else if(result[7].matched)
+ boundary = result[7];
+ else
+ {
+ LOG_ERROR("Failed to match boundary in content-type=" << head_info.m_content_type);
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+
+ return true;
+ }
+ };
+
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ //inline
+ template<class t_transport>
+ bool invoke_request(const std::string& url, t_transport& tr, unsigned int timeout, const http_response_info** ppresponse_info, const std::string& method = "GET", const std::string& body = std::string(), const fields_list& additional_params = fields_list())
+ {
+ http::url_content u_c;
+ bool res = parse_url(url, u_c);
+
+ if(!tr.is_connected())
+ {
+ CHECK_AND_ASSERT_MES(res, false, "failed to parse url: " << url);
+
+ if(!u_c.port)
+ u_c.port = 80;//default for http
+
+ res = tr.connect(u_c.host, static_cast<int>(u_c.port), timeout);
+ CHECK_AND_ASSERT_MES(res, false, "failed to connect " << u_c.host << ":" << u_c.port);
+ }
+
+ return tr.invoke(u_c.uri, method, body, ppresponse_info, additional_params);
+ }
+
+ }
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_client_abstract_invoke.h b/contrib/epee/include/net/http_client_abstract_invoke.h
new file mode 100644
index 000000000..425a355ee
--- /dev/null
+++ b/contrib/epee/include/net/http_client_abstract_invoke.h
@@ -0,0 +1,98 @@
+
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+#include "storages/serializeble_struct_helper.h"
+
+namespace epee
+{
+ namespace net_utils
+ {
+ namespace http
+ {
+ template<class TArg, class TResult, class TTransport>
+ bool invoke_http_json_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET")
+ {
+ std::string req_param;
+ StorageNamed::InMemStorageSpace::json::store_t_to_json(out_struct, req_param);
+
+ const http_response_info* pri = NULL;
+ if(!invoke_request(url, transport, timeout, &pri, method, req_param))
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url);
+ return false;
+ }
+
+ if(!pri->m_response_code)
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
+ return false;
+ }
+
+ if(pri->m_response_code != 200)
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
+ return false;
+ }
+
+ return StorageNamed::InMemStorageSpace::json::load_t_from_json(result_struct, pri->m_body);
+ }
+
+
+
+ template<class TArg, class TResult, class TTransport>
+ bool invoke_http_bin_remote_command(const std::string& url, TArg& out_struct, TResult& result_struct, TTransport& transport, unsigned int timeout = 5000, const std::string& method = "GET")
+ {
+ std::string req_param;
+ epee::StorageNamed::save_struct_as_storage_to_buff(out_struct, req_param);
+
+ const http_response_info* pri = NULL;
+ if(!invoke_request(url, transport, timeout, &pri, method, req_param))
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url);
+ return false;
+ }
+
+ if(!pri->m_response_code)
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url << ", internal error (null response ptr)");
+ return false;
+ }
+
+ if(pri->m_response_code != 200)
+ {
+ LOG_PRINT_L1("Failed to invoke http request to " << url << ", wrong response code: " << pri->m_response_code);
+ return false;
+ }
+
+ return epee::StorageNamed::load_struct_from_storage_buff(result_struct, pri->m_body);
+ }
+
+
+ }
+ }
+}
diff --git a/contrib/epee/include/net/http_client_base.h b/contrib/epee/include/net/http_client_base.h
new file mode 100644
index 000000000..571e27f73
--- /dev/null
+++ b/contrib/epee/include/net/http_client_base.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+
+namespace epee
+{
+ namespace net_utils
+ {
+ struct i_sub_handler
+ {
+ virtual ~i_sub_handler(){}
+
+ virtual bool update_in( std::string& piece_of_transfer)=0;
+ virtual void stop(std::string& OUT collect_remains)=0;
+ virtual bool update_and_stop(std::string& OUT collect_remains, bool& is_changed)
+ {
+ is_changed = true;
+ bool res = this->update_in(collect_remains);
+ if(res)
+ this->stop(collect_remains);
+ return res;
+ }
+ };
+
+
+ struct i_target_handler
+ {
+ virtual ~i_target_handler(){}
+ virtual bool handle_target_data( std::string& piece_of_transfer)=0;
+ };
+
+
+ class do_nothing_sub_handler: public i_sub_handler
+ {
+ public:
+ do_nothing_sub_handler(i_target_handler* powner_filter):m_powner_filter(powner_filter)
+ {}
+ virtual bool update_in( std::string& piece_of_transfer)
+ {
+ return m_powner_filter->handle_target_data(piece_of_transfer);
+ }
+ virtual void stop(std::string& OUT collect_remains)
+ {
+
+ }
+ i_target_handler* m_powner_filter;
+ };
+ }
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_client_via_api_helper.h b/contrib/epee/include/net/http_client_via_api_helper.h
new file mode 100644
index 000000000..45a70993b
--- /dev/null
+++ b/contrib/epee/include/net/http_client_via_api_helper.h
@@ -0,0 +1,177 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+#include <wininet.h>
+#include <atlutil.h>
+#pragma comment(lib, "Wininet.lib")
+
+namespace epee
+{
+namespace net_utils
+{
+ inline
+ bool http_ssl_invoke(const std::string& url, const std::string usr, const std::string psw, std::string& http_response_body, bool use_post = false)
+ {
+ bool final_res = false;
+
+ ATL::CUrl url_obj;
+ BOOL crack_rss = url_obj.CrackUrl(string_encoding::convert_to_t<std::basic_string<TCHAR> >(url).c_str());
+
+ HINTERNET hinet = ::InternetOpenA(SHARED_JOBSCOMMON_HTTP_AGENT, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+ if(!hinet)
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetOpenA, \nError: " << err << " " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ return false;
+ }
+
+ DWORD dwFlags = 0;
+ DWORD dwBuffLen = sizeof(dwFlags);
+
+ if(usr.size())
+ {
+ dwFlags |= INTERNET_FLAG_IGNORE_CERT_CN_INVALID|INTERNET_FLAG_IGNORE_CERT_DATE_INVALID|
+ INTERNET_FLAG_PRAGMA_NOCACHE | SECURITY_FLAG_IGNORE_UNKNOWN_CA|INTERNET_FLAG_SECURE;
+ }else
+ {
+ dwFlags |= INTERNET_FLAG_PRAGMA_NOCACHE;
+ }
+
+
+ int port = url_obj.GetPortNumber();
+ BOOL res = FALSE;
+
+ HINTERNET hsession = ::InternetConnectA(hinet, string_encoding::convert_to_ansii(url_obj.GetHostName()).c_str(), port/*INTERNET_DEFAULT_HTTPS_PORT*/, usr.c_str(), psw.c_str(), INTERNET_SERVICE_HTTP, dwFlags, NULL);
+ if(hsession)
+ {
+ const std::string uri = string_encoding::convert_to_ansii(url_obj.GetUrlPath()) + string_encoding::convert_to_ansii(url_obj.GetExtraInfo());
+
+ HINTERNET hrequest = ::HttpOpenRequestA(hsession, use_post?"POST":NULL, uri.c_str(), NULL, NULL,NULL, dwFlags, NULL);
+ if(hrequest)
+ {
+ while(true)
+ {
+ res = ::HttpSendRequestA(hrequest, NULL, 0, NULL, 0);
+ if(!res)
+ {
+ //ERROR_INTERNET_INVALID_CA 45
+ //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5)
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call HttpSendRequestA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ break;
+ }
+
+ DWORD code = 0;
+ DWORD buf_len = sizeof(code);
+ DWORD index = 0;
+ res = ::HttpQueryInfo(hrequest, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &code, &buf_len, &index);
+ if(!res)
+ {
+ //ERROR_INTERNET_INVALID_CA 45
+ //ERROR_INTERNET_INVALID_URL (INTERNET_ERROR_BASE + 5)
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call HttpQueryInfo, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ break;
+ }
+ if(code < 200 || code > 299)
+ {
+ LOG_PRINT("Wrong server response, HttpQueryInfo returned statuse code" << code , LOG_LEVEL_0);
+ break;
+ }
+
+
+ char buff[100000] = {0};
+ DWORD readed = 0;
+ while(true)
+ {
+ res = ::InternetReadFile(hrequest, buff, sizeof(buff), &readed);
+ if(!res)
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetReadFile, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ break;
+ }
+ if(readed)
+ {
+ http_response_body.append(buff, readed);
+ }
+ else
+ break;
+ }
+
+ if(!res)
+ break;
+
+
+ //we success
+ final_res = true;
+
+ res = ::InternetCloseHandle(hrequest);
+ if(!res)
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ //ERROR_INTERNET_INVALID_CA
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetOpenUrlA, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ return false;
+ }
+
+ res = ::InternetCloseHandle(hsession);
+ if(!res)
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ }
+ }else
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetConnectA(" << string_encoding::convert_to_ansii(url_obj.GetHostName()) << ", port " << port << " \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ }
+
+
+
+ res = ::InternetCloseHandle(hinet);
+ if(!res)
+ {
+ int err = ::GetLastError();
+ LOG_PRINT("Failed to call InternetCloseHandle, \nError: " << log_space::get_win32_err_descr(err), LOG_LEVEL_0);
+ }
+ return final_res;
+ }
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_protocol_handler.h b/contrib/epee/include/net/http_protocol_handler.h
new file mode 100644
index 000000000..4aebcf2aa
--- /dev/null
+++ b/contrib/epee/include/net/http_protocol_handler.h
@@ -0,0 +1,209 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#ifndef _HTTP_SERVER_H_
+#define _HTTP_SERVER_H_
+
+#include <string>
+#include "net_utils_base.h"
+#include "to_nonconst_iterator.h"
+#include "http_base.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace http
+ {
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct http_server_config
+ {
+ std::string m_folder;
+ critical_section m_lock;
+ };
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class simple_http_connection_handler
+ {
+ public:
+ typedef net_utils::connection_context_base connection_context;
+ typedef http_server_config config_type;
+
+ simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config);
+ virtual ~simple_http_connection_handler(){}
+
+ bool release_protocol()
+ {
+ return true;
+ }
+
+ virtual bool thread_init()
+ {
+ return true;
+ }
+
+ virtual bool thread_deinit()
+ {
+ return true;
+ }
+ bool after_init_connection()
+ {
+ return true;
+ }
+ virtual bool handle_recv(const void* ptr, size_t cb);
+ virtual bool handle_request(const http::http_request_info& query_info, http_response_info& response);
+
+
+ //temporary here
+ //bool parse_uri(const std::string uri, uri_content& content);
+
+ private:
+ enum machine_state{
+ http_state_retriving_comand_line,
+ http_state_retriving_header,
+ http_state_retriving_body,
+ http_state_connection_close,
+ http_state_error
+ };
+
+ enum body_transfer_type{
+ http_body_transfer_chunked,
+ http_body_transfer_measure,//mean "Content-Length" valid
+ http_body_transfer_chunked_instead_measure,
+ http_body_transfer_connection_close,
+ http_body_transfer_multipart,
+ http_body_transfer_undefined
+ };
+
+ bool handle_buff_in(std::string& buf);
+
+ bool analize_cached_request_header_and_invoke_state(size_t pos);
+
+ bool handle_invoke_query_line();
+ bool parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos);
+ std::string::size_type match_end_of_header(const std::string& buf);
+ bool get_len_from_content_lenght(const std::string& str, size_t& len);
+ bool handle_retriving_query_body();
+ bool handle_query_measure();
+ bool set_ready_state();
+ bool slash_to_back_slash(std::string& str);
+ std::string get_file_mime_tipe(const std::string& path);
+ std::string get_response_header(const http_response_info& response);
+
+ //major function
+ inline bool handle_request_and_send_response(const http::http_request_info& query_info);
+
+
+ std::string get_not_found_response_body(const std::string& URI);
+
+ std::string m_root_path;
+ std::string m_cache;
+ machine_state m_state;
+ body_transfer_type m_body_transfer_type;
+ bool m_is_stop_handling;
+ http::http_request_info m_query_info;
+ size_t m_len_summary, m_len_remain;
+ config_type& m_config;
+ bool m_want_close;
+ protected:
+ i_service_endpoint* m_psnd_hndlr;
+ };
+
+
+ struct i_http_server_handler
+ {
+ virtual ~i_http_server_handler(){}
+ virtual bool handle_http_request(const http_request_info& query_info, http_response_info& response, const net_utils::connection_context_base& m_conn_context)=0;
+ virtual bool init_server_thread(){return true;}
+ virtual bool deinit_server_thread(){return true;}
+ };
+
+
+ struct custum_handler_config: public http_server_config
+ {
+ i_http_server_handler* m_phandler;
+ };
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class http_custom_handler: public simple_http_connection_handler
+ {
+ public:
+ typedef custum_handler_config config_type;
+
+ http_custom_handler(i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):simple_http_connection_handler(psnd_hndlr, config),
+ m_config(config),
+ m_conn_context(conn_context)
+ {}
+ inline bool handle_request(const http_request_info& query_info, http_response_info& response)
+ {
+ CHECK_AND_ASSERT_MES(m_config.m_phandler, false, "m_config.m_phandler is NULL!!!!");
+ //fill with default values
+ response.m_mime_tipe = "text/plain";
+ response.m_response_code = 200;
+ response.m_response_comment = "OK";
+ response.m_body.clear();
+ return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context);
+ }
+
+ virtual bool thread_init()
+ {
+ return m_config.m_phandler->init_server_thread();;
+ }
+
+ virtual bool thread_deinit()
+ {
+ return m_config.m_phandler->deinit_server_thread();
+ }
+ void handle_qued_callback()
+ {}
+ bool after_init_connection()
+ {
+ return true;
+ }
+
+ private:
+ //simple_http_connection_handler::config_type m_stub_config;
+ config_type& m_config;
+ const net_utils::connection_context_base& m_conn_context;
+ };
+ }
+}
+}
+
+#include "http_protocol_handler.inl"
+
+#endif //_HTTP_SERVER_H_
diff --git a/contrib/epee/include/net/http_protocol_handler.inl b/contrib/epee/include/net/http_protocol_handler.inl
new file mode 100644
index 000000000..810c46db9
--- /dev/null
+++ b/contrib/epee/include/net/http_protocol_handler.inl
@@ -0,0 +1,664 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#include <boost/regex.hpp>
+#include <boost/lexical_cast.hpp>
+#include "http_protocol_handler.h"
+#include "reg_exp_definer.h"
+#include "string_tools.h"
+#include "file_io_utils.h"
+#include "net_parse_helpers.h"
+
+#define HTTP_MAX_URI_LEN 9000
+#define HTTP_MAX_HEADER_LEN 100000
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace http
+ {
+
+ struct multipart_entry
+ {
+ std::list<std::pair<std::string, std::string> > m_etc_header_fields;
+ std::string m_content_disposition;
+ std::string m_content_type;
+ std::string m_body;
+ };
+
+ inline
+ bool match_boundary(const std::string& content_type, std::string& boundary)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_match_boundary, "boundary=(.*?)(($)|([;\\s,]))", boost::regex::icase | boost::regex::normal);
+ // 1
+ boost::smatch result;
+ if(boost::regex_search(content_type, result, rexp_match_boundary, boost::match_default) && result[0].matched)
+ {
+ boundary = result[1];
+ return true;
+ }
+
+ return false;
+ }
+
+ inline
+ bool parse_header(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_mach_field,
+ "\n?((Content-Disposition)|(Content-Type)"
+ // 12 3
+ "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
+ //4 56 7
+ boost::regex::icase | boost::regex::normal);
+
+ boost::smatch result;
+ std::string::const_iterator it_current_bound = it_begin;
+ std::string::const_iterator it_end_bound = it_end;
+
+ //lookup all fields and fill well-known fields
+ while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
+ {
+ const size_t field_val = 6;
+ const size_t field_etc_name = 4;
+
+ int i = 2; //start position = 2
+ if(result[i++].matched)//"Content-Disposition"
+ entry.m_content_disposition = result[field_val];
+ else if(result[i++].matched)//"Content-Type"
+ entry.m_content_type = result[field_val];
+ else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
+ entry.m_etc_header_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
+ else
+ {
+ LOG_ERROR("simple_http_connection_handler::parse_header() not matched last entry in:"<<std::string(it_current_bound, it_end));
+ }
+
+ it_current_bound = result[(int)result.size()-1].first;
+ }
+ return true;
+ }
+
+ inline
+ bool handle_part_of_multipart(std::string::const_iterator it_begin, std::string::const_iterator it_end, multipart_entry& entry)
+ {
+ std::string end_str = "\r\n\r\n";
+ std::string::const_iterator end_header_it = std::search(it_begin, it_end, end_str.begin(), end_str.end());
+ if(end_header_it == it_end)
+ {
+ //header not matched
+ return false;
+ }
+
+ if(!parse_header(it_begin, end_header_it+4, entry))
+ {
+ LOG_ERROR("Failed to parse header:" << std::string(it_begin, end_header_it+2));
+ return false;
+ }
+
+ entry.m_body.assign(end_header_it+4, it_end);
+
+ return true;
+ }
+
+ inline
+ bool parse_multipart_body(const std::string& content_type, const std::string& body, std::list<multipart_entry>& out_values)
+ {
+ //bool res = file_io_utils::load_file_to_string("C:\\public\\multupart_data", body);
+
+ std::string boundary;
+ if(!match_boundary(content_type, boundary))
+ {
+ LOG_PRINT("Failed to match boundary in content type: " << content_type, LOG_LEVEL_0);
+ return false;
+ }
+
+ boundary+="\r\n";
+ bool is_stop = false;
+ bool first_step = true;
+
+ std::string::const_iterator it_begin = body.begin();
+ std::string::const_iterator it_end;
+ while(!is_stop)
+ {
+ std::string::size_type pos = body.find(boundary, std::distance(body.begin(), it_begin));
+
+ if(std::string::npos == pos)
+ {
+ is_stop = true;
+ boundary.erase(boundary.size()-2, 2);
+ boundary+= "--";
+ pos = body.find(boundary, std::distance(body.begin(), it_begin));
+ if(std::string::npos == pos)
+ {
+ LOG_PRINT("Error: Filed to match closing multipart tag", LOG_LEVEL_0);
+ it_end = body.end();
+ }else
+ {
+ it_end = body.begin() + pos;
+ }
+ }else
+ it_end = body.begin() + pos;
+
+
+ if(first_step && !is_stop)
+ {
+ first_step = false;
+ it_begin = it_end + boundary.size();
+ std::string temp = "\r\n--";
+ boundary = temp + boundary;
+ continue;
+ }
+
+ out_values.push_back(multipart_entry());
+ if(!handle_part_of_multipart(it_begin, it_end, out_values.back()))
+ {
+ LOG_PRINT("Failed to handle_part_of_multipart", LOG_LEVEL_0);
+ return false;
+ }
+
+ it_begin = it_end + boundary.size();
+ }
+
+ return true;
+ }
+
+
+
+
+ //--------------------------------------------------------------------------------------------
+ inline
+ simple_http_connection_handler::simple_http_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config):
+ m_state(http_state_retriving_comand_line),
+ m_body_transfer_type(http_body_transfer_undefined),
+ m_is_stop_handling(false),
+ m_len_summary(0),
+ m_len_remain(0),
+ m_config(config),
+ m_want_close(false),
+ m_psnd_hndlr(psnd_hndlr)
+ {
+
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::set_ready_state()
+ {
+ m_is_stop_handling = false;
+ m_state = http_state_retriving_comand_line;
+ m_body_transfer_type = http_body_transfer_undefined;
+ m_query_info.clear();
+ m_len_summary = 0;
+ return true;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_recv(const void* ptr, size_t cb)
+ {
+ std::string buf((const char*)ptr, cb);
+ //LOG_PRINT_L0("HTTP_RECV: " << ptr << "\r\n" << buf);
+ //file_io_utils::save_string_to_file(string_tools::get_current_module_folder() + "/" + boost::lexical_cast<std::string>(ptr), std::string((const char*)ptr, cb));
+
+ bool res = handle_buff_in(buf);
+ if(m_want_close/*m_state == http_state_connection_close || m_state == http_state_error*/)
+ return false;
+ return res;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_buff_in(std::string& buf)
+ {
+
+ if(m_cache.size())
+ m_cache += buf;
+ else
+ m_cache.swap(buf);
+
+ m_is_stop_handling = false;
+ while(!m_is_stop_handling)
+ {
+ switch(m_state)
+ {
+ case http_state_retriving_comand_line:
+ //The HTTP protocol does not place any a priori limit on the length of a URI. (c)RFC2616
+ //but we forebly restirct it len to HTTP_MAX_URI_LEN to make it more safely
+ if(!m_cache.size())
+ break;
+
+ //check_and_handle_fake_response();
+ if((m_cache[0] == '\r' || m_cache[0] == '\n'))
+ {
+ //some times it could be that before query line cold be few line breaks
+ //so we have to be calm without panic with assers
+ m_cache.erase(0, 1);
+ break;
+ }
+
+ if(std::string::npos != m_cache.find('\n', 0))
+ handle_invoke_query_line();
+ else
+ {
+ m_is_stop_handling = true;
+ if(m_cache.size() > HTTP_MAX_URI_LEN)
+ {
+ LOG_ERROR("simple_http_connection_handler::handle_buff_out: Too long URI line");
+ m_state = http_state_error;
+ return false;
+ }
+ }
+ break;
+ case http_state_retriving_header:
+ {
+ std::string::size_type pos = match_end_of_header(m_cache);
+ if(std::string::npos == pos)
+ {
+ m_is_stop_handling = true;
+ if(m_cache.size() > HTTP_MAX_HEADER_LEN)
+ {
+ LOG_ERROR("simple_http_connection_handler::handle_buff_in: Too long header area");
+ m_state = http_state_error;
+ return false;
+ }
+ break;
+ }
+ analize_cached_request_header_and_invoke_state(pos);
+ break;
+ }
+ case http_state_retriving_body:
+ return handle_retriving_query_body();
+ case http_state_connection_close:
+ return false;
+ default:
+ LOG_ERROR("simple_http_connection_handler::handle_char_out: Wrong state: " << m_state);
+ return false;
+ case http_state_error:
+ LOG_ERROR("simple_http_connection_handler::handle_char_out: Error state!!!");
+ return false;
+ }
+
+ if(!m_cache.size())
+ m_is_stop_handling = true;
+ }
+
+ return true;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool analize_http_method(const boost::smatch& result, http::http_method& method, int& http_ver_major, int& http_ver_minor)
+ {
+ CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed...");
+ http_ver_major = boost::lexical_cast<int>(result[11]);
+ http_ver_minor = boost::lexical_cast<int>(result[12]);
+ if(result[4].matched)
+ method = http::http_method_get;
+ else if(result[5].matched)
+ method = http::http_method_head;
+ else if(result[6].matched)
+ method = http::http_method_post;
+ else if(result[7].matched)
+ method = http::http_method_put;
+ else
+ method = http::http_method_etc;
+
+ return true;
+ }
+
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_invoke_query_line()
+ {
+ LOG_FRAME("simple_http_connection_handler::handle_recognize_protocol_out(*)", LOG_LEVEL_3);
+
+ STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^(((OPTIONS)|(GET)|(HEAD)|(POST)|(PUT)|(DELETE)|(TRACE)) (\\S+) HTTP/(\\d+).(\\d+))\r?\n", boost::regex::icase | boost::regex::normal);
+ // 123 4 5 6 7 8 9 10 11 12
+ //size_t match_len = 0;
+ boost::smatch result;
+ if(boost::regex_search(m_cache, result, rexp_match_command_line, boost::match_default) && result[0].matched)
+ {
+ analize_http_method(result, m_query_info.m_http_method, m_query_info.m_http_ver_hi, m_query_info.m_http_ver_hi);
+ m_query_info.m_URI = result[10];
+ parse_uri(m_query_info.m_URI, m_query_info.m_uri_content);
+ m_query_info.m_http_method_str = result[2];
+ m_query_info.m_full_request_str = result[0];
+
+ m_cache.erase(m_cache.begin(), to_nonsonst_iterator(m_cache, result[0].second));
+
+ m_state = http_state_retriving_header;
+
+ return true;
+ }else
+ {
+ m_state = http_state_error;
+ LOG_ERROR("simple_http_connection_handler::handle_invoke_query_line(): Failed to match first line: " << m_cache);
+ return false;
+ }
+
+ return false;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline std::string::size_type simple_http_connection_handler::match_end_of_header(const std::string& buf)
+ {
+
+ //Here we returning head size, including terminating sequence (\r\n\r\n or \n\n)
+ std::string::size_type res = buf.find("\r\n\r\n");
+ if(std::string::npos != res)
+ return res+4;
+ res = buf.find("\n\n");
+ if(std::string::npos != res)
+ return res+2;
+ return res;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::analize_cached_request_header_and_invoke_state(size_t pos)
+ {
+ //LOG_PRINT_L4("HTTP HEAD:\r\n" << m_cache.substr(0, pos));
+
+ LOG_FRAME("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(*)", LOG_LEVEL_3);
+
+ m_query_info.m_full_request_buf_size = pos;
+ m_query_info.m_request_head.assign(m_cache.begin(), m_cache.begin()+pos);
+
+ if(!parse_cached_header(m_query_info.m_header_info, m_cache, pos))
+ {
+ LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): failed to anilize request header: " << m_cache);
+ m_state = http_state_error;
+ }
+
+ m_cache.erase(0, pos);
+
+ std::string req_command_str = m_query_info.m_full_request_str;
+ //if we have POST or PUT command, it is very possible tha we will get body
+ //but now, we suppose than we have body only in case of we have "ContentLength"
+ if(m_query_info.m_header_info.m_content_length.size())
+ {
+ m_state = http_state_retriving_body;
+ m_body_transfer_type = http_body_transfer_measure;
+ if(!get_len_from_content_lenght(m_query_info.m_header_info.m_content_length, m_len_summary))
+ {
+ LOG_ERROR("simple_http_connection_handler::analize_cached_request_header_and_invoke_state(): Failed to get_len_from_content_lenght();, m_query_info.m_content_length="<<m_query_info.m_header_info.m_content_length);
+ m_state = http_state_error;
+ return false;
+ }
+ if(0 == m_len_summary)
+ { //current query finished, next will be next query
+ if(handle_request_and_send_response(m_query_info))
+ set_ready_state();
+ else
+ m_state = http_state_error;
+ }
+ m_len_remain = m_len_summary;
+ }else
+ {//current query finished, next will be next query
+ handle_request_and_send_response(m_query_info);
+ set_ready_state();
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_retriving_query_body()
+ {
+ switch(m_body_transfer_type)
+ {
+ case http_body_transfer_measure:
+ return handle_query_measure();
+ case http_body_transfer_chunked:
+ case http_body_transfer_connection_close:
+ case http_body_transfer_multipart:
+ case http_body_transfer_undefined:
+ default:
+ LOG_ERROR("simple_http_connection_handler::handle_retriving_query_body(): Unexpected m_body_query_type state:" << m_body_transfer_type);
+ m_state = http_state_error;
+ return false;
+ }
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_query_measure()
+ {
+
+ if(m_len_remain >= m_cache.size())
+ {
+ m_len_remain -= m_cache.size();
+ m_query_info.m_body += m_cache;
+ m_cache.clear();
+ }else
+ {
+ m_query_info.m_body.append(m_cache.begin(), m_cache.begin() + m_len_remain);
+ m_cache.erase(0, m_len_remain);
+ m_len_remain = 0;
+ }
+
+ if(!m_len_remain)
+ {
+ if(handle_request_and_send_response(m_query_info))
+ set_ready_state();
+ else
+ m_state = http_state_error;
+ }
+ return true;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
+ {
+ LOG_FRAME("http_stream_filter::parse_cached_header(*)", LOG_LEVEL_3);
+
+ STATIC_REGEXP_EXPR_1(rexp_mach_field,
+ "\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)"
+ // 12 3 4 5 6 7 8 9
+ "|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
+ //10 1112 13
+ boost::regex::icase | boost::regex::normal);
+
+ boost::smatch result;
+ std::string::const_iterator it_current_bound = m_cache_to_process.begin();
+ std::string::const_iterator it_end_bound = m_cache_to_process.begin()+pos;
+
+ body_info.clear();
+
+ //lookup all fields and fill well-known fields
+ while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
+ {
+ const size_t field_val = 12;
+ const size_t field_etc_name = 10;
+
+ int i = 2; //start position = 2
+ if(result[i++].matched)//"Connection"
+ body_info.m_connection = result[field_val];
+ else if(result[i++].matched)//"Referer"
+ body_info.m_referer = result[field_val];
+ else if(result[i++].matched)//"Content-Length"
+ body_info.m_content_length = result[field_val];
+ else if(result[i++].matched)//"Content-Type"
+ body_info.m_content_type = result[field_val];
+ else if(result[i++].matched)//"Transfer-Encoding"
+ body_info.m_transfer_encoding = result[field_val];
+ else if(result[i++].matched)//"Content-Encoding"
+ body_info.m_content_encoding = result[field_val];
+ else if(result[i++].matched)//"Host"
+ body_info.m_host = result[field_val];
+ else if(result[i++].matched)//"Cookie"
+ body_info.m_cookie = result[field_val];
+ else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
+ body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
+ else
+ {
+ LOG_ERROR("simple_http_connection_handler::parse_cached_header() not matched last entry in:"<<m_cache_to_process);
+ }
+
+ it_current_bound = result[(int)result.size()-1]. first;
+ }
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::get_len_from_content_lenght(const std::string& str, size_t& OUT len)
+ {
+ STATIC_REGEXP_EXPR_1(rexp_mach_field, "\\d+", boost::regex::normal);
+ std::string res;
+ boost::smatch result;
+ if(!(boost::regex_search( str, result, rexp_mach_field, boost::match_default) && result[0].matched))
+ return false;
+
+ len = boost::lexical_cast<size_t>(result[0]);
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_request_and_send_response(const http::http_request_info& query_info)
+ {
+ http_response_info response;
+ bool res = handle_request(query_info, response);
+ //CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" );
+
+ std::string response_data = get_response_header(response);
+
+ //LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body);
+ LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data);
+
+ m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size());
+ if(response.m_body.size())
+ m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size());
+ return res;
+ }
+ //-----------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::handle_request(const http::http_request_info& query_info, http_response_info& response)
+ {
+
+ std::string uri_to_path = query_info.m_uri_content.m_path;
+ if("/" == uri_to_path)
+ uri_to_path = "/index.html";
+
+ //slash_to_back_slash(uri_to_path);
+ m_config.m_lock.lock();
+ std::string destination_file_path = m_config.m_folder + uri_to_path;
+ m_config.m_lock.unlock();
+ if(!file_io_utils::load_file_to_string(destination_file_path.c_str(), response.m_body))
+ {
+ LOG_PRINT("URI \""<< query_info.m_full_request_str.substr(0, query_info.m_full_request_str.size()-2) << "\" [" << destination_file_path << "] Not Found (404 )" , LOG_LEVEL_1);
+ response.m_body = get_not_found_response_body(query_info.m_URI);
+ response.m_response_code = 404;
+ response.m_response_comment = "Not found";
+ response.m_mime_tipe = "text/html";
+ return true;
+ }
+
+ LOG_PRINT(" -->> " << query_info.m_full_request_str << "\r\n<<--OK" , LOG_LEVEL_3);
+ response.m_response_code = 200;
+ response.m_response_comment = "OK";
+ response.m_mime_tipe = get_file_mime_tipe(uri_to_path);
+
+
+ return true;
+ }
+ //-----------------------------------------------------------------------------------
+ inline std::string simple_http_connection_handler::get_response_header(const http_response_info& response)
+ {
+ std::string buf = "HTTP/1.1 ";
+ buf += boost::lexical_cast<std::string>(response.m_response_code) + " " + response.m_response_comment + "\r\n" +
+ "Server: Siski v0.1\r\n"
+ "Content-Length: ";
+ buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n";
+ buf += "Content-Type: ";
+ buf += response.m_mime_tipe + "\r\n";
+
+ buf += "Last-Modified: ";
+ time_t tm;
+ time(&tm);
+ buf += misc_utils::get_internet_time_str(tm) + "\r\n";
+ buf += "Accept-Ranges: bytes\r\n";
+ //Wed, 01 Dec 2010 03:27:41 GMT"
+
+ string_tools::trim(m_query_info.m_header_info.m_connection);
+ if(m_query_info.m_header_info.m_connection.size())
+ {
+ if(!string_tools::compare_no_case("close", m_query_info.m_header_info.m_connection))
+ {
+ //closing connection after sending
+ buf += "Connection: close\r\n";
+ m_state = http_state_connection_close;
+ m_want_close = true;
+ }
+ }
+ //add additional fields, if it is
+ for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++)
+ buf += it->first + ":" + it->second + "\r\n";
+
+ buf+="\r\n";
+
+ return buf;
+ }
+ //-----------------------------------------------------------------------------------
+ inline std::string simple_http_connection_handler::get_file_mime_tipe(const std::string& path)
+ {
+ std::string result;
+ std::string ext = string_tools::get_extension(path);
+ if(!string_tools::compare_no_case(ext, "gif"))
+ result = "image/gif";
+ else if(!string_tools::compare_no_case(ext, "jpg"))
+ result = "image/jpeg";
+ else if(!string_tools::compare_no_case(ext, "html"))
+ result = "text/html";
+ else if(!string_tools::compare_no_case(ext, "htm"))
+ result = "text/html";
+ else if(!string_tools::compare_no_case(ext, "js"))
+ result = "application/x-javascript";
+ else if(!string_tools::compare_no_case(ext, "css"))
+ result = "text/css";
+ else if(!string_tools::compare_no_case(ext, "xml"))
+ result = "application/xml";
+ else if(!string_tools::compare_no_case(ext, "svg"))
+ result = "image/svg+xml";
+
+
+ return result;
+ }
+ //-----------------------------------------------------------------------------------
+ inline std::string simple_http_connection_handler::get_not_found_response_body(const std::string& URI)
+ {
+ std::string body =
+ "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ "<html><head>\r\n"
+ "<title>404 Not Found</title>\r\n"
+ "</head><body>\r\n"
+ "<h1>Not Found</h1>\r\n"
+ "<p>The requested URL \r\n";
+ body += URI;
+ body += "was not found on this server.</p>\r\n"
+ "</body></html>\r\n";
+
+ return body;
+ }
+ //--------------------------------------------------------------------------------------------
+ inline bool simple_http_connection_handler::slash_to_back_slash(std::string& str)
+ {
+ for(std::string::iterator it = str.begin(); it!=str.end(); it++)
+ if('/' == *it)
+ *it = '\\';
+ return true;
+ }
+ }
+}
+}
+
+//--------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------
+//-------------------------------------------------------------------------------------------- \ No newline at end of file
diff --git a/contrib/epee/include/net/http_server_cp.h b/contrib/epee/include/net/http_server_cp.h
new file mode 100644
index 000000000..bbb167f9f
--- /dev/null
+++ b/contrib/epee/include/net/http_server_cp.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#ifndef _HTTP_SERVER_CP_H_
+#define _HTTP_SERVER_CP_H_
+
+#include "abstract_tcp_server_cp.h"
+#include "http_server.h"
+namespace epee
+{
+namespace net_utils
+{
+ typedef cp_server_impl<http::simple_http_connection_handler> cp_http_server_file_system;
+ typedef cp_server_impl<http::http_custom_handler> cp_http_server_custum_handling;
+}
+}
+
+
+
+#endif
+
+
diff --git a/contrib/epee/include/net/http_server_cp2.h b/contrib/epee/include/net/http_server_cp2.h
new file mode 100644
index 000000000..dd76d06f8
--- /dev/null
+++ b/contrib/epee/include/net/http_server_cp2.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#ifndef _HTTP_SERVER_CP2_H_
+#define _HTTP_SERVER_CP2_H_
+
+#include "abstract_tcp_server2.h"
+#include "http_protocol_handler.h"
+namespace epee
+{
+namespace net_utils
+{
+ typedef boosted_tcp_server<http::simple_http_connection_handler> boosted_http_server_file_system;
+ typedef boosted_tcp_server<http::http_custom_handler> boosted_http_server_custum_handling;
+}
+}
+
+
+#endif
+
+
diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h
new file mode 100644
index 000000000..7a8bdd4ad
--- /dev/null
+++ b/contrib/epee/include/net/http_server_handlers_map2.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#pragma once
+#include "serialization/keyvalue_serialization.h"
+#include "storages/portable_storage_template_helper.h"
+#include "http_base.h"
+
+
+#define CHAIN_HTTP_TO_MAP2() bool handle_http_request(const epee::net_utils::http::http_request_info& query_info, \
+ epee::net_utils::http::http_response_info& response, \
+ const epee::net_utils::connection_context_base& m_conn_context) \
+{\
+ LOG_PRINT_L2("HTTP [" << epee::string_tools::get_ip_string_from_int32(m_conn_context.m_remote_ip ) << "] " << query_info.m_http_method_str << " " << query_info.m_URI); \
+ response.m_response_code = 200; \
+ response.m_response_comment = "Ok"; \
+ if(!handle_http_request_map(query_info, response, m_conn_context)) \
+ {response.m_response_code = 404;response.m_response_comment = "Not found";} \
+ return true; \
+}
+
+
+#define BEGIN_URI_MAP2() bool handle_http_request_map(const epee::net_utils::http::http_request_info& query_info, \
+ epee::net_utils::http::http_response_info& response_info, \
+ const epee::net_utils::connection_context_base& m_conn_context) { \
+ bool handled = false; \
+ if(false) return true; //just a stub to have "else if"
+
+#define MAP_URI2(pattern, callback) else if(std::string::npos != query_info.m_URI.find(pattern)) return callback(query_info, response_info, m_conn_context);
+
+#define MAP_URI_AUTO_XML2(s_pattern, callback_f, command_type) //TODO: don't think i ever again will use xml - ambiguous and "overtagged" format
+
+#define MAP_URI_AUTO_JON2(s_pattern, callback_f, command_type) \
+ else if(query_info.m_URI == s_pattern) \
+ { \
+ handled = true; \
+ boost::uint64_t ticks = misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::request> req; \
+ bool parse_res = epee::serialization::load_t_from_json(static_cast<command_type::request&>(req), query_info.m_body); \
+ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse json: \r\n" << query_info.m_body); \
+ boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::response> resp;\
+ if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \
+ { \
+ LOG_ERROR("Failed to " << #callback_f << "()"); \
+ response_info.m_response_code = 500; \
+ response_info.m_response_comment = "Internal Server Error"; \
+ return true; \
+ } \
+ boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
+ epee::serialization::store_t_to_json(static_cast<command_type::response&>(resp), response_info.m_body); \
+ boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
+ response_info.m_mime_tipe = "application/json"; \
+ response_info.m_header_info.m_content_type = " application/json"; \
+ LOG_PRINT( s_pattern << " processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
+ }
+
+#define MAP_URI_AUTO_BIN2(s_pattern, callback_f, command_type) \
+ else if(query_info.m_URI == s_pattern) \
+ { \
+ handled = true; \
+ boost::uint64_t ticks = misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::request> req; \
+ bool parse_res = epee::serialization::load_t_from_binary(static_cast<command_type::request&>(req), query_info.m_body); \
+ CHECK_AND_ASSERT_MES(parse_res, false, "Failed to parse bin body data, body size=" << query_info.m_body.size()); \
+ boost::uint64_t ticks1 = misc_utils::get_tick_count(); \
+ boost::value_initialized<command_type::response> resp;\
+ if(!callback_f(static_cast<command_type::request&>(req), static_cast<command_type::response&>(resp))) \
+ { \
+ LOG_ERROR("Failed to " << #callback_f << "()"); \
+ response_info.m_response_code = 500; \
+ response_info.m_response_comment = "Internal Server Error"; \
+ return true; \
+ } \
+ boost::uint64_t ticks2 = misc_utils::get_tick_count(); \
+ epee::serialization::store_t_to_binary(static_cast<command_type::response&>(resp), response_info.m_body); \
+ boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
+ response_info.m_mime_tipe = " application/octet-stream"; \
+ response_info.m_header_info.m_content_type = " application/octet-stream"; \
+ LOG_PRINT( s_pattern << "() processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
+ }
+
+#define CHAIN_URI_MAP2(callback) else {callback(query_info, response_info, m_conn_context);handled = true;}
+
+#define END_URI_MAP2() return handled;}
+
+
+
+
+namespace epee
+{
+ namespace json_rpc
+ {
+ template<typename t_param>
+ struct request
+ {
+ std::string version;
+ std::string method;
+ std::string id;
+ t_param params;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(version)
+ KV_SERIALIZE(method)
+ KV_SERIALIZE(id)
+ KV_SERIALIZE(params)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct error
+ {
+ int64_t code;
+ std::string message;
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(code)
+ KV_SERIALIZE(message)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ struct dummy_error
+ {
+ BEGIN_KV_SERIALIZE_MAP()
+ END_KV_SERIALIZE_MAP()
+ };
+
+ template<typename t_param, typename t_error>
+ struct response
+ {
+ t_param result;
+ t_error error;
+ std::string id;
+
+ BEGIN_KV_SERIALIZE_MAP()
+ KV_SERIALIZE(result)
+ KV_SERIALIZE(error)
+ KV_SERIALIZE(id)
+ END_KV_SERIALIZE_MAP()
+ };
+
+ typedef response<std::string, error> error_response;
+ }
+}
+
+
+
+
+#define BEGIN_JSON_RPC_MAP(uri) else if(query_info.m_URI == uri) \
+ { \
+ boost::uint64_t ticks = epee::misc_utils::get_tick_count(); \
+ epee::serialization::portable_storage ps; \
+ if(!ps.load_from_json(query_info.m_body)) \
+ { \
+ boost::value_initialized<epee::json_rpc::error_response> rsp; \
+ static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
+ static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
+ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
+ return true; \
+ } \
+ std::string callback_name; \
+ if(!ps.get_value("method", callback_name, nullptr)) \
+ { \
+ epee::json_rpc::error_response rsp; \
+ rsp.error.code = -32600; \
+ rsp.error.message = "Invalid Request"; \
+ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
+ return true; \
+ } \
+ if(false) return true; //just a stub to have "else if"
+
+#define MAP_JON_RPC_WE(method_name, callback_f, command_type) \
+ else if(callback_name == method_name) \
+{ \
+ handled = true; \
+ boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
+ epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
+ req.load(ps); \
+ boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
+ epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
+ resp.id = req.id; \
+ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
+ fail_resp.id = req.id; \
+ if(!callback_f(req.params, resp.result, fail_resp.error)) \
+{ \
+ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
+ return true; \
+} \
+ boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
+ epee::serialization::store_t_to_json(resp, response_info.m_body); \
+ boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
+ response_info.m_mime_tipe = "application/json"; \
+ response_info.m_header_info.m_content_type = " application/json"; \
+ LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
+ return true;\
+}
+
+
+#define MAP_JON_RPC(method_name, callback_f, command_type) \
+ else if(callback_name == method_name) \
+{ \
+ handled = true; \
+ boost::value_initialized<epee::json_rpc::request<command_type::request> > req_; \
+ epee::json_rpc::request<command_type::request>& req = static_cast<epee::json_rpc::request<command_type::request>&>(req_);\
+ req.load(ps); \
+ boost::uint64_t ticks1 = epee::misc_utils::get_tick_count(); \
+ boost::value_initialized<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> > resp_; \
+ epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error>& resp = static_cast<epee::json_rpc::response<command_type::response, epee::json_rpc::dummy_error> &>(resp_); \
+ resp.id = req.id; \
+ if(!callback_f(req.params, resp.result)) \
+{ \
+ epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \
+ fail_resp.id = req.id; \
+ fail_resp.error.code = -32603; \
+ fail_resp.error.message = "Internal error"; \
+ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(fail_resp), response_info.m_body); \
+ return true; \
+} \
+ boost::uint64_t ticks2 = epee::misc_utils::get_tick_count(); \
+ epee::serialization::store_t_to_json(resp, response_info.m_body); \
+ boost::uint64_t ticks3 = epee::misc_utils::get_tick_count(); \
+ response_info.m_mime_tipe = "application/json"; \
+ response_info.m_header_info.m_content_type = " application/json"; \
+ LOG_PRINT( query_info.m_URI << "[" << method_name << "] processed with " << ticks1-ticks << "/"<< ticks2-ticks1 << "/" << ticks3-ticks2 << "ms", LOG_LEVEL_2); \
+ return true;\
+}
+
+
+#define END_JSON_RPC_MAP() \
+ epee::json_rpc::error_response rsp; \
+ rsp.error.code = -32601; \
+ rsp.error.message = "Method not found"; \
+ epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \
+ return true; \
+ }
+
+
diff --git a/contrib/epee/include/net/http_server_impl_base.h b/contrib/epee/include/net/http_server_impl_base.h
new file mode 100644
index 000000000..f81b4f601
--- /dev/null
+++ b/contrib/epee/include/net/http_server_impl_base.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+
+
+#include <boost/thread.hpp>
+#include <boost/bind.hpp>
+
+#include "net/http_server_cp2.h"
+#include "net/http_server_handlers_map2.h"
+
+namespace epee
+{
+
+ template<class t_child_class>
+ class http_server_impl_base: public net_utils::http::i_http_server_handler
+ {
+
+ public:
+ http_server_impl_base()
+ : m_net_server()
+ {}
+
+ explicit http_server_impl_base(boost::asio::io_service& external_io_service)
+ : m_net_server(external_io_service)
+ {}
+
+ bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0")
+ {
+
+ //set self as callback handler
+ m_net_server.get_config_object().m_phandler = static_cast<t_child_class*>(this);
+
+ //here set folder for hosting reqests
+ m_net_server.get_config_object().m_folder = "";
+
+ LOG_PRINT_L0("Binding on " << bind_ip << ":" << bind_port);
+ bool res = m_net_server.init_server(bind_port, bind_ip);
+ if(!res)
+ {
+ LOG_ERROR("Failed to bind server");
+ return false;
+ }
+ return true;
+ }
+
+ bool run(size_t threads_count, bool wait = true)
+ {
+ //go to loop
+ LOG_PRINT("Run net_service loop( " << threads_count << " threads)...", LOG_LEVEL_0);
+ if(!m_net_server.run_server(threads_count, wait))
+ {
+ LOG_ERROR("Failed to run net tcp server!");
+ }
+
+ if(wait)
+ LOG_PRINT("net_service loop stopped.", LOG_LEVEL_0);
+ return true;
+ }
+
+ bool deinit()
+ {
+ return m_net_server.deinit_server();
+ }
+
+ bool timed_wait_server_stop(uint64_t ms)
+ {
+ return m_net_server.timed_wait_server_stop(ms);
+ }
+
+ bool send_stop_signal()
+ {
+ m_net_server.send_stop_signal();
+ return true;
+ }
+
+ int get_binded_port()
+ {
+ return m_net_server.get_binded_port();
+ }
+
+ protected:
+ net_utils::boosted_http_server_custum_handling m_net_server;
+ };
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/http_server_thread_per_connect.h b/contrib/epee/include/net/http_server_thread_per_connect.h
new file mode 100644
index 000000000..bec43b726
--- /dev/null
+++ b/contrib/epee/include/net/http_server_thread_per_connect.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _HTTP_SERVER_CP_H_
+#define _HTTP_SERVER_CP_H_
+
+#include "abstract_tcp_server.h"
+#include "http_server.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ typedef abstract_tcp_server<http::simple_http_connection_handler> mt_http_server_file_system;
+ typedef abstract_tcp_server<http::http_custom_handler> mt_http_server_custum_handling;
+
+}
+}
+
+
+#endif
+
+
diff --git a/contrib/epee/include/net/levin_base.h b/contrib/epee/include/net/levin_base.h
new file mode 100644
index 000000000..503a9e5df
--- /dev/null
+++ b/contrib/epee/include/net/levin_base.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _LEVIN_BASE_H_
+#define _LEVIN_BASE_H_
+
+#include "net_utils_base.h"
+
+#define LEVIN_SIGNATURE 0x0101010101012101LL //Bender's nightmare
+
+namespace epee
+{
+namespace levin
+{
+#pragma pack(push)
+#pragma pack(1)
+ struct bucket_head
+ {
+ boost::uint64_t m_signature;
+ boost::uint64_t m_cb;
+ bool m_have_to_return_data;
+ boost::uint32_t m_command;
+ boost::int32_t m_return_code;
+ boost::uint32_t m_reservedA; //probably some flags in future
+ boost::uint32_t m_reservedB; //probably some check sum in future
+ };
+#pragma pack(pop)
+
+
+#pragma pack(push)
+#pragma pack(1)
+ struct bucket_head2
+ {
+ boost::uint64_t m_signature;
+ boost::uint64_t m_cb;
+ bool m_have_to_return_data;
+ boost::uint32_t m_command;
+ boost::int32_t m_return_code;
+ boost::uint32_t m_flags;
+ boost::uint32_t m_protocol_version;
+ };
+#pragma pack(pop)
+
+
+#define LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED 0
+#define LEVIN_DEFAULT_MAX_PACKET_SIZE 100000000 //100MB by default
+
+#define LEVIN_PACKET_REQUEST 0x00000001
+#define LEVIN_PACKET_RESPONSE 0x00000002
+
+
+#define LEVIN_PROTOCOL_VER_0 0
+#define LEVIN_PROTOCOL_VER_1 1
+
+ template<class t_connection_context = net_utils::connection_context_base>
+ struct levin_commands_handler
+ {
+ virtual int invoke(int command, const std::string& in_buff, std::string& buff_out, t_connection_context& context)=0;
+ virtual int notify(int command, const std::string& in_buff, t_connection_context& context)=0;
+ virtual void callback(t_connection_context& context){};
+
+ virtual void on_connection_new(t_connection_context& context){};
+ virtual void on_connection_close(t_connection_context& context){};
+
+ };
+
+#define LEVIN_OK 0
+#define LEVIN_ERROR_CONNECTION -1
+#define LEVIN_ERROR_CONNECTION_NOT_FOUND -2
+#define LEVIN_ERROR_CONNECTION_DESTROYED -3
+#define LEVIN_ERROR_CONNECTION_TIMEDOUT -4
+#define LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL -5
+#define LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED -6
+#define LEVIN_ERROR_FORMAT -7
+
+#define DESCRIBE_RET_CODE(code) case code: return #code;
+ inline
+ const char* get_err_descr(int err)
+ {
+ switch(err)
+ {
+ DESCRIBE_RET_CODE(LEVIN_OK);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NOT_FOUND);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_DESTROYED);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_TIMEDOUT);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_NO_DUPLEX_PROTOCOL);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED);
+ DESCRIBE_RET_CODE(LEVIN_ERROR_FORMAT);
+ default:
+ return "unknown code";
+ }
+ }
+
+
+}
+}
+
+
+#endif //_LEVIN_BASE_H_
diff --git a/contrib/epee/include/net/levin_client.h b/contrib/epee/include/net/levin_client.h
new file mode 100644
index 000000000..335f6ba02
--- /dev/null
+++ b/contrib/epee/include/net/levin_client.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+
+#ifndef _LEVIN_CLIENT_H_
+#define _LEVIN_CLIENT_H_
+
+#include "net_helper.h"
+#include "levin_base.h"
+
+
+#ifndef MAKE_IP
+#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
+#endif
+
+namespace epee
+{
+namespace levin
+{
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class levin_client_impl
+ {
+ public:
+ levin_client_impl();
+ virtual ~levin_client_impl();
+
+ bool connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0");
+ bool connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip = "0.0.0.0");
+ bool is_connected();
+ bool disconnect();
+
+ virtual int invoke(int command, const std::string& in_buff, std::string& buff_out);
+ virtual int notify(int command, const std::string& in_buff);
+
+ protected:
+ net_utils::blocked_mode_client m_transport;
+ };
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class levin_client_impl2: public levin_client_impl
+ {
+ public:
+
+ int invoke(int command, const std::string& in_buff, std::string& buff_out);
+ int notify(int command, const std::string& in_buff);
+ };
+
+}
+namespace net_utils
+{
+ typedef levin::levin_client_impl levin_client;
+ typedef levin::levin_client_impl2 levin_client2;
+}
+}
+
+#include "levin_client.inl"
+
+#endif //_LEVIN_CLIENT_H_
diff --git a/contrib/epee/include/net/levin_client.inl b/contrib/epee/include/net/levin_client.inl
new file mode 100644
index 000000000..ae159da6e
--- /dev/null
+++ b/contrib/epee/include/net/levin_client.inl
@@ -0,0 +1,194 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+#include "string_tools.h"
+namespace epee
+{
+namespace levin
+{
+inline
+bool levin_client_impl::connect(u_long ip, int port, unsigned int timeout, const std::string& bind_ip)
+{
+ return m_transport.connect(string_tools::get_ip_string_from_int32(ip), port, timeout, timeout, bind_ip);
+}
+//------------------------------------------------------------------------------
+inline
+ bool levin_client_impl::connect(const std::string& addr, int port, unsigned int timeout, const std::string& bind_ip)
+{
+ return m_transport.connect(addr, port, timeout, timeout, bind_ip);
+}
+//------------------------------------------------------------------------------
+inline
+bool levin_client_impl::is_connected()
+{
+ return m_transport.is_connected();
+}
+//------------------------------------------------------------------------------
+inline
+bool levin_client_impl::disconnect()
+{
+ return m_transport.disconnect();
+}
+//------------------------------------------------------------------------------
+inline
+levin_client_impl::levin_client_impl()
+{
+}
+//------------------------------------------------------------------------------
+inline
+levin_client_impl::~levin_client_impl()
+{
+ disconnect();
+}
+//------------------------------------------------------------------------------
+inline
+int levin_client_impl::invoke(int command, const std::string& in_buff, std::string& buff_out)
+{
+ if(!is_connected())
+ return -1;
+
+ bucket_head head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = true;
+ head.m_command = command;
+ if(!m_transport.send(&head, sizeof(head)))
+ return -1;
+
+ if(!m_transport.send(in_buff))
+ return -1;
+
+ std::string local_buff;
+ if(!m_transport.recv_n(local_buff, sizeof(bucket_head)))
+ return -1;
+
+ head = *(bucket_head*)local_buff.data();
+
+
+ if(head.m_signature!=LEVIN_SIGNATURE)
+ {
+ LOG_PRINT_L0("Signature missmatch in response");
+ return -1;
+ }
+
+ if(!m_transport.recv_n(buff_out, head.m_cb))
+ return -1;
+
+ return head.m_return_code;
+}
+//------------------------------------------------------------------------------
+inline
+int levin_client_impl::notify(int command, const std::string& in_buff)
+{
+ if(!is_connected())
+ return -1;
+
+ bucket_head head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = false;
+ head.m_command = command;
+
+ if(!m_transport.send((const char*)&head, sizeof(head)))
+ return -1;
+
+ if(!m_transport.send(in_buff))
+ return -1;
+
+ return 1;
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+inline
+ int levin_client_impl2::invoke(int command, const std::string& in_buff, std::string& buff_out)
+{
+ if(!is_connected())
+ return -1;
+
+ bucket_head2 head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = true;
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ if(!m_transport.send(&head, sizeof(head)))
+ return -1;
+
+ if(!m_transport.send(in_buff))
+ return -1;
+
+ std::string local_buff;
+ if(!m_transport.recv_n(local_buff, sizeof(bucket_head2)))
+ return -1;
+
+ head = *(bucket_head2*)local_buff.data();
+
+
+ if(head.m_signature!=LEVIN_SIGNATURE)
+ {
+ LOG_PRINT_L0("Signature missmatch in response");
+ return -1;
+ }
+
+ if(!m_transport.recv_n(buff_out, head.m_cb))
+ return -1;
+
+ return head.m_return_code;
+}
+//------------------------------------------------------------------------------
+inline
+ int levin_client_impl2::notify(int command, const std::string& in_buff)
+{
+ if(!is_connected())
+ return -1;
+
+ bucket_head2 head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = false;
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_REQUEST;
+
+ if(!m_transport.send((const char*)&head, sizeof(head)))
+ return -1;
+
+ if(!m_transport.send(in_buff))
+ return -1;
+
+ return 1;
+}
+
+}
+}
+//------------------------------------------------------------------------------ \ No newline at end of file
diff --git a/contrib/epee/include/net/levin_client_async.h b/contrib/epee/include/net/levin_client_async.h
new file mode 100644
index 000000000..b02fa7ee7
--- /dev/null
+++ b/contrib/epee/include/net/levin_client_async.h
@@ -0,0 +1,577 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#pragma once
+
+#include ""
+#include "net_helper.h"
+#include "levin_base.h"
+
+
+namespace epee
+{
+namespace levin
+{
+
+ /************************************************************************
+ * levin_client_async - probably it is not really fast implementation,
+ * each handler thread could make up to 30 ms latency.
+ * But, handling events in reader thread will cause dead locks in
+ * case of recursive call (call invoke() to the same connection
+ * on reader thread on remote invoke() handler)
+ ***********************************************************************/
+
+
+ class levin_client_async
+ {
+ levin_commands_handler* m_pcommands_handler;
+ volatile boost::uint32_t m_is_stop;
+ volatile boost::uint32_t m_threads_count;
+ ::critical_section m_send_lock;
+
+ std::string m_local_invoke_buff;
+ ::critical_section m_local_invoke_buff_lock;
+ volatile int m_invoke_res;
+
+ volatile boost::uint32_t m_invoke_data_ready;
+ volatile boost::uint32_t m_invoke_is_active;
+
+ boost::mutex m_invoke_event;
+ boost::condition_variable m_invoke_cond;
+ size_t m_timeout;
+
+ ::critical_section m_recieved_packets_lock;
+ struct packet_entry
+ {
+ bucket_head m_hd;
+ std::string m_body;
+ boost::uint32_t m_connection_index;
+ };
+ std::list<packet_entry> m_recieved_packets;
+ /*
+ m_current_connection_index needed when some connection was broken and reconnected - in this
+ case we could have some received packets in que, which shoud not be handled
+ */
+ volatile boost::uint32_t m_current_connection_index;
+ ::critical_section m_invoke_lock;
+ ::critical_section m_reciev_packet_lock;
+ ::critical_section m_connection_lock;
+ net_utils::blocked_mode_client m_transport;
+ public:
+ levin_client_async():m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
+ {}
+ levin_client_async(const levin_client_async& /*v*/):m_pcommands_handler(NULL), m_is_stop(0), m_threads_count(0), m_invoke_data_ready(0), m_invoke_is_active(0)
+ {}
+ ~levin_client_async()
+ {
+ boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);
+ disconnect();
+
+
+ while(boost::interprocess::ipcdetail::atomic_read32(&m_threads_count))
+ ::Sleep(100);
+ }
+
+ void set_handler(levin_commands_handler* phandler)
+ {
+ m_pcommands_handler = phandler;
+ }
+
+ bool connect(boost::uint32_t ip, boost::uint32_t port, boost::uint32_t timeout)
+ {
+ loop_call_guard();
+ critical_region cr(m_connection_lock);
+
+ m_timeout = timeout;
+ bool res = false;
+ CRITICAL_REGION_BEGIN(m_reciev_packet_lock);
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ res = levin_client_impl::connect(ip, port, timeout);
+ boost::interprocess::ipcdetail::atomic_inc32(&m_current_connection_index);
+ CRITICAL_REGION_END();
+ CRITICAL_REGION_END();
+ if(res && !boost::interprocess::ipcdetail::atomic_read32(&m_threads_count) )
+ {
+ //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 0);//m_is_stop = false;
+ boost::thread( boost::bind(&levin_duplex_client::reciever_thread, this) );
+ boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) );
+ boost::thread( boost::bind(&levin_duplex_client::handler_thread, this) );
+ }
+
+ return res;
+ }
+ bool is_connected()
+ {
+ loop_call_guard();
+ critical_region cr(m_cs);
+ return levin_client_impl::is_connected();
+ }
+
+ inline
+ bool check_connection()
+ {
+ loop_call_guard();
+ critical_region cr(m_cs);
+
+ if(!is_connected())
+ {
+ if( !reconnect() )
+ {
+ LOG_ERROR("Reconnect Failed. Failed to invoke() becouse not connected!");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ //------------------------------------------------------------------------------
+ inline
+ bool recv_n(SOCKET s, char* pbuff, size_t cb)
+ {
+ while(cb)
+ {
+ int res = ::recv(m_socket, pbuff, (int)cb, 0);
+
+ if(SOCKET_ERROR == res)
+ {
+ if(!m_connected)
+ return false;
+
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to recv(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ disconnect();
+ //reconnect();
+ return false;
+ }else if(res == 0)
+ {
+ disconnect();
+ //reconnect();
+ return false;
+ }
+ LOG_PRINT_L4("[" << m_socket <<"] RECV " << res);
+ cb -= res;
+ pbuff += res;
+ }
+
+ return true;
+ }
+
+ //------------------------------------------------------------------------------
+ inline
+ bool recv_n(SOCKET s, std::string& buff)
+ {
+ size_t cb_remain = buff.size();
+ char* m_current_ptr = (char*)buff.data();
+ return recv_n(s, m_current_ptr, cb_remain);
+ }
+
+ bool disconnect()
+ {
+ //boost::interprocess::ipcdetail::atomic_write32(&m_is_stop, 1);//m_is_stop = true;
+ loop_call_guard();
+ critical_region cr(m_cs);
+ levin_client_impl::disconnect();
+
+ CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
+ m_local_invoke_buff.clear();
+ m_invoke_res = LEVIN_ERROR_CONNECTION_DESTROYED;
+ CRITICAL_REGION_END();
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true;
+ m_invoke_cond.notify_all();
+ return true;
+ }
+
+ void loop_call_guard()
+ {
+
+ }
+
+ void on_leave_invoke()
+ {
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 0);
+ }
+
+ int invoke(const GUID& target, int command, const std::string& in_buff, std::string& buff_out)
+ {
+
+ critical_region cr_invoke(m_invoke_lock);
+
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_is_active, 1);
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 0);
+ misc_utils::destr_ptr hdlr = misc_utils::add_exit_scope_handler(boost::bind(&levin_duplex_client::on_leave_invoke, this));
+
+ loop_call_guard();
+
+ if(!check_connection())
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+
+ bucket_head head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = true;
+ head.m_id = target;
+#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS
+ ::UuidCreate(&head.m_id);
+#endif
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ LOG_PRINT("[" << m_socket <<"] Sending invoke data", LOG_LEVEL_4);
+
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head));
+ int res = ::send(m_socket, (const char*)&head, sizeof(head), 0);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ disconnect();
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ }
+ LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size());
+ res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ disconnect();
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ }
+ CRITICAL_REGION_END();
+ LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
+
+ //hard coded timeout in 10 minutes for maximum invoke period. if it happens, it could mean only some real troubles.
+ boost::system_time timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100);
+ size_t timeout_count = 0;
+ boost::unique_lock<boost::mutex> lock(m_invoke_event);
+
+ while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_data_ready))
+ {
+ if(!m_invoke_cond.timed_wait(lock, timeout))
+ {
+ if(timeout_count < 10)
+ {
+ //workaround to avoid freezing at timed_wait called after notify_all.
+ timeout = boost::get_system_time()+ boost::posix_time::milliseconds(100);
+ ++timeout_count;
+ continue;
+ }else if(timeout_count == 10)
+ {
+ //workaround to avoid freezing at timed_wait called after notify_all.
+ timeout = boost::get_system_time()+ boost::posix_time::minutes(10);
+ ++timeout_count;
+ continue;
+ }else
+ {
+ LOG_PRINT("[" << m_socket <<"] Timeout on waiting invoke result. ", LOG_LEVEL_0);
+ //disconnect();
+ return LEVIN_ERROR_CONNECTION_TIMEDOUT;
+ }
+ }
+ }
+
+
+ CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
+ buff_out.swap(m_local_invoke_buff);
+ m_local_invoke_buff.clear();
+ CRITICAL_REGION_END();
+ return m_invoke_res;
+ }
+
+ int notify(const GUID& target, int command, const std::string& in_buff)
+ {
+ if(!check_connection())
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ bucket_head head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = false;
+ head.m_id = target;
+#ifdef TRACE_LEVIN_PACKETS_BY_GUIDS
+ ::UuidCreate(&head.m_id);
+#endif
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ LOG_PRINT_L4("[" << m_socket <<"] SEND " << sizeof(head));
+ int res = ::send(m_socket, (const char*)&head, sizeof(head), 0);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ disconnect();
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ }
+ LOG_PRINT_L4("[" << m_socket <<"] SEND " << (int)in_buff.size());
+ res = ::send(m_socket, in_buff.data(), (int)in_buff.size(), 0);
+ if(SOCKET_ERROR == res)
+ {
+ int err = ::WSAGetLastError();
+ LOG_ERROR("Failed to send(), err = " << err << " \"" << socket_errors::get_socket_error_text(err) <<"\"");
+ disconnect();
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ }
+ CRITICAL_REGION_END();
+ LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
+
+ return 1;
+ }
+
+
+ private:
+ bool have_some_data(SOCKET sock, int interval = 1)
+ {
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+
+ fd_set fdse;
+ FD_ZERO(&fdse);
+ FD_SET(sock, &fdse);
+
+
+ timeval tv;
+ tv.tv_sec = interval;
+ tv.tv_usec = 0;
+
+ int sel_res = select(0, &fds, 0, &fdse, &tv);
+ if(0 == sel_res)
+ return false;
+ else if(sel_res == SOCKET_ERROR)
+ {
+ if(m_is_stop)
+ return false;
+ int err_code = ::WSAGetLastError();
+ LOG_ERROR("Filed to call select, err code = " << err_code);
+ disconnect();
+ }else
+ {
+ if(fds.fd_array[0])
+ {//some read operations was performed
+ return true;
+ }else if(fdse.fd_array[0])
+ {//some error was at the socket
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ bool reciev_and_process_incoming_data()
+ {
+ bucket_head head = {0};
+ boost::uint32_t conn_index = 0;
+ bool is_request = false;
+ std::string local_buff;
+ CRITICAL_REGION_BEGIN(m_reciev_packet_lock);//to protect from socket reconnect between head and body
+
+ if(!recv_n(m_socket, (char*)&head, sizeof(head)))
+ {
+ if(m_is_stop)
+ return false;
+ LOG_ERROR("Failed to recv_n");
+ return false;
+ }
+
+ conn_index = boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index);
+
+ if(head.m_signature!=LEVIN_SIGNATURE)
+ {
+ LOG_ERROR("Signature missmatch in response");
+ return false;
+ }
+
+ is_request = (head.m_protocol_version == LEVIN_PROTOCOL_VER_1 && head.m_flags&LEVIN_PACKET_REQUEST);
+
+
+ local_buff.resize((size_t)head.m_cb);
+ if(!recv_n(m_socket, local_buff))
+ {
+ if(m_is_stop)
+ return false;
+ LOG_ERROR("Filed to reciev");
+ return false;
+ }
+ CRITICAL_REGION_END();
+
+ LOG_PRINT_L4("LEVIN_PACKET_RECIEVED. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
+
+ if(is_request)
+ {
+ CRITICAL_REGION_BEGIN(m_recieved_packets_lock);
+ m_recieved_packets.resize(m_recieved_packets.size() + 1);
+ m_recieved_packets.back().m_hd = head;
+ m_recieved_packets.back().m_body.swap(local_buff);
+ m_recieved_packets.back().m_connection_index = conn_index;
+ CRITICAL_REGION_END();
+ /*
+
+ */
+ }else
+ {//this is some response
+
+ CRITICAL_REGION_BEGIN(m_local_invoke_buff_lock);
+ m_local_invoke_buff.swap(local_buff);
+ m_invoke_res = head.m_return_code;
+ CRITICAL_REGION_END();
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_data_ready, 1); //m_invoke_data_ready = true;
+ m_invoke_cond.notify_all();
+
+ }
+ return true;
+ }
+
+ bool reciever_thread()
+ {
+ LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread started.[m_threads_count=" << m_threads_count << "]");
+ log_space::log_singletone::set_thread_log_prefix("RECIEVER_WORKER");
+ boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count);
+
+ while(!m_is_stop)
+ {
+ if(!m_connected)
+ {
+ Sleep(100);
+ continue;
+ }
+
+ if(have_some_data(m_socket, 1))
+ {
+ if(!reciev_and_process_incoming_data())
+ {
+ if(m_is_stop)
+ {
+ break;//boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
+ //return true;
+ }
+ LOG_ERROR("Failed to reciev_and_process_incoming_data. shutting down");
+ //boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
+ //disconnect_no_wait();
+ //break;
+ }
+ }
+ }
+
+ boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
+ LOG_PRINT_L3("[" << m_socket <<"] Socket reciever thread stopped.[m_threads_count=" << m_threads_count << "]");
+ return true;
+ }
+
+ bool process_recieved_packet(bucket_head& head, const std::string& local_buff, boost::uint32_t conn_index)
+ {
+
+ net_utils::connection_context_base conn_context;
+ conn_context.m_remote_ip = m_ip;
+ conn_context.m_remote_port = m_port;
+ if(head.m_have_to_return_data)
+ {
+ std::string return_buff;
+ if(m_pcommands_handler)
+ head.m_return_code = m_pcommands_handler->invoke(head.m_id, head.m_command, local_buff, return_buff, conn_context);
+ else
+ head.m_return_code = LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED;
+
+
+
+ head.m_cb = return_buff.size();
+ head.m_have_to_return_data = false;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_RESPONSE;
+
+ std::string send_buff((const char*)&head, sizeof(head));
+ send_buff += return_buff;
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ if(conn_index != boost::interprocess::ipcdetail::atomic_read32(&m_current_connection_index))
+ {//there was reconnect, send response back is not allowed
+ return true;
+ }
+ int res = ::send(m_socket, (const char*)send_buff.data(), send_buff.size(), 0);
+ if(res == SOCKET_ERROR)
+ {
+ int err_code = ::WSAGetLastError();
+ LOG_ERROR("Failed to send, err = " << err_code);
+ return false;
+ }
+ CRITICAL_REGION_END();
+ LOG_PRINT_L4("LEVIN_PACKET_SENT. [len=" << head.m_cb << ", flags=" << head.m_flags << ", is_cmd=" << head.m_have_to_return_data <<", cmd_id = " << head.m_command << ", pr_v=" << head.m_protocol_version << ", uid=" << string_tools::get_str_from_guid_a(head.m_id) << "]");
+
+ }
+ else
+ {
+ if(m_pcommands_handler)
+ m_pcommands_handler->notify(head.m_id, head.m_command, local_buff, conn_context);
+ }
+
+ return true;
+ }
+
+ bool handler_thread()
+ {
+ LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread started.[m_threads_count=" << m_threads_count << "]");
+ log_space::log_singletone::set_thread_log_prefix("HANDLER_WORKER");
+ boost::interprocess::ipcdetail::atomic_inc32(&m_threads_count);
+
+ while(!m_is_stop)
+ {
+ bool have_some_work = false;
+ std::string local_buff;
+ bucket_head bh = {0};
+ boost::uint32_t conn_index = 0;
+
+ CRITICAL_REGION_BEGIN(m_recieved_packets_lock);
+ if(m_recieved_packets.size())
+ {
+ bh = m_recieved_packets.begin()->m_hd;
+ conn_index = m_recieved_packets.begin()->m_connection_index;
+ local_buff.swap(m_recieved_packets.begin()->m_body);
+ have_some_work = true;
+ m_recieved_packets.pop_front();
+ }
+ CRITICAL_REGION_END();
+
+ if(have_some_work)
+ {
+ process_recieved_packet(bh, local_buff, conn_index);
+ }else
+ {
+ //Idle when no work
+ Sleep(30);
+ }
+ }
+
+ boost::interprocess::ipcdetail::atomic_dec32(&m_threads_count);
+ LOG_PRINT_L3("[" << m_socket <<"] Socket handler thread stopped.[m_threads_count=" << m_threads_count << "]");
+ return true;
+ }
+ };
+
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/levin_client_async.inl b/contrib/epee/include/net/levin_client_async.inl
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/contrib/epee/include/net/levin_client_async.inl
diff --git a/contrib/epee/include/net/levin_helper.h b/contrib/epee/include/net/levin_helper.h
new file mode 100644
index 000000000..a8406103c
--- /dev/null
+++ b/contrib/epee/include/net/levin_helper.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#pragma once
+
+#include "levin_base.h"
+#include "serializeble_struct_helper.h"
+
+namespace epee
+{
+namespace levin
+{
+ template<class t_struct>
+ bool pack_struct_to_levin_message(const t_struct& t, std::string& buff, int command_id)
+ {
+ buff.resize(sizeof(levin::bucket_head));
+ levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = 0;
+ head.m_have_to_return_data = true;
+ head.m_command = command_id;
+ head.m_return_code = 1;
+ head.m_reservedA = rand(); //probably some flags in future
+ head.m_reservedB = rand(); //probably some check summ in future
+
+ std::string buff_strg;
+ if(!StorageNamed::save_struct_as_storage_to_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg))
+ return false;
+
+ head.m_cb = buff_strg.size();
+ buff.append(buff_strg);
+ return true;
+ }
+
+
+ bool pack_data_to_levin_message(const std::string& data, std::string& buff, int command_id)
+ {
+ buff.resize(sizeof(levin::bucket_head));
+ levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = 0;
+ head.m_have_to_return_data = true;
+ head.m_command = command_id;
+ head.m_return_code = 1;
+ head.m_reservedA = rand(); //probably some flags in future
+ head.m_reservedB = rand(); //probably some check summ in future
+
+ head.m_cb = data.size();
+ buff.append(data);
+ return true;
+ }
+
+ bool load_levin_data_from_levin_message(std::string& levin_data, const std::string& buff, int& command)
+ {
+ if(buff.size() < sizeof(levin::bucket_head) )
+ {
+ LOG_PRINT_L3("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message");
+ return false;
+ }
+
+ levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
+ if(head.m_signature != LEVIN_SIGNATURE)
+ {
+ LOG_PRINT_L3("Failed to read signature in levin message, at load_struct_from_levin_message");
+ return false;
+ }
+ if(head.m_cb != buff.size()-sizeof(levin::bucket_head))
+ {
+ LOG_PRINT_L3("sizes missmatch, at load_struct_from_levin_message");
+ return false;
+ }
+
+ //std::string buff_strg;
+ levin_data.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head));
+ command = head.m_command;
+ return true;
+ }
+
+ template<class t_struct>
+ bool load_struct_from_levin_message(t_struct& t, const std::string& buff, int& command)
+ {
+ if(buff.size() < sizeof(levin::bucket_head) )
+ {
+ LOG_ERROR("size of buff(" << buff.size() << ") is too small, at load_struct_from_levin_message");
+ return false;
+ }
+
+ levin::bucket_head& head = *(levin::bucket_head*)(&buff[0]);
+ if(head.m_signature != LEVIN_SIGNATURE)
+ {
+ LOG_ERROR("Failed to read signature in levin message, at load_struct_from_levin_message");
+ return false;
+ }
+ if(head.m_cb != buff.size()-sizeof(levin::bucket_head))
+ {
+ LOG_ERROR("sizes missmatch, at load_struct_from_levin_message");
+ return false;
+ }
+
+ std::string buff_strg;
+ buff_strg.assign(&buff[sizeof(levin::bucket_head)], buff.size()-sizeof(levin::bucket_head));
+
+ if(!StorageNamed::load_struct_from_storage_buff_t<t_struct, StorageNamed::DefaultStorageType>(t, buff_strg))
+ {
+ LOG_ERROR("Failed to read storage, at load_struct_from_levin_message");
+ return false;
+ }
+ command = head.m_command;
+ return true;
+ }
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/levin_protocol_handler.h b/contrib/epee/include/net/levin_protocol_handler.h
new file mode 100644
index 000000000..adc6e95d5
--- /dev/null
+++ b/contrib/epee/include/net/levin_protocol_handler.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _LEVIN_PROTOCOL_HANDLER_H_
+#define _LEVIN_PROTOCOL_HANDLER_H_
+
+#include <boost/uuid/uuid_generators.hpp>
+#include "levin_base.h"
+
+namespace epee
+{
+namespace levin
+{
+ template<class t_connection_context = net_utils::connection_context_base>
+ struct protocl_handler_config
+ {
+ levin_commands_handler<t_connection_context>* m_pcommands_handler;
+ };
+
+ template<class t_connection_context = net_utils::connection_context_base>
+ class protocol_handler
+ {
+ public:
+ typedef t_connection_context connection_context;
+ typedef protocl_handler_config<t_connection_context> config_type;
+
+ protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context);
+ virtual ~protocol_handler(){}
+
+ virtual bool handle_recv(const void* ptr, size_t cb);
+
+ bool after_init_connection(){return true;}
+ private:
+ enum connection_data_state
+ {
+ conn_state_reading_head,
+ conn_state_reading_body
+ };
+
+
+ config_type& m_config;
+ t_connection_context& m_conn_context;
+ net_utils::i_service_endpoint* m_psnd_hndlr;
+ std::string m_cach_in_buffer;
+ connection_data_state m_state;
+ bucket_head m_current_head;
+ };
+
+ template<class t_connection_context>
+ protocol_handler<t_connection_context>::protocol_handler(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, t_connection_context& conn_context):
+ m_config(config),
+ m_conn_context(conn_context),
+ m_psnd_hndlr(psnd_hndlr),
+ m_state(conn_state_reading_head),
+ m_current_head(bucket_head())
+ {}
+
+ template<class t_connection_context>
+ bool protocol_handler<t_connection_context>::handle_recv(const void* ptr, size_t cb)
+ {
+ if(!m_config.m_pcommands_handler)
+ {
+ LOG_ERROR("Command handler not set!");
+ return false;
+ }
+ m_cach_in_buffer.append((const char*)ptr, cb);
+
+ bool is_continue = true;
+ while(is_continue)
+ {
+ switch(m_state)
+ {
+ case conn_state_reading_head:
+ if(m_cach_in_buffer.size() < sizeof(bucket_head))
+ {
+ if(m_cach_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cach_in_buffer.data()) != LEVIN_SIGNATURE)
+ {
+ LOG_ERROR("Signature missmatch on accepted connection");
+ return false;
+ }
+ is_continue = false;
+ break;
+ }
+ {
+ bucket_head* phead = (bucket_head*)m_cach_in_buffer.data();
+ if(LEVIN_SIGNATURE != phead->m_signature)
+ {
+ LOG_ERROR("Signature missmatch on accepted connection");
+ return false;
+ }
+ m_current_head = *phead;
+ }
+ m_cach_in_buffer.erase(0, sizeof(bucket_head));
+ m_state = conn_state_reading_body;
+ break;
+ case conn_state_reading_body:
+ if(m_cach_in_buffer.size() < m_current_head.m_cb)
+ {
+ is_continue = false;
+ break;
+ }
+ {
+ std::string buff_to_invoke;
+ if(m_cach_in_buffer.size() == m_current_head.m_cb)
+ buff_to_invoke.swap(m_cach_in_buffer);
+ else
+ {
+ buff_to_invoke.assign(m_cach_in_buffer, 0, (std::string::size_type)m_current_head.m_cb);
+ m_cach_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb);
+ }
+
+
+ if(m_current_head.m_have_to_return_data)
+ {
+ std::string return_buff;
+ m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(m_current_head.m_command, buff_to_invoke, return_buff, m_conn_context);
+ m_current_head.m_cb = return_buff.size();
+ m_current_head.m_have_to_return_data = false;
+ std::string send_buff((const char*)&m_current_head, sizeof(m_current_head));
+ send_buff += return_buff;
+
+ if(!m_psnd_hndlr->do_send(send_buff.data(), send_buff.size()))
+ return false;
+
+ }
+ else
+ m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_conn_context);
+ }
+ m_state = conn_state_reading_head;
+ break;
+ default:
+ LOG_ERROR("Undefined state in levin_server_impl::connection_handler, m_state=" << m_state);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+
+
+
+
+}
+}
+
+
+
+
+#endif //_LEVIN_PROTOCOL_HANDLER_H_
+
diff --git a/contrib/epee/include/net/levin_protocol_handler_async.h b/contrib/epee/include/net/levin_protocol_handler_async.h
new file mode 100644
index 000000000..dc4f41146
--- /dev/null
+++ b/contrib/epee/include/net/levin_protocol_handler_async.h
@@ -0,0 +1,778 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+#pragma once
+#include <boost/uuid/uuid_generators.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+
+#include <atomic>
+
+#include "levin_base.h"
+#include "misc_language.h"
+
+
+namespace epee
+{
+namespace levin
+{
+
+/************************************************************************/
+/* */
+/************************************************************************/
+template<class t_connection_context>
+class async_protocol_handler;
+
+template<class t_connection_context>
+class async_protocol_handler_config
+{
+ typedef std::map<boost::uuids::uuid, async_protocol_handler<t_connection_context>* > connections_map;
+ critical_section m_connects_lock;
+ connections_map m_connects;
+
+ void add_connection(async_protocol_handler<t_connection_context>* pc);
+ void del_connection(async_protocol_handler<t_connection_context>* pc);
+
+ async_protocol_handler<t_connection_context>* find_connection(boost::uuids::uuid connection_id) const;
+ int find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph);
+
+ friend class async_protocol_handler<t_connection_context>;
+
+public:
+ typedef t_connection_context connection_context;
+ levin_commands_handler<t_connection_context>* m_pcommands_handler;
+ boost::uint64_t m_max_packet_size;
+ boost::uint64_t m_invoke_timeout;
+
+ int invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id);
+ template<class callback_t>
+ int invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED);
+
+ int notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id);
+ bool close(boost::uuids::uuid connection_id);
+ bool update_connection_context(const t_connection_context& contxt);
+ bool request_callback(boost::uuids::uuid connection_id);
+ template<class callback_t>
+ bool foreach_connection(callback_t cb);
+ size_t get_connections_count();
+
+ async_protocol_handler_config():m_pcommands_handler(NULL), m_max_packet_size(LEVIN_DEFAULT_MAX_PACKET_SIZE)
+ {}
+};
+
+
+/************************************************************************/
+/* */
+/************************************************************************/
+template<class t_connection_context = net_utils::connection_context_base>
+class async_protocol_handler
+{
+public:
+ typedef t_connection_context connection_context;
+ typedef async_protocol_handler_config<t_connection_context> config_type;
+
+ enum stream_state
+ {
+ stream_state_head,
+ stream_state_body
+ };
+
+ std::atomic<bool> m_deletion_initiated;
+ std::atomic<bool> m_protocol_released;
+ volatile uint32_t m_invoke_buf_ready;
+
+ volatile int m_invoke_result_code;
+
+ critical_section m_local_inv_buff_lock;
+ std::string m_local_inv_buff;
+
+ critical_section m_send_lock;
+ critical_section m_call_lock;
+
+ volatile uint32_t m_wait_count;
+ volatile uint32_t m_close_called;
+ bucket_head2 m_current_head;
+ net_utils::i_service_endpoint* m_pservice_endpoint;
+ config_type& m_config;
+ t_connection_context& m_connection_context;
+
+ std::string m_cache_in_buffer;
+ stream_state m_state;
+
+ boost::int32_t m_oponent_protocol_ver;
+ bool m_connection_initialized;
+
+ struct invoke_response_handler_base
+ {
+ virtual bool handle(int res, const std::string& buff, connection_context& context)=0;
+ virtual bool is_timer_started() const=0;
+ virtual void cancel()=0;
+ virtual bool cancel_timer()=0;
+ };
+ template <class callback_t>
+ struct anvoke_handler: invoke_response_handler_base
+ {
+ anvoke_handler(const callback_t& cb, uint64_t timeout, async_protocol_handler& con, int command)
+ :m_cb(cb), m_con(con), m_timer(con.m_pservice_endpoint->get_io_service()), m_timer_started(false),
+ m_cancel_timer_called(false), m_timer_cancelled(false), m_command(command)
+ {
+ if(m_con.start_outer_call())
+ {
+ m_timer.expires_from_now(boost::posix_time::milliseconds(timeout));
+ m_timer.async_wait([&con, command, cb](const boost::system::error_code& ec)
+ {
+ if(ec == boost::asio::error::operation_aborted)
+ return;
+ LOG_PRINT_CC(con.get_context_ref(), "Timeout on invoke operation happened, command: " << command, LOG_LEVEL_2);
+ std::string fake;
+ cb(LEVIN_ERROR_CONNECTION_TIMEDOUT, fake, con.get_context_ref());
+ con.close();
+ con.finish_outer_call();
+ });
+ m_timer_started = true;
+ }
+ }
+ virtual ~anvoke_handler()
+ {}
+ callback_t m_cb;
+ async_protocol_handler& m_con;
+ boost::asio::deadline_timer m_timer;
+ bool m_timer_started;
+ bool m_cancel_timer_called;
+ bool m_timer_cancelled;
+ int m_command;
+ virtual bool handle(int res, const std::string& buff, typename async_protocol_handler::connection_context& context)
+ {
+ if(!cancel_timer())
+ return false;
+ m_cb(res, buff, context);
+ m_con.finish_outer_call();
+ return true;
+ }
+ virtual bool is_timer_started() const
+ {
+ return m_timer_started;
+ }
+ virtual void cancel()
+ {
+ if(cancel_timer())
+ {
+ std::string fake;
+ m_cb(LEVIN_ERROR_CONNECTION_DESTROYED, fake, m_con.get_context_ref());
+ m_con.finish_outer_call();
+ }
+ }
+ virtual bool cancel_timer()
+ {
+ if(!m_cancel_timer_called)
+ {
+ m_cancel_timer_called = true;
+ boost::system::error_code ignored_ec;
+ m_timer_cancelled = 1 == m_timer.cancel(ignored_ec);
+ }
+ return m_timer_cancelled;
+ }
+ };
+ critical_section m_invoke_response_handlers_lock;
+ std::list<boost::shared_ptr<invoke_response_handler_base> > m_invoke_response_handlers;
+
+ template<class callback_t>
+ bool add_invoke_response_handler(callback_t cb, uint64_t timeout, async_protocol_handler& con, int command)
+ {
+ CRITICAL_REGION_LOCAL(m_invoke_response_handlers_lock);
+ boost::shared_ptr<invoke_response_handler_base> handler(boost::make_shared<anvoke_handler<callback_t>>(cb, timeout, con, command));
+ m_invoke_response_handlers.push_back(handler);
+ return handler->is_timer_started();
+ }
+ template<class callback_t> friend struct anvoke_handler;
+public:
+ async_protocol_handler(net_utils::i_service_endpoint* psnd_hndlr,
+ config_type& config,
+ t_connection_context& conn_context):
+ m_current_head(bucket_head2()),
+ m_pservice_endpoint(psnd_hndlr),
+ m_config(config),
+ m_connection_context(conn_context),
+ m_state(stream_state_head)
+ {
+ m_close_called = 0;
+ m_deletion_initiated = false;
+ m_protocol_released = false;
+ m_wait_count = 0;
+ m_oponent_protocol_ver = 0;
+ m_connection_initialized = false;
+ }
+ virtual ~async_protocol_handler()
+ {
+ m_deletion_initiated = true;
+ if(m_connection_initialized)
+ {
+ m_config.del_connection(this);
+ }
+
+ for (size_t i = 0; i < 60 * 1000 / 100 && 0 != boost::interprocess::ipcdetail::atomic_read32(&m_wait_count); ++i)
+ {
+ misc_utils::sleep_no_w(100);
+ }
+ CHECK_AND_ASSERT_MES_NO_RET(0 == boost::interprocess::ipcdetail::atomic_read32(&m_wait_count), "Failed to wait for operation completion. m_wait_count = " << m_wait_count);
+
+ LOG_PRINT_CC(m_connection_context, "~async_protocol_handler()", LOG_LEVEL_4);
+ }
+
+ bool start_outer_call()
+ {
+ LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] -->> start_outer_call");
+ if(!m_pservice_endpoint->add_ref())
+ {
+ LOG_PRINT_CC_RED(m_connection_context, "[levin_protocol] -->> start_outer_call failed", LOG_LEVEL_4);
+ return false;
+ }
+ boost::interprocess::ipcdetail::atomic_inc32(&m_wait_count);
+ return true;
+ }
+ bool finish_outer_call()
+ {
+ LOG_PRINT_CC_L4(m_connection_context, "[levin_protocol] <<-- finish_outer_call");
+ boost::interprocess::ipcdetail::atomic_dec32(&m_wait_count);
+ m_pservice_endpoint->release();
+ return true;
+ }
+
+ bool release_protocol()
+ {
+ decltype(m_invoke_response_handlers) local_invoke_response_handlers;
+ CRITICAL_REGION_BEGIN(m_invoke_response_handlers_lock);
+ local_invoke_response_handlers.swap(m_invoke_response_handlers);
+ m_protocol_released = true;
+ CRITICAL_REGION_END();
+
+ // Never call callback inside critical section, that can cause deadlock. Callback can be called when
+ // invoke_response_handler_base is cancelled
+ std::for_each(local_invoke_response_handlers.begin(), local_invoke_response_handlers.end(), [](const boost::shared_ptr<invoke_response_handler_base>& pinv_resp_hndlr) {
+ pinv_resp_hndlr->cancel();
+ });
+
+ return true;
+ }
+
+ bool close()
+ {
+ boost::interprocess::ipcdetail::atomic_inc32(&m_close_called);
+
+ m_pservice_endpoint->close();
+ return true;
+ }
+
+ void update_connection_context(const connection_context& contxt)
+ {
+ m_connection_context = contxt;
+ }
+
+ void request_callback()
+ {
+ misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
+ boost::bind(&async_protocol_handler::finish_outer_call, this));
+
+ m_pservice_endpoint->request_callback();
+ }
+
+ void handle_qued_callback()
+ {
+ m_config.m_pcommands_handler->callback(m_connection_context);
+ }
+
+ virtual bool handle_recv(const void* ptr, size_t cb)
+ {
+ if(boost::interprocess::ipcdetail::atomic_read32(&m_close_called))
+ return false; //closing connections
+
+ if(!m_config.m_pcommands_handler)
+ {
+ LOG_ERROR_CC(m_connection_context, "Commands handler not set!");
+ return false;
+ }
+
+ if(m_cache_in_buffer.size() + cb > m_config.m_max_packet_size)
+ {
+ LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
+ << ", packet received " << m_cache_in_buffer.size() + cb
+ << ", connection will be closed.");
+ return false;
+ }
+
+ m_cache_in_buffer.append((const char*)ptr, cb);
+
+ bool is_continue = true;
+ while(is_continue)
+ {
+ switch(m_state)
+ {
+ case stream_state_body:
+ if(m_cache_in_buffer.size() < m_current_head.m_cb)
+ {
+ is_continue = false;
+ break;
+ }
+ {
+ std::string buff_to_invoke;
+ if(m_cache_in_buffer.size() == m_current_head.m_cb)
+ buff_to_invoke.swap(m_cache_in_buffer);
+ else
+ {
+ buff_to_invoke.assign(m_cache_in_buffer, 0, (std::string::size_type)m_current_head.m_cb);
+ m_cache_in_buffer.erase(0, (std::string::size_type)m_current_head.m_cb);
+ }
+
+ bool is_response = (m_oponent_protocol_ver == LEVIN_PROTOCOL_VER_1 && m_current_head.m_flags&LEVIN_PACKET_RESPONSE);
+
+ LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_RECIEVED. [len=" << m_current_head.m_cb
+ << ", flags" << m_current_head.m_flags
+ << ", r?=" << m_current_head.m_have_to_return_data
+ <<", cmd = " << m_current_head.m_command
+ << ", v=" << m_current_head.m_protocol_version);
+
+ if(is_response)
+ {//response to some invoke
+
+ epee::critical_region_t<decltype(m_invoke_response_handlers_lock)> invoke_response_handlers_guard(m_invoke_response_handlers_lock);
+ if(!m_invoke_response_handlers.empty())
+ {//async call scenario
+ boost::shared_ptr<invoke_response_handler_base> response_handler = m_invoke_response_handlers.front();
+ bool timer_cancelled = response_handler->cancel_timer();
+ // Don't pop handler, to avoid destroying it
+ if(timer_cancelled)
+ m_invoke_response_handlers.pop_front();
+ invoke_response_handlers_guard.unlock();
+
+ if(timer_cancelled)
+ response_handler->handle(m_current_head.m_command, buff_to_invoke, m_connection_context);
+ }
+ else
+ {
+ invoke_response_handlers_guard.unlock();
+ //use sync call scenario
+ if(!boost::interprocess::ipcdetail::atomic_read32(&m_wait_count) && !boost::interprocess::ipcdetail::atomic_read32(&m_close_called))
+ {
+ LOG_ERROR_CC(m_connection_context, "no active invoke when response came, wtf?");
+ return false;
+ }else
+ {
+ CRITICAL_REGION_BEGIN(m_local_inv_buff_lock);
+ buff_to_invoke.swap(m_local_inv_buff);
+ buff_to_invoke.clear();
+ m_invoke_result_code = m_current_head.m_return_code;
+ CRITICAL_REGION_END();
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 1);
+ }
+ }
+ }else
+ {
+ if(m_current_head.m_have_to_return_data)
+ {
+ std::string return_buff;
+ m_current_head.m_return_code = m_config.m_pcommands_handler->invoke(
+ m_current_head.m_command,
+ buff_to_invoke,
+ return_buff,
+ m_connection_context);
+ m_current_head.m_cb = return_buff.size();
+ m_current_head.m_have_to_return_data = false;
+ m_current_head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ m_current_head.m_flags = LEVIN_PACKET_RESPONSE;
+ std::string send_buff((const char*)&m_current_head, sizeof(m_current_head));
+ send_buff += return_buff;
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ if(!m_pservice_endpoint->do_send(send_buff.data(), send_buff.size()))
+ return false;
+ CRITICAL_REGION_END();
+ LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << m_current_head.m_cb
+ << ", flags" << m_current_head.m_flags
+ << ", r?=" << m_current_head.m_have_to_return_data
+ <<", cmd = " << m_current_head.m_command
+ << ", ver=" << m_current_head.m_protocol_version);
+ }
+ else
+ m_config.m_pcommands_handler->notify(m_current_head.m_command, buff_to_invoke, m_connection_context);
+ }
+ }
+ m_state = stream_state_head;
+ break;
+ case stream_state_head:
+ {
+ if(m_cache_in_buffer.size() < sizeof(bucket_head2))
+ {
+ if(m_cache_in_buffer.size() >= sizeof(boost::uint64_t) && *((boost::uint64_t*)m_cache_in_buffer.data()) != LEVIN_SIGNATURE)
+ {
+ LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
+ return false;
+ }
+ is_continue = false;
+ break;
+ }
+
+ bucket_head2* phead = (bucket_head2*)m_cache_in_buffer.data();
+ if(LEVIN_SIGNATURE != phead->m_signature)
+ {
+ LOG_ERROR_CC(m_connection_context, "Signature mismatch, connection will be closed");
+ return false;
+ }
+ m_current_head = *phead;
+
+ m_cache_in_buffer.erase(0, sizeof(bucket_head2));
+ m_state = stream_state_body;
+ m_oponent_protocol_ver = m_current_head.m_protocol_version;
+ if(m_current_head.m_cb > m_config.m_max_packet_size)
+ {
+ LOG_ERROR_CC(m_connection_context, "Maximum packet size exceed!, m_max_packet_size = " << m_config.m_max_packet_size
+ << ", packet header received " << m_current_head.m_cb
+ << ", connection will be closed.");
+ return false;
+ }
+ }
+ break;
+ default:
+ LOG_ERROR_CC(m_connection_context, "Undefined state in levin_server_impl::connection_handler, m_state=" << m_state);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool after_init_connection()
+ {
+ if (!m_connection_initialized)
+ {
+ m_connection_initialized = true;
+ m_config.add_connection(this);
+ }
+ return true;
+ }
+
+ template<class callback_t>
+ bool async_invoke(int command, const std::string& in_buff, callback_t cb, size_t timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
+ {
+ misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
+ boost::bind(&async_protocol_handler::finish_outer_call, this));
+
+ if(timeout == LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED)
+ timeout = m_config.m_invoke_timeout;
+
+ int err_code = LEVIN_OK;
+ do
+ {
+ if(m_deletion_initiated)
+ {
+ err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
+ break;
+ }
+
+ CRITICAL_REGION_LOCAL(m_call_lock);
+
+ if(m_deletion_initiated)
+ {
+ err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
+ break;
+ }
+
+ bucket_head2 head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = true;
+
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ CRITICAL_REGION_LOCAL1(m_invoke_response_handlers_lock);
+ if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
+ {
+ LOG_ERROR_CC(m_connection_context, "Failed to do_send");
+ err_code = LEVIN_ERROR_CONNECTION;
+ break;
+ }
+
+ if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
+ {
+ LOG_ERROR_CC(m_connection_context, "Failed to do_send");
+ err_code = LEVIN_ERROR_CONNECTION;
+ break;
+ }
+
+ if(!add_invoke_response_handler(cb, timeout, *this, command))
+ {
+ err_code = LEVIN_ERROR_CONNECTION_DESTROYED;
+ break;
+ }
+ CRITICAL_REGION_END();
+ } while (false);
+
+ if (LEVIN_OK != err_code)
+ {
+ std::string stub_buff;
+ // Never call callback inside critical section, that can cause deadlock
+ cb(err_code, stub_buff, m_connection_context);
+ return false;
+ }
+
+ return true;
+ }
+
+ int invoke(int command, const std::string& in_buff, std::string& buff_out)
+ {
+ misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
+ boost::bind(&async_protocol_handler::finish_outer_call, this));
+
+ if(m_deletion_initiated)
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ CRITICAL_REGION_LOCAL(m_call_lock);
+
+ if(m_deletion_initiated)
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ bucket_head2 head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_cb = in_buff.size();
+ head.m_have_to_return_data = true;
+
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+
+ boost::interprocess::ipcdetail::atomic_write32(&m_invoke_buf_ready, 0);
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
+ {
+ LOG_ERROR_CC(m_connection_context, "Failed to do_send");
+ return LEVIN_ERROR_CONNECTION;
+ }
+
+ if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
+ {
+ LOG_ERROR_CC(m_connection_context, "Failed to do_send");
+ return LEVIN_ERROR_CONNECTION;
+ }
+ CRITICAL_REGION_END();
+
+ LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb
+ << ", f=" << head.m_flags
+ << ", r?=" << head.m_have_to_return_data
+ << ", cmd = " << head.m_command
+ << ", ver=" << head.m_protocol_version);
+
+ uint64_t ticks_start = misc_utils::get_tick_count();
+
+ while(!boost::interprocess::ipcdetail::atomic_read32(&m_invoke_buf_ready) && !m_deletion_initiated && !m_protocol_released)
+ {
+ if(misc_utils::get_tick_count() - ticks_start > m_config.m_invoke_timeout)
+ {
+ LOG_PRINT_CC_L2(m_connection_context, "invoke timeout (" << m_config.m_invoke_timeout << "), closing connection ");
+ close();
+ return LEVIN_ERROR_CONNECTION_TIMEDOUT;
+ }
+ if(!m_pservice_endpoint->call_run_once_service_io())
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ }
+
+ if(m_deletion_initiated || m_protocol_released)
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ CRITICAL_REGION_BEGIN(m_local_inv_buff_lock);
+ buff_out.swap(m_local_inv_buff);
+ m_local_inv_buff.clear();
+ CRITICAL_REGION_END();
+
+ return m_invoke_result_code;
+ }
+
+ int notify(int command, const std::string& in_buff)
+ {
+ misc_utils::auto_scope_leave_caller scope_exit_handler = misc_utils::create_scope_leave_handler(
+ boost::bind(&async_protocol_handler::finish_outer_call, this));
+
+ if(m_deletion_initiated)
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ CRITICAL_REGION_LOCAL(m_call_lock);
+
+ if(m_deletion_initiated)
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+
+ bucket_head2 head = {0};
+ head.m_signature = LEVIN_SIGNATURE;
+ head.m_have_to_return_data = false;
+ head.m_cb = in_buff.size();
+
+ head.m_command = command;
+ head.m_protocol_version = LEVIN_PROTOCOL_VER_1;
+ head.m_flags = LEVIN_PACKET_REQUEST;
+ CRITICAL_REGION_BEGIN(m_send_lock);
+ if(!m_pservice_endpoint->do_send(&head, sizeof(head)))
+ {
+ LOG_ERROR_CC(m_connection_context, "Failed to do_send()");
+ return -1;
+ }
+
+ if(!m_pservice_endpoint->do_send(in_buff.data(), (int)in_buff.size()))
+ {
+ LOG_ERROR("Failed to do_send()");
+ return -1;
+ }
+ CRITICAL_REGION_END();
+ LOG_PRINT_CC_L4(m_connection_context, "LEVIN_PACKET_SENT. [len=" << head.m_cb <<
+ ", f=" << head.m_flags <<
+ ", r?=" << head.m_have_to_return_data <<
+ ", cmd = " << head.m_command <<
+ ", ver=" << head.m_protocol_version);
+
+ return 1;
+ }
+ //------------------------------------------------------------------------------------------
+ boost::uuids::uuid get_connection_id() {return m_connection_context.m_connection_id;}
+ //------------------------------------------------------------------------------------------
+ t_connection_context& get_context_ref() {return m_connection_context;}
+};
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+void async_protocol_handler_config<t_connection_context>::del_connection(async_protocol_handler<t_connection_context>* pconn)
+{
+ CRITICAL_REGION_BEGIN(m_connects_lock);
+ m_connects.erase(pconn->get_connection_id());
+ CRITICAL_REGION_END();
+ m_pcommands_handler->on_connection_close(pconn->m_connection_context);
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+void async_protocol_handler_config<t_connection_context>::add_connection(async_protocol_handler<t_connection_context>* pconn)
+{
+ CRITICAL_REGION_BEGIN(m_connects_lock);
+ m_connects[pconn->get_connection_id()] = pconn;
+ CRITICAL_REGION_END();
+ m_pcommands_handler->on_connection_new(pconn->m_connection_context);
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+async_protocol_handler<t_connection_context>* async_protocol_handler_config<t_connection_context>::find_connection(boost::uuids::uuid connection_id) const
+{
+ auto it = m_connects.find(connection_id);
+ return it == m_connects.end() ? 0 : it->second;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+int async_protocol_handler_config<t_connection_context>::find_and_lock_connection(boost::uuids::uuid connection_id, async_protocol_handler<t_connection_context>*& aph)
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ aph = find_connection(connection_id);
+ if(0 == aph)
+ return LEVIN_ERROR_CONNECTION_NOT_FOUND;
+ if(!aph->start_outer_call())
+ return LEVIN_ERROR_CONNECTION_DESTROYED;
+ return LEVIN_OK;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+int async_protocol_handler_config<t_connection_context>::invoke(int command, const std::string& in_buff, std::string& buff_out, boost::uuids::uuid connection_id)
+{
+ async_protocol_handler<t_connection_context>* aph;
+ int r = find_and_lock_connection(connection_id, aph);
+ return LEVIN_OK == r ? aph->invoke(command, in_buff, buff_out) : r;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context> template<class callback_t>
+int async_protocol_handler_config<t_connection_context>::invoke_async(int command, const std::string& in_buff, boost::uuids::uuid connection_id, callback_t cb, size_t timeout)
+{
+ async_protocol_handler<t_connection_context>* aph;
+ int r = find_and_lock_connection(connection_id, aph);
+ return LEVIN_OK == r ? aph->async_invoke(command, in_buff, cb, timeout) : r;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context> template<class callback_t>
+bool async_protocol_handler_config<t_connection_context>::foreach_connection(callback_t cb)
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ for(auto& c: m_connects)
+ {
+ async_protocol_handler<t_connection_context>* aph = c.second;
+ if(!cb(aph->get_context_ref()))
+ return false;
+ }
+ return true;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+size_t async_protocol_handler_config<t_connection_context>::get_connections_count()
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ return m_connects.size();
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+int async_protocol_handler_config<t_connection_context>::notify(int command, const std::string& in_buff, boost::uuids::uuid connection_id)
+{
+ async_protocol_handler<t_connection_context>* aph;
+ int r = find_and_lock_connection(connection_id, aph);
+ return LEVIN_OK == r ? aph->notify(command, in_buff) : r;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+bool async_protocol_handler_config<t_connection_context>::close(boost::uuids::uuid connection_id)
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ async_protocol_handler<t_connection_context>* aph = find_connection(connection_id);
+ return 0 != aph ? aph->close() : false;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+bool async_protocol_handler_config<t_connection_context>::update_connection_context(const t_connection_context& contxt)
+{
+ CRITICAL_REGION_LOCAL(m_connects_lock);
+ async_protocol_handler<t_connection_context>* aph = find_connection(contxt.m_connection_id);
+ if(0 == aph)
+ return false;
+ aph->update_connection_context(contxt);
+ return true;
+}
+//------------------------------------------------------------------------------------------
+template<class t_connection_context>
+bool async_protocol_handler_config<t_connection_context>::request_callback(boost::uuids::uuid connection_id)
+{
+ async_protocol_handler<t_connection_context>* aph;
+ int r = find_and_lock_connection(connection_id, aph);
+ if(LEVIN_OK == r)
+ {
+ aph->request_callback();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+}
+}
diff --git a/contrib/epee/include/net/levin_server_cp.h b/contrib/epee/include/net/levin_server_cp.h
new file mode 100644
index 000000000..8ece35059
--- /dev/null
+++ b/contrib/epee/include/net/levin_server_cp.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#ifndef _HTTP_SERVER_CP_H_
+#define _HTTP_SERVER_CP_H_
+
+#include "abstract_tcp_server_cp.h"
+#include "levin_protocol_handler.h"
+namespace epee
+{
+namespace net_utils
+{
+ typedef cp_server_impl<levin::protocol_handler> cp_levin_server;
+}
+}
+
+
+
+#endif
+
+
diff --git a/contrib/epee/include/net/levin_server_cp2.h b/contrib/epee/include/net/levin_server_cp2.h
new file mode 100644
index 000000000..b29d49bf8
--- /dev/null
+++ b/contrib/epee/include/net/levin_server_cp2.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _HTTP_SERVER_CP_H_
+#define _HTTP_SERVER_CP_H_
+
+#include "abstract_tcp_server2.h"
+#include "levin_protocol_handler.h"
+#include "levin_protocol_handler_async.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ typedef boosted_tcp_server<levin::protocol_handler<> > boosted_levin_server;
+ typedef boosted_tcp_server<levin::async_protocol_handler<> > boosted_levin_async_server;
+}
+}
+
+
+
+#endif
+
+
diff --git a/contrib/epee/include/net/local_ip.h b/contrib/epee/include/net/local_ip.h
new file mode 100644
index 000000000..028ad73ef
--- /dev/null
+++ b/contrib/epee/include/net/local_ip.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#pragma once
+
+namespace epee
+{
+ namespace net_utils
+ {
+ inline
+ bool is_ip_local(boost::uint32_t ip)
+ {
+ /*
+ local ip area
+ 10.0.0.0 — 10.255.255.255
+ 172.16.0.0 — 172.31.255.255
+ 192.168.0.0 — 192.168.255.255
+ */
+ if( (ip | 0xffffff00) == 0xffffff0a)
+ return true;
+
+ if( (ip | 0xffff0000) == 0xffffa8c0)
+ return true;
+
+ if( (ip | 0xffffff00) == 0xffffffac)
+ {
+ uint32_t second_num = (ip << 8) & 0xff000000;
+ if(second_num >= 16 && second_num <= 31 )
+ return true;
+ }
+ return false;
+ }
+ inline
+ bool is_ip_loopback(boost::uint32_t ip)
+ {
+ if( (ip | 0xffffff00) == 0xffffff7f)
+ return true;
+ //MAKE_IP
+ /*
+ loopback ip
+ 127.0.0.0 — 127.255.255.255
+ */
+ return false;
+ }
+
+ }
+}
+
diff --git a/contrib/epee/include/net/multiprotocols_server.h b/contrib/epee/include/net/multiprotocols_server.h
new file mode 100644
index 000000000..4807a4421
--- /dev/null
+++ b/contrib/epee/include/net/multiprotocols_server.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _MULTIPROTOCOLS_SERVER_H_
+#define _MULTIPROTOCOLS_SERVER_H_
+
+//#include "abstract_tcp_server_cp.h"
+#include "protocol_switcher.h"
+#include "abstract_tcp_server2.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ //typedef cp_server_impl<net_utils::protocol_switcher> multiprotocol_server;
+ typedef boosted_tcp_server<net_utils::protocol_switcher> boosted_multiprotocol_server;
+}
+}
+
+
+#endif //_MULTIPROTOCOLS_SERVER_H_
+
diff --git a/contrib/epee/include/net/munin_connection_handler.h b/contrib/epee/include/net/munin_connection_handler.h
new file mode 100644
index 000000000..8579339c5
--- /dev/null
+++ b/contrib/epee/include/net/munin_connection_handler.h
@@ -0,0 +1,376 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _MUNIN_CONNECTION_HANDLER_H_
+#define _MUNIN_CONNECTION_HANDLER_H_
+
+#include <string>
+#include "net_utils_base.h"
+#include "to_nonconst_iterator.h"
+#include "http_base.h"
+#include "reg_exp_definer.h"
+
+#define MUNIN_ARGS_DEFAULT(vertial_lable_str) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " \n"
+#define MUNIN_ARGS_FORCE_AUPPER_LIMIT(vertial_lable_str, limit) "graph_args --base 1000 -l 0 --vertical-label " vertial_lable_str " --rigid --upper-limit " limit " \n"
+#define MUNIN_TITLE(title_str) "graph_title " title_str "\n"
+#define MUNIN_CATEGORY(category_str) "graph_category " category_str "\n"
+#define MUNIN_INFO(info_str) "graph_info " info_str "\n"
+#define MUNIN_ENTRY(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n"
+#define MUNIN_ENTRY_AREA(var_name) #var_name".label " #var_name "\n" #var_name".info "#var_name".\n" #var_name".draw AREASTACK\n"
+#define MUNIN_ENTRY_ALIAS(var_name, alias) #var_name".label " #alias"\n" #var_name".info "#alias".\n"
+#define BEGIN_MUNIN_SERVICE(servivece_name_str) if(servivece_name_str == pservice->m_service_name) {
+#define END_MUNIN_SERVICE() }
+#define MUNIN_SERVICE_PARAM(munin_var_name_str, variable) paramters_text += std::string() + munin_var_name_str ".value " + boost::lexical_cast<std::string>(variable) + "\n"
+
+
+
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace munin
+ {
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct munin_service;
+
+ struct munin_service_data_provider
+ {
+ virtual bool update_service_data(munin_service* pservice, std::string& paramters_text)=0;
+ };
+
+ struct munin_service
+ {
+ std::string m_service_name;
+ std::string m_service_config_string;
+ munin_service_data_provider* m_pdata_provider;
+ };
+
+ struct node_server_config
+ {
+ std::list<munin_service> m_services;
+ //TODO:
+ };
+
+ struct fake_send_handler: public i_service_endpoint
+ {
+ virtual bool do_send(const void* ptr, size_t cb)
+ {
+ m_cache += std::string((const char*)ptr, cb);
+ return true;
+ }
+ public:
+
+ std::string m_cache;
+ };
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class munin_node_server_connection_handler
+ {
+ public:
+ typedef node_server_config config_type;
+ typedef connection_context_base connection_context;
+
+ munin_node_server_connection_handler(i_service_endpoint* psnd_hndlr, config_type& config, const connection_context_base& context):m_psnd_hndlr(psnd_hndlr),
+ m_machine_state(http_state_retriving_comand_line),
+ m_config(config)
+ {
+ init();
+ }
+ virtual ~munin_node_server_connection_handler()
+ {
+
+ }
+
+ bool release_protocol()
+ {
+ return true;
+ }
+ bool after_init_connection()
+ {
+ std::string hello_str = "# munin node at ";
+ hello_str += m_host_name + "\n";
+ send_hook(hello_str);
+ return true;
+ }
+
+ virtual bool thread_init()
+ {
+ return true;
+ }
+
+ virtual bool thread_deinit()
+ {
+ return true;
+ }
+
+ void handle_qued_callback()
+ {
+
+ }
+
+ virtual bool handle_recv(const void* ptr, size_t cb)
+ {
+
+ const char* pbuff = (const char*)ptr;
+ std::string recvd_buff(pbuff, cb);
+ LOG_PRINT("munin_recv: \n" << recvd_buff, LOG_LEVEL_3);
+
+ m_cache += recvd_buff;
+
+ bool stop_handling = false;
+ while(!stop_handling)
+ {
+ switch(m_machine_state)
+ {
+ case http_state_retriving_comand_line:
+ {
+
+ std::string::size_type fpos = m_cache.find('\n');
+ if(std::string::npos != fpos )
+ {
+ bool res = handle_command(m_cache);
+ if(!res)
+ return false;
+ m_cache.erase(0, fpos+1);
+ continue;
+ }
+ stop_handling = true;
+ }
+ break;
+ case http_state_error:
+ stop_handling = true;
+ return false;
+ default:
+ LOG_ERROR("Error in munin state machine! Unkonwon state=" << m_machine_state);
+ stop_handling = true;
+ m_machine_state = http_state_error;
+ return false;
+ }
+
+ }
+
+ return true;
+ }
+
+ private:
+
+
+ bool init()
+ {
+ char hostname[64] = {0};
+ int res = gethostname(hostname, 64);
+ hostname[63] = 0;//be happy
+ m_host_name = hostname;
+ return true;
+ }
+ bool handle_command(const std::string& command)
+ {
+ // list, nodes, config, fetch, version or quit
+ STATIC_REGEXP_EXPR_1(rexp_match_command_line, "^((list)|(nodes)|(config)|(fetch)|(version)|(quit))(\\s+(\\S+))?", boost::regex::icase | boost::regex::normal);
+ // 12 3 4 5 6 7 8 9
+ size_t match_len = 0;
+ boost::smatch result;
+ if(boost::regex_search(command, result, rexp_match_command_line, boost::match_default) && result[0].matched)
+ {
+ if(result[2].matched)
+ {//list command
+ return handle_list_command();
+ }else if(result[3].matched)
+ {//nodes command
+ return handle_nodes_command();
+ }else if(result[4].matched)
+ {//config command
+ if(result[9].matched)
+ return handle_config_command(result[9]);
+ else
+ {
+ send_hook("Unknown service\n");
+ }
+ }else if(result[5].matched)
+ {//fetch command
+ if(result[9].matched)
+ return handle_fetch_command(result[9]);
+ else
+ {
+ send_hook("Unknown service\n");
+ }
+ }else if(result[6].matched)
+ {//version command
+ return handle_version_command();
+ }else if(result[7].matched)
+ {//quit command
+ return handle_quit_command();
+ }
+ else
+ return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");
+ }
+
+ return send_hook("Unknown command. Try list, nodes, config, fetch, version or quit\n");;
+ }
+
+ bool handle_list_command()
+ {
+ std::string buff_to_send;
+ for(std::list<munin_service>::const_iterator it = m_config.m_services.begin(); it!=m_config.m_services.end();it++)
+ {
+ buff_to_send += it->m_service_name + " ";
+ }
+ buff_to_send+='\n';
+ return send_hook(buff_to_send);
+ }
+ bool handle_nodes_command()
+ {
+ //supports only one node - host name
+ send_hook(m_host_name + "\n.\n");
+ return true;
+ }
+ bool handle_config_command(const std::string& service_name)
+ {
+ munin_service* psrv = get_service_by_name(service_name);
+ if(!psrv)
+ return send_hook(std::string() + "Unknown service\n");
+
+
+ return send_hook(psrv->m_service_config_string + ".\n");
+ }
+
+ bool handle_fetch_command(const std::string& service_name)
+ {
+ munin_service* psrv = get_service_by_name(service_name);
+ if(!psrv)
+ return send_hook(std::string() + "Unknown service\n");
+
+ std::string buff;
+ psrv->m_pdata_provider->update_service_data(psrv, buff);
+
+ buff += ".\n";
+ return send_hook(buff);
+ }
+ bool handle_version_command()
+ {
+ return send_hook("Munin node component by Andrey Sabelnikov\n");
+ }
+ bool handle_quit_command()
+ {
+ return false;
+ }
+
+ bool send_hook(const std::string& buff)
+ {
+ LOG_PRINT("munin_send: \n" << buff, LOG_LEVEL_3);
+
+ if(m_psnd_hndlr)
+ return m_psnd_hndlr->do_send(buff.data(), buff.size());
+ else
+ return false;
+ }
+
+
+ munin_service* get_service_by_name(const std::string& srv_name)
+ {
+ std::list<munin_service>::iterator it = m_config.m_services.begin();
+ for(; it!=m_config.m_services.end(); it++)
+ if(it->m_service_name == srv_name)
+ break;
+
+ if(it==m_config.m_services.end())
+ return NULL;
+
+ return &(*it);
+ }
+
+ enum machine_state{
+ http_state_retriving_comand_line,
+ http_state_error
+ };
+
+
+ config_type& m_config;
+ machine_state m_machine_state;
+ std::string m_cache;
+ std::string m_host_name;
+ protected:
+ i_service_endpoint* m_psnd_hndlr;
+ };
+
+
+ inline bool test_self()
+ {
+ /*WSADATA w;
+ ::WSAStartup(MAKEWORD(1, 1), &w);
+ node_server_config sc;
+ sc.m_services.push_back(munin_service());
+ sc.m_services.back().m_service_name = "test_service";
+
+ sc.m_services.back().m_service_config_string =
+ "graph_args --base 1000 -l 0 --vertical-label N --upper-limit 329342976\n"
+ "graph_title REPORTS STATICTICS\n"
+ "graph_category bind\n"
+ "graph_info This graph shows how many reports came in fixed time period.\n"
+ "graph_order apps free swap\n"
+ "apps.label apps\n"
+ "apps.draw AREA\n"
+ "apps.info Memory used by user-space applications.\n"
+ "swap.label swap\n"
+ "swap.draw STACK\n"
+ "swap.info Swap space used.\n"
+ "free.label unused\n"
+ "free.draw STACK\n"
+ "free.info Wasted memory. Memory that is not used for anything at all.\n"
+ "committed.label committed\n"
+ "committed.draw LINE2\n"
+ "committed.warn 625410048\n"
+ "committed.info The amount of memory that would be used if all the memory that's been allocated were to be used.\n";
+
+
+ sc.m_services.push_back(munin_service());
+ sc.m_services.back().m_service_name = "test_service1";
+ fake_send_handler fh;
+ munin_node_server_connection_handler mh(&fh, sc);
+
+ std::string buff = "list\n";
+ mh.handle_recv(buff.data(), buff.size());
+
+
+ buff = "nodes\n";
+ mh.handle_recv(buff.data(), buff.size());
+*/
+ return true;
+ }
+
+ }
+}
+}
+#endif//!_MUNIN_CONNECTION_HANDLER_H_ \ No newline at end of file
diff --git a/contrib/epee/include/net/munin_node_server.h b/contrib/epee/include/net/munin_node_server.h
new file mode 100644
index 000000000..07637f550
--- /dev/null
+++ b/contrib/epee/include/net/munin_node_server.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _MUNIN_NODE_SERVER_H_
+#define _MUNIN_NODE_SERVER_H_
+
+#include <string>
+//#include "net_utils_base.h"
+#include "munin_connection_handler.h"
+//#include "abstract_tcp_server.h"
+//#include "abstract_tcp_server_cp.h"
+#include "abstract_tcp_server2.h"
+namespace epee
+{
+namespace net_utils
+{
+ namespace munin
+ {
+ typedef boosted_tcp_server<munin_node_server_connection_handler> munin_node_server;
+ //typedef cp_server_impl<munin_node_server_connection_handler> munin_node_cp_server;
+ }
+}
+}
+#endif//!_MUNIN_NODE_SERVER_H_ \ No newline at end of file
diff --git a/contrib/epee/include/net/net_helper.h b/contrib/epee/include/net/net_helper.h
new file mode 100644
index 000000000..d2a4cfec3
--- /dev/null
+++ b/contrib/epee/include/net/net_helper.h
@@ -0,0 +1,683 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+
+//#include <Winsock2.h>
+//#include <Ws2tcpip.h>
+#include <boost/lexical_cast.hpp>
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <boost/asio.hpp>
+#include <boost/preprocessor/selection/min.hpp>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+#include <boost/interprocess/detail/atomic.hpp>
+#include "net/net_utils_base.h"
+#include "misc_language.h"
+//#include "profile_tools.h"
+#include "../string_tools.h"
+
+#ifndef MAKE_IP
+#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
+#endif
+
+
+namespace epee
+{
+namespace net_utils
+{
+
+ class blocked_mode_client
+ {
+
+
+ struct handler_obj
+ {
+ handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred)
+ {}
+ handler_obj(const handler_obj& other_obj):ref_error(other_obj.ref_error), ref_bytes_transferred(other_obj.ref_bytes_transferred)
+ {}
+
+ boost::system::error_code& ref_error;
+ size_t& ref_bytes_transferred;
+
+ void operator()(const boost::system::error_code& error, // Result of operation.
+ std::size_t bytes_transferred // Number of bytes read.
+ )
+ {
+ ref_error = error;
+ ref_bytes_transferred = bytes_transferred;
+ }
+ };
+
+ public:
+ inline
+ blocked_mode_client():m_socket(m_io_service),
+ m_initialized(false),
+ m_connected(false),
+ m_deadline(m_io_service),
+ m_shutdowned(0)
+ {
+
+
+ m_initialized = true;
+
+
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+
+ }
+ inline
+ ~blocked_mode_client()
+ {
+ //profile_tools::local_coast lc("~blocked_mode_client()", 3);
+ shutdown();
+ }
+
+ inline void set_recv_timeout(int reciev_timeout)
+ {
+ m_reciev_timeout = reciev_timeout;
+ }
+
+ inline
+ bool connect(const std::string& addr, int port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ return connect(addr, std::to_string(port), connect_timeout, reciev_timeout, bind_ip);
+ }
+
+ inline
+ bool connect(const std::string& addr, const std::string& port, unsigned int connect_timeout, unsigned int reciev_timeout, const std::string& bind_ip = "0.0.0.0")
+ {
+ m_connect_timeout = connect_timeout;
+ m_reciev_timeout = reciev_timeout;
+ m_connected = false;
+ if(!m_reciev_timeout)
+ m_reciev_timeout = m_connect_timeout;
+
+ try
+ {
+ m_socket.close();
+ // Get a list of endpoints corresponding to the server name.
+
+
+ //////////////////////////////////////////////////////////////////////////
+
+ boost::asio::ip::tcp::resolver resolver(m_io_service);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), addr, port);
+ boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
+ boost::asio::ip::tcp::resolver::iterator end;
+ if(iterator == end)
+ {
+ LOG_ERROR("Failed to resolve " << addr);
+ return false;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+
+ //boost::asio::ip::tcp::endpoint remote_endpoint(boost::asio::ip::address::from_string(addr.c_str()), port);
+ boost::asio::ip::tcp::endpoint remote_endpoint(*iterator);
+
+
+ m_socket.open(remote_endpoint.protocol());
+ if(bind_ip != "0.0.0.0" && bind_ip != "0" && bind_ip != "" )
+ {
+ boost::asio::ip::tcp::endpoint local_endpoint(boost::asio::ip::address::from_string(addr.c_str()), 0);
+ m_socket.bind(local_endpoint);
+ }
+
+
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_connect_timeout));
+
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ //m_socket.connect(remote_endpoint);
+ m_socket.async_connect(remote_endpoint, boost::lambda::var(ec) = boost::lambda::_1);
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (!ec && m_socket.is_open())
+ {
+ m_connected = true;
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ return true;
+ }else
+ {
+ LOG_PRINT("Some problems at connect, message: " << ec.message(), LOG_LEVEL_3);
+ return false;
+ }
+
+ }
+ catch(const boost::system::system_error& er)
+ {
+ LOG_PRINT("Some problems at connect, message: " << er.what(), LOG_LEVEL_4);
+ return false;
+ }
+ catch(...)
+ {
+ LOG_PRINT("Some fatal problems.", LOG_LEVEL_4);
+ return false;
+ }
+
+ return true;
+ }
+
+
+ inline
+ bool disconnect()
+ {
+ try
+ {
+ if(m_connected)
+ {
+ m_connected = false;
+ m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
+
+ }
+ }
+
+ catch(const boost::system::system_error& /*er*/)
+ {
+ //LOG_ERROR("Some problems at disconnect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ //LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+ return true;
+ }
+
+
+ inline
+ bool send(const std::string& buff)
+ {
+
+ try
+ {
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(buff), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }
+ */
+ boost::system::error_code ec;
+
+ size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
+
+
+
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at send, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool is_connected()
+ {
+ return m_connected && m_socket.is_open();
+ //TRY_ENTRY()
+ //return m_socket.is_open();
+ //CATCH_ENTRY_L0("is_connected", false)
+ }
+
+ inline
+ bool recv(std::string& buff)
+ {
+
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
+
+ handler_obj hndlr(ec, bytes_transfered);
+
+ char local_buff[10000] = {0};
+ //m_socket.async_read_some(boost::asio::buffer(local_buff, sizeof(local_buff)), hndlr);
+ boost::asio::async_read(m_socket, boost::asio::buffer(local_buff, sizeof(local_buff)), boost::asio::transfer_at_least(1), hndlr);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
+
+
+ if (ec)
+ {
+ LOG_PRINT_L4("READ ENDS: Connection err_code " << ec.value());
+ if(ec == boost::asio::error::eof)
+ {
+ LOG_PRINT_L4("Connection err_code eof.");
+ //connection closed there, empty
+ return true;
+ }
+
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ LOG_PRINT_L4("READ ENDS: Success. bytes_tr: " << bytes_transfered);
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ /*if(!bytes_transfered)
+ return false;*/
+
+ buff.assign(local_buff, bytes_transfered);
+ return true;
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
+
+
+
+ return false;
+
+ }
+
+ inline bool recv_n(std::string& buff, boost::int64_t sz)
+ {
+
+ try
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ m_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ //boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+
+ buff.resize(static_cast<size_t>(sz));
+ boost::system::error_code ec = boost::asio::error::would_block;
+ size_t bytes_transfered = 0;
+
+
+ handler_obj hndlr(ec, bytes_transfered);
+
+ //char local_buff[10000] = {0};
+ boost::asio::async_read(m_socket, boost::asio::buffer((char*)buff.data(), buff.size()), boost::asio::transfer_at_least(buff.size()), hndlr);
+
+ // Block until the asynchronous operation has completed.
+ while (ec == boost::asio::error::would_block && !boost::interprocess::ipcdetail::atomic_read32(&m_shutdowned))
+ {
+ m_io_service.run_one();
+ }
+
+ if (ec)
+ {
+ LOG_PRINT_L3("Problems at read: " << ec.message());
+ m_connected = false;
+ return false;
+ }else
+ {
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ if(bytes_transfered != buff.size())
+ {
+ LOG_ERROR("Transferred missmatch with transfer_at_least value: m_bytes_transferred=" << bytes_transfered << " at_least value=" << buff.size());
+ return false;
+ }
+
+ return true;
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at read, message: " << er.what());
+ m_connected = false;
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems at read.");
+ return false;
+ }
+
+
+
+ return false;
+ }
+
+ bool shutdown()
+ {
+ m_deadline.cancel();
+ boost::system::error_code ignored_ec;
+ m_socket.cancel(ignored_ec);
+ m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ m_socket.close(ignored_ec);
+ boost::interprocess::ipcdetail::atomic_write32(&m_shutdowned, 1);
+ m_connected = false;
+ return true;
+ }
+
+ void set_connected(bool connected)
+ {
+ m_connected = connected;
+ }
+ boost::asio::io_service& get_io_service()
+ {
+ return m_io_service;
+ }
+
+ boost::asio::ip::tcp::socket& get_socket()
+ {
+ return m_socket;
+ }
+
+ private:
+
+ void check_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ m_connected = false;
+ m_socket.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_deadline.async_wait(boost::bind(&blocked_mode_client::check_deadline, this));
+ }
+
+
+
+ protected:
+ boost::asio::io_service m_io_service;
+ boost::asio::ip::tcp::socket m_socket;
+ int m_connect_timeout;
+ int m_reciev_timeout;
+ bool m_initialized;
+ bool m_connected;
+ boost::asio::deadline_timer m_deadline;
+ volatile boost::uint32_t m_shutdowned;
+ };
+
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class async_blocked_mode_client: public blocked_mode_client
+ {
+ public:
+ async_blocked_mode_client():m_send_deadline(blocked_mode_client::m_io_service)
+ {
+
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_send_deadline();
+ }
+ ~async_blocked_mode_client()
+ {
+ m_send_deadline.cancel();
+ }
+
+ bool shutdown()
+ {
+ blocked_mode_client::shutdown();
+ m_send_deadline.cancel();
+ return true;
+ }
+
+ inline
+ bool send(const void* data, size_t sz)
+ {
+ try
+ {
+ /*
+ m_send_deadline.expires_from_now(boost::posix_time::milliseconds(m_reciev_timeout));
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(m_socket, boost::asio::buffer(data, sz), boost::lambda::var(ec) = boost::lambda::_1);
+
+ // Block until the asynchronous operation has completed.
+ while(ec == boost::asio::error::would_block)
+ {
+ m_io_service.run_one();
+ }*/
+
+ boost::system::error_code ec;
+
+ size_t writen = m_socket.write_some(boost::asio::buffer(data, sz), ec);
+
+ if (!writen || ec)
+ {
+ LOG_PRINT_L3("Problems at write: " << ec.message());
+ return false;
+ }else
+ {
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+ }
+
+ catch(const boost::system::system_error& er)
+ {
+ LOG_ERROR("Some problems at connect, message: " << er.what());
+ return false;
+ }
+ catch(...)
+ {
+ LOG_ERROR("Some fatal problems.");
+ return false;
+ }
+
+ return true;
+ }
+
+
+ private:
+
+ boost::asio::deadline_timer m_send_deadline;
+
+ void check_send_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (m_send_deadline.expires_at() <= boost::asio::deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ LOG_PRINT_L3("Timed out socket");
+ m_socket.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ m_send_deadline.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ m_send_deadline.async_wait(boost::bind(&async_blocked_mode_client::check_send_deadline, this));
+ }
+ };
+}
+}
diff --git a/contrib/epee/include/net/net_parse_helpers.h b/contrib/epee/include/net/net_parse_helpers.h
new file mode 100644
index 000000000..16641a970
--- /dev/null
+++ b/contrib/epee/include/net/net_parse_helpers.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+#include "http_base.h"
+#include "reg_exp_definer.h"
+
+
+namespace epee
+{
+namespace net_utils
+{
+
+ inline bool parse_uri_query(const std::string& query, std::list<std::pair<std::string, std::string> >& params)
+ {
+ enum state
+ {
+ st_param_name,
+ st_param_val
+ };
+ state st = st_param_name;
+ std::string::const_iterator start_it = query.begin();
+ std::pair<std::string, std::string> e;
+ for(std::string::const_iterator it = query.begin(); it != query.end(); it++)
+ {
+ switch(st)
+ {
+ case st_param_name:
+ if(*it == '=')
+ {
+ e.first.assign(start_it, it);
+ start_it = it;++start_it;
+ st = st_param_val;
+ }
+ break;
+ case st_param_val:
+ if(*it == '&')
+ {
+ e.second.assign(start_it, it);
+ start_it = it;++start_it;
+ params.push_back(e);
+ e.first.clear();e.second.clear();
+ st = st_param_name;
+ }
+ break;
+ default:
+ LOG_ERROR("Unknown state " << (int)st);
+ return false;
+ }
+ }
+ if(st == st_param_name)
+ {
+ if(start_it != query.end())
+ {
+ e.first.assign(start_it, query.end());
+ params.push_back(e);
+ }
+ }else
+ {
+ if(start_it != query.end())
+ e.second.assign(start_it, query.end());
+
+ if(e.first.size())
+ params.push_back(e);
+ }
+ return true;
+ }
+
+ inline
+ bool parse_uri(const std::string uri, http::uri_content& content)
+ {
+
+ ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
+ content.m_query_params.clear();
+ STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
+
+ boost::smatch result;
+ if(!boost::regex_search(uri, result, rexp_match_uri, boost::match_default) && result[0].matched)
+ {
+ LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << uri);
+ content.m_path = uri;
+ return true;
+ }
+ if(result[1].matched)
+ {
+ content.m_path = result[1];
+ }
+ if(result[3].matched)
+ {
+ content.m_query = result[3];
+ }
+ if(result[5].matched)
+ {
+ content.m_fragment = result[5];
+ }
+ if(content.m_query.size())
+ {
+ parse_uri_query(content.m_query, content.m_query_params);
+ }
+ return true;
+ }
+
+
+ inline
+ bool parse_url(const std::string url_str, http::url_content& content)
+ {
+
+ ///iframe_test.html?api_url=http://api.vk.com/api.php&api_id=3289090&api_settings=1&viewer_id=562964060&viewer_type=0&sid=0aad8d1c5713130f9ca0076f2b7b47e532877424961367d81e7fa92455f069be7e21bc3193cbd0be11895&secret=368ebbc0ef&access_token=668bc03f43981d883f73876ffff4aa8564254b359cc745dfa1b3cde7bdab2e94105d8f6d8250717569c0a7&user_id=0&group_id=0&is_app_user=1&auth_key=d2f7a895ca5ff3fdb2a2a8ae23fe679a&language=0&parent_language=0&ad_info=ElsdCQBaQlxiAQRdFUVUXiN2AVBzBx5pU1BXIgZUJlIEAWcgAUoLQg==&referrer=unknown&lc_name=9834b6a3&hash=
+ //STATIC_REGEXP_EXPR_1(rexp_match_uri, "^([^?#]*)(\\?([^#]*))?(#(.*))?", boost::regex::icase | boost::regex::normal);
+ STATIC_REGEXP_EXPR_1(rexp_match_uri, "^((.*?)://)?(([^/:]*)(:(\\d+))?)(.*)?", boost::regex::icase | boost::regex::normal);
+ // 12 34 5 6 7
+ content.port = 0;
+ boost::smatch result;
+ if(!boost::regex_search(url_str, result, rexp_match_uri, boost::match_default) && result[0].matched)
+ {
+ LOG_PRINT_L0("[PARSE URI] regex not matched for uri: " << rexp_match_uri);
+ //content.m_path = uri;
+ return true;
+ }
+ if(result[2].matched)
+ {
+ content.schema = result[2];
+ }
+ if(result[4].matched)
+ {
+ content.host = result[4];
+ }
+ if(result[6].matched)
+ {
+ content.port = boost::lexical_cast<boost::uint64_t>(result[6]);
+ }
+ if(result[7].matched)
+ {
+ content.uri = result[7];
+ return parse_uri(result[7], content.m_uri_content);
+ }
+
+ return true;
+ }
+
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/net_utils_base.h b/contrib/epee/include/net/net_utils_base.h
new file mode 100644
index 000000000..86797bb85
--- /dev/null
+++ b/contrib/epee/include/net/net_utils_base.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _NET_UTILS_BASE_H_
+#define _NET_UTILS_BASE_H_
+
+#include <boost/uuid/uuid.hpp>
+#include "string_tools.h"
+
+#ifndef MAKE_IP
+#define MAKE_IP( a1, a2, a3, a4 ) (a1|(a2<<8)|(a3<<16)|(a4<<24))
+#endif
+
+
+namespace epee
+{
+namespace net_utils
+{
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct connection_context_base
+ {
+ const boost::uuids::uuid m_connection_id;
+ const boost::uint32_t m_remote_ip;
+ const boost::uint32_t m_remote_port;
+ const bool m_is_income;
+
+ connection_context_base(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income):
+ m_connection_id(connection_id),
+ m_remote_ip(remote_ip),
+ m_remote_port(remote_port),
+ m_is_income(is_income)
+
+
+ {}
+
+ connection_context_base(): m_connection_id(),
+ m_remote_ip(0),
+ m_remote_port(0),
+ m_is_income(false)
+ {}
+
+ connection_context_base& operator=(const connection_context_base& a)
+ {
+ set_details(a.m_connection_id, a.m_remote_ip, a.m_remote_port, a.m_is_income);
+ return *this;
+ }
+
+ private:
+ template<class t_protocol_handler>
+ friend class connection;
+ void set_details(boost::uuids::uuid connection_id, long remote_ip, int remote_port, bool is_income)
+ {
+ this->~connection_context_base();
+ new(this) connection_context_base(connection_id, remote_ip, remote_port, is_income);
+ }
+
+ };
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ struct i_service_endpoint
+ {
+ virtual bool do_send(const void* ptr, size_t cb)=0;
+ virtual bool close()=0;
+ virtual bool call_run_once_service_io()=0;
+ virtual bool request_callback()=0;
+ virtual boost::asio::io_service& get_io_service()=0;
+ //protect from deletion connection object(with protocol instance) during external call "invoke"
+ virtual bool add_ref()=0;
+ virtual bool release()=0;
+ protected:
+ virtual ~i_service_endpoint(){}
+ };
+
+
+ //some helpers
+
+
+ inline
+ std::string print_connection_context(const connection_context_base& ctx)
+ {
+ std::stringstream ss;
+ ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << " " << epee::string_tools::get_str_from_guid_a(ctx.m_connection_id) << (ctx.m_is_income ? " INC":" OUT");
+ return ss.str();
+ }
+
+ inline
+ std::string print_connection_context_short(const connection_context_base& ctx)
+ {
+ std::stringstream ss;
+ ss << epee::string_tools::get_ip_string_from_int32(ctx.m_remote_ip) << ":" << ctx.m_remote_port << (ctx.m_is_income ? " INC":" OUT");
+ return ss.str();
+ }
+
+#define LOG_PRINT_CC(ct, message, log_level) LOG_PRINT("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
+#define LOG_PRINT_CC_GREEN(ct, message, log_level) LOG_PRINT_GREEN("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
+#define LOG_PRINT_CC_RED(ct, message, log_level) LOG_PRINT_RED("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
+#define LOG_PRINT_CC_BLUE(ct, message, log_level) LOG_PRINT_BLUE("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message, log_level)
+#define LOG_ERROR_CC(ct, message) LOG_ERROR("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+
+#define LOG_PRINT_CC_L0(ct, message) LOG_PRINT_L0("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+#define LOG_PRINT_CC_L1(ct, message) LOG_PRINT_L1("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+#define LOG_PRINT_CC_L2(ct, message) LOG_PRINT_L2("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+#define LOG_PRINT_CC_L3(ct, message) LOG_PRINT_L3("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+#define LOG_PRINT_CC_L4(ct, message) LOG_PRINT_L4("[" << epee::net_utils::print_connection_context_short(ct) << "]" << message)
+
+#define LOG_PRINT_CCONTEXT_L0(message) LOG_PRINT_CC_L0(context, message)
+#define LOG_PRINT_CCONTEXT_L1(message) LOG_PRINT_CC_L1(context, message)
+#define LOG_PRINT_CCONTEXT_L2(message) LOG_PRINT_CC_L2(context, message)
+#define LOG_PRINT_CCONTEXT_L3(message) LOG_PRINT_CC_L3(context, message)
+#define LOG_ERROR_CCONTEXT(message) LOG_ERROR_CC(context, message)
+
+#define LOG_PRINT_CCONTEXT_GREEN(message, log_level) LOG_PRINT_CC_GREEN(context, message, log_level)
+#define LOG_PRINT_CCONTEXT_RED(message, log_level) LOG_PRINT_CC_RED(context, message, log_level)
+#define LOG_PRINT_CCONTEXT_BLUE(message, log_level) LOG_PRINT_CC_BLUE(context, message, log_level)
+
+#define CHECK_AND_ASSERT_MES_CC(condition, return_val, err_message) CHECK_AND_ASSERT_MES(condition, return_val, "[" << epee::net_utils::print_connection_context_short(context) << "]" << err_message)
+
+}
+}
+
+#endif //_NET_UTILS_BASE_H_
diff --git a/contrib/epee/include/net/protocol_switcher.h b/contrib/epee/include/net/protocol_switcher.h
new file mode 100644
index 000000000..f9a6dbe6f
--- /dev/null
+++ b/contrib/epee/include/net/protocol_switcher.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#ifndef _PROTOCOL_SWITCHER_H_
+#define _PROTOCOL_SWITCHER_H_
+
+#include "levin_base.h"
+#include "http_server.h"
+#include "levin_protocol_handler.h"
+//#include "abstract_tcp_server.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ struct protocl_switcher_config
+ {
+ http::http_custom_handler::config_type m_http_config;
+ levin::protocol_handler::config_type m_levin_config;
+ };
+
+
+ struct i_protocol_handler
+ {
+ virtual bool handle_recv(const void* ptr, size_t cb)=0;
+ };
+
+ template<class t>
+ class t_protocol_handler: public i_protocol_handler
+ {
+ public:
+ typedef t t_type;
+ t_protocol_handler(i_service_endpoint* psnd_hndlr, typename t_type::config_type& config, const connection_context& conn_context):m_hadler(psnd_hndlr, config, conn_context)
+ {}
+ private:
+ bool handle_recv(const void* ptr, size_t cb)
+ {
+ return m_hadler.handle_recv(ptr, cb);
+ }
+ t_type m_hadler;
+ };
+
+
+ class protocol_switcher
+ {
+ public:
+ typedef protocl_switcher_config config_type;
+
+ protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context);
+ virtual ~protocol_switcher(){}
+
+ virtual bool handle_recv(const void* ptr, size_t cb);
+
+ bool after_init_connection(){return true;}
+ private:
+ t_protocol_handler<http::http_custom_handler> m_http_handler;
+ t_protocol_handler<levin::protocol_handler> m_levin_handler;
+ i_protocol_handler* pcurrent_handler;
+
+ std::string m_cached_buff;
+ };
+
+ protocol_switcher::protocol_switcher(net_utils::i_service_endpoint* psnd_hndlr, config_type& config, const net_utils::connection_context_base& conn_context):m_http_handler(psnd_hndlr, config.m_http_config, conn_context), m_levin_handler(psnd_hndlr, config.m_levin_config, conn_context), pcurrent_handler(NULL)
+ {}
+
+ bool protocol_switcher::handle_recv(const void* ptr, size_t cb)
+ {
+ if(pcurrent_handler)
+ return pcurrent_handler->handle_recv(ptr, cb);
+ else
+ {
+ m_cached_buff.append((const char*)ptr, cb);
+ if(m_cached_buff.size() < sizeof(boost::uint64_t))
+ return true;
+
+ if(*((boost::uint64_t*)&m_cached_buff[0]) == LEVIN_SIGNATURE)
+ {
+ pcurrent_handler = &m_levin_handler;
+ return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size());
+ }
+ if(m_cached_buff.substr(0, 4) == "GET " || m_cached_buff.substr(0, 4) == "POST")
+ {
+ pcurrent_handler = &m_http_handler;
+ return pcurrent_handler->handle_recv(m_cached_buff.data(), m_cached_buff.size());
+ }else
+ {
+ LOG_ERROR("Wrong protocol accepted on port...");
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+}
+#endif //_PROTOCOL_SWITCHER_H_ \ No newline at end of file
diff --git a/contrib/epee/include/net/rpc_method_name.h b/contrib/epee/include/net/rpc_method_name.h
new file mode 100644
index 000000000..c226639c4
--- /dev/null
+++ b/contrib/epee/include/net/rpc_method_name.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+#pragma once
+
+
+#define RPC_METHOD_NAME(name) static inline const char* methodname(){return name;} \ No newline at end of file
diff --git a/contrib/epee/include/net/smtp.h b/contrib/epee/include/net/smtp.h
new file mode 100644
index 000000000..d2e8598fd
--- /dev/null
+++ b/contrib/epee/include/net/smtp.h
@@ -0,0 +1,181 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+#include <iostream>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/archive/iterators/base64_from_binary.hpp>
+#include <boost/archive/iterators/transform_width.hpp>
+#include <boost/archive/iterators/ostream_iterator.hpp>
+
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace smtp
+ {
+
+ using boost::asio::ip::tcp;
+ using namespace boost::archive::iterators;
+ typedef base64_from_binary<transform_width<const char *,6,8> > base64_text;
+
+ /************************************************************************/
+ /* */
+ /************************************************************************/
+ class smtp_client
+ {
+ public:
+ smtp_client(std::string pServer,unsigned int pPort,std::string pUser,std::string pPassword):
+ mServer(pServer),mPort(pPort),mUserName(pUser),mPassword(pPassword),mSocket(mIOService),mResolver(mIOService)
+ {
+ tcp::resolver::query qry(mServer,boost::lexical_cast<std::string>( mPort ));
+ mResolver.async_resolve(qry,boost::bind(&smtp_client::handleResolve,this,boost::asio::placeholders::error,
+ boost::asio::placeholders::iterator));
+ }
+ bool Send(std::string pFrom,std::string pTo,std::string pSubject,std::string pMessage)
+ {
+ mHasError = true;
+ mFrom=pFrom;
+ mTo=pTo;
+ mSubject=pSubject;
+ mMessage=pMessage;
+ mIOService.run();
+ return !mHasError;
+ }
+ private:
+ std::string encodeBase64(std::string pData)
+ {
+ std::stringstream os;
+ size_t sz=pData.size();
+ std::copy(base64_text(pData.c_str()),base64_text(pData.c_str()+sz),std::ostream_iterator<char>(os));
+ return os.str();
+ }
+ void handleResolve(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
+ {
+ if(!err)
+ {
+ tcp::endpoint endpoint=*endpoint_iterator;
+ mSocket.async_connect(endpoint,
+ boost::bind(&smtp_client::handleConnect,this,boost::asio::placeholders::error,++endpoint_iterator));
+ }
+ else
+ {
+ mHasError=true;
+ mErrorMsg= err.message();
+ }
+ }
+ void writeLine(std::string pData)
+ {
+ std::ostream req_strm(&mRequest);
+ req_strm << pData << "\r\n";
+ boost::asio::write(mSocket,mRequest);
+ req_strm.clear();
+ }
+ void readLine(std::string& pData)
+ {
+ boost::asio::streambuf response;
+ boost::asio::read_until(mSocket, response, "\r\n");
+ std::istream response_stream(&response);
+ response_stream >> pData;
+ }
+ void handleConnect(const boost::system::error_code& err,tcp::resolver::iterator endpoint_iterator)
+ {
+ if (!err)
+ {
+ std::string read_buff;
+ // The connection was successful. Send the request.
+ std::ostream req_strm(&mRequest);
+ writeLine("EHLO "+mServer);
+ readLine(read_buff);//220
+ writeLine("AUTH LOGIN");
+ readLine(read_buff);//
+ writeLine(encodeBase64(mUserName));
+ readLine(read_buff);
+ writeLine(encodeBase64(mPassword));
+ readLine(read_buff);
+ writeLine( "MAIL FROM:<"+mFrom+">");
+ writeLine( "RCPT TO:<"+mTo+">");
+ writeLine( "DATA");
+ writeLine( "SUBJECT:"+mSubject);
+ writeLine( "From:"+mFrom);
+ writeLine( "To:"+mTo);
+ writeLine( "");
+ writeLine( mMessage );
+ writeLine( "\r\n.\r\n");
+ readLine(read_buff);
+ if(read_buff == "250")
+ mHasError = false;
+ writeLine( "QUIT");
+ }
+ else
+ {
+ mHasError=true;
+ mErrorMsg= err.message();
+ }
+ }
+ std::string mServer;
+ std::string mUserName;
+ std::string mPassword;
+ std::string mFrom;
+ std::string mTo;
+ std::string mSubject;
+ std::string mMessage;
+ unsigned int mPort;
+ boost::asio::io_service mIOService;
+ tcp::resolver mResolver;
+ tcp::socket mSocket;
+ boost::asio::streambuf mRequest;
+ boost::asio::streambuf mResponse;
+ bool mHasError;
+ std::string mErrorMsg;
+ };
+
+
+ bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_email, /*"STIL CRAWLER",*/
+ const std::string& maillist, const std::string& subject, const std::string& body)
+ {
+ STD_TRY_BEGIN();
+ //smtp_client mailc("yoursmtpserver.com",25,"user@yourdomain.com","password");
+ //mailc.Send("from@yourdomain.com","to@somewhere.com","subject","Hello from C++ SMTP Client!");
+ smtp_client mailc(server,port,login,pass);
+ return mailc.Send(from_email,maillist,subject,body);
+ STD_TRY_CATCH("at send_mail", false);
+ }
+
+ }
+}
+}
+
+//#include "smtp.inl" \ No newline at end of file
diff --git a/contrib/epee/include/net/smtp.inl b/contrib/epee/include/net/smtp.inl
new file mode 100644
index 000000000..d42c8b950
--- /dev/null
+++ b/contrib/epee/include/net/smtp.inl
@@ -0,0 +1,1569 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+#include "md5.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace smtp
+ {
+
+
+ //////////////////////////////////////////////////////////////////////////
+ inline char * convert_hex( unsigned char *in, int len )
+ {
+ static char hex[] = "0123456789abcdef";
+ char * out;
+ int i;
+
+ out = (char *) malloc(len * 2 + 1);
+ if (out == NULL)
+ return NULL;
+
+ for (i = 0; i < len; i++) {
+ out[i * 2] = hex[in[i] >> 4];
+ out[i * 2 + 1] = hex[in[i] & 15];
+ }
+
+ out[i*2] = 0;
+
+ return out;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline char * hash_md5(const char * sec_key, const char * data, int len)
+ {
+ char key[65], digest[24];
+ char * hash_hex;
+
+ int sec_len, i;
+
+ sec_len = strlen(sec_key);
+
+ if (sec_len < 64) {
+ memcpy(key, sec_key, sec_len);
+ for (i = sec_len; i < 64; i++) {
+ key[i] = 0;
+ }
+ } else {
+ memcpy(key, sec_key, 64);
+ }
+
+ md5::hmac_md5( (const unsigned char*)data, len, (const unsigned char*)key, 64, (unsigned char*)digest );
+ hash_hex = convert_hex( (unsigned char*)digest, 16 );
+
+ return hash_hex;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ inline CSMTPClient::CSMTPClient(void)
+ {
+ m_dwSupportedAuthModesCount = 0;
+ m_bConnected = FALSE;
+ m_hSocket = INVALID_SOCKET;
+ m_pErrorText = NULL;
+
+ // Initialize WinSock
+ WORD wVer = MAKEWORD( 2, 2 );
+ if ( WSAStartup( wVer, &m_wsaData ) != NO_ERROR )
+ {
+ SetErrorText( "WSAStartup.", WSAGetLastError() );
+ throw;
+ }
+ if ( LOBYTE( m_wsaData.wVersion ) != 2 || HIBYTE( m_wsaData.wVersion ) != 2 )
+ {
+ SetErrorText( "Can't find a useable WinSock DLL." );
+ WSACleanup();
+ throw;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline CSMTPClient::~CSMTPClient(void)
+ {
+ if ( m_pErrorText )
+ {
+ free( m_pErrorText );
+ m_pErrorText = NULL;
+ }
+
+ if ( m_bConnected )
+ ServerDisconnect();
+
+ // Cleanup
+ WSACleanup();
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline void CSMTPClient::SetErrorText( LPCSTR szErrorText, DWORD dwErrorCode )
+ {
+ if ( m_pErrorText )
+ {
+ free( m_pErrorText );
+ m_pErrorText = NULL;
+ }
+
+ LPVOID lpMsgBuf = NULL;
+ if ( dwErrorCode )
+ {
+ FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ dwErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR) &lpMsgBuf,
+ 0, NULL );
+ }
+
+ if ( szErrorText && strlen( szErrorText ) )
+ {
+ m_pErrorText = (LPBYTE)malloc( strlen( szErrorText ) + 1 );
+ strcpy( (char*)m_pErrorText, szErrorText );
+
+ if ( lpMsgBuf )
+ {
+ strcat( (char*)m_pErrorText, " " );
+ strcpy( (char*)m_pErrorText, (char*)lpMsgBuf );
+
+ LocalFree( lpMsgBuf );
+ }
+ }
+ }
+
+ inline void CSMTPClient::SetErrorText( PBYTE szErrorText, DWORD dwErrorCode )
+ {
+ SetErrorText( (LPCSTR)szErrorText, dwErrorCode );
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline char* CSMTPClient::GetLastErrorText()
+ {
+ return (char*)m_pErrorText;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline DWORD CSMTPClient::ReceiveData( SOCKET hSocket, PBYTE pReceiveBuffer, DWORD dwReceiveBufferSize )
+ {
+ DWORD dwReceivedDataSize = 0;
+
+ if ( hSocket != INVALID_SOCKET && pReceiveBuffer && dwReceiveBufferSize )
+ {
+ int iReceived = 0;
+ int iLength = 0;
+
+ iLength = recv( hSocket, (LPSTR)pReceiveBuffer + iReceived, dwReceiveBufferSize - iReceived,
+ NO_FLAGS );
+
+ if ( iLength != 0 && iLength != SOCKET_ERROR )
+ iReceived += iLength;
+
+ dwReceivedDataSize = iReceived;
+
+ pReceiveBuffer[ iReceived ] = 0;
+ }
+
+ return dwReceivedDataSize;
+ }
+
+ inline //////////////////////////////////////////////////////////////////////////
+ DWORD CSMTPClient::SendData( SOCKET hSocket, PBYTE pSendBuffer, DWORD dwSendBufferSize )
+ {
+ DWORD dwSended = 0;
+
+ if ( hSocket != INVALID_SOCKET && pSendBuffer && dwSendBufferSize )
+ {
+ int iSended = 0;
+ int iLength = 0;
+
+ while ( iLength != SOCKET_ERROR && dwSendBufferSize - iSended > 0 )
+ {
+ iLength = send( hSocket, (LPSTR)pSendBuffer + iSended, dwSendBufferSize - iSended,
+ NO_FLAGS );
+
+ if ( iLength != 0 && iLength != SOCKET_ERROR )
+ iSended += iLength;
+ }
+
+ dwSended = iSended;
+ }
+
+ //if ( dwSended )
+ // printf( "C: %s", pSendBuffer );
+
+ return dwSended;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline unsigned short CSMTPClient::GetResponseCode( LPBYTE pBuffer, DWORD dwBufferSize )
+ {
+ unsigned short iCode = 0;
+
+ if ( dwBufferSize >= 3 )
+ {
+ CHAR szResponseCode[ 4 ] = { 0 };
+ memcpy( szResponseCode, pBuffer, 3 );
+ szResponseCode[ 3 ] = 0;
+ iCode = atoi( szResponseCode );
+ }
+
+ return iCode;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline void CSMTPClient::ParseESMTPExtensions( LPBYTE pBuffer, DWORD dwBufferSize )
+ {
+ const char *szSubstring = strstr( (const char*)pBuffer, "250-AUTH " );
+ if ( !szSubstring )
+ {
+ szSubstring = strstr( (const char*)pBuffer, "250 AUTH " );
+ }
+
+ if ( szSubstring )
+ {
+ const char *szSubstringEnd = strstr( (const char*)szSubstring, "\r\n" );
+ if ( szSubstringEnd )
+ {
+ szSubstring += 9;
+ char szAuthMode[ 256 ] = { 0 };
+ for ( ; szSubstring < szSubstringEnd + 1 ; szSubstring++ )
+ {
+ if ( *szSubstring == ' ' || *szSubstring == '\r' )
+ {
+ if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_PLAIN ) == 0 )
+ {
+ m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_PLAIN;
+ m_dwSupportedAuthModesCount++;
+ }
+ else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_LOGIN ) == 0 )
+ {
+ m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_LOGIN;
+ m_dwSupportedAuthModesCount++;
+ }
+ else if ( _strcmpi( szAuthMode, SMTP_COMMAND_AUTH_CRAM_MD5 ) == 0 )
+ {
+ m_aSupportedAuthModes[ m_dwSupportedAuthModesCount ] = AUTH_MODE_CRAM_MD5;
+ m_dwSupportedAuthModesCount++;
+ }
+
+ szAuthMode[ 0 ] = 0;
+
+ if ( m_dwSupportedAuthModesCount == MAX_AUTH_MODES_COUND )
+ break;
+ }
+ else
+ {
+ szAuthMode[ strlen( szAuthMode ) + 1 ] = 0;
+ szAuthMode[ strlen( szAuthMode ) ] = *szSubstring;
+ }
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber )
+ {
+ if ( m_bConnected )
+ ServerDisconnect();
+
+ m_bConnected = FALSE;
+ m_hSocket = INVALID_SOCKET;
+
+ m_hSocket = _connectServerSocket( szServerAddress, iPortNumber );
+
+ if ( m_hSocket != INVALID_SOCKET )
+ {
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ // Connected. Wait server hello string.
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ // Check 220
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 220 )
+ {
+ SetErrorText( pReceiveBuffer );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error. ", WSAGetLastError() );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+
+ // EHLO / HELO
+ BYTE szHelloBuffer[ 256 ];
+ sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_EHLO, (char*)szServerAddress );
+ if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ // Check 250
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode == 500 )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ sprintf( (char*)szHelloBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_HELO, (char*)szServerAddress );
+ if ( SendData( m_hSocket, (PBYTE)szHelloBuffer, strlen( (const char*)szHelloBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+
+ iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 250 )
+ {
+ SetErrorText( pReceiveBuffer );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+ }
+ else if ( iResponseCode != 250 )
+ {
+ SetErrorText( pReceiveBuffer );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+
+ // Parse AUTH supported modes
+ ParseESMTPExtensions( pReceiveBuffer, iReceived );
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ ServerDisconnect();
+ return FALSE;
+ }
+
+ free( pReceiveBuffer );
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ m_bConnected = TRUE;
+
+ return TRUE;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerConnect( LPCSTR szServerAddress, const unsigned short iPortNumber, LPCSTR szUsername, LPCSTR szPassword )
+ {
+ BOOL bSuccess = FALSE;
+
+ bSuccess = ServerConnect( szServerAddress, iPortNumber );
+ if ( bSuccess )
+ {
+ if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) )
+ {
+ ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 );
+ }
+ else
+ if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) )
+ {
+ ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN );
+ }
+ else
+ if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) )
+ {
+ ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN );
+ }
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline SOCKET CSMTPClient::_connectServerSocket( LPCSTR szServerAddress, const unsigned short iPortNumber )
+ {
+ int nConnect;
+ short nProtocolPort = iPortNumber;
+ LPHOSTENT lpHostEnt;
+ SOCKADDR_IN sockAddr;
+
+ SOCKET hServerSocket = INVALID_SOCKET;
+
+ lpHostEnt = gethostbyname( szServerAddress );
+ if (lpHostEnt)
+ {
+ hServerSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
+ if (hServerSocket != INVALID_SOCKET)
+ {
+ sockAddr.sin_family = AF_INET;
+ sockAddr.sin_port = htons( nProtocolPort );
+ sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
+
+ nConnect = connect( hServerSocket, (PSOCKADDR)&sockAddr,
+ sizeof(sockAddr) );
+
+ if ( nConnect != 0 )
+ {
+ SetErrorText( "connect error.", WSAGetLastError() );
+ hServerSocket = INVALID_SOCKET;
+ }
+ }
+ else
+ {
+ SetErrorText( "Invalid socket." );
+ throw;
+ }
+ }
+ else
+ {
+ SetErrorText( "Error retrieving host by name.", WSAGetLastError() );
+ }
+
+ return hServerSocket ;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline void CSMTPClient::ServerDisconnect()
+ {
+ if ( m_hSocket != INVALID_SOCKET )
+ {
+ if ( SendData( m_hSocket, (PBYTE)SMTP_COMMAND_QUIT, strlen( SMTP_COMMAND_QUIT ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ return;
+ }
+
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+
+ if ( iReceived )
+ SetErrorText( pReceiveBuffer );
+
+ free( pReceiveBuffer );
+ }
+
+ m_hSocket = INVALID_SOCKET;
+ }
+
+ m_bConnected = FALSE;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::GetAuthModeIsSupported( int iMode )
+ {
+ BOOL bSupported = FALSE;
+
+ for ( int i = 0 ; i < m_dwSupportedAuthModesCount ; i++ )
+ {
+ if ( m_aSupportedAuthModes[ i ] == iMode )
+ {
+ bSupported = TRUE;
+ break;
+ }
+ }
+
+ return bSupported;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword, int iAuthMode )
+ {
+ BOOL bSuccess = FALSE;
+
+ if ( iAuthMode == AUTH_MODE_PLAIN )
+ {
+ bSuccess = ServerLoginMethodPlain( szUsername, szPassword );
+ }
+ else if ( iAuthMode == AUTH_MODE_LOGIN )
+ {
+ bSuccess = ServerLoginMethodLogin( szUsername, szPassword );
+ }
+ else if ( iAuthMode == AUTH_MODE_CRAM_MD5 )
+ {
+ bSuccess = ServerLoginMethodCramMD5( szUsername, szPassword );
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerLogin( LPCSTR szUsername, LPCSTR szPassword )
+ {
+ BOOL bSuccess = FALSE;
+
+ if ( GetAuthModeIsSupported( AUTH_MODE_CRAM_MD5 ) )
+ {
+ bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_CRAM_MD5 );
+ }
+ else
+ if ( GetAuthModeIsSupported( AUTH_MODE_PLAIN ) )
+ {
+ bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_PLAIN );
+ }
+ else
+ if ( GetAuthModeIsSupported( AUTH_MODE_LOGIN ) )
+ {
+ bSuccess = ServerLogin( szUsername, szPassword, AUTH_MODE_LOGIN );
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerLoginMethodPlain( LPCSTR szUsername, LPCSTR szPassword )
+ {
+ BOOL bSuccess = FALSE;
+
+ BYTE szCommandBuffer[ 256 ];
+ sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_PLAIN );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ return FALSE;
+ }
+
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ // Connected. Wait server hello string.
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 334
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 334 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Encode.
+ DWORD dwLoginBuffer = strlen( szUsername ) + strlen( szPassword ) + 3;
+ char *pLoginBuffer = (char*)malloc( dwLoginBuffer );
+ if ( pLoginBuffer )
+ {
+ ZeroMemory( pLoginBuffer, dwLoginBuffer );
+ strcpy( pLoginBuffer + 1, szUsername );
+ strcpy( pLoginBuffer + 1 + strlen( szUsername ) + 1, szPassword );
+
+ Base64Coder coder;
+ coder.Encode( (const PBYTE)pLoginBuffer, dwLoginBuffer - 1 );
+ LPCSTR szLoginBufferEncoded = coder.EncodedMessage();
+
+ if ( szLoginBufferEncoded && strlen( szLoginBufferEncoded ) > 0 )
+ {
+ DWORD dwSendBufferSize = strlen( szLoginBufferEncoded ) + 4;
+ char* pSendBuffer = (char*)malloc( dwSendBufferSize );
+ if ( pSendBuffer )
+ {
+ strcpy( pSendBuffer, szLoginBufferEncoded );
+ strcat( pSendBuffer, "\r\n" );
+
+ if ( SendData( m_hSocket, (PBYTE)pSendBuffer, strlen( (const char*)pSendBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pSendBuffer );
+ free( pLoginBuffer );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( pSendBuffer );
+ }
+ }
+
+ free( pLoginBuffer );
+
+ // check result
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 235
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 235 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ bSuccess = TRUE;
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+
+ free( pReceiveBuffer );
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerLoginMethodLogin( LPCSTR szUsername, LPCSTR szPassword )
+ {
+ BOOL bSuccess = FALSE;
+
+ BYTE szCommandBuffer[ 256 ];
+ sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_LOGIN );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ return FALSE;
+ }
+
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 334
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 334 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Check request
+ if ( iReceived > 6 )
+ {
+ Base64Coder coder;
+ coder.Decode( pReceiveBuffer + 4, iReceived - 6 );
+ LPCSTR szRequest = coder.DecodedMessage();
+ if ( szRequest && strlen( szRequest ) > 0 )
+ {
+ if ( strcmpi( szRequest, "Username:" ) == 0 )
+ {
+ coder.Encode( (const PBYTE)szUsername, strlen( szUsername ) );
+ LPCSTR szUsernameEncoded = coder.EncodedMessage();
+
+ char* szLoginUsernameBuffer = (char*)malloc( strlen( szUsernameEncoded ) + 4 );
+ if ( szLoginUsernameBuffer )
+ {
+ strcpy( szLoginUsernameBuffer, szUsernameEncoded );
+ strcat( szLoginUsernameBuffer, "\r\n" );
+
+ if ( SendData( m_hSocket, (PBYTE)szLoginUsernameBuffer, strlen( (const char*)szLoginUsernameBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( szLoginUsernameBuffer );
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 334
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 334 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Check request
+ if ( iReceived > 6 )
+ {
+ coder.Decode( pReceiveBuffer + 4, iReceived - 6 );
+ LPCSTR szRequest2 = coder.DecodedMessage();
+ if ( szRequest2 && strlen( szRequest2 ) > 0 )
+ {
+ if ( strcmpi( szRequest2, "Password:" ) == 0 )
+ {
+ coder.Encode( (const PBYTE)szPassword, strlen( szPassword ) );
+ LPCSTR szPasswordEncoded = coder.EncodedMessage();
+
+ char* szLoginPasswordBuffer = (char*)malloc( strlen( szPasswordEncoded ) + 4 );
+ if ( szLoginPasswordBuffer )
+ {
+ strcpy( szLoginPasswordBuffer, szPasswordEncoded );
+ strcat( szLoginPasswordBuffer, "\r\n" );
+
+ if ( SendData( m_hSocket, (PBYTE)szLoginPasswordBuffer, strlen( (const char*)szLoginPasswordBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( szLoginPasswordBuffer );
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 235
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 235 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ bSuccess = TRUE;
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( pReceiveBuffer );
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::ServerLoginMethodCramMD5( LPCSTR szUsername, LPCSTR szPassword )
+ {
+ BOOL bSuccess = FALSE;
+
+ BYTE szCommandBuffer[ 256 ];
+ sprintf( (char*)szCommandBuffer, "%s %s\r\n", (char*)SMTP_COMMAND_AUTH, (char*)SMTP_COMMAND_AUTH_CRAM_MD5 );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ return FALSE;
+ }
+
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ // Connected. Wait server hello string.
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 334
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 334 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Check request
+ if ( iReceived > 6 )
+ {
+ Base64Coder coder;
+ coder.Decode( pReceiveBuffer + 4, iReceived - 6 );
+ LPCSTR szResponse = coder.DecodedMessage();
+ if ( szResponse && strlen( szResponse ) > 0 )
+ {
+ char *auth_hex = hash_md5( szPassword, szResponse, strlen(szResponse) );
+ if ( !auth_hex )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ char *szCommand = (char*)malloc( strlen( szUsername ) + strlen( auth_hex ) + 5 );
+ if ( szCommand )
+ {
+ sprintf( szCommand, "%s %s", szUsername, auth_hex );
+
+ free( auth_hex );
+
+ coder.Encode( (const PBYTE)szCommand, strlen( szCommand ) );
+
+ free( szCommand );
+
+ LPCSTR szAuthEncoded = coder.EncodedMessage();
+ if ( szAuthEncoded == NULL )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ char *szAuthCommand = (char*)malloc( strlen( szAuthEncoded ) + 4 );
+ if ( szAuthCommand )
+ {
+ strcpy( szAuthCommand, szAuthEncoded );
+ strcat( szAuthCommand, "\r\n" );
+
+ // Send auth data
+ if ( SendData( m_hSocket, (PBYTE)szAuthCommand, strlen( (const char*)szAuthCommand ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( szAuthCommand );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Check response
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 235
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 235 )
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ bSuccess = TRUE;
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( szAuthCommand );
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ free( auth_hex );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ free( pReceiveBuffer );
+ }
+ else
+ {
+ SetErrorText( "malloc() failed.", GetLastError() );
+ }
+
+ return bSuccess;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ inline BOOL CSMTPClient::SendMessage( LPCSTR szFromAddress, LPCSTR szFromName, LPCSTR szToAddresses, LPCSTR szSubject, LPCSTR szXMailer, LPBYTE pBodyBuffer, DWORD dwBodySize )
+ {
+ BOOL bSuccess = FALSE;
+
+ // Format Header
+ if ( !szFromAddress )
+ {
+ SetErrorText( "SendMessage. Invalid Parameters!" );
+ return NULL;
+ }
+
+ char *szHeaderBuffer = (char*)malloc( 1024 * 16 );
+ if ( szHeaderBuffer )
+ {
+ // get the current date and time
+ char szDate[ 500 ];
+ char sztTime[ 500 ];
+
+ SYSTEMTIME st = { 0 };
+ ::GetSystemTime(&st);
+
+ ::GetDateFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), 0, &st, "ddd',' dd MMM yyyy", szDate , sizeof( szDate ) );
+ ::GetTimeFormatA( MAKELCID( MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), TIME_FORCE24HOURFORMAT, &st, "HH':'mm':'ss", sztTime, sizeof( sztTime ) );
+
+ sprintf( szHeaderBuffer, "DATE: %s %s\r\n", szDate, sztTime );
+
+ // X-Mailer Field
+ if ( szXMailer && strlen( szXMailer ) )
+ {
+ strcat( szHeaderBuffer, "X-Mailer: " );
+ strcat( szHeaderBuffer, szXMailer );
+ strcat( szHeaderBuffer, "\r\n" );
+ }
+
+ // From:
+ strcat( szHeaderBuffer, "From: " );
+ if ( szFromName )
+ {
+ strcat( szHeaderBuffer, "\"" );
+ strcat( szHeaderBuffer, szFromName );
+ strcat( szHeaderBuffer, "\" <" );
+ strcat( szHeaderBuffer, szFromAddress );
+ strcat( szHeaderBuffer, ">\r\n" );
+ }
+ else
+ {
+ strcat( szHeaderBuffer, "<" );
+ strcat( szHeaderBuffer, szFromAddress );
+ strcat( szHeaderBuffer, ">\r\n" );
+ }
+
+ // Subject:
+ if ( szSubject && strlen( szSubject ) )
+ {
+ strcat( szHeaderBuffer, "Subject: " );
+ strcat( szHeaderBuffer, szSubject );
+ strcat( szHeaderBuffer, "\r\n" );
+ }
+
+ // To Fields
+ strcat( szHeaderBuffer, "To: " );
+ strcat( szHeaderBuffer, szToAddresses );
+ strcat( szHeaderBuffer, "\r\n" );
+
+ // MIME
+ strcat( szHeaderBuffer, "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n" );
+
+ // End Header
+ strcat( szHeaderBuffer, "\r\n" );
+ }
+ else
+ {
+ SetErrorText( "malloc error.", GetLastError() );
+ return FALSE;
+ }
+
+
+ BYTE szCommandBuffer[ 256 ];
+ sprintf( (char*)szCommandBuffer, "MAIL FROM:<%s> SIZE=%u\r\n", (char*)szFromAddress, strlen( szHeaderBuffer ) + dwBodySize + 2 );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ DWORD dwReceiveBufferSize = 1024*16;
+ PBYTE pReceiveBuffer = (PBYTE)malloc( dwReceiveBufferSize );
+ if ( pReceiveBuffer )
+ {
+ DWORD iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 250
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 250 )
+ {
+ free( szHeaderBuffer );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( szHeaderBuffer );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ // Post "RCTP TO:"
+ char *szCurrentAddr = (char*)malloc( strlen( szToAddresses ) + 1 );
+ if ( !szCurrentAddr )
+ {
+ SetErrorText( "malloc error.", GetLastError() );
+ free( szHeaderBuffer );
+ free( pReceiveBuffer );
+ return FALSE;
+ }
+
+ const char* szToOffset = szToAddresses;
+ char* szZap = NULL;
+
+ BOOL bRCPTAccepted = FALSE;
+ do
+ {
+ strcpy( szCurrentAddr, szToOffset );
+ char *szExtractedAdress = szCurrentAddr;
+ szZap = strchr( szCurrentAddr, ',' );
+
+ if ( szZap )
+ {
+ *szZap = 0;
+ szToOffset = szZap + 1;
+ }
+
+ char *pSkobka1 = strchr( szCurrentAddr, '<' );
+ char *pSkobka2 = strchr( szCurrentAddr, '>' );
+
+ if ( pSkobka1 && pSkobka2 && pSkobka2 > pSkobka1 )
+ {
+ szExtractedAdress = pSkobka1 + 1;
+ *pSkobka2 = NULL;
+ }
+
+ if ( szExtractedAdress && strlen( szExtractedAdress ) > 0 )
+ {
+ sprintf( (char*)szCommandBuffer, "RCPT TO:<%s>\r\n", (char*)szExtractedAdress );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( szCurrentAddr );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 250
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode == 250 )
+ {
+ bRCPTAccepted = TRUE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( szCurrentAddr );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+ }
+
+ } while( szZap );
+
+ free( szCurrentAddr );
+
+ if ( bRCPTAccepted )
+ {
+ sprintf( (char*)szCommandBuffer, "DATA\r\n" );
+ if ( SendData( m_hSocket, (PBYTE)szCommandBuffer, strlen( (const char*)szCommandBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 354
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode != 354 )
+ {
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ // Send message data (header + body + .)
+ if ( SendData( m_hSocket, (PBYTE)szHeaderBuffer, strlen( (const char*)szHeaderBuffer ) ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ if ( SendData( m_hSocket, (PBYTE)pBodyBuffer, dwBodySize ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ if ( SendData( m_hSocket, (PBYTE)"\r\n.\r\n", 5 ) == 0 )
+ {
+ SetErrorText( "SendData error.", WSAGetLastError() );
+ free( pReceiveBuffer );
+ free( szHeaderBuffer );
+ return FALSE;
+ }
+
+ iReceived = ReceiveData( m_hSocket, pReceiveBuffer, dwReceiveBufferSize );
+ if ( iReceived )
+ {
+ SetErrorText( pReceiveBuffer );
+
+ // Check 250
+ int iResponseCode = GetResponseCode( pReceiveBuffer, iReceived );
+ if ( iResponseCode == 250 )
+ {
+ bSuccess = TRUE;
+ }
+ }
+ else
+ {
+ SetErrorText( "ReceiveData error.", WSAGetLastError() );
+ }
+ }
+
+ free( pReceiveBuffer );
+ }
+ else
+ {
+ SetErrorText( "malloc error.", GetLastError() );
+ }
+
+ if ( szHeaderBuffer )
+ free( szHeaderBuffer );
+
+ return bSuccess;
+ }
+
+
+
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+
+
+#ifndef PAGESIZE
+#define PAGESIZE 4096
+#endif
+
+#ifndef ROUNDTOPAGE
+#define ROUNDTOPAGE(a) (((a/4096)+1)*4096)
+#endif
+
+ //////////////////////////////////////////////////////////////////////
+ // Construction/Destruction
+ //////////////////////////////////////////////////////////////////////
+
+ inline Base64Coder::Base64Coder()
+ : m_pDBuffer(NULL),
+ m_pEBuffer(NULL),
+ m_nDBufLen(0),
+ m_nEBufLen(0)
+ {
+
+ }
+
+ inline Base64Coder::~Base64Coder()
+ {
+ if(m_pDBuffer != NULL)
+ delete [] m_pDBuffer;
+
+ if(m_pEBuffer != NULL)
+ delete [] m_pEBuffer;
+ }
+
+ inline LPCSTR Base64Coder::DecodedMessage() const
+ {
+ return (LPCSTR) m_pDBuffer;
+ }
+
+ inline LPCSTR Base64Coder::EncodedMessage() const
+ {
+ return (LPCSTR) m_pEBuffer;
+ }
+
+ inline void Base64Coder::AllocEncode(DWORD nSize)
+ {
+ if(m_nEBufLen < nSize)
+ {
+ if(m_pEBuffer != NULL)
+ delete [] m_pEBuffer;
+
+ m_nEBufLen = ROUNDTOPAGE(nSize);
+ m_pEBuffer = new BYTE[m_nEBufLen];
+ }
+
+ ::ZeroMemory(m_pEBuffer, m_nEBufLen);
+ m_nEDataLen = 0;
+ }
+
+ inline void Base64Coder::AllocDecode(DWORD nSize)
+ {
+ if(m_nDBufLen < nSize)
+ {
+ if(m_pDBuffer != NULL)
+ delete [] m_pDBuffer;
+
+ m_nDBufLen = ROUNDTOPAGE(nSize);
+ m_pDBuffer = new BYTE[m_nDBufLen];
+ }
+
+ ::ZeroMemory(m_pDBuffer, m_nDBufLen);
+ m_nDDataLen = 0;
+ }
+
+ inline void Base64Coder::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen)
+ {
+ DWORD i = 0;
+
+ AllocEncode(nBufLen);
+ while(i < nBufLen)
+ {
+ if(!_IsBadMimeChar(pBuffer[i]))
+ {
+ m_pEBuffer[m_nEDataLen] = pBuffer[i];
+ m_nEDataLen++;
+ }
+
+ i++;
+ }
+ }
+
+ inline void Base64Coder::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen)
+ {
+ AllocDecode(nBufLen);
+ ::CopyMemory(m_pDBuffer, pBuffer, nBufLen);
+ m_nDDataLen = nBufLen;
+ }
+
+ inline void Base64Coder::Encode(const PBYTE pBuffer, DWORD nBufLen)
+ {
+ SetDecodeBuffer(pBuffer, nBufLen);
+ AllocEncode(nBufLen * 2);
+
+ TempBucket Raw;
+ DWORD nIndex = 0;
+
+ while((nIndex + 3) <= nBufLen)
+ {
+ Raw.Clear();
+ ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3);
+ Raw.nSize = 3;
+ _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
+ nIndex += 3;
+ m_nEDataLen += 4;
+ }
+
+ if(nBufLen > nIndex)
+ {
+ Raw.Clear();
+ Raw.nSize = (BYTE) (nBufLen - nIndex);
+ ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex);
+ _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen);
+ m_nEDataLen += 4;
+ }
+ }
+
+ inline void Base64Coder::Encode(LPCSTR szMessage)
+ {
+ if(szMessage != NULL)
+ Base64Coder::Encode((const PBYTE)szMessage, strlen( (const char*)szMessage));
+ }
+
+ inline void Base64Coder::Decode(const PBYTE pBuffer, DWORD dwBufLen)
+ {
+ if(is_init())
+ _Init();
+
+ SetEncodeBuffer(pBuffer, dwBufLen);
+
+ AllocDecode(dwBufLen);
+
+ TempBucket Raw;
+
+ DWORD nIndex = 0;
+
+ while((nIndex + 4) <= m_nEDataLen)
+ {
+ Raw.Clear();
+ Raw.nData[0] = DecodeTable()[m_pEBuffer[nIndex]];
+ Raw.nData[1] = DecodeTable()[m_pEBuffer[nIndex + 1]];
+ Raw.nData[2] = DecodeTable()[m_pEBuffer[nIndex + 2]];
+ Raw.nData[3] = DecodeTable()[m_pEBuffer[nIndex + 3]];
+
+ if(Raw.nData[2] == 255)
+ Raw.nData[2] = 0;
+ if(Raw.nData[3] == 255)
+ Raw.nData[3] = 0;
+
+ Raw.nSize = 4;
+ _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
+ nIndex += 4;
+ m_nDDataLen += 3;
+ }
+
+ // If nIndex < m_nEDataLen, then we got a decode message without padding.
+ // We may want to throw some kind of warning here, but we are still required
+ // to handle the decoding as if it was properly padded.
+ if(nIndex < m_nEDataLen)
+ {
+ Raw.Clear();
+ for(DWORD i = nIndex; i < m_nEDataLen; i++)
+ {
+ Raw.nData[i - nIndex] = DecodeTable()[m_pEBuffer[i]];
+ Raw.nSize++;
+ if(Raw.nData[i - nIndex] == 255)
+ Raw.nData[i - nIndex] = 0;
+ }
+
+ _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen);
+ m_nDDataLen += (m_nEDataLen - nIndex);
+ }
+ }
+
+ inline void Base64Coder::Decode(LPCSTR szMessage)
+ {
+ if(szMessage != NULL)
+ Base64Coder::Decode((const PBYTE)szMessage, strlen((const char*)szMessage));
+ }
+
+ inline DWORD Base64Coder::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer)
+ {
+ TempBucket Data;
+ DWORD nCount = 0;
+
+ _DecodeRaw(Data, Decode);
+
+ for(int i = 0; i < 3; i++)
+ {
+ pBuffer[i] = Data.nData[i];
+ if(pBuffer[i] != 255)
+ nCount++;
+ }
+
+ return nCount;
+ }
+
+
+ inline void Base64Coder::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer)
+ {
+ TempBucket Data;
+
+ _EncodeRaw(Data, Decode);
+
+ for(int i = 0; i < 4; i++)
+ pBuffer[i] = Base64Digits()[Data.nData[i]];
+
+ switch(Decode.nSize)
+ {
+ case 1:
+ pBuffer[2] = '=';
+ case 2:
+ pBuffer[3] = '=';
+ }
+ }
+
+ inline void Base64Coder::_DecodeRaw(TempBucket &Data, const TempBucket &Decode)
+ {
+ BYTE nTemp;
+
+ Data.nData[0] = Decode.nData[0];
+ Data.nData[0] <<= 2;
+
+ nTemp = Decode.nData[1];
+ nTemp >>= 4;
+ nTemp &= 0x03;
+ Data.nData[0] |= nTemp;
+
+ Data.nData[1] = Decode.nData[1];
+ Data.nData[1] <<= 4;
+
+ nTemp = Decode.nData[2];
+ nTemp >>= 2;
+ nTemp &= 0x0F;
+ Data.nData[1] |= nTemp;
+
+ Data.nData[2] = Decode.nData[2];
+ Data.nData[2] <<= 6;
+ nTemp = Decode.nData[3];
+ nTemp &= 0x3F;
+ Data.nData[2] |= nTemp;
+ }
+
+ inline void Base64Coder::_EncodeRaw(TempBucket &Data, const TempBucket &Decode)
+ {
+ BYTE nTemp;
+
+ Data.nData[0] = Decode.nData[0];
+ Data.nData[0] >>= 2;
+
+ Data.nData[1] = Decode.nData[0];
+ Data.nData[1] <<= 4;
+ nTemp = Decode.nData[1];
+ nTemp >>= 4;
+ Data.nData[1] |= nTemp;
+ Data.nData[1] &= 0x3F;
+
+ Data.nData[2] = Decode.nData[1];
+ Data.nData[2] <<= 2;
+
+ nTemp = Decode.nData[2];
+ nTemp >>= 6;
+
+ Data.nData[2] |= nTemp;
+ Data.nData[2] &= 0x3F;
+
+ Data.nData[3] = Decode.nData[2];
+ Data.nData[3] &= 0x3F;
+ }
+
+ inline BOOL Base64Coder::_IsBadMimeChar(BYTE nData)
+ {
+ switch(nData)
+ {
+ case '\r': case '\n': case '\t': case ' ' :
+ case '\b': case '\a': case '\f': case '\v':
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ inline void Base64Coder::_Init()
+ { // Initialize Decoding table.
+
+ int i;
+
+ for(i = 0; i < 256; i++)
+ DecodeTable()[i] = -2;
+
+ for(i = 0; i < 64; i++)
+ {
+ DecodeTable()[Base64Digits()[i]] = i;
+ DecodeTable()[Base64Digits()[i]|0x80] = i;
+ }
+
+ DecodeTable()['='] = -1;
+ DecodeTable()['='|0x80] = -1;
+
+ is_init() = TRUE;
+ }
+
+
+ }
+}
+} \ No newline at end of file
diff --git a/contrib/epee/include/net/smtp_helper.h b/contrib/epee/include/net/smtp_helper.h
new file mode 100644
index 000000000..b8252e1cf
--- /dev/null
+++ b/contrib/epee/include/net/smtp_helper.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the Andrey N. Sabelnikov nor the
+// names of its contributors may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY
+// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+
+
+
+
+#pragma once
+#include "smtp.h"
+
+namespace epee
+{
+namespace net_utils
+{
+ namespace smtp
+ {
+
+ inline bool send_mail(const std::string& server, int port, const std::string& login, const std::string& pass, const std::string& from_addres, const std::string& from_name, const std::string& maillist, const std::string& subject, const std::string& mail_body)
+ {
+ net_utils::smtp::CSMTPClient smtp;
+
+ if ( !smtp.ServerConnect( server.c_str(), port ) )
+ {
+ LOG_PRINT("Reporting: Failed to connect to server " << server <<":"<< port, LOG_LEVEL_0);
+ return false;
+ }
+
+ if(login.size() && pass.size())
+ {
+ if ( !smtp.ServerLogin( login.c_str(), pass.c_str()) )
+ {
+ LOG_PRINT("Reporting: Failed to auth on server " << server <<":"<< port, LOG_LEVEL_0);
+ return false;
+
+ }
+ }
+
+ if ( !smtp.SendMessage( from_addres.c_str(),
+ from_name.c_str(),
+ maillist.c_str(),
+ subject.c_str(),
+ "bicycle-client",
+ (LPBYTE)mail_body.data(),
+ mail_body.size()))
+ {
+ char *szErrorText = smtp.GetLastErrorText();
+ if ( szErrorText )
+ {
+ LOG_PRINT("Failed to send message, error text: " << szErrorText, LOG_LEVEL_0);
+ }
+ else
+ {
+ LOG_PRINT("Failed to send message, error text: null", LOG_LEVEL_0);
+ }
+ return false;
+ }
+
+ smtp.ServerDisconnect();
+
+ return true;
+
+
+ }
+ }
+}
+} \ No newline at end of file