diff options
author | Antonio Juarez <antonio.maria.juarez@live.com> | 2014-03-03 22:07:58 +0000 |
---|---|---|
committer | Antonio Juarez <antonio.maria.juarez@live.com> | 2014-03-03 22:07:58 +0000 |
commit | 296ae46ed8f8f6e5f986f978febad302e3df231a (patch) | |
tree | 1629164454a239308f33c9e12afb22e7f3cd8eeb /contrib/epee/include | |
parent | changed name (diff) | |
download | monero-296ae46ed8f8f6e5f986f978febad302e3df231a.tar.xz |
moved all stuff to github
Diffstat (limited to '')
89 files changed, 22294 insertions, 0 deletions
diff --git a/contrib/epee/include/ado_db_helper.h b/contrib/epee/include/ado_db_helper.h new file mode 100644 index 000000000..ed4e5b30f --- /dev/null +++ b/contrib/epee/include/ado_db_helper.h @@ -0,0 +1,1095 @@ +// 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 _DB_ADO_HELPER_H_ +#define _DB_ADO_HELPER_H_ + +#include <vector> +#include <comutil.h> +#include "string_coding.h" +#include "math_helper.h" +#include "file_io_utils.h" +#include "global_stream_operators.h" + + + +#define BEGIN_TRY_SECTION() try { + +#define CATCH_TRY_SECTION(ret_val) CATCH_TRY_SECTION_MESS(ret_val, "") + +#define CATCH_TRY_SECTION_MESS(ret_val, mess_where) }\ + catch(const std::exception&ex)\ + {\ + LOG_PRINT_J("DB_ERROR: " << ex.what(), LOG_LEVEL_0);\ + return ret_val;\ + }\ + catch(const _com_error& comm_err)\ + {\ + const TCHAR* pstr = comm_err.Description();\ + std::string descr = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ + const TCHAR* pmessage = comm_err.ErrorMessage();\ + pstr = comm_err.Source();\ + std::string source = string_encoding::convert_to_ansii(pstr?pstr:TEXT(""));\ + LOG_PRINT_J("COM_ERROR " << mess_where << ":\n\tDescriprion:" << descr << ", \n\t Message: " << string_encoding::convert_to_ansii(pmessage) << "\n\t Source: " << source, LOG_LEVEL_0);\ + return ret_val;\ + }\ + catch(...)\ + {\ + LOG_PRINT_J("..._ERROR: Unknown error.", LOG_LEVEL_0);\ + return ret_val;\ + }\ + +namespace epee +{ +namespace ado_db_helper +{ + + struct profile_entry + { + profile_entry():m_call_count(0), m_max_time(0), m_min_time(0) + {} + //std::string m_sql; + math_helper::average<DWORD, 10> m_avrg; + size_t m_call_count; + DWORD m_max_time; + DWORD m_min_time; + }; + + class profiler_manager + { + public: + typedef std::map<std::string, profile_entry> sqls_map; + profiler_manager(){} + + static bool sort_by_timing(const sqls_map::iterator& a, const sqls_map::iterator& b) + { + return a->second.m_avrg.get_avg() > b->second.m_avrg.get_avg(); + } + + bool flush_log(const std::string& path) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + std::stringstream strm; + strm << "SQL PROFILE:\r\nStatements: " << m_sqls.size() << "\r\n"; + std::list<sqls_map::iterator> m_sorted_by_time_sqls; + for(std::map<std::string, profile_entry>::iterator it = m_sqls.begin();it!=m_sqls.end();it++) + m_sorted_by_time_sqls.push_back(it); + + m_sorted_by_time_sqls.sort(sort_by_timing); + + for(std::list<sqls_map::iterator>::iterator it = m_sorted_by_time_sqls.begin();it!=m_sorted_by_time_sqls.end();it++) + { + strm << "---------------------------------------------------------------------------------------------------------\r\nSQL: " << (*it)->first << "\r\n"; + strm << "\tavrg: " << (*it)->second.m_avrg.get_avg() << "\r\n\tmax: " << (*it)->second.m_max_time << "\r\n\tmin: " << (*it)->second.m_min_time << "\r\n\tcount: " << (*it)->second.m_call_count << "\r\n"; + } + + return file_io_utils::save_string_to_file(path.c_str(), strm.str()); + CRITICAL_REGION_END(); + } + + bool push_entry(const std::string sql, DWORD time) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + profile_entry& entry_ref = m_sqls[sql]; + entry_ref.m_avrg.push(time); + entry_ref.m_call_count++; + if(time > entry_ref.m_max_time) entry_ref.m_max_time = time; + if(time < entry_ref.m_min_time || entry_ref.m_min_time == 0) entry_ref.m_min_time = time; + CRITICAL_REGION_END(); + return true; + } + + bool get_entry_avarege(const std::string sql, DWORD& time) + { + CRITICAL_REGION_BEGIN(m_sqls_lock); + sqls_map::iterator it = m_sqls.find(sql); + if(it==m_sqls.end()) + return false; + + time = static_cast<DWORD>(it->second.m_avrg.get_avg()); + CRITICAL_REGION_END(); + return true; + } + + private: + + sqls_map m_sqls; + critical_section m_sqls_lock; + }; +inline + profiler_manager* get_set_profiler(bool need_to_set = false, profiler_manager** pprofiler = NULL) + { + static profiler_manager* pmanager = NULL; + if(need_to_set) + pmanager = *pprofiler; + //else + // *pprofiler = pmanager; + + return pmanager; + } +inline + bool init() // INIT and DEINIT are NOT THREAD SAFE operations, CALL it BEFOR u start using this wrapper. + { + profiler_manager* pmanager = new profiler_manager(); + get_set_profiler(true, &pmanager); + return true; + } +inline + bool deinit() + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + delete pmanager; + return true; + } + inline bool push_timing(const std::string sql, DWORD time) + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + return pmanager->push_entry(sql, time); + return true; + } + + inline bool flush_profiler(const std::string path) + { + profiler_manager* pmanager = get_set_profiler(); + //get_set_profiler(false, &pmanager); + if(pmanager) + return pmanager->flush_log(path); + return true; + } + + class timing_guard + { + DWORD m_start_time; + std::string m_sql; + + public: + timing_guard(const std::string& sql) + { + m_start_time = ::GetTickCount(); + m_sql = sql; + } + + ~timing_guard() + { + DWORD timing = ::GetTickCount() - m_start_time; + push_timing(m_sql, timing); + } + }; +#define PROFILE_SQL(sql) timing_guard local_timing(sql) + + + typedef std::vector<std::vector<_variant_t> > table; + + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::string& parametr) + { + _variant_t param(parametr.c_str()); + ADODB::ADO_LONGPTR size = sizeof(parametr); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarChar, ADODB::adParamInput, static_cast<long>(parametr.size()+1), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::wstring& parametr) + { + _variant_t param(parametr.c_str()); + ADODB::ADO_LONGPTR size = sizeof(parametr); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("", ADODB::adVarWChar, ADODB::adParamInput, static_cast<long>(parametr.size()+2), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const __int64 parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast<long>(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBigInt, ADODB::adParamInput, static_cast<long>(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned __int64 parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast<long>(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedBigInt, ADODB::adParamInput, static_cast<long>(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr(ADODB::_CommandPtr cmd, const int parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast<long>(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, static_cast<long>(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, const unsigned int parametr) + { + _variant_t param(parametr); + ADODB::ADO_LONGPTR size = static_cast<long>(sizeof(parametr)); + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adUnsignedInt, ADODB::adParamInput, static_cast<long>(size), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, float parametr) + { + _variant_t param; + param.ChangeType(VT_R4); + param.fltVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adSingle, ADODB::adParamInput, static_cast<long>(sizeof(float)), param); + cmd->Parameters->Append(param_obj); + return true; + } + + inline bool add_parametr(ADODB::_CommandPtr cmd, bool parametr) + { + _variant_t param; + param = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adBoolean, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr(ADODB::_CommandPtr cmd, _variant_t parametr) + { + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBTimeStamp, ADODB::adParamInput, sizeof(parametr), parametr); + cmd->Parameters->Append(param_obj); + return true; + } + + + inline bool add_parametr_as_double(ADODB::_CommandPtr cmd, const DATE parametr) + { + _variant_t param; + param.ChangeType(VT_R8); + param.dblVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); + cmd->Parameters->Append(param_obj); + return true; + } + + template<typename TParam> + inline bool add_parametr(ADODB::_CommandPtr cmd, const std::list<TParam> params) + { + for(std::list<TParam>::const_iterator it = params.begin(); it!=params.end(); it++) + if(!add_parametr(cmd, *it)) + return false; + return true; + } + + /* + inline bool add_parametr(ADODB::_CommandPtr cmd, const size_t parametr) + { + _variant_t param; + param.ChangeType(VT_I4); + param.intVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adInteger, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + return true; + }*/ + + + inline bool add_parametr(ADODB::_CommandPtr cmd, const DATE parametr) + { + /*_variant_t param; + param.ChangeType(VT_R8); + param.dblVal = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDouble, ADODB::adParamInput, sizeof(float), param); + cmd->Parameters->Append(param_obj);*/ + + _variant_t param; + param.ChangeType(VT_DATE); + param.date = parametr; + ADODB::_ParameterPtr param_obj = cmd->CreateParameter("parametr", ADODB::adDBDate, ADODB::adParamInput, sizeof(parametr), param); + cmd->Parameters->Append(param_obj); + + return true; + } + + + inline bool execute_helper(ADODB::_CommandPtr cmd, _variant_t* pcount_processed = NULL) + { + //BEGIN_TRY_SECTION(); + + cmd->Execute(pcount_processed, NULL, ADODB::adExecuteNoRecords); + + + //CATCH_TRY_SECTION(false); + + return true; + } + + + inline bool select_helper(ADODB::_CommandPtr cmd, table& result_vector) + { + result_vector.clear(); + //BEGIN_TRY_SECTION(); + + ADODB::_RecordsetPtr precordset = cmd->Execute(NULL, NULL, NULL); + if(!precordset) + { + LOG_ERROR("DB_ERROR: cmd->Execute returned NULL!!!"); + return false; + } + + //if(precordset->EndOfFile == EOF) + //{ + // return true; + //} + /*try + { + if(precordset->MoveFirst()!= S_OK) + { + LOG_ERROR("DB_ERROR: Filed to move first!!!"); + return false; + } + } + catch (...) + { + return true; + }*/ + + size_t current_record_index = 0; + while(precordset->EndOfFile != EOF) + { + result_vector.push_back(table::value_type()); + size_t fields_count = precordset->Fields->Count; + result_vector[current_record_index].resize(fields_count); + for(size_t current_field_index = 0; current_field_index < fields_count; current_field_index++) + { + _variant_t var; + var.ChangeType(VT_I2); + var.intVal = static_cast<INT>(current_field_index); + result_vector[current_record_index][current_field_index] = precordset->Fields->GetItem(var)->Value; + } + precordset->MoveNext(); + current_record_index++; + } + //CATCH_TRY_SECTION(false); + return true; + } + + + template<typename TParam1> + struct adapter_zero + { + + }; + + template<typename TParam1> + struct adapter_single + { + TParam1 tparam1; + }; + template<typename TParam1, typename TParam2> + struct adapter_double + { + TParam1 tparam1; + TParam2 tparam2; + }; + + + template<typename TParam1, typename TParam2, typename TParam3> + struct adapter_triple + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + }; + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4> + struct adapter_quad + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + }; + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5> + struct adapter_quanto + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + }; + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6> + struct adapter_sixto + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + }; + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7> + struct adapter_sevento + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + TParam7 tparam7; + }; + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7, typename TParam8, typename TParam9> + struct adapter_nine + { + TParam1 tparam1; + TParam2 tparam2; + TParam3 tparam3; + TParam4 tparam4; + TParam5 tparam5; + TParam6 tparam6; + TParam7 tparam7; + TParam8 tparam8; + TParam9 tparam9; + }; + + template<typename TParam1> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_zero<TParam1>& params) + { + return true; + } + + template<typename TParam1> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_single<TParam1>& params) + { + return add_parametr(cmd, params.tparam1); + } + + template<typename TParam1, typename TParam2> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_double<TParam1, TParam2>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + return add_parametr(cmd, params.tparam2); + } + + template<typename TParam1, typename TParam2, typename TParam3> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_triple<TParam1, TParam2, TParam3>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + return add_parametr(cmd, params.tparam3); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quad<TParam1, TParam2, TParam3, TParam4>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + return add_parametr(cmd, params.tparam4); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_quanto<TParam1, TParam2, TParam3, TParam4, TParam5>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + return add_parametr(cmd, params.tparam5); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sixto<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + return add_parametr(cmd, params.tparam6); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_sevento<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + if(!add_parametr(cmd, params.tparam6)) return false; + return add_parametr(cmd, params.tparam7); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7, typename TParam8, typename TParam9> + bool add_parametrs_multi(ADODB::_CommandPtr cmd, const adapter_nine<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8, TParam9>& params) + { + if(!add_parametr(cmd, params.tparam1)) return false; + if(!add_parametr(cmd, params.tparam2)) return false; + if(!add_parametr(cmd, params.tparam3)) return false; + if(!add_parametr(cmd, params.tparam4)) return false; + if(!add_parametr(cmd, params.tparam5)) return false; + if(!add_parametr(cmd, params.tparam6)) return false; + if(!add_parametr(cmd, params.tparam7)) return false; + if(!add_parametr(cmd, params.tparam8)) return false; + return add_parametr(cmd, params.tparam9); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7> + std::string print_parameters_multi(const adapter_sevento<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7; + return strm.str(); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7, typename TParam8, typename TParam9> + std::string print_parameters_multi(const adapter_nine<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8, TParam9>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6 << ", " << params.tparam7 << ", " << params.tparam8 << ", " << params.tparam9; + return strm.str(); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6> + std::string print_parameters_multi(const adapter_sixto<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5 << ", " << params.tparam6; + return strm.str(); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5> + std::string print_parameters_multi(const adapter_quanto<TParam1, TParam2, TParam3, TParam4, TParam5>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4 << ", " << params.tparam5; + return strm.str(); + } + + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4> + std::string print_parameters_multi(const adapter_quad<TParam1, TParam2, TParam3, TParam4>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3 << ", " << params.tparam4; + return strm.str(); + } + + template<typename TParam1, typename TParam2, typename TParam3> + std::string print_parameters_multi(const adapter_triple<TParam1, TParam2, TParam3>& params) + { + std::stringstream strm; + strm << params.tparam1 << ", " << params.tparam2 << ", " << params.tparam3; + return strm.str(); + } + + template<typename TParam> + std::string get_str_param(const TParam& prm) + { + std::stringstream strm; + strm << prm; + return strm.str(); + } + + template<typename TParam> + std::string get_str_param(const std::list<TParam>& prm_lst) + { + std::stringstream strm; + for(std::list<TParam>::const_iterator it = prm_lst.begin();it!=prm_lst.end();it++) + strm << get_str_param(*it) << ", "; + return strm.str(); + } + + + template<typename TParam1, typename TParam2> + std::string print_parameters_multi(const adapter_double<TParam1, TParam2>& params) + { + std::stringstream strm; + strm << get_str_param(params.tparam1) << ", " << get_str_param(params.tparam2); + return strm.str(); + } + + template<typename TParam1> + std::string print_parameters_multi(const adapter_single<TParam1>& params) + { + std::stringstream strm; + strm << get_str_param(params.tparam1); + return strm.str(); + } + + template<typename TParam1> + std::string print_parameters_multi(const adapter_zero<TParam1>& params) + { + std::stringstream strm; + strm << "(no parametrs)"; + return strm.str(); + } + + + template<typename TParams> + bool execute_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, _variant_t* pcount_processed = NULL) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + if(!add_parametrs_multi(cmd, parametrs)) + return false; + + cmd->ActiveConnection = pconnection; + res = execute_helper(cmd, pcount_processed); + + CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); + return res; + } + + + template<typename TParams> + inline + bool select_helper_multiparam(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + + if(!add_parametrs_multi(cmd, parametrs)) + return false; + + cmd->ActiveConnection = pconnection; + res = select_helper(cmd, result_vector); + CATCH_TRY_SECTION_MESS(false, "while statment: " << sql_statment << " [params]: " << print_parameters_multi(parametrs)); + return res; + } + + + template<typename TParams> + inline + bool select_helper_param_container(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParams& parametrs, table& result_vector) + { + PROFILE_SQL(sql_statment); + bool res = false; + BEGIN_TRY_SECTION(); + ADODB::_CommandPtr cmd; + cmd.CreateInstance(__uuidof(ADODB::Command)); + cmd->CommandText = _bstr_t(sql_statment.c_str()); + + + for(TParams::const_iterator it = parametrs.begin(); it!=parametrs.end(); it++) + { + add_parametr(cmd, *it); + } + + cmd->ActiveConnection = pconnection; + res = select_helper(cmd, result_vector); + + CATCH_TRY_SECTION(false); + return res; + } + + + inline + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, _variant_t* pvt = NULL) + { + adapter_zero<int> params; + return execute_helper_multiparam(pconnection, sql_statment, params, pvt); + } + + template<typename TParam> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr) + { + adapter_single<TParam> params; + params.tparam1 = parametr; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + + template<typename TParam1, typename TParam2> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2) + { + adapter_double<TParam1, TParam2> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + return execute_helper_multiparam(pconnection, sql_statment, params); + + } + + template<typename TParam1, typename TParam2, typename TParam3> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3) + { + adapter_triple<TParam1, TParam2, typename TParam3> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4) + { + adapter_quad<TParam1, TParam2, TParam3, TParam4> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5) + { + adapter_quanto<TParam1, TParam2, TParam3, TParam4, TParam5> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6) + { + adapter_sixto<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7> + bool execute_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1& parametr1, const TParam2& parametr2, const TParam3& parametr3, const TParam4& parametr4, const TParam5& parametr5, const TParam6& parametr6, const TParam7& parametr7) + { + adapter_sevento<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + return execute_helper_multiparam(pconnection, sql_statment, params); + } + + inline + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, table& result_vector) + { + adapter_zero<int> params; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + template<typename TParam> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam& parametr, table& result_vector) + { + adapter_single<TParam> params; + params.tparam1 = parametr; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template<typename TParam1, typename TParam2> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, table& result_vector) + { + adapter_double<TParam1, TParam2> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + + } + + template<typename TParam1, typename TParam2, typename TParam3> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, table& result_vector) + { + adapter_triple<TParam1, TParam2, typename TParam3> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, table& result_vector) + { + adapter_quad<TParam1, TParam2, TParam3, TParam4> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, table& result_vector) + { + adapter_quanto<TParam1, TParam2, TParam3, TParam4, TParam5> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, table& result_vector) + { + adapter_sixto<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7, table& result_vector) + { + adapter_sevento<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename TParam5, typename TParam6, typename TParam7, typename TParam8, typename TParam9> + bool select_helper(ADODB::_ConnectionPtr pconnection, const std::string& sql_statment, const TParam1 parametr1, const TParam2 parametr2, const TParam3 parametr3, const TParam4 parametr4, const TParam5 parametr5, const TParam6 parametr6, const TParam7 parametr7,const TParam8 parametr8,const TParam9 parametr9, table& result_vector) + { + adapter_nine<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8, TParam9> params; + params.tparam1 = parametr1; + params.tparam2 = parametr2; + params.tparam3 = parametr3; + params.tparam4 = parametr4; + params.tparam5 = parametr5; + params.tparam6 = parametr6; + params.tparam7 = parametr7; + params.tparam8 = parametr8; + params.tparam9 = parametr9; + return select_helper_multiparam(pconnection, sql_statment, params, result_vector); + } + + + + + /************************************************************************/ + /* */ + /************************************************************************/ + + class per_thread_connection_pool + { + public: + bool init(const std::string& connection_string, const std::string& login, const std::string& pass) + { + m_connection_string = connection_string; + m_login = login; + m_password = pass; + if(!get_db_connection().GetInterfacePtr()) + return false; + + return true; + } + + ADODB::_ConnectionPtr& get_db_connection() + { + + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr<ADODB::_ConnectionPtr>& conn_ptr = m_db_connections[::GetCurrentThreadId()]; + m_db_connections_lock.unlock(); + if(!conn_ptr.get()) + { + conn_ptr.reset(new ADODB::_ConnectionPtr()); + ADODB::_ConnectionPtr& conn = *conn_ptr.get(); + //init new connection + + BEGIN_TRY_SECTION(); + //_bstr_t str = _bstr_t("Provider=SQLOLEDB;Data Source=SRV1;Integrated Security=SSPI;Initial Catalog=dispatcher;"); + + if(S_OK != conn.CreateInstance(__uuidof(ADODB::Connection))) + { + LOG_ERROR("Failed to Create, instance, was CoInitialize called ???!"); + return conn; + } + + HRESULT res = conn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); + if(res != S_OK) + { + LOG_ERROR("Failed to connect do DB, connection str:" << m_connection_string); + return conn; + } + CATCH_TRY_SECTION_MESS(conn, "while creating another connection"); + LOG_PRINT("New DB Connection added for threadid=" << ::GetCurrentThreadId(), LOG_LEVEL_0); + ado_db_helper::execute_helper(conn, "set enable_seqscan=false;"); + return conn; + } + + return *conn_ptr.get(); + } + + //---------------------------------------------------------------------------------------------- + bool check_status() + { + ADODB::_ConnectionPtr& rconn = get_db_connection(); + if(!ado_db_helper::execute_helper(rconn, "SET CLIENT_ENCODING TO 'SQL_ASCII'")) + { + + try{ + HRESULT res = rconn->Close(); + } + catch(...) + { + + }; + BEGIN_TRY_SECTION(); + + HRESULT res = rconn->Open(_bstr_t(m_connection_string.c_str()), _bstr_t(m_login.c_str()), _bstr_t(m_password.c_str()), NULL); + if(res != S_OK) + { + LOG_PRINT("Failed to restore connection to local AI DB", LOG_LEVEL_1); + return false; + } + CATCH_TRY_SECTION(false); + } + + return true; + } + + protected: + private: + std::map<DWORD, boost::shared_ptr<ADODB::_ConnectionPtr> > m_db_connections; + critical_section m_db_connections_lock; + std::string m_connection_string; + std::string m_login; + std::string m_password; + }; + + + template<typename TParam1, typename default_id_type, typename t_conn> + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, t_conn& c) + { + ado_db_helper::adapter_single<TParam1> params; + params.tparam1 = parametr_1; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + + template<typename TParam1, typename TParam2, typename default_id_type, typename t_conn> + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, t_conn& c) + { + ado_db_helper::adapter_double<TParam1, TParam2> params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + + template<typename TParam1, typename TParam2, typename TParam3, typename default_id_type, typename t_conn> + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, t_conn& c) + { + ado_db_helper::adapter_triple<TParam1, TParam2, TParam3> params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + params.tparam3 = parametr_3; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + template<typename TParam1, typename TParam2, typename TParam3, typename TParam4, typename default_id_type, typename t_conn> + bool find_or_add_t(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParam1 parametr_1, TParam2 parametr_2, TParam3 parametr_3, TParam4 parametr_4, t_conn& c) + { + ado_db_helper::adapter_quad<TParam1, TParam2, TParam3, TParam4> params; + params.tparam1 = parametr_1; + params.tparam2 = parametr_2; + params.tparam3 = parametr_3; + params.tparam4 = parametr_4; + return find_or_add_t_multiparametred(sql_select_statment, sql_insert_statment, id, new_object_added, params, c); + } + + template<typename TParams, typename default_id_type, typename t_conn> + bool find_or_add_t_multiparametred(const std::string& sql_select_statment, const std::string& sql_insert_statment, OUT default_id_type& id, OUT bool& new_object_added, TParams params, t_conn& c) + { + + //CHECK_CONNECTION(false); + + new_object_added = false; + ado_db_helper::table result_table; + + bool res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); + if(!result_table.size()) + { + res = select_helper_multiparam(c.get_db_connection(), sql_insert_statment, params, result_table); + if(!res || !result_table.size()) + { + //last time try to select + res = select_helper_multiparam(c.get_db_connection(), sql_select_statment, params, result_table); + CHECK_AND_ASSERT_MES(res, false, "Failed to execute statment: " << sql_select_statment); + CHECK_AND_ASSERT_MES(result_table.size(), false, "No records returned from statment: " << sql_select_statment); + }else + { + new_object_added = true; + } + } + + BEGIN_TRY_SECTION() + id = result_table[0][0]; + CATCH_TRY_SECTION_MESS(false, "while converting returned value [find_or_add_t_multiparametred()]"); + + return true; + } + +} +} +#endif //!_DB_HELPER_H_ diff --git a/contrib/epee/include/console_handler.h b/contrib/epee/include/console_handler.h new file mode 100644 index 000000000..238abc80c --- /dev/null +++ b/contrib/epee/include/console_handler.h @@ -0,0 +1,310 @@ +// 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 +{ + + + + + template<class t_server> + bool empty_commands_handler(t_server* psrv, const std::string& command) + { + return true; + } + + + template<class t_server, class chain_handler> + bool default_console_handler(t_server* psrv, chain_handler ch_handler, const std::string usage = "") + { + TRY_ENTRY(); + bool continue_handle = true; + while(continue_handle) + { + char command_buff[400] = {0}; + std::string command; + std::cin.getline(command_buff, 399); + if(std::cin.eof() || std::cin.fail()) + { + LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0); + continue_handle = false; + break; + } + command = command_buff; + + if(!command.compare("exit") || !command.compare("q") ) + { + psrv->send_stop_signal(); + continue_handle = false; + }else if ( !command.compare(0, 7, "set_log")) + { + //parse set_log command + if(command.size() != 9) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + int n = 0; + if(!string_tools::get_xtype_from_string(n, command.substr(8, 1))) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + log_space::get_set_log_detalisation_level(true, n); + LOG_PRINT_L0("New log level set " << n); + } + else if(ch_handler(psrv, command)) + continue; + else + { + std::cout << "unknown command: " << command << std::endl; + std::cout << usage; + } + } + return true; + CATCH_ENTRY_L0("console_handler", false); + } + + template<class chain_handler> + bool default_console_handler2(chain_handler ch_handler, const std::string usage) + { + TRY_ENTRY(); + bool continue_handle = true; + while(continue_handle) + { + char command_buff[400] = {0}; + std::string command; + std::cin.getline(command_buff, 399); + if(std::cin.eof() || std::cin.fail()) + { + + LOG_PRINT("std::cin.eof() or std::cin.fail(), stopping...", LOG_LEVEL_0); + continue_handle = false; + break; + } + command = command_buff; + + if(!command.compare("exit") || !command.compare("q") ) + { + continue_handle = false; + }else if ( !command.compare(0, 7, "set_log")) + { + //parse set_log command + if(command.size() != 9) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + int n = 0; + if(!string_tools::get_xtype_from_string(n, command.substr(8, 1))) + { + std::cout << "wrong syntax: " << command << std::endl << "use set_log n" << std::endl; + continue; + } + log_space::get_set_log_detalisation_level(true, n); + LOG_PRINT_L0("New log level set " << n); + } + else if(ch_handler(command)) + continue; + else + { + std::cout << "unknown command: " << command << std::endl; + std::cout << usage; + } + } + return true; + CATCH_ENTRY_L0("console_handler", false); + } + + + + + template<class t_server, class t_handler> + bool start_default_console(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + boost::thread( boost::bind(default_console_handler<t_server, t_handler>, ptsrv, handlr, usage) ); + return true; + } + + template<class t_server> + bool start_default_console(t_server* ptsrv, const std::string& usage = "") + { + return start_default_console(ptsrv, empty_commands_handler<t_server>, usage); + } + + template<class t_server, class t_handler> + bool no_srv_param_adapter(t_server* ptsrv, const std::string& cmd, t_handler handlr) + { + return handlr(cmd); + } + + template<class t_server, class t_handler> + bool run_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + return default_console_handler(ptsrv, boost::bind<bool>(no_srv_param_adapter<t_server, t_handler>, _1, _2, handlr), usage); + } + + template<class t_server, class t_handler> + bool start_default_console_handler_no_srv_param(t_server* ptsrv, t_handler handlr, const std::string& usage = "") + { + boost::thread( boost::bind(run_default_console_handler_no_srv_param<t_server, t_handler>, ptsrv, handlr, usage) ); + return true; + } + + /*template<class a> + bool f(int i, a l) + { + return true; + }*/ + /* + template<class chain_handler> + bool default_console_handler2(chain_handler ch_handler, const std::string usage) + */ + + + /*template<class t_handler> + bool start_default_console2(t_handler handlr, const std::string& usage = "") + { + //std::string usage_local = usage; + boost::thread( boost::bind(default_console_handler2<t_handler>, handlr, usage) ); + //boost::function<bool ()> p__ = boost::bind(f<t_handler>, 1, handlr); + //boost::function<bool ()> p__ = boost::bind(default_console_handler2<t_handler>, handlr, usage); + //boost::thread tr(p__); + return true; + }*/ + + /************************************************************************/ + /* */ + /************************************************************************/ + class console_handlers_binder + { + typedef boost::function<bool (const std::vector<std::string> &)> console_command_handler; + typedef std::map<std::string, std::pair<console_command_handler, std::string> > command_handlers_map; + std::unique_ptr<boost::thread> m_console_thread; + command_handlers_map m_command_handlers; + public: + std::string get_usage() + { + std::stringstream ss; + size_t max_command_len = 0; + for(auto& x:m_command_handlers) + if(x.first.size() > max_command_len) + max_command_len = x.first.size(); + + for(auto& x:m_command_handlers) + { + ss.width(max_command_len + 3); + ss << std::left << x.first << x.second.second << ENDL; + } + return ss.str(); + } + void set_handler(const std::string& cmd, const console_command_handler& hndlr, const std::string& usage = "") + { + command_handlers_map::mapped_type & vt = m_command_handlers[cmd]; + vt.first = hndlr; + vt.second = usage; + } + bool process_command_vec(const std::vector<std::string>& cmd) + { + if(!cmd.size()) + return false; + auto it = m_command_handlers.find(cmd.front()); + if(it == m_command_handlers.end()) + return false; + std::vector<std::string> cmd_local(cmd.begin()+1, cmd.end()); + return it->second.first(cmd_local); + } + + bool process_command_str(const std::string& cmd) + { + std::vector<std::string> cmd_v; + boost::split(cmd_v,cmd,boost::is_any_of(" "), boost::token_compress_on); + return process_command_vec(cmd_v); + } + + /*template<class t_srv> + bool start_handling(t_srv& srv, const std::string& usage_string = "") + { + start_default_console_handler_no_srv_param(&srv, boost::bind(&console_handlers_binder::process_command_str, this, _1)); + return true; + }*/ + + bool start_handling(const std::string& usage_string = "") + { + m_console_thread.reset(new boost::thread(boost::bind(&console_handlers_binder::run_handling, this, usage_string) )); + return true; + } + + bool stop_handling() + { + if(m_console_thread.get()) + m_console_thread->interrupt(); + return true; + } + + + bool run_handling(const std::string usage_string) + { + return default_console_handler2(boost::bind(&console_handlers_binder::process_command_str, this, _1), usage_string); + } + + /*template<class t_srv> + bool run_handling(t_srv& srv, const std::string& usage_string) + { + return run_default_console_handler_no_srv_param(&srv, boost::bind<bool>(&console_handlers_binder::process_command_str, this, _1), usage_string); + }*/ + + }; + + /* work around because of broken boost bind */ + template<class t_server> + class srv_console_handlers_binder: public console_handlers_binder + { + bool process_command_str(t_server* /*psrv*/, const std::string& cmd) + { + return console_handlers_binder::process_command_str(cmd); + } + public: + bool start_handling(t_server* psrv, const std::string& usage_string = "") + { + boost::thread(boost::bind(&srv_console_handlers_binder<t_server>::run_handling, this, psrv, usage_string) ); + return true; + } + + bool run_handling(t_server* psrv, const std::string usage_string) + { + return default_console_handler(psrv, boost::bind(&srv_console_handlers_binder<t_server>::process_command_str, this, _1, _2), usage_string); + } + }; + + +} + + diff --git a/contrib/epee/include/file_io_utils.h b/contrib/epee/include/file_io_utils.h new file mode 100644 index 000000000..7e8521838 --- /dev/null +++ b/contrib/epee/include/file_io_utils.h @@ -0,0 +1,455 @@ +// 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 _FILE_IO_UTILS_H_ +#define _FILE_IO_UTILS_H_ + + +//#include <sys/types.h> +//#include <sys/stat.h> + +#include <iostream> +#include <boost/filesystem.hpp> + + +#ifndef MAKE64 + #define MAKE64(low,high) ((__int64)(((DWORD)(low)) | ((__int64)((DWORD)(high))) << 32)) +#endif + +#ifdef WINDOWS_PLATFORM +#include <psapi.h> +#include <strsafe.h> +#include <string.h> +#include <mbstring.h> + +#endif + + + +namespace epee +{ +namespace file_io_utils +{ +#ifdef WINDOWS_PLATFORM + + inline + std::string get_temp_file_name_a() + { + std::string str_result; + char sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) + return str_result; + + char sz_temp_file[MAX_PATH*2] = {0}; + if(!::GetTempFileNameA( sz_temp, "mail", 0, sz_temp_file)) + return str_result; + sz_temp_file[sizeof(sz_temp_file)-1] = 0; //be happy! + str_result = sz_temp_file; + return str_result; + } + + +#ifdef BOOST_LEXICAL_CAST_INCLUDED + inline + bool get_not_used_filename(const std::string& folder, OUT std::string& result_name) + { + DWORD folder_attr = ::GetFileAttributesA(folder.c_str()); + if(folder_attr == INVALID_FILE_ATTRIBUTES) + return false; + if(!(folder_attr&FILE_ATTRIBUTE_DIRECTORY)) + return false; + + + std::string base_name = folder + "\\tmp"; + std::string tmp_name; + bool name_found = false; + int current_index = 0; + tmp_name = base_name + boost::lexical_cast<std::string>(current_index) + ".tmp"; + while(!name_found) + { + if(INVALID_FILE_ATTRIBUTES == ::GetFileAttributesA(tmp_name.c_str())) + name_found = true; + else + { + current_index++; + tmp_name = base_name + boost::lexical_cast<std::string>(current_index) + ".tmp"; + } + } + result_name = tmp_name; + return true; + } +#endif + + inline + std::string get_temp_folder_a() + { + std::string str_result; + char sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathA( sizeof( sz_temp ), sz_temp )) + return str_result; + sz_temp[(sizeof(sz_temp)/sizeof(sz_temp[0])) -1] = 0; + str_result = sz_temp; + return str_result; + } + + std::string convert_from_device_path_to_standart(const std::string& path) + { + + + STRSAFE_LPSTR pszFilename = (STRSAFE_LPSTR)path.c_str(); + + // Translate path with device name to drive letters. + char szTemp[4000] = {0}; + + if (::GetLogicalDriveStringsA(sizeof(szTemp)-1, szTemp)) + { + char szName[MAX_PATH]; + char szDrive[3] = " :"; + BOOL bFound = FALSE; + char* p = szTemp; + + do + { + // Copy the drive letter to the template string + *szDrive = *p; + + // Look up each device name + if (::QueryDosDeviceA(szDrive, szName, sizeof(szName))) + { + UINT uNameLen = strlen(szName); + + if (uNameLen < MAX_PATH) + { + bFound = _mbsnbicmp((const unsigned char*)pszFilename, (const unsigned char*)szName, + uNameLen) == 0; + + if (bFound) + { + // Reconstruct pszFilename using szTempFile + // Replace device path with DOS path + char szTempFile[MAX_PATH] = {0}; + StringCchPrintfA(szTempFile, + MAX_PATH, + "%s%s", + szDrive, + pszFilename+uNameLen); + return szTempFile; + //::StringCchCopyNA(pszFilename, MAX_PATH+1, szTempFile, strlen(szTempFile)); + } + } + } + + // Go to the next NULL character. + while (*p++); + } while (!bFound && *p); // end of string + } + + return ""; + } + + inline + std::string get_process_path_by_pid(DWORD pid) + { + std::string res; + + HANDLE hprocess = 0; + if( hprocess = ::OpenProcess( PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pid) ) + { + char buff[MAX_PATH]= {0}; + if(!::GetModuleFileNameExA( hprocess, 0, buff, MAX_PATH - 1 )) + res = "Unknown_b"; + else + { + buff[MAX_PATH - 1]=0; //be happy! + res = buff; + std::string::size_type a = res.rfind( '\\' ); + if ( a != std::string::npos ) + res.erase( 0, a+1); + + } + ::CloseHandle( hprocess ); + }else + res = "Unknown_a"; + + return res; + } + + + + + + inline + std::wstring get_temp_file_name_w() + { + std::wstring str_result; + wchar_t sz_temp[MAX_PATH*2] = {0}; + if(!::GetTempPathW( sizeof(sz_temp)/sizeof(sz_temp[0]), sz_temp )) + return str_result; + + wchar_t sz_temp_file[MAX_PATH+1] = {0}; + if(!::GetTempFileNameW( sz_temp, L"mail", 0, sz_temp_file)) + return str_result; + + sz_temp_file[(sizeof(sz_temp_file)/sizeof(sz_temp_file[0]))-1] = 0; //be happy! + str_result = sz_temp_file; + return str_result; + } +#endif + inline + bool is_file_exist(const std::string& path) + { + boost::filesystem::path p(path); + return boost::filesystem::exists(p); + } + + /* + inline + bool save_string_to_handle(HANDLE hfile, const std::string& str) + { + + + + if( INVALID_HANDLE_VALUE != hfile ) + { + DWORD dw; + if( !::WriteFile( hfile, str.data(), (DWORD) str.size(), &dw, NULL) ) + { + int err_code = GetLastError(); + //LOG_PRINT("Failed to write to file handle: " << hfile<< " Last error code:" << err_code << " : " << log_space::get_win32_err_descr(err_code), LOG_LEVEL_2); + return false; + } + ::CloseHandle(hfile); + return true; + }else + { + //LOG_WIN32_ERROR(::GetLastError()); + return false; + } + + return false; + }*/ + + + + inline + bool save_string_to_file(const std::string& path_to_file, const std::string& str) + { + + try + { + std::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc); + fstream << str; + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + /* + inline + bool load_form_handle(HANDLE hfile, std::string& str) + { + if( INVALID_HANDLE_VALUE != hfile ) + { + bool res = true; + DWORD dw = 0; + DWORD fsize = ::GetFileSize(hfile, &dw); + if(fsize > 300000000) + { + ::CloseHandle(hfile); + return false; + } + if(fsize) + { + str.resize(fsize); + if(!::ReadFile( hfile, (LPVOID)str.data(), (DWORD)str.size(), &dw, NULL)) + res = false; + } + ::CloseHandle(hfile); + return res; + } + return false; + } + */ + inline + bool get_file_time(const std::string& path_to_file, OUT time_t& ft) + { + boost::system::error_code ec; + ft = boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ec); + if(!ec) + return true; + else + return false; + } + + inline + bool set_file_time(const std::string& path_to_file, const time_t& ft) + { + boost::system::error_code ec; + boost::filesystem::last_write_time(boost::filesystem::path(path_to_file), ft, ec); + if(!ec) + return true; + else + return false; + } + + + inline + bool load_file_to_string(const std::string& path_to_file, std::string& target_str) + { + try + { + std::ifstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file, std::ios_base::binary | std::ios_base::in | std::ios::ate); + + std::ifstream::pos_type file_size = fstream.tellg(); + + if(file_size > 1000000000) + return false;//don't go crazy + size_t file_size_t = static_cast<size_t>(file_size); + + target_str.resize(file_size_t); + + fstream.seekg (0, std::ios::beg); + fstream.read((char*)target_str.data(), target_str.size()); + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + inline + bool append_string_to_file(const std::string& path_to_file, const std::string& str) + { + try + { + std::ofstream fstream; + fstream.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fstream.open(path_to_file.c_str(), std::ios_base::binary | std::ios_base::out | std::ios_base::app); + fstream << str; + fstream.close(); + return true; + } + + catch(...) + { + return false; + } + } + + /* + bool remove_dir_and_subirs(const char* path_to_dir); + + inline + bool clean_dir(const char* path_to_dir) + { + if(!path_to_dir) + return false; + + std::string folder = path_to_dir; + WIN32_FIND_DATAA find_data = {0}; + HANDLE hfind = ::FindFirstFileA((folder + "\\*.*").c_str(), &find_data); + if(INVALID_HANDLE_VALUE == hfind) + return false; + do{ + if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) + continue; + + if(find_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) + { + if(!remove_dir_and_subirs((folder + "\\" + find_data.cFileName).c_str())) + return false; + }else + { + if(!::DeleteFileA((folder + "\\" + find_data.cFileName).c_str())) + return false; + } + + + }while(::FindNextFileA(hfind, &find_data)); + ::FindClose(hfind); + + return true; + } + */ +#ifdef WINDOWS_PLATFORM + inline bool get_folder_content(const std::string& path, std::list<WIN32_FIND_DATAA>& OUT target_list) + { + WIN32_FIND_DATAA find_data = {0}; + HANDLE hfind = ::FindFirstFileA((path + "\\*.*").c_str(), &find_data); + if(INVALID_HANDLE_VALUE == hfind) + return false; + do{ + if(!strcmp("..", find_data.cFileName) || (!strcmp(".", find_data.cFileName))) + continue; + + target_list.push_back(find_data); + + }while(::FindNextFileA(hfind, &find_data)); + ::FindClose(hfind); + + return true; + } +#endif + inline bool get_folder_content(const std::string& path, std::list<std::string>& OUT target_list, bool only_files = false) + { + try + { + + boost::filesystem::directory_iterator end_itr; // default construction yields past-the-end + for ( boost::filesystem::directory_iterator itr( path ); itr != end_itr; ++itr ) + { + if ( only_files && boost::filesystem::is_directory(itr->status()) ) + { + continue; + } + target_list.push_back(itr->path().filename().string()); + } + + } + + catch(...) + { + return false; + } + return true; + } +} +} + +#endif //_FILE_IO_UTILS_H_ diff --git a/contrib/epee/include/global_stream_operators.h b/contrib/epee/include/global_stream_operators.h new file mode 100644 index 000000000..6fbdbc2ed --- /dev/null +++ b/contrib/epee/include/global_stream_operators.h @@ -0,0 +1,35 @@ +// 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 + +std::stringstream& operator<<(std::stringstream& out, const std::wstring& ws) +{ + std::string as = string_encoding::convert_to_ansii(ws); + out << as; + return out; +} diff --git a/contrib/epee/include/gzip_encoding.h b/contrib/epee/include/gzip_encoding.h new file mode 100644 index 000000000..2be51e77d --- /dev/null +++ b/contrib/epee/include/gzip_encoding.h @@ -0,0 +1,227 @@ +// 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 _GZIP_ENCODING_H_ +#define _GZIP_ENCODING_H_ +#include "net/http_client_base.h" +#include "zlib/zlib.h" +//#include "http.h" + + +namespace epee +{ +namespace net_utils +{ + + + + class content_encoding_gzip: public i_sub_handler + { + public: + /*! \brief + * Function content_encoding_gzip : Constructor + * + */ + inline + content_encoding_gzip(i_target_handler* powner_filter, bool is_deflate_mode = false):m_powner_filter(powner_filter), + m_is_stream_ended(false), + m_is_deflate_mode(is_deflate_mode), + m_is_first_update_in(true) + { + memset(&m_zstream_in, 0, sizeof(m_zstream_in)); + memset(&m_zstream_out, 0, sizeof(m_zstream_out)); + int ret = 0; + if(is_deflate_mode) + { + ret = inflateInit(&m_zstream_in); + ret = deflateInit(&m_zstream_out, Z_DEFAULT_COMPRESSION); + }else + { + ret = inflateInit2(&m_zstream_in, 0x1F); + ret = deflateInit2(&m_zstream_out, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8, Z_DEFAULT_STRATEGY); + } + } + /*! \brief + * Function content_encoding_gzip : Destructor + * + */ + inline + ~content_encoding_gzip() + { + inflateEnd(& m_zstream_in ); + deflateEnd(& m_zstream_out ); + } + /*! \brief + * Function update_in : Entry point for income data + * + */ + inline + virtual bool update_in( std::string& piece_of_transfer) + { + + bool is_first_time_here = m_is_first_update_in; + m_is_first_update_in = false; + + if(m_pre_decode.size()) + m_pre_decode += piece_of_transfer; + else + m_pre_decode.swap(piece_of_transfer); + piece_of_transfer.clear(); + + std::string decode_summary_buff; + + size_t ungzip_size = m_pre_decode.size() * 0x30; + std::string current_decode_buff(ungzip_size, 'X'); + + //Here the cycle is introduced where we unpack the buffer, the cycle is required + //because of the case where if after unpacking the data will exceed the awaited size, we will not halt with error + bool continue_unpacking = true; + bool first_step = true; + while(m_pre_decode.size() && continue_unpacking) + { + + //fill buffers + m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); + m_zstream_in.avail_in = (uInt)m_pre_decode.size(); + m_zstream_in.next_out = (Bytef*)current_decode_buff.data(); + m_zstream_in.avail_out = (uInt)ungzip_size; + + int flag = Z_SYNC_FLUSH; + int ret = inflate(&m_zstream_in, flag); + CHECK_AND_ASSERT_MES(ret>=0 || m_zstream_in.avail_out ||m_is_deflate_mode, false, "content_encoding_gzip::update_in() Failed to inflate. err = " << ret); + + if(Z_STREAM_END == ret) + m_is_stream_ended = true; + else if(Z_DATA_ERROR == ret && is_first_time_here && m_is_deflate_mode&& first_step) + { + // some servers (notably Apache with mod_deflate) don't generate zlib headers + // insert a dummy header and try again + static char dummy_head[2] = + { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + }; + inflateReset(&m_zstream_in); + m_zstream_in.next_in = (Bytef*) dummy_head; + m_zstream_in.avail_in = sizeof(dummy_head); + + ret = inflate(&m_zstream_in, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + m_pre_decode.swap(piece_of_transfer); + return false; + } + m_zstream_in.next_in = (Bytef*)m_pre_decode.data(); + m_zstream_in.avail_in = (uInt)m_pre_decode.size(); + + ret = inflate(&m_zstream_in, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + m_pre_decode.swap(piece_of_transfer); + return false; + } + } + + + //leave only unpacked part in the output buffer to start with it the next time + m_pre_decode.erase(0, m_pre_decode.size()-m_zstream_in.avail_in); + //if decoder gave nothing to return, then everything is ahead, now simply break + if(ungzip_size == m_zstream_in.avail_out) + break; + + //decode_buff currently stores data parts that were unpacked, fix this size + current_decode_buff.resize(ungzip_size - m_zstream_in.avail_out); + if(decode_summary_buff.size()) + decode_summary_buff += current_decode_buff; + else + current_decode_buff.swap(decode_summary_buff); + + current_decode_buff.resize(ungzip_size); + first_step = false; + } + + //Process these data if required + bool res = true; + + res = m_powner_filter->handle_target_data(decode_summary_buff); + + return true; + + } + /*! \brief + * Function stop : Entry point for stop signal and flushing cached data buffer. + * + */ + inline + virtual void stop(std::string& OUT collect_remains) + { + } + protected: + private: + /*! \brief + * Pointer to parent HTTP-parser + */ + i_target_handler* m_powner_filter; + /*! \brief + * ZLIB object for income stream + */ + z_stream m_zstream_in; + /*! \brief + * ZLIB object for outcome stream + */ + z_stream m_zstream_out; + /*! \brief + * Data that could not be unpacked immediately, left to wait for the next packet of data + */ + std::string m_pre_decode; + /*! \brief + * The data are accumulated for a package in the buffer to send the web client + */ + std::string m_pre_encode; + /*! \brief + * Signals that stream looks like ended + */ + bool m_is_stream_ended; + /*! \brief + * If this flag is set, income data is in HTTP-deflate mode + */ + bool m_is_deflate_mode; + /*! \brief + * Marks that it is a first data packet + */ + bool m_is_first_update_in; + }; +} +} + + + +#endif //_GZIP_ENCODING_H_ diff --git a/contrib/epee/include/hmac-md5.h b/contrib/epee/include/hmac-md5.h new file mode 100644 index 000000000..2a4e0d401 --- /dev/null +++ b/contrib/epee/include/hmac-md5.h @@ -0,0 +1,93 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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. + */ + +/* hmac-md5.h -- HMAC_MD5 functions + */ + +/* + * $Id: hmac-md5.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ + */ + +#ifndef HMAC_MD5_H +#define HMAC_MD5_H 1 + +namespace md5 +{ + + + +#define HMAC_MD5_SIZE 16 + + /* intermediate MD5 context */ + typedef struct HMAC_MD5_CTX_s { + MD5_CTX ictx, octx; + } HMAC_MD5_CTX; + + /* intermediate HMAC state + * values stored in network byte order (Big Endian) + */ + typedef struct HMAC_MD5_STATE_s { + UINT4 istate[4]; + UINT4 ostate[4]; + } HMAC_MD5_STATE; + + /* One step hmac computation + * + * digest may be same as text or key + */ + void hmac_md5(const unsigned char *text, int text_len, + const unsigned char *key, int key_len, + unsigned char digest[HMAC_MD5_SIZE]); + + /* create context from key + */ + void hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, int key_len); + + /* precalculate intermediate state from key + */ + void hmac_md5_precalc(HMAC_MD5_STATE *hmac, + const unsigned char *key, int key_len); + + /* initialize context from intermediate state + */ + void hmac_md5_import(HMAC_MD5_CTX *hmac, HMAC_MD5_STATE *state); + +#define hmac_md5_update(hmac, text, text_len) MD5Update(&(hmac)->ictx, (text), (text_len)) + + /* finish hmac from intermediate result. Intermediate result is zeroed. + */ + void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac); + +} + +#endif /* HMAC_MD5_H */ diff --git a/contrib/epee/include/include_base_utils.h b/contrib/epee/include/include_base_utils.h new file mode 100644 index 000000000..8412a0083 --- /dev/null +++ b/contrib/epee/include/include_base_utils.h @@ -0,0 +1,34 @@ +// 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 BOOST_FILESYSTEM_VERSION 3 +#define ENABLE_RELEASE_LOGGING + +#include "misc_log_ex.h" + + diff --git a/contrib/epee/include/math_helper.h b/contrib/epee/include/math_helper.h new file mode 100644 index 000000000..44efd4682 --- /dev/null +++ b/contrib/epee/include/math_helper.h @@ -0,0 +1,272 @@ +// 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 <list> +#include <numeric> +#include <boost/timer.hpp> +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/random_generator.hpp> + +#include "misc_os_dependent.h" + +namespace epee +{ +namespace math_helper +{ + + template<typename val, int default_base> + class average + { + public: + + average() + { + m_base = default_base; + m_last_avg_val = 0; + } + + bool set_base() + { + CRITICAL_REGION_LOCAL(m_lock); + + m_base = default_base; + if(m_list.size() > m_base) + m_list.resize(m_base); + + return true; + } + + typedef val value_type; + + void push(const value_type& vl) + { + CRITICAL_REGION_LOCAL(m_lock); + +//#ifndef DEBUG_STUB + m_list.push_back(vl); + if(m_list.size() > m_base ) + m_list.pop_front(); +//#endif + } + + double update(const value_type& vl) + { + CRITICAL_REGION_LOCAL(m_lock); +//#ifndef DEBUG_STUB + push(vl); +//#endif + + return get_avg(); + } + + double get_avg() + { + CRITICAL_REGION_LOCAL(m_lock); + + value_type vl = std::accumulate(m_list.begin(), m_list.end(), value_type(0)); + if(m_list.size()) + return m_last_avg_val = (double)(vl/m_list.size()); + + return m_last_avg_val = (double)vl; + } + + value_type get_last_val() + { + CRITICAL_REGION_LOCAL(m_lock); + if(m_list.size()) + return m_list.back(); + + return 0; + } + + private: + unsigned int m_base; + double m_last_avg_val; + std::list<value_type> m_list; + critical_section m_lock; + }; + + +#ifdef WINDOWS_PLATFORM + + /************************************************************************/ + /* */ + /************************************************************************/ + class timing_guard_base + { + public: + virtual ~timing_guard_base(){}; + }; + + template<class T> + class timing_guard: public timing_guard_base + { + public: + timing_guard(T& avrg):m_avrg(avrg) + { + m_start_ticks = ::GetTickCount(); + } + + ~timing_guard() + { + m_avrg.push(::GetTickCount()-m_start_ticks); + } + + private: + T& m_avrg; + DWORD m_start_ticks; + }; + + template<class t_timing> + timing_guard_base* create_timing_guard(t_timing& timing){return new timing_guard<t_timing>(timing);} + +#define BEGIN_TIMING_ZONE(timing_var) { boost::shared_ptr<math_helper::timing_guard_base> local_timing_guard_ptr(math_helper::create_timing_guard(timing_var)); +#define END_TIMING_ZONE() } +#endif + +//#ifdef WINDOWS_PLATFORM_EX + template<boost::uint64_t default_time_window> + class speed + { + public: + + speed() + { + m_time_window = default_time_window; + m_last_speed_value = 0; + } + bool chick() + { +#ifndef DEBUG_STUB + boost::uint64_t ticks = misc_utils::get_tick_count(); + CRITICAL_REGION_BEGIN(m_lock); + m_chicks.push_back(ticks); + CRITICAL_REGION_END(); + //flush(ticks); +#endif + return true; + } + + bool chick(size_t count) + { + for(size_t s = 0; s != count; s++) + chick(); + + return true; + } + + + size_t get_speed() + { + flush(misc_utils::get_tick_count()); + return m_last_speed_value = m_chicks.size(); + } + private: + + bool flush(boost::uint64_t ticks) + { + CRITICAL_REGION_BEGIN(m_lock); + std::list<boost::uint64_t>::iterator it = m_chicks.begin(); + while(it != m_chicks.end()) + { + if(*it + m_time_window < ticks) + m_chicks.erase(it++); + else + break; + } + CRITICAL_REGION_END(); + return true; + } + + std::list<boost::uint64_t> m_chicks; + boost::uint64_t m_time_window; + size_t m_last_speed_value; + critical_section m_lock; + }; +//#endif + + template<class tlist> + void randomize_list(tlist& t_list) + { + for(typename tlist::iterator it = t_list.begin();it!=t_list.end();it++) + { + size_t offset = rand()%t_list.size(); + typename tlist::iterator it_2 = t_list.begin(); + for(size_t local_offset = 0;local_offset!=offset;local_offset++) + it_2++; + if(it_2 == it) + continue; + std::swap(*it_2, *it); + } + + } +PRAGMA_WARNING_PUSH +PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") + inline + uint64_t generated_random_uint64() + { + boost::uuids::uuid id___ = boost::uuids::random_generator()(); + return *reinterpret_cast<uint64_t*>(&id___.data[0]); //(*reinterpret_cast<uint64_t*>(&id___.data[0]) ^ *reinterpret_cast<uint64_t*>(&id___.data[8])); + } +PRAGMA_WARNING_POP + template<int default_interval, bool start_immediate = true> + class once_a_time_seconds + { + public: + once_a_time_seconds():m_interval(default_interval) + { + m_last_worked_time = 0; + if(!start_immediate) + time(&m_last_worked_time); + } + + template<class functor_t> + bool do_call(functor_t functr) + { + time_t current_time = 0; + time(¤t_time); + + if(current_time - m_last_worked_time > m_interval) + { + bool res = functr(); + time(&m_last_worked_time); + return res; + } + return true; + } + + private: + time_t m_last_worked_time; + time_t m_interval; + }; +} +}
\ No newline at end of file diff --git a/contrib/epee/include/md5_l.h b/contrib/epee/include/md5_l.h new file mode 100644 index 000000000..fe4c67db6 --- /dev/null +++ b/contrib/epee/include/md5_l.h @@ -0,0 +1,97 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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. + */ + +/* + * $Id: md5.h,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ + */ + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. +These notices must be retained in any copies of any part of this +documentation and/or software. + */ +#ifndef MD5_H +#define MD5_H + + +#include "md5global.h" + +namespace md5 +{ + /* MD5 context. */ + typedef struct { + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ + } MD5_CTX; + + static void MD5Init(MD5_CTX * context); + static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ); + static void MD5Final ( unsigned char digest[16], MD5_CTX *context ); + static void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest); + + + inline bool md5( unsigned char *input, int ilen, unsigned char output[16] ) + { + MD5_CTX ctx; + + MD5Init( &ctx ); + MD5Update( &ctx, input, ilen ); + MD5Final( output, &ctx); + + memset( &ctx, 0, sizeof( MD5_CTX) ); + return true; + } + + +} + +#include "md5_l.inl" + +#endif diff --git a/contrib/epee/include/md5_l.inl b/contrib/epee/include/md5_l.inl new file mode 100644 index 000000000..c3da1a3b0 --- /dev/null +++ b/contrib/epee/include/md5_l.inl @@ -0,0 +1,563 @@ +/* +* libEtPan! -- a mail stuff library +* +* Copyright (C) 2001, 2005 - DINH Viet Hoa +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. 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. +* 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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. +*/ + +/* +* $Id: md5.c,v 1.1.1.1 2005/03/18 20:17:27 zautrix Exp $ +*/ + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm +*/ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. +*/ + +/* do i need all of this just for htonl()? damn. */ +//#include <sys/types.h> +//#include <sys/param.h> +//#include <sys/socket.h> +//#include <netinet/in.h> + + + +#include "md5global.h" +#include "md5_l.h" +#include "hmac-md5.h" + +namespace md5 +{ + /* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + + /* + static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); + static void Encode PROTO_LIST + ((unsigned char *, UINT4 *, unsigned int)); + static void Decode PROTO_LIST + ((UINT4 *, unsigned char *, unsigned int)); + static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); + static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); + */ + + static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; + } + + /* Note: Replace "for loop" with standard memset if possible. + */ + + static void MD5_memset (POINTER output, int value, unsigned int len) + { + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; + } + + static void MD5Transform (UINT4 state[4], unsigned char block[64]); + + static unsigned char* PADDING() + { + static unsigned char local_PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + return local_PADDING; + + } + + + + /* F, G, H and I are basic MD5 functions. + + */ +#ifdef I + /* This might be defined via NANA */ +#undef I +#endif + +#define MD5_M_F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define MD5_M_G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define MD5_M_H(x, y, z) ((x) ^ (y) ^ (z)) +#define MD5_M_I(x, y, z) ((y) ^ ((x) | (~z))) + + /* ROTATE_LEFT rotates x left n bits. + + */ + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + + /* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + Rotation is separate from addition to prevent recomputation. + */ + +#define FF(a, b, c, d, x, s, ac) { (a) += MD5_M_F ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define GG(a, b, c, d, x, s, ac) { (a) += MD5_M_G ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define HH(a, b, c, d, x, s, ac) { (a) += MD5_M_H ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } +#define II(a, b, c, d, x, s, ac) { (a) += MD5_M_I ((b), (c), (d)) + (x) + (UINT4)(ac); (a) = ROTATE_LEFT ((a), (s)); (a) += (b); } + + /* MD5 initialization. Begins an MD5 operation, writing a new context. + */ + + static void MD5Init(MD5_CTX * context) + { + context->count[0] = context->count[1] = 0; + + /* Load magic initialization constants. + + */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; + } + + /* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the context. + */ + + static void MD5Update( MD5_CTX *context, const unsigned char *input, unsigned int inputLen ) + { + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. + + */ + if (inputLen >= partLen) + { + MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)input, partLen ); + MD5Transform( context->state, context->buffer ); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, (unsigned char*)&input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy( (POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i ); + + } + + /* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + + */ + + static void Encode (unsigned char *output, UINT4 *input, unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } + } + + /* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + + */ + + static void Decode (UINT4 *output, unsigned char *input, unsigned int len) + { + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) + | (((UINT4)input[j+3]) << 24); + } + + /* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + + */ + + static void MD5Final ( unsigned char digest[16], MD5_CTX *context ) + { + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. + + */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING(), padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. + + */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); + } + + /* MD5 basic transformation. Transforms state based on block. + + */ + + static void MD5Transform (UINT4 state[4], unsigned char block[64]) + { + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + */ + MD5_memset ((POINTER)x, 0, sizeof (x)); + } + + /* Note: Replace "for loop" with standard memcpy if possible. + + */ + inline + void hmac_md5_init(HMAC_MD5_CTX *hmac, + const unsigned char *key, + int key_len) + { + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + + MD5Init(&hmac->ictx); /* init inner context */ + MD5Update(&hmac->ictx, k_ipad, 64); /* apply inner pad */ + + MD5Init(&hmac->octx); /* init outer context */ + MD5Update(&hmac->octx, k_opad, 64); /* apply outer pad */ + + /* scrub the pads and key context (if used) */ + MD5_memset( (POINTER)&k_ipad, 0, sizeof(k_ipad)); + MD5_memset( (POINTER)&k_opad, 0, sizeof(k_opad)); + MD5_memset( (POINTER)&tk, 0, sizeof(tk)); + + /* and we're done. */ + } + + /* The precalc and import routines here rely on the fact that we pad + * the key out to 64 bytes and use that to initialize the md5 + * contexts, and that updating an md5 context with 64 bytes of data + * leaves nothing left over; all of the interesting state is contained + * in the state field, and none of it is left over in the count and + * buffer fields. So all we have to do is save the state field; we + * can zero the others when we reload it. Which is why the decision + * was made to pad the key out to 64 bytes in the first place. */ + inline + void hmac_md5_precalc(HMAC_MD5_STATE *state, + const unsigned char *key, + int key_len) + { + HMAC_MD5_CTX hmac; + unsigned lupe; + + hmac_md5_init(&hmac, key, key_len); + for (lupe = 0; lupe < 4; lupe++) { + state->istate[lupe] = htonl(hmac.ictx.state[lupe]); + state->ostate[lupe] = htonl(hmac.octx.state[lupe]); + } + MD5_memset( (POINTER)&hmac, 0, sizeof(hmac)); + } + + + inline + void hmac_md5_import(HMAC_MD5_CTX *hmac, + HMAC_MD5_STATE *state) + { + unsigned lupe; + MD5_memset( (POINTER)hmac, 0, sizeof(HMAC_MD5_CTX)); + for (lupe = 0; lupe < 4; lupe++) { + hmac->ictx.state[lupe] = ntohl(state->istate[lupe]); + hmac->octx.state[lupe] = ntohl(state->ostate[lupe]); + } + /* Init the counts to account for our having applied + * 64 bytes of key; this works out to 0x200 (64 << 3; see + * MD5Update above...) */ + hmac->ictx.count[0] = hmac->octx.count[0] = 0x200; + } + + inline + void hmac_md5_final(unsigned char digest[HMAC_MD5_SIZE], + HMAC_MD5_CTX *hmac) + { + MD5Final(digest, &hmac->ictx); /* Finalize inner md5 */ + MD5Update(&hmac->octx, digest, 16); /* Update outer ctx */ + MD5Final(digest, &hmac->octx); /* Finalize outer md5 */ + } + + + void hmac_md5(const unsigned char* text, int text_len, const unsigned char* key, int key_len, unsigned char *digest) + { + MD5_CTX context; + + unsigned char k_ipad[65]; /* inner padding - + * key XORd with ipad + */ + unsigned char k_opad[65]; /* outer padding - + * key XORd with opad + */ + unsigned char tk[16]; + int i; + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) { + + MD5_CTX tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* + * the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected + */ + + /* start out by storing key in pads */ + MD5_memset(k_ipad, '\0', sizeof k_ipad); + MD5_memset(k_opad, '\0', sizeof k_opad); + MD5_memcpy( k_ipad, (POINTER)key, key_len); + MD5_memcpy( k_opad, (POINTER)key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + k_ipad[i] ^= 0x36; + k_opad[i] ^= 0x5c; + } + /* + * perform inner MD5 + */ + + MD5Init(&context); /* init context for 1st + * pass */ + MD5Update(&context, k_ipad, 64); /* start with inner pad */ + MD5Update(&context, text, text_len); /* then text of datagram */ + MD5Final(digest, &context); /* finish up 1st pass */ + + /* + * perform outer MD5 + */ + MD5Init(&context); /* init context for 2nd + * pass */ + MD5Update(&context, k_opad, 64); /* start with outer pad */ + MD5Update(&context, digest, 16); /* then results of 1st + * hash */ + MD5Final(digest, &context); /* finish up 2nd pass */ + + } +}
\ No newline at end of file diff --git a/contrib/epee/include/md5global.h b/contrib/epee/include/md5global.h new file mode 100644 index 000000000..afc229019 --- /dev/null +++ b/contrib/epee/include/md5global.h @@ -0,0 +1,77 @@ +/* + * libEtPan! -- a mail stuff library + * + * Copyright (C) 2001, 2005 - DINH Viet Hoa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the libEtPan! project 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 AUTHORS 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 AUTHORS OR CONTRIBUTORS 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. + */ + +/* + * $Id: md5global.h,v 1.1.1.1 2005/03/18 20:17:28 zautrix Exp $ + */ + +/* GLOBAL.H - RSAREF types and constants + */ + +#ifndef MD5GLOBAL_H +#define MD5GLOBAL_H + +namespace md5 +{ + + + /* PROTOTYPES should be set to one if and only if the compiler supports + function argument prototyping. + The following makes PROTOTYPES default to 0 if it has not already + been defined with C compiler flags. + */ +#ifndef PROTOTYPES +#define PROTOTYPES 0 +#endif + + /* POINTER defines a generic pointer type */ + typedef unsigned char *POINTER; + + /* UINT2 defines a two byte word */ + typedef unsigned short int UINT2; + + /* UINT4 defines a four byte word */ + //typedef unsigned long int UINT4; + typedef unsigned int UINT4; + + /* PROTO_LIST is defined depending on how PROTOTYPES is defined above. + If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it + returns an empty list. + */ +#if PROTOTYPES +#define PROTO_LIST(list) list +#else +#define PROTO_LIST(list) () +#endif + +} + +#endif diff --git a/contrib/epee/include/misc_language.h b/contrib/epee/include/misc_language.h new file mode 100644 index 000000000..d5157365c --- /dev/null +++ b/contrib/epee/include/misc_language.h @@ -0,0 +1,162 @@ +// 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 <limits> +#include <boost/thread.hpp> +#include <boost/utility/value_init.hpp> +namespace epee +{ +#define STD_TRY_BEGIN() try { + +#define STD_TRY_CATCH(where_, ret_val) \ + } \ + catch (const std::exception &e) \ + { \ + LOG_ERROR("EXCEPTION: " << where_ << ", mes: "<< e.what()); \ + return ret_val; \ + } \ + catch (...) \ + { \ + LOG_ERROR("EXCEPTION: " << where_ ); \ + return ret_val; \ + } + + + +#define AUTO_VAL_INIT(v) boost::value_initialized<decltype(v)>() + +namespace misc_utils +{ + template<typename t_type> + t_type get_max_t_val(t_type t) + { + return (std::numeric_limits<t_type>::max)(); + } + + + template<typename t_iterator> + t_iterator move_it_forward(t_iterator it, size_t count) + { + while(count--) + it++; + return it; + } + + template<typename t_iterator> + t_iterator move_it_backward(t_iterator it, size_t count) + { + while(count--) + it--; + return it; + } + + + // TEMPLATE STRUCT less + template<class _Ty> + struct less_as_pod + : public std::binary_function<_Ty, _Ty, bool> + { // functor for operator< + bool operator()(const _Ty& _Left, const _Ty& _Right) const + { // apply operator< to operands + return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; + } + }; + + template<class _Ty> + bool is_less_as_pod(const _Ty& _Left, const _Ty& _Right) + { // apply operator< to operands + return memcmp(&_Left, &_Right, sizeof(_Left)) < 0; + } + + + inline + bool sleep_no_w(long ms ) + { + boost::this_thread::sleep( + boost::get_system_time() + + boost::posix_time::milliseconds( std::max<long>(ms,0) ) ); + + return true; + } + + template<class type_vec_type> + type_vec_type median(std::vector<type_vec_type> &v) + { + if(v.empty()) + return boost::value_initialized<type_vec_type>(); + if(v.size() == 1) + return v[0]; + + size_t n = (v.size()) / 2; + std::sort(v.begin(), v.end()); + //nth_element(v.begin(), v.begin()+n-1, v.end()); + if(v.size()%2) + {//1, 3, 5... + return v[n]; + }else + {//2, 4, 6... + return (v[n-1] + v[n])/2; + } + + } + + /************************************************************************/ + /* */ + /************************************************************************/ + + struct call_befor_die_base + { + virtual ~call_befor_die_base(){} + }; + + typedef boost::shared_ptr<call_befor_die_base> auto_scope_leave_caller; + + + template<class t_scope_leave_handler> + struct call_befor_die: public call_befor_die_base + { + t_scope_leave_handler m_func; + call_befor_die(t_scope_leave_handler f):m_func(f) + {} + ~call_befor_die() + { + m_func(); + } + }; + + template<class t_scope_leave_handler> + auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f) + { + auto_scope_leave_caller slc(new call_befor_die<t_scope_leave_handler>(f)); + return slc; + } + +} +} diff --git a/contrib/epee/include/misc_log_ex.h b/contrib/epee/include/misc_log_ex.h new file mode 100644 index 000000000..446d4bd3b --- /dev/null +++ b/contrib/epee/include/misc_log_ex.h @@ -0,0 +1,1426 @@ +// 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 _MISC_LOG_EX_H_ +#define _MISC_LOG_EX_H_ + +//#include <windows.h> +#include <string> +#include <iostream> +#include <sstream> +#include <iomanip> +#include <fstream> +#include <algorithm> +#include <list> +#include <map> +#include <time.h> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <boost/filesystem.hpp> +#include <boost/algorithm/string.hpp> + +#include "static_initializer.h" +#include "string_tools.h" +#include "time_helper.h" +#include "misc_os_dependent.h" + + +#include "syncobj.h" + + + + +#define LOG_LEVEL_SILENT -1 +#define LOG_LEVEL_0 0 +#define LOG_LEVEL_1 1 +#define LOG_LEVEL_2 2 +#define LOG_LEVEL_3 3 +#define LOG_LEVEL_4 4 +#define LOG_LEVEL_MIN LOG_LEVEL_SILENT +#define LOG_LEVEL_MAX LOG_LEVEL_4 + + + + +#define LOGGER_NULL 0 +#define LOGGER_FILE 1 +#define LOGGER_DEBUGGER 2 +#define LOGGER_CONSOLE 3 +#define LOGGER_DUMP 4 + + +#ifndef LOCAL_ASSERT +#include <assert.h> +#if (defined _MSC_VER) +#define LOCAL_ASSERT(expr) {if(epee::debug::get_set_enable_assert()){_ASSERTE(expr);}} +#else +#define LOCAL_ASSERT(expr) +#endif + +#endif + +namespace epee +{ +namespace debug +{ + inline bool get_set_enable_assert(bool set = false, bool v = false) + { + static bool e = true; + if(set) + e = v; + return e; + } +} +namespace log_space +{ + class logger; + class log_message; + class log_singletone; + + /************************************************************************/ + /* */ + /************************************************************************/ + enum console_colors + { + console_color_default, + console_color_white, + console_color_red, + console_color_green, + console_color_blue, + console_color_cyan, + console_color_magenta, + console_color_yellow + }; + + + struct ibase_log_stream + { + ibase_log_stream(){} + virtual ~ibase_log_stream(){} + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL)=0; + virtual int get_type(){return 0;} + + virtual bool set_max_logfile_size(boost::uint64_t max_size){return true;}; + virtual bool set_log_rotate_cmd(const std::string& cmd){return true;}; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + /*struct ibase_log_value + { + public: + virtual void debug_out( std::stringstream* p_stream)const = 0; + };*/ + + /************************************************************************/ + /* */ + /************************************************************************/ + /*class log_message: public std::stringstream + { + public: + log_message(const log_message& lm): std::stringstream(), std::stringstream::basic_ios() + {} + log_message(){} + + template<class T> + log_message& operator<< (T t) + { + std::stringstream* pstrstr = this; + (*pstrstr) << t; + + return *this; + } + }; + inline + log_space::log_message& operator<<(log_space::log_message& sstream, const ibase_log_value& log_val) + { + log_val.debug_out(&sstream); + return sstream; + } + */ + /************************************************************************/ + /* */ + /************************************************************************/ + struct delete_ptr + { + template <class P> + void operator() (P p) + { + delete p.first; + } + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + //------------------------------------------------------------------------ +#define max_dbg_str_len 80 +#ifdef _MSC_VER + class debug_output_stream: public ibase_log_stream + { + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) + { + for ( int i = 0; i < buffer_len; i = i + max_dbg_str_len ) + { + std::string s( buffer + i, buffer_len- i < max_dbg_str_len ? + buffer_len - i : max_dbg_str_len ); + + ::OutputDebugStringA( s.c_str() ); + } + return true; + } + + }; +#endif + + inline void set_console_color(int color, bool bright) + { + switch(color) + { + case console_color_default: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE| (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0m"; +#endif + } + break; + case console_color_white: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;37m"; + else + std::cout << "\033[0;37m"; +#endif + } + break; + case console_color_red: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;31m"; + else + std::cout << "\033[0;31m"; +#endif + } + break; + case console_color_green: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;32m"; + else + std::cout << "\033[0;32m"; +#endif + } + break; + + case console_color_blue: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_INTENSITY);//(bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;34m"; + else + std::cout << "\033[0;34m"; +#endif + } + break; + + case console_color_cyan: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_GREEN | FOREGROUND_BLUE | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;36m"; + else + std::cout << "\033[0;36m"; +#endif + } + break; + + case console_color_magenta: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_BLUE | FOREGROUND_RED | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;35m"; + else + std::cout << "\033[0;35m"; +#endif + } + break; + + case console_color_yellow: + { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | (bright ? FOREGROUND_INTENSITY:0)); +#else + if(bright) + std::cout << "\033[1;33m"; + else + std::cout << "\033[0;33m"; +#endif + } + break; + + } + } + + inline void reset_console_color() { +#ifdef WIN32 + HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(h_stdout, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); +#else + std::cout << "\033[0m"; +#endif + } + + class console_output_stream: public ibase_log_stream + { +#ifdef _MSC_VER + bool m_have_to_kill_console; +#endif + + public: + console_output_stream() + { +#ifdef _MSC_VER + + if(!::GetStdHandle(STD_OUTPUT_HANDLE)) + m_have_to_kill_console = true; + else + m_have_to_kill_console = false; + + ::AllocConsole(); +#endif + } + + ~console_output_stream() + { +#ifdef _MSC_VER + if(m_have_to_kill_console) + ::FreeConsole(); +#endif + } + int get_type(){return LOGGER_CONSOLE;} + + + + virtual bool out_buffer( const char* buffer, int buffer_len , int log_level, int color, const char* plog_name = NULL) + { + if(plog_name) + return true; //skip alternative logs from console + + set_console_color(color, log_level < 1); + +#ifdef _MSC_VER + const char* ptarget_buf = NULL; + char* pallocated_buf = NULL; + + // + int i = 0; + for(; i < buffer_len; i++) + if(buffer[i] == '\a') break; + if(i == buffer_len) + ptarget_buf = buffer; + else + { + pallocated_buf = new char[buffer_len]; + ptarget_buf = pallocated_buf; + for(i = 0; i < buffer_len; i++) + { + if(buffer[i] == '\a') + pallocated_buf[i] = '^'; + else + pallocated_buf[i] = buffer[i]; + } + } + + //boost::uint32_t b = 0; + //::WriteConsoleA(::GetStdHandle(STD_OUTPUT_HANDLE), ptarget_buf, buffer_len, (DWORD*)&b, 0); + std::cout << ptarget_buf; + if(pallocated_buf) delete [] pallocated_buf; +#else + std::string buf(buffer, buffer_len); + for(size_t i = 0; i!= buf.size(); i++) + { + if(buf[i] == 7 || buf[i] == -107) + buf[i] = '^'; + } + + std::cout << buf; +#endif + reset_console_color(); + return true; + } + + + }; + + inline bool rotate_log_file(const char* pfile_path) + { +#ifdef _MSC_VER + if(!pfile_path) + return false; + + std::string file_path = pfile_path; + std::string::size_type a = file_path .rfind('.'); + if ( a != std::string::npos ) + file_path .erase( a, file_path .size()); + + ::DeleteFileA( (file_path + ".0").c_str() ); + ::MoveFileA( (file_path + ".log").c_str(), (file_path + ".0").c_str() ); +#else + return false;//not implemented yet +#endif + return true; + } + + + + + //--------------------------------------------------------------------------// + class file_output_stream : public ibase_log_stream + { + public: + typedef std::map<std::string, std::ofstream*> named_log_streams; + + file_output_stream( std::string default_log_file_name, std::string log_path ) + { + m_default_log_filename = default_log_file_name; + m_max_logfile_size = 0; + m_default_log_path = log_path; + m_pdefault_file_stream = add_new_stream_and_open(default_log_file_name.c_str()); + } + + ~file_output_stream() + { + for(named_log_streams::iterator it = m_log_file_names.begin(); it!=m_log_file_names.end(); it++) + { + if ( it->second->is_open() ) + { + it->second->flush(); + it->second->close(); + } + delete it->second; + } + } + private: + named_log_streams m_log_file_names; + std::string m_default_log_path; + std::ofstream* m_pdefault_file_stream; + std::string m_log_rotate_cmd; + std::string m_default_log_filename; + boost::uint64_t m_max_logfile_size; + + + std::ofstream* add_new_stream_and_open(const char* pstream_name) + { + //log_space::rotate_log_file((m_default_log_path + "\\" + pstream_name).c_str()); + + std::ofstream* pstream = (m_log_file_names[pstream_name] = new std::ofstream); + std::string target_path = m_default_log_path + "/" + pstream_name; + pstream->open( target_path.c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(pstream->fail()) + return NULL; + return pstream; + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + m_max_logfile_size = max_size; + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + m_log_rotate_cmd = cmd; + return true; + } + + + + virtual bool out_buffer( const char* buffer, int buffer_len, int log_level, int color, const char* plog_name = NULL ) + { + std::ofstream* m_target_file_stream = m_pdefault_file_stream; + if(plog_name) + { //find named stream + named_log_streams::iterator it = m_log_file_names.find(plog_name); + if(it == m_log_file_names.end()) + m_target_file_stream = add_new_stream_and_open(plog_name); + else + m_target_file_stream = it->second; + } + if(!m_target_file_stream || !m_target_file_stream->is_open()) + return false;//TODO: add assert here + + m_target_file_stream->write(buffer, buffer_len ); + m_target_file_stream->flush(); + + if(m_max_logfile_size) + { + std::ofstream::pos_type pt = m_target_file_stream->tellp(); + boost::uint64_t current_sz = pt; + if(current_sz > m_max_logfile_size) + { + std::cout << "current_sz= " << current_sz << " m_max_logfile_size= " << m_max_logfile_size << std::endl; + std::string log_file_name; + if(!plog_name) + log_file_name = m_default_log_filename; + else + log_file_name = plog_name; + + m_target_file_stream->close(); + std::string new_log_file_name = log_file_name; + + time_t tm = 0; + time(&tm); + + int err_count = 0; + boost::system::error_code ec; + do + { + new_log_file_name = string_tools::cut_off_extension(log_file_name); + if(err_count) + new_log_file_name += misc_utils::get_time_str_v2(tm) + "(" + boost::lexical_cast<std::string>(err_count) + ")" + ".log"; + else + new_log_file_name += misc_utils::get_time_str_v2(tm) + ".log"; + + err_count++; + }while(boost::filesystem::exists(m_default_log_path + "/" + new_log_file_name, ec)); + + std::string new_log_file_path = m_default_log_path + "/" + new_log_file_name; + boost::filesystem::rename(m_default_log_path + "/" + log_file_name, new_log_file_path, ec); + if(ec) + { + std::cout << "Filed to rename, ec = " << ec.message() << std::endl; + } + + if(m_log_rotate_cmd.size()) + { + + std::string m_log_rotate_cmd_local_copy = m_log_rotate_cmd; + //boost::replace_all(m_log_rotate_cmd, "[*SOURCE*]", new_log_file_path); + boost::replace_all(m_log_rotate_cmd_local_copy, "[*TARGET*]", new_log_file_path); + + misc_utils::call_sys_cmd(m_log_rotate_cmd_local_copy); + } + + m_target_file_stream->open( (m_default_log_path + "/" + log_file_name).c_str(), std::ios_base::out | std::ios::app /*ios_base::trunc */); + if(m_target_file_stream->fail()) + return false; + } + } + + return true; + } + int get_type(){return LOGGER_FILE;} + }; + /************************************************************************/ + /* */ + /************************************************************************/ + class log_stream_splitter + { + public: + typedef std::list<std::pair<ibase_log_stream*, int> > streams_container; + + log_stream_splitter(){} + ~log_stream_splitter() + { + //free pointers + std::for_each(m_log_streams.begin(), m_log_streams.end(), delete_ptr()); + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_max_logfile_size(max_size); + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + it->first->set_log_rotate_cmd(cmd); + return true; + } + + bool do_log_message(const std::string& rlog_mes, int log_level, int color, const char* plog_name = NULL) + { + std::string str_mess = rlog_mes; + size_t str_len = str_mess.size(); + const char* pstr = str_mess.c_str(); + for(streams_container::iterator it = m_log_streams.begin(); it!=m_log_streams.end();it++) + if(it->second >= log_level) + it->first->out_buffer(pstr, (int)str_len, log_level, color, plog_name); + return true; + } + + bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4 ) + { + ibase_log_stream* ls = NULL; + + switch( type ) + { + case LOGGER_FILE: + ls = new file_output_stream( pdefault_file_name, pdefault_log_folder ); + break; + + case LOGGER_DEBUGGER: +#ifdef _MSC_VER + ls = new debug_output_stream( ); +#else + return false;//not implemented yet +#endif + break; + case LOGGER_CONSOLE: + ls = new console_output_stream( ); + break; + } + + if ( ls ) { + m_log_streams.push_back(streams_container::value_type(ls, log_level_limit)); + return true; + } + return ls ? true:false; + } + bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) + { + m_log_streams.push_back(streams_container::value_type(pstream, log_level_limit) ); + return true; + } + + bool remove_logger(int type) + { + streams_container::iterator it = m_log_streams.begin(); + for(;it!=m_log_streams.end(); it++) + { + if(it->first->get_type() == type) + { + delete it->first; + m_log_streams.erase(it); + return true; + } + } + return false; + + } + + protected: + private: + + streams_container m_log_streams; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + inline int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1); + inline int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0); + inline bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false); + inline bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false); + + + inline std::string get_daytime_string2() + { + boost::posix_time::ptime p = boost::posix_time::microsec_clock::local_time(); + return misc_utils::get_time_str_v3(p); + } + inline std::string get_day_time_string() + { + return get_daytime_string2(); + //time_t tm = 0; + //time(&tm); + //return misc_utils::get_time_str(tm); + } + + inline std::string get_time_string() + { + return get_daytime_string2(); + + } +#ifdef _MSC_VER + inline std::string get_time_string_adv(SYSTEMTIME* pst = NULL) + { + SYSTEMTIME st = {0}; + if(!pst) + { + pst = &st; + GetSystemTime(&st); + } + std::stringstream str_str; + str_str.fill('0'); + str_str << std::setw(2) << pst->wHour << "_" + << std::setw(2) << pst->wMinute << "_" + << std::setw(2) << pst->wSecond << "_" + << std::setw(3) << pst->wMilliseconds; + return str_str.str(); + } +#endif + + + + + + class logger + { + public: + friend class log_singletone; + + logger() + { + CRITICAL_REGION_BEGIN(m_critical_sec); + init(); + CRITICAL_REGION_END(); + } + ~logger() + { + } + + bool set_max_logfile_size(boost::uint64_t max_size) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_max_logfile_size(max_size); + CRITICAL_REGION_END(); + return true; + } + + bool set_log_rotate_cmd(const std::string& cmd) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.set_log_rotate_cmd(cmd); + CRITICAL_REGION_END(); + return true; + } + + bool take_away_journal(std::list<std::string>& journal) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_journal.swap(journal); + CRITICAL_REGION_END(); + return true; + } + + bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool add_to_journal = false, const char* plog_name = NULL) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_log_target.do_log_message(rlog_mes, log_level, color, plog_name); + if(add_to_journal) + m_journal.push_back(rlog_mes); + + return true; + CRITICAL_REGION_END(); + } + + bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder , int log_level_limit = LOG_LEVEL_4) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger( type, pdefault_file_name, pdefault_log_folder, log_level_limit); + CRITICAL_REGION_END(); + } + bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.add_logger(pstream, log_level_limit); + CRITICAL_REGION_END(); + } + + bool remove_logger(int type) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + return m_log_target.remove_logger(type); + CRITICAL_REGION_END(); + } + + + bool set_thread_prefix(const std::string& prefix) + { + CRITICAL_REGION_BEGIN(m_critical_sec); + m_thr_prefix_strings[misc_utils::get_thread_string_id()] = prefix; + CRITICAL_REGION_END(); + return true; + } + + + std::string get_default_log_file() + { + return m_default_log_file; + } + + std::string get_default_log_folder() + { + return m_default_log_folder; + } + + protected: + private: + bool init() + { + // + + m_process_name = string_tools::get_current_module_name(); + + init_log_path_by_default(); + + //init default set of loggers + init_default_loggers(); + + std::stringstream ss; + ss << get_time_string() << " Init logging. Level=" << get_set_log_detalisation_level() + << " Log path=" << m_default_log_folder << std::endl; + this->do_log_message(ss.str(), console_color_white, LOG_LEVEL_0); + return true; + } + bool init_default_loggers() + { + //TODO: + return true; + } + + bool init_log_path_by_default() + { + //load process name + m_default_log_folder = string_tools::get_current_module_folder(); + + m_default_log_file = m_process_name; + std::string::size_type a = m_default_log_file.rfind('.'); + if ( a != std::string::npos ) + m_default_log_file.erase( a, m_default_log_file.size()); + m_default_log_file += ".log"; + + return true; + } + + log_stream_splitter m_log_target; + + std::string m_default_log_folder; + std::string m_default_log_file; + std::string m_process_name; + std::map<std::string, std::string> m_thr_prefix_strings; + std::list<std::string> m_journal; + critical_section m_critical_sec; + }; + /************************************************************************/ + /* */ + /************************************************************************/ + class log_singletone + { + public: + friend class initializer<log_singletone>; + friend class logger; + static int get_log_detalisation_level() + { + get_or_create_instance();//to initialize logger, if it not initialized + return get_set_log_detalisation_level(); + } + + static bool is_filter_error(int error_code) + { + return false; + } + + + static bool do_log_message(const std::string& rlog_mes, int log_level, int color, bool keep_in_journal, const char* plog_name = NULL) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + else + { //globally uninitialized, create new logger for each call of do_log_message() and then delete it + plogger = new logger(); + //TODO: some extra initialization + res = plogger->do_log_message(rlog_mes, log_level, color, keep_in_journal, plog_name); + delete plogger; + plogger = NULL; + } + return res; + } + + static bool take_away_journal(std::list<std::string>& journal) + { + logger* plogger = get_or_create_instance(); + bool res = false; + if(plogger) + res = plogger->take_away_journal(journal); + + return res; + } + + static bool set_max_logfile_size(boost::uint64_t file_size) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_max_logfile_size(file_size); + } + + + static bool set_log_rotate_cmd(const std::string& cmd) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_log_rotate_cmd(cmd); + } + + + static bool add_logger( int type, const char* pdefault_file_name, const char* pdefault_log_folder, int log_level_limit = LOG_LEVEL_4) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(type, pdefault_file_name, pdefault_log_folder, log_level_limit); + } + + static std::string get_default_log_file() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_file(); + + return ""; + } + + static std::string get_default_log_folder() + { + logger* plogger = get_or_create_instance(); + if(plogger) + return plogger->get_default_log_folder(); + + return ""; + } + + static bool add_logger( ibase_log_stream* pstream, int log_level_limit = LOG_LEVEL_4 ) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->add_logger(pstream, log_level_limit); + } + + + static bool remove_logger( int type ) + { + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->remove_logger(type); + } +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + static int get_set_log_detalisation_level(bool is_need_set = false, int log_level_to_set = LOG_LEVEL_1) + { + static int log_detalisation_level = LOG_LEVEL_1; + if(is_need_set) + log_detalisation_level = log_level_to_set; + return log_detalisation_level; + } +POP_WARNINGS + static int get_set_time_level(bool is_need_set = false, int time_log_level = LOG_LEVEL_0) + { + static int val_time_log_level = LOG_LEVEL_0; + if(is_need_set) + val_time_log_level = time_log_level; + + return val_time_log_level; + } + + static int get_set_process_level(bool is_need_set = false, int process_log_level = LOG_LEVEL_0) + { + static int val_process_log_level = LOG_LEVEL_0; + if(is_need_set) + val_process_log_level = process_log_level; + + return val_process_log_level; + } + + /*static int get_set_tid_level(bool is_need_set = false, int tid_log_level = LOG_LEVEL_0) + { + static int val_tid_log_level = LOG_LEVEL_0; + if(is_need_set) + val_tid_log_level = tid_log_level; + + return val_tid_log_level; + }*/ + + static bool get_set_need_thread_id(bool is_need_set = false, bool is_need_val = false) + { + static bool is_need = false; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + + static bool get_set_need_proc_name(bool is_need_set = false, bool is_need_val = false) + { + static bool is_need = true; + if(is_need_set) + is_need = is_need_val; + + return is_need; + } + static boost::uint64_t get_set_err_count(bool is_need_set = false, boost::uint64_t err_val = false) + { + static boost::uint64_t err_count = 0; + if(is_need_set) + err_count = err_val; + + return err_count; + } + + +#ifdef _MSC_VER + + + static void SetThreadName( DWORD dwThreadID, const char* threadName) + { +#define MS_VC_EXCEPTION 0x406D1388 + +#pragma pack(push,8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; +#pragma pack(pop) + + + + Sleep(10); + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = (char*)threadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } + } +#endif + + static bool set_thread_log_prefix(const std::string& prefix) + { +#ifdef _MSC_VER + SetThreadName(-1, prefix.c_str()); +#endif + + + logger* plogger = get_or_create_instance(); + if(!plogger) return false; + return plogger->set_thread_prefix(prefix); + } + + + static std::string get_prefix_entry() + { + std::stringstream str_prefix; + //write time entry + if ( get_set_time_level() <= get_set_log_detalisation_level() ) + str_prefix << get_day_time_string() << " "; + + //write process info + logger* plogger = get_or_create_instance(); + //bool res = false; + if(!plogger) + { //globally uninitialized, create new logger for each call of get_prefix_entry() and then delete it + plogger = new logger(); + } + + //if ( get_set_need_proc_name() && get_set_process_level() <= get_set_log_detalisation_level() ) + // str_prefix << "[" << plogger->m_process_name << " (id=" << GetCurrentProcessId() << ")] "; +//#ifdef _MSC_VER_EX + if ( get_set_need_thread_id() /*&& get_set_tid_level() <= get_set_log_detalisation_level()*/ ) + str_prefix << "tid:" << misc_utils::get_thread_string_id() << " "; +//#endif + + if(plogger->m_thr_prefix_strings.size()) + { + CRITICAL_REGION_LOCAL(plogger->m_critical_sec); + std::string thr_str = misc_utils::get_thread_string_id(); + std::map<std::string, std::string>::iterator it = plogger->m_thr_prefix_strings.find(thr_str); + if(it!=plogger->m_thr_prefix_strings.end()) + { + str_prefix << it->second; + } + } + + + if(get_set_is_uninitialized()) + delete plogger; + + return str_prefix.str(); + } + + private: + log_singletone(){}//restric to create an instance + //static initializer<log_singletone> m_log_initializer;//must be in one .cpp file (for example main.cpp) via DEFINE_LOGGING macro + + static bool init() + { + return true;/*do nothing here*/ + } + static bool un_init() + { + //delete object + logger* plogger = get_set_instance_internal(); + if(plogger) delete plogger; + //set uninitialized + get_set_is_uninitialized(true, true); + get_set_instance_internal(true, NULL); + return true; + } + + static logger* get_or_create_instance() + { + logger* plogger = get_set_instance_internal(); + if(!plogger) + if(!get_set_is_uninitialized()) + get_set_instance_internal(true, plogger = new logger); + + return plogger; + } + + static logger* get_set_instance_internal(bool is_need_set = false, logger* pnew_logger_val = NULL) + { + static logger* val_plogger = NULL; + + if(is_need_set) + val_plogger = pnew_logger_val; + + return val_plogger; + } + + static bool get_set_is_uninitialized(bool is_need_set = false, bool is_uninitialized = false) + { + static bool val_is_uninitialized = false; + + if(is_need_set) + val_is_uninitialized = is_uninitialized; + + return val_is_uninitialized; + } + //static int get_set_error_filter(bool is_need_set = false) + }; + + const static initializer<log_singletone> log_initializer; + /************************************************************************/ + /* */ +// /************************************************************************/ +// class log_array_value +// { +// int num; +// log_message& m_ref_log_mes; +// +// public: +// +// log_array_value( log_message& log_mes ) : num(0), m_ref_log_mes(log_mes) {} +// +// void operator ( )( ibase_log_value *val ) { +// m_ref_log_mes << "\n[" << num++ << "] "/* << val*/; } +// +// +// template<class T> +// void operator ()(T &value ) +// { +// m_ref_log_mes << "\n[" << num++ << "] " << value; +// } +// }; + + class log_frame + { + std::string m_name; + int m_level; + const char* m_plog_name; + public: + + log_frame(const std::string& name, int dlevel = LOG_LEVEL_2 , const char* plog_name = NULL) + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + m_plog_name = plog_name; + if ( dlevel <= log_singletone::get_log_detalisation_level() ) + { + m_name = name; + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "-->>" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), dlevel, console_color_default, false, m_plog_name); + } + m_level = dlevel; +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + ~log_frame() + { +#ifdef _MSC_VER + int lasterr=::GetLastError(); +#endif + + if (m_level <= log_singletone::get_log_detalisation_level() ) + { + std::stringstream ss; + ss << log_space::log_singletone::get_prefix_entry() << "<<--" << m_name << std::endl; + log_singletone::do_log_message(ss.str(), m_level, console_color_default, false,m_plog_name); + } +#ifdef _MSC_VER + ::SetLastError(lasterr); +#endif + } + }; + + inline int get_set_time_level(bool is_need_set, int time_log_level) + { + return log_singletone::get_set_time_level(is_need_set, time_log_level); + } + inline int get_set_log_detalisation_level(bool is_need_set, int log_level_to_set) + { + return log_singletone::get_set_log_detalisation_level(is_need_set, log_level_to_set); + } + inline std::string get_prefix_entry() + { + return log_singletone::get_prefix_entry(); + } + inline bool get_set_need_thread_id(bool is_need_set, bool is_need_val) + { + return log_singletone::get_set_need_thread_id(is_need_set, is_need_val); + } + inline bool get_set_need_proc_name(bool is_need_set, bool is_need_val ) + { + return log_singletone::get_set_need_proc_name(is_need_set, is_need_val); + } + + inline std::string get_win32_err_descr(int err_no) + { +#ifdef _MSC_VER + LPVOID lpMsgBuf; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + err_no, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*) &lpMsgBuf, + 0, NULL ); + + std::string fix_sys_message = "(null)"; + if(lpMsgBuf) fix_sys_message = (char*)lpMsgBuf; + std::string::size_type a; + if ( (a = fix_sys_message.rfind( '\n' )) != std::string::npos ) + fix_sys_message.erase(a); + if ( (a = fix_sys_message.rfind( '\r' )) != std::string::npos ) + fix_sys_message.erase(a); + + LocalFree(lpMsgBuf); + return fix_sys_message; +#else + return "Not implemented yet"; +#endif + } + + inline bool getwin32_err_text(std::stringstream& ref_message, int error_no) + { + ref_message << "win32 error:" << get_win32_err_descr(error_no); + return true; + } +} +#if defined(_DEBUG) || defined(__GNUC__) + #define ENABLE_LOGGING_INTERNAL +#endif + +#if defined(ENABLE_RELEASE_LOGGING) + #define ENABLE_LOGGING_INTERNAL +#endif + + +#if defined(ENABLE_LOGGING_INTERNAL) + +#define LOG_PRINT_NO_PREFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str() , y, epee::log_space::console_color_default, false, log_name);}} + +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + + +#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x; epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + + +#define LOG_PRINT2(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, false, log_name);}} + +#define LOG_PRINT_COLOR2(log_name, x, y, color) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, color, false, log_name);}} + + +#define LOG_PRINT2_JORNAL(log_name, x, y) {if ( y <= epee::log_space::log_singletone::get_log_detalisation_level() )\ + {std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << x << std::endl;epee::log_space::log_singletone::do_log_message(ss________.str(), y, epee::log_space::console_color_default, true, log_name);}} + + +#define LOG_ERROR2(log_name, x) { \ + std::stringstream ss________; ss________ << epee::log_space::log_singletone::get_prefix_entry() << "ERROR " << __FILE__ << ":" << __LINE__ << " " << x << std::endl; epee::log_space::log_singletone::do_log_message(ss________.str(), LOG_LEVEL_0, epee::log_space::console_color_red, true, log_name);LOCAL_ASSERT(0); epee::log_space::log_singletone::get_set_err_count(true, epee::log_space::log_singletone::get_set_err_count()+1);} + +#define LOG_FRAME2(log_name, x, y) epee::log_space::log_frame frame(x, y, log_name) + +#else + + +#define LOG_PRINT_NO_PREFIX2(log_name, x, y) + +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX2(log_name, x, y) + +#define LOG_PRINT_NO_POSTFIX2(log_name, x, y) + +#define LOG_PRINT_COLOR2(log_name, x, y, color) + +#define LOG_PRINT2_JORNAL(log_name, x, y) + +#define LOG_PRINT2(log_name, x, y) + +#define LOG_ERROR2(log_name, x) + + +#define LOG_FRAME2(log_name, x, y) + + +#endif + + +#ifndef LOG_DEFAULT_TARGET + #define LOG_DEFAULT_TARGET NULL +#endif + + +#define LOG_PRINT_NO_POSTFIX(mess, level) LOG_PRINT_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT_NO_PREFIX(mess, level) LOG_PRINT_NO_PREFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT_NO_PREFIX_NO_POSTFIX(mess, level) LOG_PRINT_NO_PREFIX_NO_POSTFIX2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_PRINT(mess, level) LOG_PRINT2(LOG_DEFAULT_TARGET, mess, level) + +#define LOG_PRINT_COLOR(mess, level, color) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, color) +#define LOG_PRINT_RED(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_red) +#define LOG_PRINT_GREEN(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_green) +#define LOG_PRINT_BLUE(mess, level) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, level, epee::log_space::console_color_blue) + +#define LOG_PRINT_RED_L0(mess) LOG_PRINT_COLOR2(LOG_DEFAULT_TARGET, mess, LOG_LEVEL_0, epee::log_space::console_color_red) + +#define LOG_PRINT_L0(mess) LOG_PRINT(mess, LOG_LEVEL_0) +#define LOG_PRINT_L1(mess) LOG_PRINT(mess, LOG_LEVEL_1) +#define LOG_PRINT_L2(mess) LOG_PRINT(mess, LOG_LEVEL_2) +#define LOG_PRINT_L3(mess) LOG_PRINT(mess, LOG_LEVEL_3) +#define LOG_PRINT_L4(mess) LOG_PRINT(mess, LOG_LEVEL_4) +#define LOG_PRINT_J(mess, level) LOG_PRINT2_JORNAL(LOG_DEFAULT_TARGET, mess, level) + +#define LOG_ERROR(mess) LOG_ERROR2(LOG_DEFAULT_TARGET, mess) +#define LOG_FRAME(mess, level) LOG_FRAME2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_VALUE(mess, level) LOG_VALUE2(LOG_DEFAULT_TARGET, mess, level) +#define LOG_ARRAY(mess, level) LOG_ARRAY2(LOG_DEFAULT_TARGET, mess, level) +//#define LOGWIN_PLATFORM_ERROR(err_no) LOGWINDWOS_PLATFORM_ERROR2(LOG_DEFAULT_TARGET, err_no) +#define LOG_SOCKET_ERROR(err_no) LOG_SOCKET_ERROR2(LOG_DEFAULT_TARGET, err_no) +//#define LOGWIN_PLATFORM_ERROR_UNCRITICAL(mess) LOGWINDWOS_PLATFORM_ERROR_UNCRITICAL2(LOG_DEFAULT_TARGET, mess) + +#define ENDL std::endl + +#define TRY_ENTRY() try { +#define CATCH_ENTRY(location, return_val) } \ + catch(const std::exception& ex) \ +{ \ + (void)(ex); \ + LOG_ERROR("Exception at [" << location << "], what=" << ex.what()); \ + return return_val; \ +}\ + catch(...)\ +{\ + LOG_ERROR("Exception at [" << location << "], generic exception \"...\"");\ + return return_val; \ +} + +#define CATCH_ENTRY_L0(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L1(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L2(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L3(lacation, return_val) CATCH_ENTRY(lacation, return_val) +#define CATCH_ENTRY_L4(lacation, return_val) CATCH_ENTRY(lacation, return_val) + + +#define ASSERT_MES_AND_THROW(message) {LOG_ERROR(message); std::stringstream ss; ss << message; throw std::runtime_error(ss.str());} +#define CHECK_AND_ASSERT_THROW_MES(expr, message) {if(!(expr)) ASSERT_MES_AND_THROW(message);} + + +#ifndef CHECK_AND_ASSERT +#define CHECK_AND_ASSERT(expr, fail_ret_val) do{if(!(expr)){LOCAL_ASSERT(expr); return fail_ret_val;};}while(0) +#endif + +#define NOTHING + +#ifndef CHECK_AND_ASSERT_MES +#define CHECK_AND_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_ERROR(message); return fail_ret_val;};}while(0) +#endif + +#ifndef CHECK_AND_NO_ASSERT_MES +#define CHECK_AND_NO_ASSERT_MES(expr, fail_ret_val, message) do{if(!(expr)) {LOG_PRINT_L0(message); /*LOCAL_ASSERT(expr);*/ return fail_ret_val;};}while(0) +#endif + + +#ifndef CHECK_AND_ASSERT_MES_NO_RET +#define CHECK_AND_ASSERT_MES_NO_RET(expr, message) do{if(!(expr)) {LOG_ERROR(message); return;};}while(0) +#endif + + +#ifndef CHECK_AND_ASSERT_MES2 +#define CHECK_AND_ASSERT_MES2(expr, message) do{if(!(expr)) {LOG_ERROR(message); };}while(0) +#endif + +} +#endif //_MISC_LOG_EX_H_ diff --git a/contrib/epee/include/misc_os_dependent.h b/contrib/epee/include/misc_os_dependent.h new file mode 100644 index 000000000..0850c7c07 --- /dev/null +++ b/contrib/epee/include/misc_os_dependent.h @@ -0,0 +1,108 @@ +// 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. +// +#ifdef WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + //#ifdef _WIN32_WINNT + // #undef _WIN32_WINNT + // #define _WIN32_WINNT 0x0600 + //#endif + + +#include <windows.h> +#endif + +#ifdef __MACH__ +#include <mach/clock.h> +#include <mach/mach.h> +#endif + +#pragma once +namespace epee +{ +namespace misc_utils +{ + + inline boost::uint64_t get_tick_count() + { +#if defined(_MSC_VER) + return ::GetTickCount64(); +#elif defined(__MACH__) + clock_serv_t cclock; + mach_timespec_t mts; + + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + + return (mts.tv_sec * 1000) + (mts.tv_nsec/1000000); +#else + struct timespec ts; + if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + return (ts.tv_sec * 1000) + (ts.tv_nsec/1000000); +#endif + } + + + inline int call_sys_cmd(const std::string& cmd) + { + std::cout << "# " << cmd << std::endl; + + FILE * fp ; + //char tstCommand[] ="ls *"; + char path[1000] = {0}; +#if !defined(__GNUC__) + fp = _popen(cmd.c_str(), "r"); +#else + fp = popen(cmd.c_str(), "r"); +#endif + while ( fgets( path, 1000, fp ) != NULL ) + std::cout << path; + +#if !defined(__GNUC__) + _pclose(fp); +#else + pclose(fp); +#endif + return 0; + + } + + + inline std::string get_thread_string_id() + { +#if defined(_MSC_VER) + return boost::lexical_cast<std::string>(GetCurrentThreadId()); +#elif defined(__GNUC__) + return boost::lexical_cast<std::string>(pthread_self()); +#endif + } +} +} 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 diff --git a/contrib/epee/include/pragma_comp_defs.h b/contrib/epee/include/pragma_comp_defs.h new file mode 100644 index 000000000..f4ef7057e --- /dev/null +++ b/contrib/epee/include/pragma_comp_defs.h @@ -0,0 +1,14 @@ +#pragma once + +#if defined(__GNUC__) + #define PRAGMA_WARNING_PUSH _Pragma("GCC diagnostic push") + #define PRAGMA_WARNING_POP _Pragma("GCC diagnostic pop") + #define PRAGMA_WARNING_DISABLE_VS(w) + #define PRAGMA_GCC(w) _Pragma(w) +#elif defined(_MSC_VER) + #define PRAGMA_WARNING_PUSH __pragma(warning( push )) + #define PRAGMA_WARNING_POP __pragma(warning( pop )) + #define PRAGMA_WARNING_DISABLE_VS(w) __pragma( warning ( disable: w )) + //#define PRAGMA_WARNING_DISABLE_GCC(w) + #define PRAGMA_GCC(w) +#endif diff --git a/contrib/epee/include/profile_tools.h b/contrib/epee/include/profile_tools.h new file mode 100644 index 000000000..ff925ea86 --- /dev/null +++ b/contrib/epee/include/profile_tools.h @@ -0,0 +1,111 @@ +// 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 _PROFILE_TOOLS_H_ +#define _PROFILE_TOOLS_H_ + +namespace epee +{ + +#ifdef ENABLE_PROFILING +#define PROFILE_FUNC(immortal_ptr_str) static profile_tools::local_call_account lcl_acc(immortal_ptr_str); \ + profile_tools::call_frame cf(lcl_acc); + +#define PROFILE_FUNC_SECOND(immortal_ptr_str) static profile_tools::local_call_account lcl_acc2(immortal_ptr_str); \ + profile_tools::call_frame cf2(lcl_acc2); + +#define PROFILE_FUNC_THIRD(immortal_ptr_str) static profile_tools::local_call_account lcl_acc3(immortal_ptr_str); \ + profile_tools::call_frame cf3(lcl_acc3); + +#define PROFILE_FUNC_ACC(acc) \ + profile_tools::call_frame cf(acc); + + +#else +#define PROFILE_FUNC(immortal_ptr_str) +#define PROFILE_FUNC_SECOND(immortal_ptr_str) +#define PROFILE_FUNC_THIRD(immortal_ptr_str) +#endif + +#define START_WAY_POINTS() boost::uint64_t _____way_point_time = misc_utils::get_tick_count(); +#define WAY_POINT(name) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} +#define WAY_POINT2(name, avrg_obj) {boost::uint64_t delta = misc_utils::get_tick_count()-_____way_point_time; avrg_obj.push(delta); LOG_PRINT("Way point " << name << ": " << delta, LOG_LEVEL_2);_____way_point_time = misc_utils::get_tick_count();} + + +#define TIME_MEASURE_START(var_name) boost::uint64_t var_name = misc_utils::get_tick_count(); +#define TIME_MEASURE_FINISH(var_name) var_name = misc_utils::get_tick_count() - var_name; + +namespace profile_tools +{ + struct local_call_account + { + local_call_account(const char* pstr):m_count_of_call(0), m_summary_time_used(0),m_pname(pstr) + {} + ~local_call_account() + { + LOG_PRINT2("profile_details.log", "PROFILE "<<m_pname<<":av_time:\t" << (m_count_of_call ? (m_summary_time_used/m_count_of_call):0) <<" sum_time:\t"<<m_summary_time_used<<" call_count:\t" << m_count_of_call, LOG_LEVEL_0); + } + + size_t m_count_of_call; + boost::uint64_t m_summary_time_used; + const char* m_pname; + }; + + struct call_frame + { + + call_frame(local_call_account& cc):m_cc(cc) + { + cc.m_count_of_call++; + m_call_time = boost::posix_time::microsec_clock::local_time(); + //::QueryPerformanceCounter((LARGE_INTEGER *)&m_call_time); + } + + ~call_frame() + { + //__int64 ret_time = 0; + + boost::posix_time::ptime now_t(boost::posix_time::microsec_clock::local_time()); + boost::posix_time::time_duration delta_microsec = now_t - m_call_time; + boost::uint64_t miliseconds_used = delta_microsec.total_microseconds(); + + //::QueryPerformanceCounter((LARGE_INTEGER *)&ret_time); + //m_call_time = (ret_time-m_call_time)/1000; + m_cc.m_summary_time_used += miliseconds_used; + } + + private: + local_call_account& m_cc; + boost::posix_time::ptime m_call_time; + }; + + +} +} + + +#endif //_PROFILE_TOOLS_H_ diff --git a/contrib/epee/include/reg_exp_definer.h b/contrib/epee/include/reg_exp_definer.h new file mode 100644 index 000000000..b05e1a9ae --- /dev/null +++ b/contrib/epee/include/reg_exp_definer.h @@ -0,0 +1,84 @@ +// 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 _REG_EXP_DEFINER_H_ +#define _REG_EXP_DEFINER_H_ + +#include <boost/interprocess/detail/atomic.hpp> + + +namespace epee +{ + class global_regexp_critical_section + { + private: + mutable critical_section regexp_lock; + public: + global_regexp_critical_section(){} + critical_section& get_lock()const {return regexp_lock;} + }; + + const static global_regexp_critical_section gregexplock; + +#define STATIC_REGEXP_EXPR_1(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_1 = 0;\ + volatile boost::uint32_t local_is_initialized_1 = regexp_initialized_1;\ + if(!local_is_initialized_1)\ + gregexplock.get_lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_1)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_1, 1);\ + gregexplock.get_lock().unlock();\ +} + +#define STATIC_REGEXP_EXPR_2(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_2 = 0;\ + volatile boost::uint32_t local_is_initialized_2 = regexp_initialized_2;\ + if(!local_is_initialized_2)\ + gregexplock.get_lock().lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_2)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_2, 1);\ + gregexplock.get_lock().lock().unlock();\ +} + +#define STATIC_REGEXP_EXPR_3(var_name, xpr_text, reg_exp_flags) \ + static volatile boost::uint32_t regexp_initialized_3 = 0;\ + volatile boost::uint32_t local_is_initialized_3 = regexp_initialized_3;\ + if(!local_is_initialized_3)\ + gregexplock.get_lock().lock().lock();\ + static const boost::regex var_name(xpr_text , reg_exp_flags);\ + if(!local_is_initialized_3)\ +{\ + boost::interprocess::ipcdetail::atomic_write32(®exp_initialized_3, 1);\ + gregexplock.get_lock().lock().unlock();\ +} +} + +#endif //_REG_EXP_DEFINER_H_ diff --git a/contrib/epee/include/reg_utils.h b/contrib/epee/include/reg_utils.h new file mode 100644 index 000000000..22227a9b2 --- /dev/null +++ b/contrib/epee/include/reg_utils.h @@ -0,0 +1,249 @@ +// 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 _MUSC_UTILS_EX_H_ +#define _MUSC_UTILS_EX_H_ + +namespace epee +{ +namespace reg_utils +{ + //----------------------------------------------------------------------------------------------------------------------------------- + template<class T> + bool RegSetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const T& valToSave, bool force_create = true) + { + HKEY hRegKey = 0; + DWORD dw = 0; + + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + if(force_create && (::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS) ) + return false; + + + DWORD val_type = (sizeof(valToSave) == sizeof(DWORD)) ? REG_DWORD:REG_BINARY; + + BOOL res = ::RegSetValueExA( hRegKey, pValName, 0, val_type, (LPBYTE)&valToSave, sizeof(valToSave)) == ERROR_SUCCESS; + + ::RegCloseKey(hRegKey); + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template<class T> + bool RegGetPODValue(HKEY hParentKey, const char* pSubKey, const char* pValName, T& valToSave) + { + HKEY hRegKey = 0; + LONG res = 0; + + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + DWORD dwType, lSize = 0; + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || (sizeof(valToSave) < lSize) ) + { + ::RegCloseKey(hRegKey); + return false; + } + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)&valToSave, &lSize); + } + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegSetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& strToSave) + { + HKEY hRegKey = 0; + DWORD dw = 0; + DWORD res_ = 0; + if( (res_ = ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw)) != ERROR_SUCCESS ) + if( (res_= ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey)) != ERROR_SUCCESS ) + return false; + + DWORD valType = REG_SZ; + const char* pStr = strToSave.c_str(); + DWORD sizeOfStr = (DWORD)strToSave.size()+1; + LSTATUS res = ::RegSetValueExA(hRegKey, pValName, 0, valType, (LPBYTE)pStr, sizeOfStr); + + ::RegCloseKey(hRegKey); + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegGetANSIString(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& strToSave) + { + HKEY hRegKey = 0; + LONG res = 0; + + + if((res = ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey)) == ERROR_SUCCESS ) + { + DWORD dwType, lSize = 0; + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res) + { + + ::RegCloseKey(hRegKey); + return false; + } + char* pTmpStr = new char[lSize+2]; + memset(pTmpStr, 0, lSize+2); + res = ::RegQueryValueExA(hRegKey, pValName, 0, &dwType, (LPBYTE)pTmpStr, &lSize); + pTmpStr[lSize+1] = 0; //be happy ;) + strToSave = pTmpStr; + delete [] pTmpStr; + ::RegCloseKey(hRegKey); + } + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template<class TMemoryObject> + bool RegSetRAWValue(HKEY hKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) + { + LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.get(0), (DWORD)valToSave.get_size()); + + return ERROR_SUCCESS==res ? true:false; + } + //---------------------------------------------------------------------------------------------------------------------------------- + bool RegSetRAWValue(HKEY hKey, const char* pValName, const std::string & valToSave, DWORD valType = REG_BINARY) + { + LONG res = ::RegSetValueExA( hKey, pValName, 0, valType, (CONST BYTE*)valToSave.data(), (DWORD)valToSave.size()); + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template<class TMemoryObject> + bool RegGetRAWValue(HKEY hKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) + { + DWORD dwType, lSize = 0; + LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || 0 >= lSize) + { + valToSave.release(); + return false; + } + if(valToSave.get_size() < lSize) + valToSave.alloc_buff(lSize); + res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.get(0), &lSize); + if(pRegType) *pRegType = dwType; + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + bool RegGetRAWValue(HKEY hKey, const char* pValName, std::string& valToSave, DWORD* pRegType) + { + DWORD dwType, lSize = 0; + LONG res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, NULL, &lSize); + if(ERROR_SUCCESS!=res || 0 >= lSize) + { + return false; + } + + valToSave.resize(lSize); + res = ::RegQueryValueExA(hKey, pValName, 0, &dwType, (LPBYTE)valToSave.data(), &lSize); + if(pRegType) *pRegType = dwType; + + return ERROR_SUCCESS==res ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template<class TMemoryObject> + bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const TMemoryObject& valToSave, DWORD valType = REG_BINARY) + { + HKEY hRegKey = 0; + DWORD dw = 0; + bool res = false; + + if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + return false; + + res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); + + ::RegCloseKey(hRegKey); + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + bool RegSetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, const std::string& valToSave, DWORD valType = REG_BINARY) + { + HKEY hRegKey = 0; + DWORD dw = 0; + bool res = false; + + if( ::RegCreateKeyExA(hParentKey, pSubKey, 0, "", REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hRegKey, &dw) != ERROR_SUCCESS ) + if( ::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_WRITE, &hRegKey) != ERROR_SUCCESS ) + return false; + + res = RegSetRAWValue(hRegKey, pValName, valToSave, valType); + + ::RegCloseKey(hRegKey); + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + template<class TMemoryObject> + bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, TMemoryObject& valToSave, DWORD* pRegType) + { + HKEY hRegKey = 0; + bool res = false; + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); + ::RegCloseKey(hRegKey); + } + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegGetRAWValue(HKEY hParentKey, const char* pSubKey, const char* pValName, std::string& valToSave, DWORD* pRegType) + { + HKEY hRegKey = 0; + bool res = false; + + if(::RegOpenKeyExA(hParentKey, pSubKey, 0, KEY_READ, &hRegKey) == ERROR_SUCCESS ) + { + res = RegGetRAWValue(hRegKey, pValName, valToSave, pRegType); + ::RegCloseKey(hRegKey); + } + return res; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegRemoveValue(HKEY hParentKey, const char* pValName) + { + //CHECK_AND_ASSERT(hParentKey&&pValName, false); + return ::RegDeleteValueA(hParentKey, pValName)==ERROR_SUCCESS ? true:false; + } + //----------------------------------------------------------------------------------------------------------------------------------- + inline + bool RegRemoveKey(HKEY hParentKey, const char* pKeyName) + { + //CHECK_AND_ASSERT(hParentKey&&pKeyName, false); + return ::RegDeleteKeyA(hParentKey, pKeyName)==ERROR_SUCCESS ? true:false; + } + +} +} +#endif //_MUSC_UTILS_EX_H_ diff --git a/contrib/epee/include/serialization/enableable.h b/contrib/epee/include/serialization/enableable.h new file mode 100644 index 000000000..ab1d799e6 --- /dev/null +++ b/contrib/epee/include/serialization/enableable.h @@ -0,0 +1,53 @@ +// 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 +{ + + template<class t_obj> + struct enableable + { + t_obj v; + bool enabled; + + enableable() + : v(t_obj()), enabled(true) + { // construct from defaults + } + + enableable(const t_obj& _v) + : v(_v), enabled(true) + { // construct from specified values + } + + enableable(const enableable<t_obj>& _v) + : v(_v.v), enabled(_v.enabled) + { // construct from specified values + } + }; +}
\ No newline at end of file diff --git a/contrib/epee/include/serialization/keyvalue_serialization.h b/contrib/epee/include/serialization/keyvalue_serialization.h new file mode 100644 index 000000000..27fb0f1e2 --- /dev/null +++ b/contrib/epee/include/serialization/keyvalue_serialization.h @@ -0,0 +1,92 @@ +// 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/utility/value_init.hpp> +#include <boost/foreach.hpp> +#include "misc_log_ex.h" +#include "enableable.h" +#include "keyvalue_serialization_overloads.h" +namespace epee +{ + /************************************************************************/ + /* Serialize map declarations */ + /************************************************************************/ +#define BEGIN_KV_SERIALIZE_MAP() \ +public: \ + template<class t_storage> \ + bool store( t_storage& st, typename t_storage::hsection hparent_section = nullptr) const\ + {\ + return serialize_map<true>(*this, st, hparent_section);\ + }\ + template<class t_storage> \ + bool _load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ + {\ + return serialize_map<false>(*this, stg, hparent_section);\ + }\ + template<class t_storage> \ + bool load( t_storage& stg, typename t_storage::hsection hparent_section = nullptr)\ + {\ + try{\ + return serialize_map<false>(*this, stg, hparent_section);\ + }\ + catch(const std::exception& err) \ + { \ + (void)(err); \ + LOG_ERROR("Exception on unserializing: " << err.what());\ + return false; \ + }\ + }\ + template<bool is_store, class this_type, class t_storage> \ + static bool serialize_map(this_type& this_ref, t_storage& stg, typename t_storage::hsection hparent_section) \ + { + +#define KV_SERIALIZE_N(varialble, val_name) \ + epee::serialization::selector<is_store>::serialize(this_ref.varialble, stg, hparent_section, val_name); + +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) \ + epee::serialization::selector<is_store>::serialize_t_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); + +#define KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, val_name) \ + static_assert(std::is_pod<decltype(this_ref.varialble)>::value, "t_type must be a POD type."); \ + KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, val_name) + +#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, val_name) \ + epee::serialization::selector<is_store>::serialize_stl_container_pod_val_as_blob(this_ref.varialble, stg, hparent_section, val_name); + +#define END_KV_SERIALIZE_MAP() return true;} + +#define KV_SERIALIZE(varialble) KV_SERIALIZE_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_N(varialble, #varialble) +#define KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(varialble) KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE_N(varialble, #varialble) //skip is_pod compile time check +#define KV_SERIALIZE_CONTAINER_POD_AS_BLOB(varialble) KV_SERIALIZE_CONTAINER_POD_AS_BLOB_N(varialble, #varialble) + +} + + + + diff --git a/contrib/epee/include/serialization/keyvalue_serialization_overloads.h b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h new file mode 100644 index 000000000..2ad9a82a4 --- /dev/null +++ b/contrib/epee/include/serialization/keyvalue_serialization_overloads.h @@ -0,0 +1,366 @@ +// 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 serialization + { + + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool serialize_t_val(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.set_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool unserialize_t_val(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.get_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + std::string blob((const char *)&d, sizeof(d)); + return stg.set_value(pname, blob, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool unserialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + std::string blob; + if(!stg.get_value(pname, blob, hparent_section)) + return false; + CHECK_AND_ASSERT_MES(blob.size() == sizeof(d), false, "unserialize_t_val_as_blob: size of " << typeid(t_type).name() << " = " << sizeof(t_type) << ", but stored blod size = " << blob.size() << ", value name = " << pname); + d = *(const t_type*)blob.data(); + return true; + } + //------------------------------------------------------------------------------------------------------------------- + template<class serializible_type, class t_storage> + static bool serialize_t_obj(const serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + CHECK_AND_ASSERT_MES(hchild_section, false, "serialize_t_obj: failed to open/create section " << pname); + return obj.store(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class serializible_type, class t_storage> + static bool unserialize_t_obj(serializible_type& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + if(!hchild_section) return false; + return obj._load(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class serializible_type, class t_storage> + static bool serialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!obj.enabled) + return true; + return serialize_t_obj(obj.v, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class serializible_type, class t_storage> + static bool unserialize_t_obj(enableable<serializible_type>& obj, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + obj.enabled = false; + typename t_storage::hsection hchild_section = stg.open_section(pname, hparent_section, true); + if(!hchild_section) return false; + obj.enabled = true; + return obj.v._load(stg, hchild_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool serialize_stl_container_t_val (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + typename t_storage::harray hval_array = stg.insert_first_value(pname, *it, hparent_section); + CHECK_AND_ASSERT_MES(hval_array, false, "failed to insert first value to storage"); + it++; + for(;it!= container.end();it++) + stg.insert_next_value(hval_array, *it); + + return true; + } + //-------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool unserialize_stl_container_t_val(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + container.clear(); + typename stl_container::value_type exchange_val; + typename t_storage::harray hval_array = stg.get_first_value(pname, exchange_val, hparent_section); + if(!hval_array) return false; + container.push_back(std::move(exchange_val)); + while(stg.get_next_value(hval_array, exchange_val)) + container.push_back(std::move(exchange_val)); + return true; + }//-------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool serialize_stl_container_pod_val_as_blob(const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + std::string mb; + mb.resize(sizeof(typename stl_container::value_type)*container.size()); + typename stl_container::value_type* p_elem = (typename stl_container::value_type*)mb.data(); + BOOST_FOREACH(const typename stl_container::value_type& v, container) + { + *p_elem = v; + p_elem++; + } + return stg.set_value(pname, mb, hparent_section); + } + //-------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool unserialize_stl_container_pod_val_as_blob(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + container.clear(); + std::string buff; + bool res = stg.get_value(pname, buff, hparent_section); + if(res) + { + size_t loaded_size = buff.size(); + typename stl_container::value_type* pelem = (typename stl_container::value_type*)buff.data(); + CHECK_AND_ASSERT_MES(!(loaded_size%sizeof(typename stl_container::value_type)), + false, + "size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type)); + size_t count = (loaded_size/sizeof(typename stl_container::value_type)); + for(size_t i = 0; i < count; i++) + container.push_back(*(pelem++)); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool serialize_stl_container_t_obj (const stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + bool res = false; + if(!container.size()) return true; + typename stl_container::const_iterator it = container.begin(); + typename t_storage::hsection hchild_section = nullptr; + typename t_storage::harray hsec_array = stg.insert_first_section(pname, hchild_section, hparent_section); + CHECK_AND_ASSERT_MES(hsec_array && hchild_section, false, "failed to insert first section with section name " << pname); + res = it->store(stg, hchild_section); + it++; + for(;it!= container.end();it++) + { + stg.insert_next_section(hsec_array, hchild_section); + res |= it->store(stg, hchild_section); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template<class stl_container, class t_storage> + static bool unserialize_stl_container_t_obj(stl_container& container, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + bool res = false; + container.clear(); + typename stl_container::value_type val = typename stl_container::value_type(); + typename t_storage::hsection hchild_section = nullptr; + typename t_storage::harray hsec_array = stg.get_first_section(pname, hchild_section, hparent_section); + if(!hsec_array || !hchild_section) return false; + res = val._load(stg, hchild_section); + container.push_back(val); + while(stg.get_next_section(hsec_array, hchild_section)) + { + typename stl_container::value_type val_l = typename stl_container::value_type(); + res |= val_l._load(stg, hchild_section); + container.push_back(std::move(val_l)); + } + return res; + } + //-------------------------------------------------------------------------------------------------------------------- + template<bool> + struct kv_serialization_overloads_impl_is_base_serializable_types; + + template<> + struct kv_serialization_overloads_impl_is_base_serializable_types<true> + { + template<class t_type, class t_storage> + static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.set_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return stg.get_value(pname, d, hparent_section); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_val(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + }; + template<> + struct kv_serialization_overloads_impl_is_base_serializable_types<false> + { + template<class t_type, class t_storage> + static bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_t_obj(d, stg, hparent_section, pname); + } + + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return serialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + static bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return unserialize_stl_container_t_obj(d, stg, hparent_section, pname); + } + }; + typedef boost::mpl::vector<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string>::type base_serializable_types; + //------------------------------------------------------------------------------------------------------------------- + template<bool> struct selector; + template<> + struct selector<true> + { + template<class t_type, class t_storage> + static bool serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialize(d, stg, hparent_section, pname); + } + + template<class t_type, class t_storage> + static bool serialize_stl_container_pod_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::serialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); + } + + template<class t_type, class t_storage> + static bool serialize_t_val_as_blob(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::serialize_t_val_as_blob(d, stg, hparent_section, pname); + } + + + }; + template<> + struct selector<false> + { + template<class t_type, class t_storage> + static bool serialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_unserialize(d, stg, hparent_section, pname); + } + template<class t_type, class t_storage> + static bool serialize_stl_container_pod_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::unserialize_stl_container_pod_val_as_blob(d, stg, hparent_section, pname); + } + + template<class t_type, class t_storage> + static bool serialize_t_val_as_blob(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return epee::serialization::unserialize_t_val_as_blob(d, stg, hparent_section, pname); + } + }; + + template<class t_type, class t_storage> + bool kv_serialize(const t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_unserialize(t_type& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_serialize(const std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_unserialize(std::vector<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_serialize(const std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_serialize(d, stg, hparent_section, pname); + } + //------------------------------------------------------------------------------------------------------------------- + template<class t_type, class t_storage> + bool kv_unserialize(std::list<t_type>& d, t_storage& stg, typename t_storage::hsection hparent_section, const char* pname) + { + return kv_serialization_overloads_impl_is_base_serializable_types<boost::mpl::contains<base_serializable_types, typename std::remove_const<t_type>::type>::value>::kv_unserialize(d, stg, hparent_section, pname); + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/serialization/serialize_base.h b/contrib/epee/include/serialization/serialize_base.h new file mode 100644 index 000000000..84a1624cb --- /dev/null +++ b/contrib/epee/include/serialization/serialize_base.h @@ -0,0 +1,2 @@ +#pragma once + diff --git a/contrib/epee/include/service_impl_base.h b/contrib/epee/include/service_impl_base.h new file mode 100644 index 000000000..6e9aefc46 --- /dev/null +++ b/contrib/epee/include/service_impl_base.h @@ -0,0 +1,323 @@ +// 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 _SERVICE_IMPL_BASE_H_ +#define _SERVICE_IMPL_BASE_H_ + +#pragma comment(lib, "advapi32.lib") + + +namespace epee +{ +class service_impl_base { + public: + service_impl_base(); + virtual ~service_impl_base(); + + virtual const char *get_name() = 0; + virtual const char *get_caption() = 0; + virtual const char *get_description() = 0; + + bool run_service(); + virtual bool install(); + virtual bool remove(); + virtual bool init(); + void set_control_accepted(unsigned controls); + void set_status(unsigned state, unsigned pending = 0); + unsigned get_control_accepted(); + + private: + virtual void service_main() = 0; + virtual unsigned service_handler(unsigned control, unsigned event_code, + void *pdata) = 0; + //------------------------------------------------------------------------- + static service_impl_base*& instance(); + //------------------------------------------------------------------------- + static DWORD __stdcall _service_handler(DWORD control, DWORD event, + void *pdata, void *pcontext); + static void __stdcall service_entry(DWORD argc, char **pargs); + virtual SERVICE_FAILURE_ACTIONSA* get_failure_actions(); + + private: + SC_HANDLE m_manager; + SC_HANDLE m_service; + SERVICE_STATUS_HANDLE m_status_handle; + DWORD m_accepted_control; +}; + +inline service_impl_base::service_impl_base() { + m_manager = 0; + m_service = 0; + m_status_handle = 0; + m_accepted_control = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN + | SERVICE_ACCEPT_PAUSE_CONTINUE; + + instance() = this; +} +//----------------------------------------------------------------------------- +inline service_impl_base::~service_impl_base() { + if (m_service) { + ::CloseServiceHandle(m_service); + } + m_service = 0; + if (m_manager) { + ::CloseServiceHandle(m_manager); + } + m_manager = 0; + instance() = 0; +} +//----------------------------------------------------------------------------- +inline service_impl_base*& service_impl_base::instance() { + static service_impl_base *pservice = NULL; + return pservice; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::install() { + CHECK_AND_ASSERT(!m_service, false); + const char *psz_descr = get_description(); + SERVICE_FAILURE_ACTIONSA* fail_acts = get_failure_actions(); + + char sz_path[MAX_PATH]; + ::GetModuleFileNameA(0, sz_path, sizeof(sz_path)); + ::GetShortPathNameA(sz_path, sz_path, sizeof(sz_path)); + + while (TRUE) { + if (!m_manager) { + m_manager = ::OpenSCManager(NULL, NULL, GENERIC_ALL); + if (!m_manager) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenSCManager(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + m_service = ::CreateServiceA(m_manager, get_name(), get_caption(), + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, + SERVICE_ERROR_IGNORE, sz_path, 0, 0, 0, 0, 0); + if (!m_service) { + int err = GetLastError(); + LOG_ERROR( + "Failed to CreateService(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + + if (psz_descr) { + SERVICE_DESCRIPTIONA sd = { (char*) psz_descr }; + if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_DESCRIPTION, + &sd)) { + int err = GetLastError(); + LOG_ERROR( + "Failed to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (fail_acts) { + if (!::ChangeServiceConfig2A(m_service, SERVICE_CONFIG_FAILURE_ACTIONS, + fail_acts)) { + int err = GetLastError(); + LOG_ERROR( + "Failed to ChangeServiceConfig2(SERVICE_CONFIG_FAILURE_ACTIONS), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + LOG_PRINT("Installed succesfully.", LOG_LEVEL_0); + return true; + } + LOG_PRINT("Failed to install.", LOG_LEVEL_0); + return false; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::remove() { + CHECK_AND_ASSERT(!m_service, false); + + while (TRUE) { + if (!m_manager) { + m_manager = ::OpenSCManager(0, 0, GENERIC_ALL); + if (!m_manager) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenSCManager(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (!m_service) { + m_service = ::OpenServiceA(m_manager, get_name(), SERVICE_STOP | DELETE); + if (!m_service) { + int err = GetLastError(); + LOG_ERROR( + "Failed to OpenService(), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + SERVICE_STATUS status = { }; + if (!::ControlService(m_service, SERVICE_CONTROL_STOP, &status)) { + int err = ::GetLastError(); + if (err == ERROR_SHUTDOWN_IN_PROGRESS) + continue; + else if (err != ERROR_SERVICE_NOT_ACTIVE) { + LOG_ERROR( + "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + } + + if (!::DeleteService(m_service)) { + int err = ::GetLastError(); + LOG_ERROR( + "Failed to ControlService(SERVICE_CONTROL_STOP), last err=" + << log_space::get_win32_err_descr(err)); + break; + } + + LOG_PRINT("Removed successfully.", LOG_LEVEL_0); + break; + } + + return true; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::init() { + return true; +} +//----------------------------------------------------------------------------- +inline +bool service_impl_base::run_service() { + CHECK_AND_ASSERT(!m_service, false); + + long error_code = 0; + + SERVICE_TABLE_ENTRYA service_table[2]; + ZeroMemory(&service_table, sizeof(service_table)); + + service_table->lpServiceName = (char*) get_name(); + service_table->lpServiceProc = service_entry; + + LOG_PRINT("[+] Start service control dispatcher for \"" << get_name() << "\"", + LOG_LEVEL_1); + + error_code = 1; + BOOL res = ::StartServiceCtrlDispatcherA(service_table); + if (!res) { + int err = GetLastError(); + LOG_PRINT( + "[+] Error starting service control dispatcher, err=" + << log_space::get_win32_err_descr(err), LOG_LEVEL_1); + return false; + } else { + LOG_PRINT("[+] End service control dispatcher for \"" << get_name() << "\"", + LOG_LEVEL_1); + } + return true; +} +//----------------------------------------------------------------------------- +inline DWORD __stdcall service_impl_base::_service_handler(DWORD control, + DWORD event, void *pdata, void *pcontext) { + CHECK_AND_ASSERT(pcontext, ERROR_CALL_NOT_IMPLEMENTED); + + service_impl_base *pservice = (service_impl_base*) pcontext; + return pservice->service_handler(control, event, pdata); +} +//----------------------------------------------------------------------------- +inline +void __stdcall service_impl_base::service_entry(DWORD argc, char **pargs) { + service_impl_base *pme = instance(); + LOG_PRINT("instance: " << pme, LOG_LEVEL_4); + if (!pme) { + LOG_ERROR("Error: at service_entry() pme = NULL"); + return; + } + pme->m_status_handle = ::RegisterServiceCtrlHandlerExA(pme->get_name(), + _service_handler, pme); + + pme->set_status(SERVICE_RUNNING); + pme->service_main(); + pme->set_status(SERVICE_STOPPED); +} +//----------------------------------------------------------------------------- +inline +void service_impl_base::set_status(unsigned state, unsigned pending) { + if (!m_status_handle) + return; + + SERVICE_STATUS status = { 0 }; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwCurrentState = state; + status.dwControlsAccepted = m_accepted_control; + /*status.dwWin32ExitCode = NO_ERROR; + status.dwServiceSpecificExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + + status.dwCurrentState = state;*/ + + if (state == SERVICE_START_PENDING || state == SERVICE_STOP_PENDING + || state == SERVICE_CONTINUE_PENDING || state == SERVICE_PAUSE_PENDING) { + status.dwWaitHint = 2000; + status.dwCheckPoint = pending; + } + ::SetServiceStatus(m_status_handle, &status); +} +//----------------------------------------------------------------------------------------- +inline +void service_impl_base::set_control_accepted(unsigned controls) { + m_accepted_control = controls; +} +//----------------------------------------------------------------------------------------- +inline +unsigned service_impl_base::get_control_accepted() { + return m_accepted_control; +} +//----------------------------------------------------------------------------------------- +inline SERVICE_FAILURE_ACTIONSA* service_impl_base::get_failure_actions() { + // first 3 failures in 30 minutes. Service will be restarted. + // do nothing for next failures + static SC_ACTION sa[] = { { SC_ACTION_RESTART, 3 * 1000 }, { + SC_ACTION_RESTART, 3 * 1000 }, { SC_ACTION_RESTART, 3 * 1000 }, { + SC_ACTION_NONE, 0 } }; + + static SERVICE_FAILURE_ACTIONSA sfa = { 1800, // interval for failures counter - 30 min + "", NULL, 4, (SC_ACTION*) &sa }; + + // TODO: refactor this code, really unsafe! + return &sfa; +} +} + +#endif //_SERVICE_IMPL_BASE_H_ diff --git a/contrib/epee/include/sha1.h b/contrib/epee/include/sha1.h new file mode 100644 index 000000000..ce42082f8 --- /dev/null +++ b/contrib/epee/include/sha1.h @@ -0,0 +1,51 @@ + +/* + Copyright (c) 2011, Micael Hildenborg + 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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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 SHA1_DEFINED +#define SHA1_DEFINED + +namespace sha1 { + + /** + @param src points to any kind of data to be hashed. + @param bytelength the number of bytes to hash from the src pointer. + @param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in. + */ + void calc(const void* src, const int bytelength, unsigned char* hash); + + /** + @param hash is 20 bytes of sha1 hash. This is the same data that is the result from the calc function. + @param hexstring should point to a buffer of at least 41 bytes of size for storing the hexadecimal representation of the hash. A zero will be written at position 40, so the buffer will be a valid zero ended string. + */ + void toHexString(const unsigned char* hash, char* hexstring); + +} // namespace sha1 + +#include "sha1.inl" + +#endif // SHA1_DEFINED diff --git a/contrib/epee/include/sha1.inl b/contrib/epee/include/sha1.inl new file mode 100644 index 000000000..d33202724 --- /dev/null +++ b/contrib/epee/include/sha1.inl @@ -0,0 +1,179 @@ + +/* + Copyright (c) 2011, Micael Hildenborg + 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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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. + */ + +/* + Contributors: + Gustav + Several members in the gamedev.se forum. + Gregory Petrosyan + */ + +#include "sha1.h" + +namespace sha1 { +namespace {// local +// Rotate an integer value to left. +inline const unsigned int rol(const unsigned int value, + const unsigned int steps) { + return ((value << steps) | (value >> (32 - steps))); +} + +// Sets the first 16 integers in the buffert to zero. +// Used for clearing the W buffert. +inline void clearWBuffert(unsigned int* buffert) { + for (int pos = 16; --pos >= 0;) + { + buffert[pos] = 0; + } +} + +inline +void innerHash(unsigned int* result, unsigned int* w) { + unsigned int a = result[0]; + unsigned int b = result[1]; + unsigned int c = result[2]; + unsigned int d = result[3]; + unsigned int e = result[4]; + + int round = 0; + +#define sha1macro(func,val) \ + { \ + const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \ + e = d; \ + d = c; \ + c = rol(b, 30); \ + b = a; \ + a = t; \ + } + + while (round < 16) { + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 20) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (~b & d), 0x5a827999) + ++round; + } + while (round < 40) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0x6ed9eba1) + ++round; + } + while (round < 60) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc) + ++round; + } + while (round < 80) { + w[round] = rol( + (w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1); + sha1macro(b ^ c ^ d, 0xca62c1d6) + ++round; + } + +#undef sha1macro + + result[0] += a; + result[1] += b; + result[2] += c; + result[3] += d; + result[4] += e; +} +} // namespace + +inline +void calc(const void* src, const int bytelength, unsigned char* hash) { + // Init the result array. + unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, + 0xc3d2e1f0 }; + + // Cast the void src pointer to be the byte array we can work with. + const unsigned char* sarray = (const unsigned char*) src; + + // The reusable round buffer + unsigned int w[80]; + + // Loop through all complete 64byte blocks. + const int endOfFullBlocks = bytelength - 64; + int endCurrentBlock; + int currentBlock(0); + + while (currentBlock <= endOfFullBlocks) { + endCurrentBlock = currentBlock + 64; + + // Init the round buffer with the 64 byte block data. + for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4) + { + // This line will swap endian on big endian and keep endian on little endian. + w[roundPos++] = (unsigned int) sarray[currentBlock + 3] + | (((unsigned int) sarray[currentBlock + 2]) << 8) + | (((unsigned int) sarray[currentBlock + 1]) << 16) + | (((unsigned int) sarray[currentBlock]) << 24); + } + innerHash(result, w); + } + + // Handle the last and not full 64 byte block if existing. + endCurrentBlock = bytelength - currentBlock; + clearWBuffert(w); + int lastBlockBytes = 0; + for (; lastBlockBytes < endCurrentBlock; ++lastBlockBytes) { + w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3); + } + w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3); + if (endCurrentBlock >= 56) { + innerHash(result, w); + clearWBuffert(w); + } + w[15] = bytelength << 3; + innerHash(result, w); + + // Store hash in result pointer, and make sure we get in in the correct order on both endian models. + for (int hashByte = 20; --hashByte >= 0;) { + hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) + & 0xff; + } +} +inline +void toHexString(const unsigned char* hash, char* hexstring) { + const char hexDigits[] = { "0123456789abcdef" }; + + for (int hashByte = 20; --hashByte >= 0;) + { + hexstring[hashByte << 1] = hexDigits[(hash[hashByte] >> 4) & 0xf]; + hexstring[(hashByte << 1) + 1] = hexDigits[hash[hashByte] & 0xf]; + } + hexstring[40] = 0; +} +} // namespace sha1 diff --git a/contrib/epee/include/soci_helper.h b/contrib/epee/include/soci_helper.h new file mode 100644 index 000000000..a154f97fc --- /dev/null +++ b/contrib/epee/include/soci_helper.h @@ -0,0 +1,142 @@ +// 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 "soci.h" +#include "soci-postgresql.h" + +using namespace epee; +namespace soci +{ + + template <> + struct type_conversion<boost::uint64_t> + { + typedef long long base_type; + + static void from_base(base_type a_, indicator ind, boost::uint64_t & mi) + { + if (ind == i_null) + { + mi = 0; + //throw soci_error("Null value not allowed for this type"); + } + mi = (boost::uint64_t)a_; + //mi.set(i); + } + + static void to_base(const boost::uint64_t & mi, base_type & i, indicator & ind) + { + i = (base_type)mi; + ind = i_ok; + } + }; + + + + template <> + struct type_conversion<bool> + { + typedef int base_type; + + static void from_base(base_type a_, indicator ind, bool& mi) + { + if (ind == i_null) + { + mi = false; + //throw soci_error("Null value not allowed for this type"); + } + mi = a_? true:false; + //mi.set(i); + } + + static void to_base(const bool & mi, base_type & i, indicator & ind) + { + i = mi? 1:0; + ind = i_ok; + } + }; + + + + class per_thread_session + { + public: + bool init(const std::string& connection_string) + { + m_connection_string = connection_string; + + return true; + } + + soci::session& get() + { + + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr<soci::session>& conn_ptr = m_db_connections[epee::misc_utils::get_thread_string_id()]; + m_db_connections_lock.unlock(); + if(!conn_ptr.get()) + { + conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); + } + //init new connection + return *conn_ptr.get(); + } + + bool reopen() + { + //soci::session + + m_db_connections_lock.lock(); + boost::shared_ptr<soci::session>& conn_ptr = m_db_connections[misc_utils::get_thread_string_id()]; + m_db_connections_lock.unlock(); + if(conn_ptr.get()) + { + conn_ptr->close(); + conn_ptr.reset(new soci::session(soci::postgresql, m_connection_string)); + } + + //init new connection + return true; + } + + //---------------------------------------------------------------------------------------------- + bool check_status() + { + return true; + } + + protected: + private: + std::map<std::string, boost::shared_ptr<soci::session> > m_db_connections; + epee::critical_section m_db_connections_lock; + std::string m_connection_string; + }; +} +/*}*/
\ No newline at end of file diff --git a/contrib/epee/include/static_initializer.h b/contrib/epee/include/static_initializer.h new file mode 100644 index 000000000..3463a5607 --- /dev/null +++ b/contrib/epee/include/static_initializer.h @@ -0,0 +1,82 @@ +// 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 _STATIC_INITIALIZER_H_ +#define _STATIC_INITIALIZER_H_ + + +namespace epee +{ +/*********************************************************************** +class initializer - useful to initialize some static classes + which have init() and un_init() static members +************************************************************************/ + +template<class to_initialize> +class initializer +{ +public: + initializer() + { + to_initialize::init(); + //get_set_is_initialized(true, true); + } + ~initializer() + { + to_initialize::un_init(); + //get_set_is_uninitialized(true, true); + } + + /*static inline bool is_initialized() + { + return get_set_is_initialized(); + } + static inline bool is_uninitialized() + { + return get_set_is_uninitialized(); + } + +private: + static inline bool get_set_is_initialized(bool need_to_set = false, bool val_to_set= false) + { + static bool val_is_initialized = false; + if(need_to_set) + val_is_initialized = val_to_set; + return val_is_initialized; + } + static inline bool get_set_is_uninitialized(bool need_to_set = false, bool val_to_set = false) + { + static bool val_is_uninitialized = false; + if(need_to_set) + val_is_uninitialized = val_to_set; + return val_is_uninitialized; + }*/ +}; + +} +#endif //_STATIC_INITIALIZER_H_ diff --git a/contrib/epee/include/storages/activity_notifier.h b/contrib/epee/include/storages/activity_notifier.h new file mode 100644 index 000000000..14b6ebbfb --- /dev/null +++ b/contrib/epee/include/storages/activity_notifier.h @@ -0,0 +1,132 @@ +// 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 "inmemtoxml.h" + +//#include "levin/levin_server.h" + +namespace epee +{ + +class activity_printer_base +{ +public: + activity_printer_base(){} + virtual ~activity_printer_base(){} +}; + +template<class A> +class notify_activity_printer: public activity_printer_base +{ +public: + notify_activity_printer(int level, A& arg, bool is_notify_mode = true):m_ref_arg(arg), m_level(level), m_is_notify_mode(is_notify_mode) + { + m_command_name = typeid(m_ref_arg).name(); + m_command_name.erase(0, 7); + m_command_name.erase(m_command_name.size()-10, m_command_name.size()-1); + if(level == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(m_command_name, level); + } + else if(level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" -->>" << m_command_name, level); + } + else if(level+2 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" -->>" << m_command_name << "\n" << StorageNamed::xml::get_t_as_xml(m_ref_arg), level); + } + } + + virtual ~notify_activity_printer() + { + if(m_is_notify_mode) + { + if(m_level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name, m_level); + } + } + } +protected: + std::string m_command_name; + A& m_ref_arg; + int m_level; + bool m_is_notify_mode; +}; + +template<class A, class R> +class command_activity_printer: public notify_activity_printer<A> +{ +public: + command_activity_printer(int level, A& arg, R& rsp):notify_activity_printer(level, arg, false), m_ref_rsp(rsp) + { + } + + virtual ~command_activity_printer() + { + if(m_level+1 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name, m_level); + } + else if(m_level+2 == log_space::get_set_log_detalisation_level()) + { + LOG_PRINT(" <<--" << m_command_name << "\n" << StorageNamed::trace_as_xml(m_ref_rsp), m_level); + } + } +private: + R& m_ref_rsp; +}; + +template<class A, class R> +activity_printer_base* create_activity_printer(int level, A& arg, R& rsp) +{ + return new command_activity_printer<A, R>(level, arg, rsp); +} + +template<class A> +activity_printer_base* create_activity_printer(int level, A& arg) +{ + return new notify_activity_printer<A>(level, arg); +} + +} + +#define PRINT_COMMAND_ACTIVITY(level) boost::shared_ptr<activity_printer_base> local_activity_printer(create_activity_printer(level, in_struct, out_struct)); +#define PRINT_NOTIFY_ACTIVITY(level) boost::shared_ptr<activity_printer_base> local_activity_printer(create_activity_printer(level, in_struct)); + +#define PRINT_ACTIVITY(level) \ +{std::string some_str = typeid(in_struct).name(); \ + some_str.erase(0, 7); \ + some_str.erase(some_str.size()-10, some_str.size()-1); \ + LOG_PRINT(some_str, level);} + +} + diff --git a/contrib/epee/include/storages/crypted_storage.h b/contrib/epee/include/storages/crypted_storage.h new file mode 100644 index 000000000..d6e6edcba --- /dev/null +++ b/contrib/epee/include/storages/crypted_storage.h @@ -0,0 +1,62 @@ +// 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 _CRYPTED_STORAGE_H_ +#define _CRYPTED_STORAGE_H_ + +#include "cryptopp_helper.h" + +namespace epee +{ +template<class t_base_storage, class crypt_provider, class t_key_provider> +class crypted_storage: public t_base_storage +{ +public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!crypt_provider::encrypt(targetObj, t_key_provider::get_storage_default_key())) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_decrypt = pTargetObj; + if(crypt_provider::decrypt(buff_to_decrypt, t_key_provider::get_storage_default_key())) + return t_base_storage::LoadFromSolidBuffer(buff_to_decrypt); + + return 0; + } +}; +} + +#endif //_CRYPTED_STORAGE_H_
\ No newline at end of file diff --git a/contrib/epee/include/storages/gzipped_inmemstorage.h b/contrib/epee/include/storages/gzipped_inmemstorage.h new file mode 100644 index 000000000..5c53fffa7 --- /dev/null +++ b/contrib/epee/include/storages/gzipped_inmemstorage.h @@ -0,0 +1,68 @@ +// 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 _GZIPPED_INMEMSTORAGE_H_ +#define _GZIPPED_INMEMSTORAGE_H_ + +#include "zlib_helper.h" +namespace epee +{ +namespace StorageNamed +{ + + template<class t_base_storage> + class gziped_storage: public t_base_storage + { + public: + size_t PackToSolidBuffer(std::string& targetObj) + { + size_t res = t_base_storage::PackToSolidBuffer(targetObj); + if(res <= 0) + return res; + + if(!zlib_helper::pack(targetObj)) + return 0; + + return targetObj.size(); + } + + size_t LoadFromSolidBuffer(const std::string& pTargetObj) + { + std::string buff_to_ungzip = pTargetObj; + if(zlib_helper::unpack(buff_to_ungzip)) + return t_base_storage::LoadFromSolidBuffer(buff_to_ungzip); + + return 0; + } + + private: + }; + +} +} + +#endif
\ No newline at end of file diff --git a/contrib/epee/include/storages/http_abstract_invoke.h b/contrib/epee/include/storages/http_abstract_invoke.h new file mode 100644 index 000000000..07720f496 --- /dev/null +++ b/contrib/epee/include/storages/http_abstract_invoke.h @@ -0,0 +1,126 @@ + +// 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 "portable_storage_template_helper.h" +#include "net/http_base.h" +#include "net/http_server_handlers_map2.h" + +namespace epee +{ + namespace net_utils + { + template<class t_request, class t_response, class t_transport> + bool invoke_http_json_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + if(!serialization::store_t_to_json(out_struct, req_param)) + return false; + + const http::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 serialization::load_t_from_json(result_struct, pri->m_body); + } + + + + template<class t_request, class t_response, class t_transport> + bool invoke_http_bin_remote_command2(const std::string& url, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& method = "GET") + { + std::string req_param; + if(!serialization::store_t_to_binary(out_struct, req_param)) + return false; + + const http::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 serialization::load_t_from_binary(result_struct, pri->m_body); + } + + template<class t_request, class t_response, class t_transport> + bool invoke_http_json_rpc(const std::string& url, const std::string& method_name, t_request& out_struct, t_response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") + { + epee::json_rpc::request<t_request> req_t = AUTO_VAL_INIT(req_t); + req_t.params = out_struct; + req_t.id = req_id; + req_t.method = method_name; + req_t.version = "2.0"; + epee::json_rpc::response<t_response, epee::json_rpc::error> resp_t = AUTO_VAL_INIT(resp_t); + if(!epee::net_utils::invoke_http_json_remote_command2(url, req_t, resp_t, transport, timeout, http_method)) + { + return false; + } + if(resp_t.error.code || resp_t.error.message.size()) + { + LOG_ERROR("RPC call of \"" << method_name << "\" returned error: " << resp_t.error.code << ", message: " << resp_t.error.message); + return false; + } + result_struct = resp_t.result; + return true; + } + + template<class t_command, class t_transport> + bool invoke_http_json_rpc(const std::string& url, typename t_command::request& out_struct, typename t_command::response& result_struct, t_transport& transport, unsigned int timeout = 5000, const std::string& http_method = "GET", const std::string& req_id = "0") + { + return invoke_http_json_rpc(url, t_command::methodname(), out_struct, result_struct, transport, timeout, http_method, req_id); + } + + } +} diff --git a/contrib/epee/include/storages/levin_abstract_invoke2.h b/contrib/epee/include/storages/levin_abstract_invoke2.h new file mode 100644 index 000000000..1b32c51d1 --- /dev/null +++ b/contrib/epee/include/storages/levin_abstract_invoke2.h @@ -0,0 +1,289 @@ +// 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 "portable_storage_template_helper.h" +#include <boost/utility/value_init.hpp> +#include "net/levin_base.h" + +namespace epee +{ + namespace net_utils + { + template<class t_arg, class t_result, class t_transport> + bool invoke_remote_command2(int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.invoke(command, buff_to_send, buff_to_recv); + if( res <=0 ) + { + LOG_PRINT_RED("Failed to invoke command " << command << " return code " << res, LOG_LEVEL_1); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + result_struct.load(stg_ret); + return true; + } + + template<class t_arg, class t_transport> + bool notify_remote_command2(int command, const t_arg& out_struct, t_transport& transport) + { + if(!transport.is_connected()) + return false; + + serialization::portable_storage stg; + out_struct.store(&stg); + std::string buff_to_send; + stg.store_to_binary(buff_to_send); + + int res = transport.notify(command, buff_to_send); + if(res <=0 ) + { + LOG_ERROR("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } + + template<class t_arg, class t_result, class t_transport> + bool invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_result& result_struct, t_transport& transport) + { + + typename serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.invoke(command, buff_to_send, buff_to_recv, conn_id); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + typename serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff_to_recv)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + return false; + } + result_struct.load(stg_ret); + + return true; + } + + template<class t_result, class t_arg, class callback_t, class t_transport> + bool async_invoke_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport, callback_t cb, size_t inv_timeout = LEVIN_DEFAULT_TIMEOUT_PRECONFIGURED) + { + typename serialization::portable_storage stg; + const_cast<t_arg&>(out_struct).store(stg);//TODO: add true const support to searilzation + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + int res = transport.invoke_async(command, buff_to_send, conn_id, [cb, command](int code, const std::string& buff, typename t_transport::connection_context& context)->bool + { + t_result result_struct = AUTO_VAL_INIT(result_struct); + if( code <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << code); + cb(code, result_struct, context); + return false; + } + serialization::portable_storage stg_ret; + if(!stg_ret.load_from_binary(buff)) + { + LOG_ERROR("Failed to load_from_binary on command " << command); + cb(LEVIN_ERROR_FORMAT, result_struct, context); + return false; + } + result_struct.load(stg_ret); + cb(code, result_struct, context); + return true; + }, inv_timeout); + if( res <=0 ) + { + LOG_PRINT_L1("Failed to invoke command " << command << " return code " << res); + return false; + } + return true; + } + + template<class t_arg, class t_transport> + bool notify_remote_command2(boost::uuids::uuid conn_id, int command, const t_arg& out_struct, t_transport& transport) + { + + serialization::portable_storage stg; + out_struct.store(stg); + std::string buff_to_send, buff_to_recv; + stg.store_to_binary(buff_to_send); + + int res = transport.notify(command, buff_to_send, conn_id); + if(res <=0 ) + { + LOG_PRINT_RED_L0("Failed to notify command " << command << " return code " << res); + return false; + } + return true; + } + //---------------------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------------------- + template<class t_owner, class t_in_type, class t_out_type, class t_context, class callback_t> + int buff_to_t_adapter(int command, const std::string& in_buff, std::string& buff_out, callback_t cb, t_context& context ) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + LOG_ERROR("Failed to load_from_binary in command " << command); + return -1; + } + boost::value_initialized<t_in_type> in_struct; + boost::value_initialized<t_out_type> out_struct; + + static_cast<t_in_type&>(in_struct).load(strg); + int res = cb(command, static_cast<t_in_type&>(in_struct), static_cast<t_out_type&>(out_struct), context); + serialization::portable_storage strg_out; + static_cast<t_out_type&>(out_struct).store(strg_out); + + if(!strg_out.store_to_binary(buff_out)) + { + LOG_ERROR("Failed to store_to_binary in command" << command); + return -1; + } + + return res; + }; + + template<class t_owner, class t_in_type, class t_context, class callback_t> + int buff_to_t_adapter(t_owner* powner, int command, const std::string& in_buff, callback_t cb, t_context& context) + { + serialization::portable_storage strg; + if(!strg.load_from_binary(in_buff)) + { + LOG_ERROR("Failed to load_from_binary in notify " << command); + return -1; + } + boost::value_initialized<t_in_type> in_struct; + static_cast<t_in_type&>(in_struct).load(strg); + return cb(command, in_struct, context); + }; + +#define CHAIN_LEVIN_INVOKE_MAP2(context_type) \ + int invoke(int command, const std::string& in_buff, std::string& buff_out, context_type& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP2(context_type) \ + int notify(int command, const std::string& in_buff, context_type& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + + +#define CHAIN_LEVIN_INVOKE_MAP() \ + int invoke(int command, const std::string& in_buff, std::string& buff_out, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; \ + return handle_invoke_map(false, command, in_buff, buff_out, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_MAP() \ + int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + { \ + bool handled = false; std::string fake_str;\ + return handle_invoke_map(true, command, in_buff, fake_str, context, handled); \ + } + +#define CHAIN_LEVIN_NOTIFY_STUB() \ + int notify(int command, const std::string& in_buff, epee::net_utils::connection_context_base& context) \ + { \ + return -1; \ + } + +#define BEGIN_INVOKE_MAP2(owner_type) \ + template <class t_context> int handle_invoke_map(bool is_notify, int command, const std::string& in_buff, std::string& buff_out, t_context& context, bool& handled) \ + { \ + typedef owner_type internal_owner_type_name; + +#define HANDLE_INVOKE2(command_id, func, type_name_in, typename_out) \ + if(!is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in, typename_out>(this, command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + +#define HANDLE_INVOKE_T2(COMMAND, func) \ + if(!is_notify && COMMAND::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename COMMAND::request, typename COMMAND::response>(command, in_buff, buff_out, boost::bind(func, this, _1, _2, _3, _4), context);} + + +#define HANDLE_NOTIFY2(command_id, func, type_name_in) \ + if(is_notify && command_id == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, type_name_in>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + +#define HANDLE_NOTIFY_T2(NOTIFY, func) \ + if(is_notify && NOTIFY::ID == command) \ + {handled=true;return epee::net_utils::buff_to_t_adapter<internal_owner_type_name, typename NOTIFY::request>(this, command, in_buff, boost::bind(func, this, _1, _2, _3), context);} + + +#define CHAIN_INVOKE_MAP2(func) \ + { \ + int res = func(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ2(obj) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, context, handled); \ + if(handled) \ + return res; \ + } + +#define CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(obj, context_type) \ + { \ + int res = obj.handle_invoke_map(is_notify, command, in_buff, buff_out, static_cast<context_type>(context), handled); \ + if(handled) return res; \ + } + + +#define END_INVOKE_MAP2() \ + LOG_ERROR("Unkonown command:" << command); \ + return LEVIN_ERROR_CONNECTION_HANDLER_NOT_DEFINED; \ + } + } +} + diff --git a/contrib/epee/include/storages/parserse_base_utils.h b/contrib/epee/include/storages/parserse_base_utils.h new file mode 100644 index 000000000..baafb5623 --- /dev/null +++ b/contrib/epee/include/storages/parserse_base_utils.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 + +namespace epee +{ +namespace misc_utils +{ + namespace parse + { + inline std::string transform_to_escape_sequence(const std::string& src) + { + //std::stringstream res; + std::string res; + for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it) + { + switch(*it) + { + case '\b': //Backspace (ascii code 08) + res+="\\b"; break; + case '\f': //Form feed (ascii code 0C) + res+="\\f"; break; + case '\n': //New line + res+="\\n"; break; + case '\r': //Carriage return + res+="\\r"; break; + case '\t': //Tab + res+="\\t"; break; + case '\v': //Vertical tab + res+="\\v"; break; + //case '\'': //Apostrophe or single quote + // res+="\\'"; break; + case '"': //Double quote + res+="\\\""; break; + case '\\': //Backslash caracter + res+="\\\\"; break; + case '/': //Backslash caracter + res+="\\/"; break; + default: + res.push_back(*it); + } + } + return res; + } + /* + + \b Backspace (ascii code 08) + \f Form feed (ascii code 0C) + \n New line + \r Carriage return + \t Tab + \v Vertical tab + \' Apostrophe or single quote + \" Double quote + \\ Backslash character + + */ + inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + bool escape_mode = false; + std::string::const_iterator it = star_end_string; + ++it; + for(;it != buf_end;it++) + { + if(escape_mode/*prev_ch == '\\'*/) + { + switch(*it) + { + case 'b': //Backspace (ascii code 08) + val.push_back(0x08);break; + case 'f': //Form feed (ascii code 0C) + val.push_back(0x0C);break; + case 'n': //New line + val.push_back('\n');break; + case 'r': //Carriage return + val.push_back('\r');break; + case 't': //Tab + val.push_back('\t');break; + case 'v': //Vertical tab + val.push_back('\v');break; + case '\'': //Apostrophe or single quote + val.push_back('\'');break; + case '"': //Double quote + val.push_back('"');break; + case '\\': //Backslash character + val.push_back('\\');break; + case '/': //Slash character + val.push_back('/');break; + default: + val.push_back(*it); + LOG_PRINT_L0("Unknown escape sequence :\"\\" << *it << "\""); + } + escape_mode = false; + }else if(*it == '"') + { + star_end_string = it; + return; + }else if(*it == '\\') + { + escape_mode = true; + } + else + { + val.push_back(*it); + } + } + ASSERT_MES_AND_THROW("Failed to match string in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_string(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + + match_string2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline void match_number2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val, bool& is_float_val, bool& is_signed_val) + { + val.clear(); + is_float_val = false; + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(isdigit(*it) || (it == star_end_string && *it == '-') || (val.size() && *it == '.' ) || (is_float_val && (*it == 'e' || *it == 'E' || *it == '-' || *it == '+' )) ) + { + if(!val.size() && *it == '-') + is_signed_val = true; + if(*it == '.' ) + is_float_val = true; + val.push_back(*it); + } + else + { + if(val.size()) + { + star_end_string = --it; + return; + } + else + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("wrong number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_number(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + bool is_v_float = false;bool is_signed_val = false; + match_number2(star_end_string, buf_end, val, is_v_float, is_signed_val); + return !is_v_float; + } + catch(...) + { + return false; + } + } + inline void match_word2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(!isalpha(*it)) + { + val.assign(star_end_string, it); + if(val.size()) + { + star_end_string = --it; + return; + }else + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + } + ASSERT_MES_AND_THROW("failed to match word number in json entry: " << std::string(star_end_string, buf_end)); + } + inline bool match_word(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + try + { + match_word2(star_end_string, buf_end, val); + return true; + } + catch(...) + { + return false; + } + } + inline bool match_word_with_extrasymb(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val) + { + val.clear(); + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(!isalnum(*it) && *it != '-' && *it != '_') + { + val.assign(star_end_string, it); + if(val.size()) + { + star_end_string = --it; + return true; + }else + return false; + } + } + return false; + } + inline bool match_word_til_equal_mark(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string::const_iterator& word_end) + { + word_end = star_end_string; + + for(std::string::const_iterator it = star_end_string;it != buf_end;it++) + { + if(isspace(*it)) + { + + continue; + }else if( *it == '=' ) + { + star_end_string = it; + word_end = it; + return true; + } + } + return false; + } + } +} +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage.h b/contrib/epee/include/storages/portable_storage.h new file mode 100644 index 000000000..8f4ebc6ae --- /dev/null +++ b/contrib/epee/include/storages/portable_storage.h @@ -0,0 +1,463 @@ +// 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 "misc_language.h" +#include "portable_storage_base.h" +#include "portable_storage_to_bin.h" +#include "portable_storage_from_bin.h" +#include "portable_storage_to_json.h" +#include "portable_storage_from_json.h" +#include "portable_storage_val_converters.h" + +namespace epee +{ + namespace serialization + { + /************************************************************************/ + /* */ + /************************************************************************/ + class portable_storage + { + public: + typedef epee::serialization::hsection hsection; + typedef epee::serialization::harray harray; + + portable_storage(){} + virtual ~portable_storage(){} + hsection open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist = false); + template<class t_value> + bool get_value(const std::string& value_name, t_value& val, hsection hparent_section); + template<class t_value> + bool set_value(const std::string& value_name, const t_value& target, hsection hparent_section); + + //serial access for arrays of values -------------------------------------- + //values + template<class t_value> + harray get_first_value(const std::string& value_name, t_value& target, hsection hparent_section); + template<class t_value> + bool get_next_value(harray hval_array, t_value& target); + template<class t_value> + harray insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section); + template<class t_value> + bool insert_next_value(harray hval_array, const t_value& target); + //sections + harray get_first_section(const std::string& pSectionName, hsection& h_child_section, hsection hparent_section); + bool get_next_section(harray hSecArray, hsection& h_child_section); + harray insert_first_section(const std::string& pSectionName, hsection& hinserted_childsection, hsection hparent_section); + bool insert_next_section(harray hSecArray, hsection& hinserted_childsection); + //------------------------------------------------------------------------ + //delete entry (section, value or array) + bool delete_entry(const std::string& pentry_name, hsection hparent_section = nullptr); + + //------------------------------------------------------------------------------- + bool store_to_binary(binarybuffer& target); + bool load_from_binary(const binarybuffer& target); + template<class trace_policy> + bool dump_as_xml(std::string& targetObj, const std::string& root_name = ""); + bool dump_as_json(std::string& targetObj, size_t indent = 0); + bool load_from_json(const std::string& source); + + private: + section m_root; + hsection get_root_section() {return &m_root;} + storage_entry* find_storage_entry(const std::string& pentry_name, hsection psection); + template<class entry_type> + storage_entry* insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry); + + hsection insert_new_section(const std::string& pentry_name, hsection psection); + +#pragma pack(push) +#pragma pack(1) + struct storage_block_header + { + uint32_t m_signature_a; + uint32_t m_signature_b; + uint8_t m_ver; + }; +#pragma pack(pop) + }; + inline + bool portable_storage::dump_as_json(std::string& buff, size_t indent) + { + TRY_ENTRY(); + std::stringstream ss; + epee::serialization::dump_as_json(ss, m_root, indent); + buff = ss.str(); + return true; + CATCH_ENTRY("portable_storage::dump_as_json", false) + } + inline + bool portable_storage::load_from_json(const std::string& source) + { + TRY_ENTRY(); + return json::load_from_json(source, *this); + CATCH_ENTRY("portable_storage::load_from_json", false) + } + + template<class trace_policy> + bool portable_storage::dump_as_xml(std::string& targetObj, const std::string& root_name) + { + return false;//TODO: don't think i ever again will use xml - ambiguous and "overtagged" format + } + + inline + bool portable_storage::store_to_binary(binarybuffer& target) + { + TRY_ENTRY(); + std::stringstream ss; + storage_block_header sbh = AUTO_VAL_INIT(sbh); + sbh.m_signature_a = PORTABLE_STORAGE_SIGNATUREA; + sbh.m_signature_b = PORTABLE_STORAGE_SIGNATUREB; + sbh.m_ver = PORTABLE_STORAGE_FORMAT_VER; + ss.write((const char*)&sbh, sizeof(storage_block_header)); + pack_entry_to_buff(ss, m_root); + target = ss.str(); + return true; + CATCH_ENTRY("portable_storage::store_to_binary", false) + } + inline + bool portable_storage::load_from_binary(const binarybuffer& source) + { + m_root.m_entries.clear(); + if(source.size() < sizeof(storage_block_header)) + { + LOG_ERROR("portable_storage: wrong binary format, packet size = " << source.size() << " less than expected sizeof(storage_block_header)=" << sizeof(storage_block_header)); + return false; + } + storage_block_header* pbuff = (storage_block_header*)source.data(); + if(pbuff->m_signature_a != PORTABLE_STORAGE_SIGNATUREA || + pbuff->m_signature_b != PORTABLE_STORAGE_SIGNATUREB + ) + { + LOG_ERROR("portable_storage: wrong binary format - signature missmatch"); + return false; + } + if(pbuff->m_ver != PORTABLE_STORAGE_FORMAT_VER) + { + LOG_ERROR("portable_storage: wrong binary format - unknown format ver = " << pbuff->m_ver); + return false; + } + TRY_ENTRY(); + throwable_buffer_reader buf_reader(source.data()+sizeof(storage_block_header), source.size()-sizeof(storage_block_header)); + buf_reader.read(m_root); + return true;//TODO: + CATCH_ENTRY("portable_storage::load_from_binary", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::open_section(const std::string& section_name, hsection hparent_section, bool create_if_notexist) + { + TRY_ENTRY(); + hparent_section = hparent_section ? hparent_section:&m_root; + storage_entry* pentry = find_storage_entry(section_name, hparent_section); + if(!pentry) + { + if(!create_if_notexist) + return nullptr; + return insert_new_section(section_name, hparent_section); + } + CHECK_AND_ASSERT(pentry , nullptr); + //check that section_entry we find is real "CSSection" + if(pentry->type() != typeid(section)) + { + if(create_if_notexist) + *pentry = storage_entry(section());//replace + else + return nullptr; + } + return &boost::get<section>(*pentry); + CATCH_ENTRY("portable_storage::open_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template<class to_type> + struct get_value_visitor: boost::static_visitor<void> + { + to_type& m_target; + get_value_visitor(to_type& target):m_target(target){} + template<class from_type> + void operator()(const from_type& v){convert_t(v, m_target);} + }; + + template<class t_value> + bool portable_storage::get_value(const std::string& value_name, t_value& val, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return false; + + get_value_visitor<t_value> gvv(val); + boost::apply_visitor(gvv, *pentry); + return true; + //CATCH_ENTRY("portable_storage::template<>get_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template<class t_value> + bool portable_storage::set_value(const std::string& value_name, const t_value& v, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> )); + TRY_ENTRY(); + if(!hparent_section) + hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, v); + if(!pentry) + return false; + return true; + } + *pentry = storage_entry(v); + return true; + CATCH_ENTRY("portable_storage::template<>set_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + storage_entry* portable_storage::find_storage_entry(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto it = psection->m_entries.find(pentry_name); + if(it == psection->m_entries.end()) + return nullptr; + + return &it->second; + CATCH_ENTRY("portable_storage::find_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template<class entry_type> + storage_entry* portable_storage::insert_new_entry_get_storage_entry(const std::string& pentry_name, hsection psection, const entry_type& entry) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(psection, nullptr); + auto ins_res = psection->m_entries.insert(std::pair<std::string, storage_entry>(pentry_name, entry)); + return &ins_res.first->second; + CATCH_ENTRY("portable_storage::insert_new_entry_get_storage_entry", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + hsection portable_storage::insert_new_section(const std::string& pentry_name, hsection psection) + { + TRY_ENTRY(); + storage_entry* pse = insert_new_entry_get_storage_entry(pentry_name, psection, section()); + if(!pse) return nullptr; + return &boost::get<section>(*pse); + CATCH_ENTRY("portable_storage::insert_new_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template<class to_type> + struct get_first_value_visitor: boost::static_visitor<bool> + { + to_type& m_target; + get_first_value_visitor(to_type& target):m_target(target){} + template<class from_type> + bool operator()(const array_entry_t<from_type>& a) + { + const from_type* pv = a.get_first_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + //--------------------------------------------------------------------------------------------------------------- + template<class t_value> + harray portable_storage::get_first_value(const std::string& value_name, t_value& target, hsection hparent_section) + { + BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> )); + //TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get<array_entry>(*pentry); + + get_first_value_visitor<t_value> gfv(target); + if(!boost::apply_visitor(gfv, ar_entry)) + return nullptr; + return &ar_entry; + //CATCH_ENTRY("portable_storage::get_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template<class to_type> + struct get_next_value_visitor: boost::static_visitor<bool> + { + to_type& m_target; + get_next_value_visitor(to_type& target):m_target(target){} + template<class from_type> + bool operator()(const array_entry_t<from_type>& a) + { + //TODO: optimize code here: work without get_next_val function + const from_type* pv = a.get_next_val(); + if(!pv) + return false; + convert_t(*pv, m_target); + return true; + } + }; + + + template<class t_value> + bool portable_storage::get_next_value(harray hval_array, t_value& target) + { + BOOST_MPL_ASSERT(( boost::mpl::contains<storage_entry::types, t_value> )); + //TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + array_entry& ar_entry = *hval_array; + get_next_value_visitor<t_value> gnv(target); + if(!boost::apply_visitor(gnv, ar_entry)) + return false; + return true; + //CATCH_ENTRY("portable_storage::get_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + template<class t_value> + harray portable_storage::insert_first_value(const std::string& value_name, const t_value& target, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(value_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(value_name, hparent_section, array_entry(array_entry_t<t_value>())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t<t_value>())); + + array_entry& arr = boost::get<array_entry>(*pentry); + if(arr.type() != typeid(array_entry_t<t_value>)) + arr = array_entry(array_entry_t<t_value>()); + + array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(arr); + arr_typed.insert_first_val(target); + return &arr; + CATCH_ENTRY("portable_storage::insert_first_value", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + template<class t_value> + bool portable_storage::insert_next_value(harray hval_array, const t_value& target) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hval_array, false); + + CHECK_AND_ASSERT_MES(hval_array->type() == typeid(array_entry_t<t_value>), + false, "unexpected type in insert_next_value: " << typeid(array_entry_t<t_value>).name()); + + array_entry_t<t_value>& arr_typed = boost::get<array_entry_t<t_value> >(*hval_array); + arr_typed.insert_next_value(target); + return true; + CATCH_ENTRY("portable_storage::insert_next_value", false); + } + //--------------------------------------------------------------------------------------------------------------- + //sections + inline + harray portable_storage::get_first_section(const std::string& sec_name, hsection& h_child_section, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + return nullptr; + if(pentry->type() != typeid(array_entry)) + return nullptr; + array_entry& ar_entry = boost::get<array_entry>(*pentry); + if(ar_entry.type() != typeid(array_entry_t<section>)) + return nullptr; + array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry); + section* psec = sec_array.get_first_val(); + if(!psec) + return nullptr; + h_child_section = psec; + return &ar_entry; + CATCH_ENTRY("portable_storage::get_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::get_next_section(harray hsec_array, hsection& h_child_section) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + if(hsec_array->type() != typeid(array_entry_t<section>)) + return nullptr; + array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array); + h_child_section = sec_array.get_next_val(); + if(!h_child_section) + return false; + return true; + CATCH_ENTRY("portable_storage::get_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + inline + harray portable_storage::insert_first_section(const std::string& sec_name, hsection& hinserted_childsection, hsection hparent_section) + { + TRY_ENTRY(); + if(!hparent_section) hparent_section = &m_root; + storage_entry* pentry = find_storage_entry(sec_name, hparent_section); + if(!pentry) + { + pentry = insert_new_entry_get_storage_entry(sec_name, hparent_section, array_entry(array_entry_t<section>())); + if(!pentry) + return nullptr; + } + if(pentry->type() != typeid(array_entry)) + *pentry = storage_entry(array_entry(array_entry_t<section>())); + + array_entry& ar_entry = boost::get<array_entry>(*pentry); + if(ar_entry.type() != typeid(array_entry_t<section>)) + ar_entry = array_entry(array_entry_t<section>()); + + array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(ar_entry); + hinserted_childsection = &sec_array.insert_first_val(section()); + return &ar_entry; + CATCH_ENTRY("portable_storage::insert_first_section", nullptr); + } + //--------------------------------------------------------------------------------------------------------------- + inline + bool portable_storage::insert_next_section(harray hsec_array, hsection& hinserted_childsection) + { + TRY_ENTRY(); + CHECK_AND_ASSERT(hsec_array, false); + CHECK_AND_ASSERT_MES(hsec_array->type() == typeid(array_entry_t<section>), + false, "unexpected type(not 'section') in insert_next_section, type: " << hsec_array->type().name()); + + array_entry_t<section>& sec_array = boost::get<array_entry_t<section>>(*hsec_array); + hinserted_childsection = &sec_array.insert_next_value(section()); + return true; + CATCH_ENTRY("portable_storage::insert_next_section", false); + } + //--------------------------------------------------------------------------------------------------------------- + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_base.h b/contrib/epee/include/storages/portable_storage_base.h new file mode 100644 index 000000000..3f1637538 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_base.h @@ -0,0 +1,160 @@ +// 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/variant.hpp> +#include <boost/any.hpp> +#include <string> +#include <list> + +#define PORTABLE_STORAGE_SIGNATUREA 0x01011101 +#define PORTABLE_STORAGE_SIGNATUREB 0x01020101 // bender's nightmare +#define PORTABLE_STORAGE_FORMAT_VER 1 + +#define PORTABLE_RAW_SIZE_MARK_MASK 0x03 +#define PORTABLE_RAW_SIZE_MARK_BYTE 0 +#define PORTABLE_RAW_SIZE_MARK_WORD 1 +#define PORTABLE_RAW_SIZE_MARK_DWORD 2 +#define PORTABLE_RAW_SIZE_MARK_INT64 3 + +#ifndef MAX_STRING_LEN_POSSIBLE +#define MAX_STRING_LEN_POSSIBLE 2000000000 //do not let string be so big +#endif + +//data types +#define SERIALIZE_TYPE_INT64 1 +#define SERIALIZE_TYPE_INT32 2 +#define SERIALIZE_TYPE_INT16 3 +#define SERIALIZE_TYPE_INT8 4 +#define SERIALIZE_TYPE_UINT64 5 +#define SERIALIZE_TYPE_UINT32 6 +#define SERIALIZE_TYPE_UINT16 7 +#define SERIALIZE_TYPE_UINT8 8 +#define SERIALIZE_TYPE_DUOBLE 9 +#define SERIALIZE_TYPE_STRING 10 +#define SERIALIZE_TYPE_BOOL 11 +#define SERIALIZE_TYPE_OBJECT 12 +#define SERIALIZE_TYPE_ARRAY 13 + +#define SERIALIZE_FLAG_ARRAY 0x80 + + +namespace epee +{ + namespace serialization + { + struct section; + + /************************************************************************/ + /* */ + /************************************************************************/ + template<class t_entry_type> + struct array_entry_t + { + array_entry_t():m_it(m_array.end()){} + + const t_entry_type* get_first_val() const + { + m_it = m_array.begin(); + return get_next_val(); + } + + t_entry_type* get_first_val() + { + m_it = m_array.begin(); + return get_next_val(); + } + + + const t_entry_type* get_next_val() const + { + if(m_it == m_array.end()) + return nullptr; + return &(*(m_it++)); + } + + t_entry_type* get_next_val() + { + if(m_it == m_array.end()) + return nullptr; + return (t_entry_type*)&(*(m_it++));//fuckoff + } + + t_entry_type& insert_first_val(const t_entry_type& v) + { + m_array.clear(); + m_it = m_array.end(); + return insert_next_value(v); + } + + t_entry_type& insert_next_value(const t_entry_type& v) + { + m_array.push_back(v); + return m_array.back(); + } + + std::list<t_entry_type> m_array; + mutable typename std::list<t_entry_type>::const_iterator m_it; + }; + + + typedef boost::make_recursive_variant< + array_entry_t<section>, + array_entry_t<uint64_t>, + array_entry_t<uint32_t>, + array_entry_t<uint16_t>, + array_entry_t<uint8_t>, + array_entry_t<int64_t>, + array_entry_t<int32_t>, + array_entry_t<int16_t>, + array_entry_t<int8_t>, + array_entry_t<double>, + array_entry_t<bool>, + array_entry_t<std::string>, + array_entry_t<section>, + array_entry_t<boost::recursive_variant_> + >::type array_entry; + + typedef boost::variant<uint64_t, uint32_t, uint16_t, uint8_t, int64_t, int32_t, int16_t, int8_t, double, bool, std::string, section, array_entry> storage_entry; + + typedef std::string binarybuffer;//it's ok + + /************************************************************************/ + /* */ + /************************************************************************/ + struct section + { + std::map<std::string, storage_entry> m_entries; + }; + + //handle-like aliases + typedef section* hsection; + typedef array_entry* harray; + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_bin.h b/contrib/epee/include/storages/portable_storage_from_bin.h new file mode 100644 index 000000000..e9b7e2e6f --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_from_bin.h @@ -0,0 +1,281 @@ +// 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 "misc_language.h" +#include "portable_storage_base.h" + +#ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT +#else +#define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 +#endif + +namespace epee +{ + namespace serialization + { + struct throwable_buffer_reader + { + throwable_buffer_reader(const void* ptr, size_t sz); + void read(void* target, size_t count); + void read_sec_name(std::string& sce_name); + template<class t_pod_type> + void read(t_pod_type& pod_val); + template<class t_type> + t_type read(); + template<class type_name> + storage_entry read_ae(); + storage_entry load_storage_array_entry(uint8_t type); + size_t read_varint(); + template<class t_type> + storage_entry read_se(); + storage_entry load_storage_entry(); + void read(section& sec); + void read(std::string& str); + private: + struct recursuion_limitation_guard + { + size_t& m_counter_ref; + recursuion_limitation_guard(size_t& counter):m_counter_ref(counter) + { + ++m_counter_ref; + CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded"); + } + ~recursuion_limitation_guard() + { + CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()"); + --m_counter_ref; + } + }; +#define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count) + + const uint8_t* m_ptr; + size_t m_count; + size_t m_recursion_count; + }; + + inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) + { + if(!ptr) + throw std::runtime_error("throwable_buffer_reader: ptr==nullptr"); + if(!sz) + throw std::runtime_error("throwable_buffer_reader: sz==0"); + m_ptr = (uint8_t*)ptr; + m_count = sz; + m_recursion_count = 0; + } + inline + void throwable_buffer_reader::read(void* target, size_t count) + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained"); + memcpy(target, m_ptr, count); + m_ptr += count; + m_count -= count; + } + inline + void throwable_buffer_reader::read_sec_name(std::string& sce_name) + { + RECURSION_LIMITATION(); + uint8_t name_len = 0; + read(name_len); + sce_name.resize(name_len); + read((void*)sce_name.data(), name_len); + } + + template<class t_pod_type> + void throwable_buffer_reader::read(t_pod_type& pod_val) + { + RECURSION_LIMITATION(); + read(&pod_val, sizeof(pod_val)); + } + + template<class t_type> + t_type throwable_buffer_reader::read() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return v; + } + + + template<class type_name> + storage_entry throwable_buffer_reader::read_ae() + { + RECURSION_LIMITATION(); + //for pod types + array_entry_t<type_name> sa; + size_t size = read_varint(); + //TODO: add some optimization here later + while(size--) + sa.m_array.push_back(read<type_name>()); + return storage_entry(array_entry(sa)); + } + + inline + storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type) + { + RECURSION_LIMITATION(); + type &= ~SERIALIZE_FLAG_ARRAY; + switch(type) + { + case SERIALIZE_TYPE_INT64: return read_ae<int64_t>(); + case SERIALIZE_TYPE_INT32: return read_ae<int32_t>(); + case SERIALIZE_TYPE_INT16: return read_ae<int16_t>(); + case SERIALIZE_TYPE_INT8: return read_ae<int8_t>(); + case SERIALIZE_TYPE_UINT64: return read_ae<uint64_t>(); + case SERIALIZE_TYPE_UINT32: return read_ae<uint32_t>(); + case SERIALIZE_TYPE_UINT16: return read_ae<uint16_t>(); + case SERIALIZE_TYPE_UINT8: return read_ae<uint8_t>(); + case SERIALIZE_TYPE_DUOBLE: return read_ae<double>(); + case SERIALIZE_TYPE_BOOL: return read_ae<bool>(); + case SERIALIZE_TYPE_STRING: return read_ae<std::string>(); + case SERIALIZE_TYPE_OBJECT: return read_ae<section>(); + case SERIALIZE_TYPE_ARRAY: return read_ae<array_entry>(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type); + } + } + + inline + size_t throwable_buffer_reader::read_varint() + { + RECURSION_LIMITATION(); + CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint"); + size_t v = 0; + uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK; + switch (size_mask) + { + case PORTABLE_RAW_SIZE_MARK_BYTE: v = read<uint8_t>();break; + case PORTABLE_RAW_SIZE_MARK_WORD: v = read<uint16_t>();break; + case PORTABLE_RAW_SIZE_MARK_DWORD: v = read<uint32_t>();break; + case PORTABLE_RAW_SIZE_MARK_INT64: v = read<uint64_t>();break; + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask); + } + v >>= 2; + return v; + } + + template<class t_type> + storage_entry throwable_buffer_reader::read_se() + { + RECURSION_LIMITATION(); + t_type v; + read(v); + return storage_entry(v); + } + + template<> + inline storage_entry throwable_buffer_reader::read_se<std::string>() + { + RECURSION_LIMITATION(); + return storage_entry(read<std::string>()); + } + + + template<> + inline storage_entry throwable_buffer_reader::read_se<section>() + { + RECURSION_LIMITATION(); + section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio + storage_entry se(s); + section& section_entry = boost::get<section>(se); + read(section_entry); + return se; + } + + template<> + inline storage_entry throwable_buffer_reader::read_se<array_entry>() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses"); + return load_storage_array_entry(ent_type); + } + + inline + storage_entry throwable_buffer_reader::load_storage_entry() + { + RECURSION_LIMITATION(); + uint8_t ent_type = 0; + read(ent_type); + if(ent_type&SERIALIZE_FLAG_ARRAY) + return load_storage_array_entry(ent_type); + + switch(ent_type) + { + case SERIALIZE_TYPE_INT64: return read_se<int64_t>(); + case SERIALIZE_TYPE_INT32: return read_se<int32_t>(); + case SERIALIZE_TYPE_INT16: return read_se<int16_t>(); + case SERIALIZE_TYPE_INT8: return read_se<int8_t>(); + case SERIALIZE_TYPE_UINT64: return read_se<uint64_t>(); + case SERIALIZE_TYPE_UINT32: return read_se<uint32_t>(); + case SERIALIZE_TYPE_UINT16: return read_se<uint16_t>(); + case SERIALIZE_TYPE_UINT8: return read_se<uint8_t>(); + case SERIALIZE_TYPE_DUOBLE: return read_se<double>(); + case SERIALIZE_TYPE_BOOL: return read_se<bool>(); + case SERIALIZE_TYPE_STRING: return read_se<std::string>(); + case SERIALIZE_TYPE_OBJECT: return read_se<section>(); + case SERIALIZE_TYPE_ARRAY: return read_se<array_entry>(); + default: + CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type); + } + } + inline + void throwable_buffer_reader::read(section& sec) + { + RECURSION_LIMITATION(); + sec.m_entries.clear(); + size_t count = read_varint(); + while(count--) + { + //read section name string + std::string sec_name; + read_sec_name(sec_name); + sec.m_entries.insert(std::make_pair(sec_name, load_storage_entry())); + } + } + inline + void throwable_buffer_reader::read(std::string& str) + { + RECURSION_LIMITATION(); + size_t len = read_varint(); + CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len); + CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count); + //do this manually to avoid double memory write in huge strings (first time at resize, second at read) + str.assign((const char*)m_ptr, len); + m_ptr+=len; + m_count -= len; + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_from_json.h b/contrib/epee/include/storages/portable_storage_from_json.h new file mode 100644 index 000000000..557db3da6 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_from_json.h @@ -0,0 +1,379 @@ +// 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 "parserse_base_utils.h" +#include "file_io_utils.h" + +namespace epee +{ + using namespace misc_utils::parse; + namespace serialization + { + namespace json + { +#define CHECK_ISSPACE() if(!isspace(*it)){ ASSERT_MES_AND_THROW("Wrong JSON character at: " << std::string(it, buf_end));} + + /*inline void parse_error() + { + ASSERT_MES_AND_THROW("json parse error"); + }*/ + template<class t_storage> + inline void run_handler(typename t_storage::hsection current_section, std::string::const_iterator& sec_buf_begin, std::string::const_iterator buf_end, t_storage& stg) + { + + std::string::const_iterator sub_element_start; + std::string name; + typename t_storage::harray h_array = nullptr; + enum match_state + { + match_state_lookup_for_section_start, + match_state_lookup_for_name, + match_state_waiting_separator, + match_state_wonder_after_separator, + match_state_wonder_after_value, + match_state_wonder_array, + match_state_array_after_value, + match_state_array_waiting_value, + match_state_error + }; + + enum array_mode + { + array_mode_undifined = 0, + array_mode_sections, + array_mode_string, + array_mode_numbers, + array_mode_booleans + }; + + match_state state = match_state_lookup_for_section_start; + array_mode array_md = array_mode_undifined; + std::string::const_iterator it = sec_buf_begin; + for(;it != buf_end;it++) + { + switch (state) + { + case match_state_lookup_for_section_start: + if(*it == '{') + state = match_state_lookup_for_name; + else CHECK_ISSPACE(); + break; + case match_state_lookup_for_name: + switch(*it) + { + case '"': + match_string2(it, buf_end, name); + state = match_state_waiting_separator; + break; + case '}': + //this is it! section ends here. + //seems that it is empty section + sec_buf_begin = it; + return; + default: + CHECK_ISSPACE(); + } + break; + case match_state_waiting_separator: + if(*it == ':') + state = match_state_wonder_after_separator; + else CHECK_ISSPACE(); + break; + case match_state_wonder_after_separator: + if(*it == '"') + {//just a named string value started + std::string val; + match_string2(it, buf_end, val); + //insert text value + stg.set_value(name, val, current_section); + state = match_state_wonder_after_value; + }else if (isdigit(*it) || *it == '-') + {//just a named number value started + std::string val; + bool is_v_float = false;bool is_signed = false; + match_number2(it, buf_end, val, is_v_float, is_signed); + if(!is_v_float) + { + if(is_signed) + { + int64_t nval = boost::lexical_cast<int64_t>(val); + stg.set_value(name, nval, current_section); + }else + { + uint64_t nval = boost::lexical_cast<uint64_t >(val); + stg.set_value(name, nval, current_section); + } + }else + { + double nval = boost::lexical_cast<double>(val); + stg.set_value(name, nval, current_section); + } + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// could be null, true or false + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "null")) + { + state = match_state_wonder_after_value; + //just skip this, + }else if(boost::iequals(word, "true")) + { + stg.set_value(name, true, current_section); + state = match_state_wonder_after_value; + }else if(boost::iequals(word, "false")) + { + stg.set_value(name, false, current_section); + state = match_state_wonder_after_value; + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else if(*it == '{') + { + //sub section here + typename t_storage::hsection new_sec = stg.open_section(name, current_section, true); + CHECK_AND_ASSERT_THROW_MES(new_sec, "Failed to insert new section in json: " << std::string(it, buf_end)); + run_handler(new_sec, it, buf_end, stg); + state = match_state_wonder_after_value; + }else if(*it == '[') + {//array of something + state = match_state_wonder_array; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_after_value: + if(*it == ',') + state = match_state_lookup_for_name; + else if(*it == '}') + { + //this is it! section ends here. + sec_buf_begin = it; + return; + }else CHECK_ISSPACE(); + break; + case match_state_wonder_array: + if(*it == '[') + { + ASSERT_MES_AND_THROW("array of array not suppoerted yet :( sorry"); + //mean array of array + } + if(*it == '{') + { + //mean array of sections + typename t_storage::hsection new_sec = nullptr; + h_array = stg.insert_first_section(name, new_sec, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array&&new_sec, "failed to create new section"); + run_handler(new_sec, it, buf_end, stg); + state = match_state_array_after_value; + array_md = array_mode_sections; + }else if(*it == '"') + { + //mean array of strings + std::string val; + match_string2(it, buf_end, val); + h_array = stg.insert_first_value(name, val, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values entry"); + state = match_state_array_after_value; + array_md = array_mode_string; + }else if (isdigit(*it) || *it == '-') + {//array of numbers value started + std::string val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + if(!is_v_float) + { + int64_t nval = boost::lexical_cast<int64_t>(val);//bool res = string_tools::string_to_num_fast(val, nval); + h_array = stg.insert_first_value(name, nval, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + }else + { + double nval = boost::lexical_cast<double>(val);//bool res = string_tools::string_to_num_fast(val, nval); + h_array = stg.insert_first_value(name, nval, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + } + + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else if(*it == ']')//empty array + { + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else if(isalpha(*it) ) + {// array of booleans + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + h_array = stg.insert_first_value(name, true, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + }else if(boost::iequals(word, "false")) + { + h_array = stg.insert_first_value(name, false, current_section); + CHECK_AND_ASSERT_THROW_MES(h_array, " failed to insert values section entry"); + state = match_state_array_after_value; + array_md = array_mode_booleans; + + }else ASSERT_MES_AND_THROW("Unknown value keyword " << word) + }else CHECK_ISSPACE(); + break; + case match_state_array_after_value: + if(*it == ',') + state = match_state_array_waiting_value; + else if(*it == ']') + { + h_array = nullptr; + array_md = array_mode_undifined; + state = match_state_wonder_after_value; + }else CHECK_ISSPACE(); + break; + case match_state_array_waiting_value: + switch(array_md) + { + case array_mode_sections: + if(*it == '{') + { + typename t_storage::hsection new_sec = NULL; + bool res = stg.insert_next_section(h_array, new_sec); + CHECK_AND_ASSERT_THROW_MES(res&&new_sec, "failed to insert next section"); + run_handler(new_sec, it, buf_end, stg); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_string: + if(*it == '"') + { + std::string val; + match_string2(it, buf_end, val); + bool res = stg.insert_next_value(h_array, val); + CHECK_AND_ASSERT_THROW_MES(res, "failed to insert values"); + state = match_state_array_after_value; + }else CHECK_ISSPACE(); + break; + case array_mode_numbers: + if (isdigit(*it) || *it == '-') + {//array of numbers value started + std::string val; + bool is_v_float = false;bool is_signed_val = false; + match_number2(it, buf_end, val, is_v_float, is_signed_val); + bool insert_res = false; + if(!is_v_float) + { + boost::int64_t nval = boost::lexical_cast<int64_t>(val); //bool res = string_tools::string_to_num_fast(val, nval); + insert_res = stg.insert_next_value(h_array, nval); + + }else + { + //TODO: optimize here if need + double nval = boost::lexical_cast<double>(val); //string_tools::string_to_num_fast(val, nval); + insert_res = stg.insert_next_value(h_array, nval); + } + CHECK_AND_ASSERT_THROW_MES(insert_res, "Failed to insert next value"); + state = match_state_array_after_value; + array_md = array_mode_numbers; + }else CHECK_ISSPACE(); + break; + case array_mode_booleans: + if(isalpha(*it) ) + {// array of booleans + std::string word; + match_word2(it, buf_end, word); + if(boost::iequals(word, "true")) + { + bool r = stg.insert_next_value(h_array, true); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + }else if(boost::iequals(word, "false")) + { + bool r = stg.insert_next_value(h_array, false); + CHECK_AND_ASSERT_THROW_MES(r, " failed to insert values section entry"); + state = match_state_array_after_value; + } + else ASSERT_MES_AND_THROW("Unknown value keyword " << word); + }else CHECK_ISSPACE(); + break; + case array_mode_undifined: + default: + ASSERT_MES_AND_THROW("Bad array state"); + } + break; + case match_state_error: + default: + ASSERT_MES_AND_THROW("WRONG JSON STATE"); + } + } + } +/* +{ + "firstName": "John", + "lastName": "Smith", + "age": 25, + "address": { + "streetAddress": "21 2nd Street", + "city": "New York", + "state": "NY", + "postalCode": -10021, + "have_boobs": true, + "have_balls": false + }, + "phoneNumber": [ + { + "type": "home", + "number": "212 555-1234" + }, + { + "type": "fax", + "number": "646 555-4567" + } + ], + "phoneNumbers": [ + "812 123-1234", + "916 123-4567" + ] +} +*/ + template<class t_storage> + inline bool load_from_json(const std::string& buff_json, t_storage& stg) + { + std::string::const_iterator sec_buf_begin = buff_json.begin(); + try + { + run_handler(nullptr, sec_buf_begin, buff_json.end(), stg); + return true; + } + catch(const std::exception& ex) + { + LOG_PRINT_RED_L0("Failed to parse json, what: " << ex.what()); + return false; + } + catch(...) + { + LOG_PRINT_RED_L0("Failed to parse json"); + return false; + } + } + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_template_helper.h b/contrib/epee/include/storages/portable_storage_template_helper.h new file mode 100644 index 000000000..2163cb879 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_template_helper.h @@ -0,0 +1,120 @@ +// 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 "parserse_base_utils.h" +#include "portable_storage.h" +#include "file_io_utils.h" + +namespace epee +{ + namespace serialization + { + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool load_t_from_json(t_struct& out, const std::string& json_buff) + { + portable_storage ps; + bool rs = ps.load_from_json(json_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool load_t_from_json_file(t_struct& out, const std::string& json_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(json_file, f_buff)) + return false; + + return load_t_from_json(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool store_t_to_json(t_struct& str_in, std::string& json_buff, size_t indent = 0) + { + portable_storage ps; + str_in.store(ps); + ps.dump_as_json(json_buff, indent); + return true; + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + std::string store_t_to_json(t_struct& str_in, size_t indent = 0) + { + std::string json_buff; + store_t_to_json(str_in, json_buff, indent); + return std::move(json_buff); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool store_t_to_json_file(t_struct& str_in, const std::string& fpath) + { + std::string json_buff; + store_t_to_json(str_in, json_buff); + return file_io_utils::save_string_to_file(fpath, json_buff); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool load_t_from_binary(t_struct& out, const std::string& binary_buff) + { + portable_storage ps; + bool rs = ps.load_from_binary(binary_buff); + if(!rs) + return false; + + return out.load(ps); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool load_t_from_binary_file(t_struct& out, const std::string& binary_file) + { + std::string f_buff; + if(!file_io_utils::load_file_to_string(binary_file, f_buff)) + return false; + + return load_t_from_binary(out, f_buff); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + bool store_t_to_binary(t_struct& str_in, std::string& binary_buff, size_t indent = 0) + { + portable_storage ps; + str_in.store(ps); + return ps.store_to_binary(binary_buff); + } + //----------------------------------------------------------------------------------------------------------- + template<class t_struct> + std::string store_t_to_binary(t_struct& str_in, size_t indent = 0) + { + std::string binary_buff; + store_t_to_binary(str_in, binary_buff, indent); + return std::move(binary_buff); + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_bin.h b/contrib/epee/include/storages/portable_storage_to_bin.h new file mode 100644 index 000000000..baf90290a --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_bin.h @@ -0,0 +1,212 @@ +// 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 "misc_language.h" +#include "portable_storage_base.h" + +namespace epee +{ + namespace serialization + { + + template<class pack_value, class t_stream> + size_t pack_varint_t(t_stream& strm, uint8_t type_or, size_t& pv) + { + pack_value v = (*((pack_value*)&pv)) << 2; + v |= type_or; + strm.write((const char*)&v, sizeof(pack_value)); + return sizeof(pack_value); + } + + PRAGMA_WARNING_PUSH + PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-aliasing\"") + template<class t_stream> + size_t pack_varint(t_stream& strm, size_t val) + { //the first two bits always reserved for size information + + if(val <= 63) + {//mean enough one byte + return pack_varint_t<uint8_t>(strm, PORTABLE_RAW_SIZE_MARK_BYTE, val); + } + else if(val <= 16383) + {//mean need word + return pack_varint_t<uint16_t>(strm, PORTABLE_RAW_SIZE_MARK_WORD, val); + }else if(val <= 1073741823) + {//mean need dword + return pack_varint_t<uint32_t>(strm, PORTABLE_RAW_SIZE_MARK_DWORD, val); + }else + { + CHECK_AND_ASSERT_THROW_MES(val <= 4611686018427387903, "failed to pack varint - too big amount = " << val); + return pack_varint_t<uint64_t>(strm, PORTABLE_RAW_SIZE_MARK_INT64, val); + } + } + PRAGMA_WARNING_POP + + template<class t_stream> + bool put_string(t_stream& strm, const std::string& v) + { + pack_varint(strm, v.size()); + if(v.size()) + strm.write((const char*)v.data(), v.size()); + return true; + } + + template<class t_stream> + struct array_entry_store_visitor: public boost::static_visitor<bool> + { + t_stream& m_strm; + + template<class t_pod_type> + bool pack_pod_array_type(uint8_t contained_type, const array_entry_t<t_pod_type>& arr_pod) + { + uint8_t type = contained_type|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_pod.m_array.size()); + for(const t_pod_type& x: arr_pod.m_array) + m_strm.write((const char*)&x, sizeof(t_pod_type)); + return true; + } + + array_entry_store_visitor(t_stream& strm):m_strm(strm){} + bool operator()(const array_entry_t<uint64_t>& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const array_entry_t<uint32_t>& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const array_entry_t<uint16_t>& v){ return pack_pod_array_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const array_entry_t<uint8_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const array_entry_t<int64_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const array_entry_t<int32_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const array_entry_t<int16_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const array_entry_t<int8_t>& v) { return pack_pod_array_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const array_entry_t<double>& v) { return pack_pod_array_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const array_entry_t<bool>& v) { return pack_pod_array_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const array_entry_t<std::string>& arr_str) + { + uint8_t type = SERIALIZE_TYPE_STRING|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_str.m_array.size()); + for(const std::string& s: arr_str.m_array) + put_string(m_strm, s); + return true; + } + bool operator()(const array_entry_t<section>& arr_sec) + { + uint8_t type = SERIALIZE_TYPE_OBJECT|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arr_sec.m_array.size()); + for(const section& s: arr_sec.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + bool operator()(const array_entry_t<array_entry>& arra_ar) + { + uint8_t type = SERIALIZE_TYPE_ARRAY|SERIALIZE_FLAG_ARRAY; + m_strm.write((const char*)&type, 1); + pack_varint(m_strm, arra_ar.m_array.size()); + for(const array_entry& s: arra_ar.m_array) + pack_entry_to_buff(m_strm, s); + return true; + } + }; + + template<class t_stream> + struct storage_entry_store_visitor: public boost::static_visitor<bool> + { + t_stream& m_strm; + storage_entry_store_visitor(t_stream& strm):m_strm(strm){} + template<class pod_type> + bool pack_pod_type(uint8_t type, const pod_type& v) + { + m_strm.write((const char*)&type, 1); + m_strm.write((const char*)&v, sizeof(pod_type)); + return true; + } + //section, array_entry + bool operator()(const uint64_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT64, v);} + bool operator()(const uint32_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT32, v);} + bool operator()(const uint16_t& v){ return pack_pod_type(SERIALIZE_TYPE_UINT16, v);} + bool operator()(const uint8_t& v) { return pack_pod_type(SERIALIZE_TYPE_UINT8, v);} + bool operator()(const int64_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT64, v);} + bool operator()(const int32_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT32, v);} + bool operator()(const int16_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT16, v);} + bool operator()(const int8_t& v) { return pack_pod_type(SERIALIZE_TYPE_INT8, v);} + bool operator()(const double& v) { return pack_pod_type(SERIALIZE_TYPE_DUOBLE, v);} + bool operator()(const bool& v) { return pack_pod_type(SERIALIZE_TYPE_BOOL, v);} + bool operator()(const std::string& v) + { + uint8_t type = SERIALIZE_TYPE_STRING; + m_strm.write((const char*)&type, 1); + put_string(m_strm, v); + return true; + } + bool operator()(const section& v) + { + uint8_t type = SERIALIZE_TYPE_OBJECT; + m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + + bool operator()(const array_entry& v) + { + //uint8_t type = SERIALIZE_TYPE_ARRAY; + //m_strm.write((const char*)&type, 1); + return pack_entry_to_buff(m_strm, v); + } + }; + + template<class t_stream> + bool pack_entry_to_buff(t_stream& strm, const array_entry& ae) + { + array_entry_store_visitor<t_stream> aesv(strm); + return boost::apply_visitor(aesv, ae); + } + + template<class t_stream> + bool pack_entry_to_buff(t_stream& strm, const storage_entry& se) + { + storage_entry_store_visitor<t_stream> sv(strm); + return boost::apply_visitor(sv, se); + } + + template<class t_stream> + bool pack_entry_to_buff(t_stream& strm, const section& sec) + { + typedef std::map<std::string, storage_entry>::value_type section_pair; + pack_varint(strm, sec.m_entries.size()); + for(const section_pair& se: sec.m_entries) + { + CHECK_AND_ASSERT_THROW_MES(se.first.size() < std::numeric_limits<uint8_t>::max(), "storage_entry_name is too long: " << se.first.size() << ", val: " << se.first); + uint8_t len = static_cast<uint8_t>(se.first.size()); + strm.write((const char*)&len, sizeof(len)); + strm.write(se.first.data(), size_t(len)); + pack_entry_to_buff(strm, se.second); + } + return true; + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_to_json.h b/contrib/epee/include/storages/portable_storage_to_json.h new file mode 100644 index 000000000..aff85b201 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_to_json.h @@ -0,0 +1,172 @@ +// 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 "misc_language.h" +#include "portable_storage_base.h" + +namespace epee +{ + namespace serialization + { + + template<class t_stream> + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const std::string& v, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const bool& v, size_t indent); + template<class t_stream, class t_type> + void dump_as_json(t_stream& strm, const t_type& v, size_t indent); + template<class t_stream> + void dump_as_json(t_stream& strm, const section& sec, size_t indent); + + + inline std::string make_indent(size_t indent) + { + return std::string(indent*2, ' '); + } + + template<class t_stream> + struct array_entry_store_to_json_visitor: public boost::static_visitor<void> + { + t_stream& m_strm; + size_t m_indent; + array_entry_store_to_json_visitor(t_stream& strm, size_t indent):m_strm(strm), m_indent(indent){} + + template<class t_type> + void operator()(const array_entry_t<t_type>& a) + { + m_strm << "["; + if(a.m_array.size()) + { + auto last_it = --a.m_array.end(); + for(auto it = a.m_array.begin(); it != a.m_array.end(); it++) + { + dump_as_json(m_strm, *it, m_indent); + if(it != last_it) + m_strm << ","; + } + } + m_strm << "]"; + } + }; + + template<class t_stream> + struct storage_entry_store_to_json_visitor: public boost::static_visitor<void> + { + t_stream& m_strm; + size_t m_indent; + storage_entry_store_to_json_visitor(t_stream& strm, size_t indent):m_strm(strm), m_indent(indent) + {} + //section, array_entry + template<class visited_type> + void operator()(const visited_type& v) + { + dump_as_json(m_strm, v, m_indent); + } + }; + + template<class t_stream> + void dump_as_json(t_stream& strm, const array_entry& ae, size_t indent) + { + array_entry_store_to_json_visitor<t_stream> aesv(strm, indent); + boost::apply_visitor(aesv, ae); + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const storage_entry& se, size_t indent) + { + storage_entry_store_to_json_visitor<t_stream> sv(strm, indent); + boost::apply_visitor(sv, se); + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const std::string& v, size_t indent) + { + strm << "\"" << misc_utils::parse::transform_to_escape_sequence(v) << "\""; + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const int8_t& v, size_t indent) + { + strm << static_cast<int32_t>(v); + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const uint8_t& v, size_t indent) + { + strm << static_cast<int32_t>(v); + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const bool& v, size_t indent) + { + if(v) + strm << "true"; + else + strm << "false"; + } + + + + template<class t_stream, class t_type> + void dump_as_json(t_stream& strm, const t_type& v, size_t indent) + { + strm << v; + } + + template<class t_stream> + void dump_as_json(t_stream& strm, const section& sec, size_t indent) + { + size_t local_indent = indent + 1; + strm << "{\r\n"; + std::string indent_str = make_indent(local_indent); + if(sec.m_entries.size()) + { + auto it_last = --sec.m_entries.end(); + for(auto it = sec.m_entries.begin(); it!= sec.m_entries.end();it++) + { + strm << indent_str << "\"" << misc_utils::parse::transform_to_escape_sequence(it->first) << "\"" << ": "; + dump_as_json(strm, it->second, local_indent); + if(it_last != it) + strm << ","; + strm << "\r\n"; + } + } + strm << make_indent(indent) << "}"; + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/storages/portable_storage_val_converters.h b/contrib/epee/include/storages/portable_storage_val_converters.h new file mode 100644 index 000000000..6ea505886 --- /dev/null +++ b/contrib/epee/include/storages/portable_storage_val_converters.h @@ -0,0 +1,169 @@ +// 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 "misc_language.h" +#include "portable_storage_base.h" +#include "warnings.h" + +namespace epee +{ + namespace serialization + { +#define ASSERT_AND_THROW_WRONG_CONVERSION() ASSERT_MES_AND_THROW("WRONG DATA CONVERSION: from type=" << typeid(from).name() << " to type " << typeid(to).name()) + + template<typename from_type, typename to_type> + void convert_int_to_uint(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) + CHECK_AND_ASSERT_THROW_MES(from >=0, "unexpected int value with signed storage value less than 0, and unsigned receiver value"); +DISABLE_GCC_AND_CLANG_WARNING(sign-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits<to_type>::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits<to_type>::max()); + to = static_cast<to_type>(from); +POP_WARNINGS + } + template<typename from_type, typename to_type> + void convert_int_to_int(const from_type& from, to_type& to) + { + CHECK_AND_ASSERT_THROW_MES(from >= boost::numeric::bounds<to_type>::lowest(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with lowest possible value = " << boost::numeric::bounds<to_type>::lowest()); +PUSH_WARNINGS +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits<to_type>::max(), "int value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits<to_type>::max()); +POP_WARNINGS + to = static_cast<to_type>(from); + } + template<typename from_type, typename to_type> + void convert_uint_to_any_int(const from_type& from, to_type& to) + { +PUSH_WARNINGS +DISABLE_VS_WARNINGS(4018) +DISABLE_CLANG_WARNING(tautological-constant-out-of-range-compare) + CHECK_AND_ASSERT_THROW_MES(from <= std::numeric_limits<to_type>::max(), "uint value overhead: try to set value " << from << " to type " << typeid(to_type).name() << " with max possible value = " << std::numeric_limits<to_type>::max()); + to = static_cast<to_type>(from); +POP_WARNINGS + } + + template<typename from_type, typename to_type, bool, bool> //is from signed, is from to signed + struct convert_to_signed_unsigned; + + template<typename from_type, typename to_type> + struct convert_to_signed_unsigned<from_type, to_type, true, true> + { + static void convert(const from_type& from, to_type& to) + {//from signed to signed + convert_int_to_int(from, to); + } + }; + + template<typename from_type, typename to_type> + struct convert_to_signed_unsigned<from_type, to_type, true, false> + { + static void convert(const from_type& from, to_type& to) + {//from signed to unsigned + convert_int_to_uint(from, to); + } + }; + + template<typename from_type, typename to_type> + struct convert_to_signed_unsigned<from_type, to_type, false, true> + { + static void convert(const from_type& from, to_type& to) + {//from unsigned to signed + convert_uint_to_any_int(from, to); + } + }; + + template<typename from_type, typename to_type> + struct convert_to_signed_unsigned<from_type, to_type, false, false> + { + static void convert(const from_type& from, to_type& to) + { + //from unsigned to unsigned + convert_uint_to_any_int(from, to); + } + }; + + template<typename from_type, typename to_type, bool> + struct convert_to_integral; + + template<typename from_type, typename to_type> + struct convert_to_integral<from_type, to_type, true> + { + static void convert(const from_type& from, to_type& to) + { + convert_to_signed_unsigned<from_type, to_type, std::is_signed<from_type>::value, std::is_signed<to_type>::value>::convert(from, to); + } + }; + + template<typename from_type, typename to_type> + struct convert_to_integral<from_type, to_type, false> + { + static void convert(const from_type& from, to_type& to) + { + ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + template<class from_type, class to_type> + struct is_convertable: std::integral_constant<bool, + std::is_integral<to_type>::value && + std::is_integral<from_type>::value && + !std::is_same<from_type, bool>::value && + !std::is_same<to_type, bool>::value > {}; + + template<typename from_type, typename to_type, bool> + struct convert_to_same; + + template<typename from_type, typename to_type> + struct convert_to_same<from_type, to_type, true> + { + static void convert(const from_type& from, to_type& to) + { + to = from; + } + }; + + template<typename from_type, typename to_type> + struct convert_to_same<from_type, to_type, false> + { + static void convert(const from_type& from, to_type& to) + { + convert_to_integral<from_type, to_type, is_convertable<from_type, to_type>::value>::convert(from, to);// ASSERT_AND_THROW_WRONG_CONVERSION(); + } + }; + + + template<class from_type, class to_type> + void convert_t(const from_type& from, to_type& to) + { + convert_to_same<from_type, to_type, std::is_same<to_type, from_type>::value>::convert(from, to); + } + } +}
\ No newline at end of file diff --git a/contrib/epee/include/string_coding.h b/contrib/epee/include/string_coding.h new file mode 100644 index 000000000..a2e3d6eb2 --- /dev/null +++ b/contrib/epee/include/string_coding.h @@ -0,0 +1,295 @@ +// 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 _STRING_CODING_H_ +#define _STRING_CODING_H_ + +#include <string> +//#include "md5_l.h" +namespace epee +{ +namespace string_encoding +{ + inline std::string convert_to_ansii(const std::wstring& str_from) + { + + std::string res(str_from.begin(), str_from.end()); + return res; + /* + std::string result; + std::locale loc; + for(unsigned int i= 0; i < str_from.size(); ++i) + { + result += std::use_facet<std::ctype<wchar_t> >(loc).narrow(str_from[i]); + } + return result; + */ + + //return boost::lexical_cast<std::string>(str_from); + /* + std::string str_trgt; + if(!str_from.size()) + return str_trgt; + int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); + if(!cb) + return str_trgt; + str_trgt.resize(cb); + ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), + (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); + return str_trgt;*/ + } +#ifdef WINDOWS_PLATFORM_EX + inline std::string convert_to_ansii_win(const std::wstring& str_from) + { + + int code_page = CP_ACP; + std::string str_trgt; + if(!str_from.size()) + return str_trgt; + int cb = ::WideCharToMultiByte( code_page, 0, str_from.data(), (__int32)str_from.size(), 0, 0, 0, 0 ); + if(!cb) + return str_trgt; + str_trgt.resize(cb); + ::WideCharToMultiByte( code_page, 0, str_from.data(), (int)str_from.size(), + (char*)str_trgt.data(), (int)str_trgt.size(), 0, 0); + return str_trgt; + } +#endif + + inline std::string convert_to_ansii(const std::string& str_from) + { + return str_from; + } + + inline std::wstring convert_to_unicode(const std::string& str_from) + { + std::wstring result; + std::locale loc; + for(unsigned int i= 0; i < str_from.size(); ++i) + { + result += std::use_facet<std::ctype<wchar_t> >(loc).widen(str_from[i]); + } + return result; + + //return boost::lexical_cast<std::wstring>(str_from); + /* + std::wstring str_trgt; + if(!str_from.size()) + return str_trgt; + + int cb = ::MultiByteToWideChar( code_page, 0, str_from.data(), (int)str_from.size(), 0, 0 ); + if(!cb) + return str_trgt; + + str_trgt.resize(cb); + ::MultiByteToWideChar( code_page, 0, str_from.data(),(int)str_from.size(), + (wchar_t*)str_trgt.data(),(int)str_trgt.size()); + return str_trgt;*/ + } + inline std::wstring convert_to_unicode(const std::wstring& str_from) + { + return str_from; + } + + template<class target_string> + inline target_string convert_to_t(const std::wstring& str_from); + + template<> + inline std::string convert_to_t<std::string>(const std::wstring& str_from) + { + return convert_to_ansii(str_from); + } + + template<> + inline std::wstring convert_to_t<std::wstring>(const std::wstring& str_from) + { + return str_from; + } + + template<class target_string> + inline target_string convert_to_t(const std::string& str_from); + + template<> + inline std::string convert_to_t<std::string>(const std::string& str_from) + { + return str_from; + } + + template<> + inline std::wstring convert_to_t<std::wstring>(const std::string& str_from) + { + return convert_to_unicode(str_from); + } + + inline + std::string& base64_chars() + { + + static std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + return chars; + + } + + inline + std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars()[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars()[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + + } + + inline + std::string base64_encode(const std::string& str) + { + return base64_encode((unsigned char const* )str.data(), str.size()); + } + + inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); + } + + + inline + std::string base64_decode(std::string const& encoded_string) { + size_t in_len = encoded_string.size(); + size_t i = 0; + size_t j = 0; + size_t in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = (unsigned char)base64_chars().find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = (unsigned char)base64_chars().find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; + } + + //md5 +#ifdef MD5_H + inline + std::string get_buf_as_hex_string(const void* pbuf, size_t len) + { + std::ostringstream result; + + const unsigned char* p_buff = (const unsigned char*)pbuf; + + for(unsigned int i=0;i<len;i++) + { // convert md to hex-represented string (hex-letters in upper case!) + result << std::setw(2) << std::setfill('0') + << std::setbase(16) << std::nouppercase + << (int)*p_buff++; + } + + return result.str(); + } + + inline + std::string get_md5_as_hexstring(const void* pbuff, size_t len) + { + unsigned char output[16] = {0}; + md5::md5((unsigned char*)pbuff, static_cast<unsigned int>(len), output); + return get_buf_as_hex_string(output, sizeof(output)); + } + + inline + std::string get_md5_as_hexstring(const std::string& src) + { + return get_md5_as_hexstring(src.data(), src.size()); + } +#endif + + +} +} + +#endif //_STRING_CODING_H_ diff --git a/contrib/epee/include/string_tools.h b/contrib/epee/include/string_tools.h new file mode 100644 index 000000000..53be7c3ae --- /dev/null +++ b/contrib/epee/include/string_tools.h @@ -0,0 +1,736 @@ +// 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 _STRING_TOOLS_H_ +#define _STRING_TOOLS_H_ + +//#include <objbase.h> +#include <locale> +#include <cstdlib> +//#include <strsafe.h> +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_io.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/asio.hpp> +#include <boost/algorithm/string/compare.hpp> +#include <boost/algorithm/string.hpp> +#include "warnings.h" + + +#ifndef OUT + #define OUT +#endif + +#ifdef WINDOWS_PLATFORM +#pragma comment (lib, "Rpcrt4.lib") +#endif + +namespace epee +{ +namespace string_tools +{ + inline std::wstring get_str_from_guid(const boost::uuids::uuid& rid) + { + return boost::lexical_cast<std::wstring>(rid); + } + //---------------------------------------------------------------------------- + inline std::string get_str_from_guid_a(const boost::uuids::uuid& rid) + { + return boost::lexical_cast<std::string>(rid); + } + //---------------------------------------------------------------------------- + inline bool get_guid_from_string( boost::uuids::uuid& inetifer, std::wstring str_id) + { + if(str_id.size() < 36) + return false; + + if('{' == *str_id.begin()) + str_id.erase(0, 1); + + if('}' == *(--str_id.end())) + str_id.erase(--str_id.end()); + + try + { + inetifer = boost::lexical_cast<boost::uuids::uuid>(str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + inline bool get_guid_from_string(OUT boost::uuids::uuid& inetifer, const std::string& str_id) + { + std::string local_str_id = str_id; + if(local_str_id.size() < 36) + return false; + + if('{' == *local_str_id.begin()) + local_str_id.erase(0, 1); + + if('}' == *(--local_str_id.end())) + local_str_id.erase(--local_str_id.end()); + + try + { + inetifer = boost::lexical_cast<boost::uuids::uuid>(local_str_id); + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template<class CharT> + std::basic_string<CharT> buff_to_hex(const std::basic_string<CharT>& s) + { + using namespace std; + basic_stringstream<CharT> hexStream; + hexStream << hex << noshowbase << setw(2); + + for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) + { + hexStream << "0x"<< static_cast<unsigned int>(static_cast<unsigned char>(*it)) << " "; + } + return hexStream.str(); + } + //---------------------------------------------------------------------------- + template<class CharT> + std::basic_string<CharT> buff_to_hex_nodelimer(const std::basic_string<CharT>& s) + { + using namespace std; + basic_stringstream<CharT> hexStream; + hexStream << hex << noshowbase; + + for(typename std::basic_string<CharT>::const_iterator it = s.begin(); it != s.end(); it++) + { + hexStream << setw(2) << setfill('0') << static_cast<unsigned int>(static_cast<unsigned char>(*it)); + } + return hexStream.str(); + } + //---------------------------------------------------------------------------- + template<class CharT> + bool parse_hexstr_to_binbuff(const std::basic_string<CharT>& s, std::basic_string<CharT>& res) + { + res.clear(); + try + { + long v = 0; + for(size_t i = 0; i < (s.size() + 1) / 2; i++) + { + CharT byte_str[3]; + size_t copied = s.copy(byte_str, 2, 2 * i); + byte_str[copied] = CharT(0); + CharT* endptr; + v = strtoul(byte_str, &endptr, 16); + if (v < 0 || 0xFF < v || endptr != byte_str + copied) + { + return false; + } + res.push_back(static_cast<unsigned char>(v)); + } + + return true; + }catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template<class t_pod_type> + bool parse_tpod_from_hex_string(const std::string& str_hash, t_pod_type& t_pod) + { + std::string buf; + bool res = epee::string_tools::parse_hexstr_to_binbuff(str_hash, buf); + if (!res || buf.size() != sizeof(t_pod_type)) + { + return false; + } + else + { + buf.copy(reinterpret_cast<char *>(&t_pod), sizeof(t_pod_type)); + return true; + } + } + //---------------------------------------------------------------------------- +PUSH_WARNINGS +DISABLE_GCC_WARNING(maybe-uninitialized) + template<class XType> + inline bool get_xtype_from_string(OUT XType& val, const std::string& str_id) + { + try + { + val = boost::lexical_cast<XType>(str_id); + return true; + } + catch(std::exception& /*e*/) + { + //const char* pmsg = e.what(); + return false; + } + + catch(...) + { + return false; + } + + return true; + } +POP_WARNINGS + //--------------------------------------------------- + template<typename int_t> + bool get_xnum_from_hex_string(const std::string str, int_t& res ) + { + try + { + std::stringstream ss; + ss << std::hex << str; + ss >> res; + return true; + } + catch(...) + { + return false; + } + } + //---------------------------------------------------------------------------- + template<class XType> + inline bool xtype_to_string(const XType& val, std::string& str) + { + try + { + str = boost::lexical_cast<std::string>(val); + } + catch(...) + { + return false; + } + + return true; + } + + typedef std::map<std::string, std::string> command_line_params_a; + typedef std::map<std::wstring, std::wstring> command_line_params_w; + + template<class t_string> + bool parse_commandline(std::map<t_string, t_string>& res, int argc, char** argv) + { + t_string key; + for(int i = 1; i < argc; i++) + { + if(!argv[i]) + break; + t_string s = argv[i]; + std::string::size_type p = s.find('='); + if(std::string::npos == p) + { + res[s] = ""; + }else + { + std::string ss; + t_string nm = s.substr(0, p); + t_string vl = s.substr(p+1, s.size()); + res[nm] = vl; + } + } + return true; + } + +/* template<typename t_type> + bool get_xparam_from_command_line(const std::map<std::string, std::string>& res, const std::basic_string<typename t_string::value_type> & key, t_type& val) + { + + } + */ + + template<class t_string, typename t_type> + bool get_xparam_from_command_line(const std::map<t_string, t_string>& res, const t_string & key, t_type& val) + { + typename std::map<t_string, t_string>::const_iterator it = res.find(key); + if(it == res.end()) + return false; + + if(it->second.size()) + { + return get_xtype_from_string(val, it->second); + } + + return true; + } + + template<class t_string, typename t_type> + t_type get_xparam_from_command_line(const std::map<t_string, t_string>& res, const t_string & key, const t_type& default_value) + { + typename std::map<t_string, t_string>::const_iterator it = res.find(key); + if(it == res.end()) + return default_value; + + if(it->second.size()) + { + t_type s; + get_xtype_from_string(s, it->second); + return s; + } + + return default_value; + } + + template<class t_string> + bool have_in_command_line(const std::map<t_string, t_string>& res, const std::basic_string<typename t_string::value_type>& key) + { + typename std::map<t_string, t_string>::const_iterator it = res.find(key); + if(it == res.end()) + return false; + + return true; + } + + //---------------------------------------------------------------------------- +//#ifdef _WINSOCK2API_ + inline std::string get_ip_string_from_int32(boost::uint32_t ip) + { + in_addr adr; + adr.s_addr = ip; + const char* pbuf = inet_ntoa(adr); + if(pbuf) + return pbuf; + else + return "[failed]"; + } + //---------------------------------------------------------------------------- + inline bool get_ip_int32_from_string(boost::uint32_t& ip, const std::string& ip_str) + { + ip = inet_addr(ip_str.c_str()); + if(INADDR_NONE == ip) + return false; + + return true; + } + //---------------------------------------------------------------------------- + inline bool parse_peer_from_string(uint32_t& ip, uint32_t& port, const std::string& addres) + { + //parse ip and address + std::string::size_type p = addres.find(':'); + if(p == std::string::npos) + { + return false; + } + std::string ip_str = addres.substr(0, p); + std::string port_str = addres.substr(p+1, addres.size()); + + if(!get_ip_int32_from_string(ip, ip_str)) + { + return false; + } + + if(!get_xtype_from_string(port, port_str)) + { + return false; + } + return true; + } + +//#endif + //---------------------------------------------------------------------------- + template<typename t> + inline std::string get_t_as_hex_nwidth(const t& v, std::streamsize w = 8) + { + std::stringstream ss; + ss << std::setfill ('0') << std::setw (w) << std::hex << std::noshowbase; + ss << v; + return ss.str(); + } + + inline std::string num_to_string_fast(boost::int64_t val) + { + /* + char buff[30] = {0}; + i64toa_s(val, buff, sizeof(buff)-1, 10); + return buff;*/ + return boost::lexical_cast<std::string>(val); + } + //---------------------------------------------------------------------------- + inline bool string_to_num_fast(const std::string& buff, boost::int64_t& val) + { + //return get_xtype_from_string(val, buff); +#if (defined _MSC_VER) + val = _atoi64(buff.c_str()); +#else + val = atoll(buff.c_str()); +#endif + /* + * val = atoi64(buff.c_str()); + */ + if(buff != "0" && val == 0) + return false; + return true; + } + //---------------------------------------------------------------------------- + inline bool string_to_num_fast(const std::string& buff, int& val) + { + val = atoi(buff.c_str()); + if(buff != "0" && val == 0) + return false; + + return true; + } + //---------------------------------------------------------------------------- +#ifdef WINDOWS_PLATFORM + inline std::string system_time_to_string(const SYSTEMTIME& st) + { + + /* + TIME_ZONE_INFORMATION tzi; + GetTimeZoneInformation(&tzi); + SystemTimeToTzSpecificLocalTime(&tzi, &stUTC, &stLocal); + */ + + char szTime[25], szDate[25]; + ::GetTimeFormatA( + LOCALE_USER_DEFAULT, // locale + TIME_FORCE24HOURFORMAT, // options + &st, // time + NULL, // time format string + szTime, // formatted string buffer + 25 // size of string buffer + ); + + ::GetDateFormatA( + LOCALE_USER_DEFAULT, // locale + NULL, // options + &st, // date + NULL, // date format + szDate, // formatted string buffer + 25 // size of buffer + ); + szTime[24] = szDate[24] = 0; //be happy :) + + std::string res = szDate; + (res += " " )+= szTime; + return res; + + } +#endif + //---------------------------------------------------------------------------- + + inline bool compare_no_case(const std::string& str1, const std::string& str2) + { + + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + inline bool compare_no_case(const std::wstring& str1, const std::wstring& str2) + { + return !boost::iequals(str1, str2); + } + //---------------------------------------------------------------------------- + inline bool is_match_prefix(const std::wstring& str1, const std::wstring& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + inline bool is_match_prefix(const std::string& str1, const std::string& prefix) + { + if(prefix.size()>str1.size()) + return false; + + if(!compare_no_case(str1.substr(0, prefix.size()), prefix)) + return true; + else + return false; + } + //---------------------------------------------------------------------------- + inline std::string& get_current_module_name() + { + static std::string module_name; + return module_name; + } + //---------------------------------------------------------------------------- + inline std::string& get_current_module_folder() + { + static std::string module_folder; + return module_folder; + } + //---------------------------------------------------------------------------- +#ifdef _WIN32 + inline std::string get_current_module_path() + { + char pname [5000] = {0}; + GetModuleFileNameA( NULL, pname, sizeof(pname)); + pname[sizeof(pname)-1] = 0; //be happy ;) + return pname; + } +#endif + //---------------------------------------------------------------------------- + inline bool set_module_name_and_folder(const std::string& path_to_process_) + { + std::string path_to_process = path_to_process_; +#ifdef _WIN32 + path_to_process = get_current_module_path(); +#endif + std::string::size_type a = path_to_process.rfind( '\\' ); + if(a == std::string::npos ) + { + a = path_to_process.rfind( '/' ); + } + if ( a != std::string::npos ) + { + get_current_module_name() = path_to_process.substr(a+1, path_to_process.size()); + get_current_module_folder() = path_to_process.substr(0, a); + return true; + }else + return false; + + } + + //---------------------------------------------------------------------------- + inline bool trim_left(std::string& str) + { + for(std::string::iterator it = str.begin(); it!= str.end() && isspace(static_cast<unsigned char>(*it));) + str.erase(str.begin()); + + return true; + } + //---------------------------------------------------------------------------- + inline bool trim_right(std::string& str) + { + + for(std::string::reverse_iterator it = str.rbegin(); it!= str.rend() && isspace(static_cast<unsigned char>(*it));) + str.erase( --((it++).base())); + + return true; + } + //---------------------------------------------------------------------------- + inline std::string& trim(std::string& str) + { + + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + inline std::string trim(const std::string& str_) + { + std::string str = str_; + trim_left(str); + trim_right(str); + return str; + } + //---------------------------------------------------------------------------- + template<class t_pod_type> + std::string pod_to_hex(const t_pod_type& s) + { + std::string buff; + buff.assign(reinterpret_cast<const char*>(&s), sizeof(s)); + return buff_to_hex_nodelimer(buff); + } + //---------------------------------------------------------------------------- + template<class t_pod_type> + bool hex_to_pod(const std::string& hex_str, t_pod_type& s) + { + std::string hex_str_tr = trim(hex_str); + if(sizeof(s)*2 != hex_str.size()) + return false; + std::string bin_buff; + if(!parse_hexstr_to_binbuff(hex_str_tr, bin_buff)) + return false; + if(bin_buff.size()!=sizeof(s)) + return false; + + s = *(t_pod_type*)bin_buff.data(); + return true; + } + //---------------------------------------------------------------------------- + inline std::string get_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return res; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + inline std::string get_filename_from_path(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('\\'); + if(std::string::npos == pos) + return str; + + res = str.substr(pos+1, str.size()-pos); + return res; + } + //---------------------------------------------------------------------------- + + + + inline std::string cut_off_extension(const std::string& str) + { + std::string res; + std::string::size_type pos = str.rfind('.'); + if(std::string::npos == pos) + return str; + + res = str.substr(0, pos); + return res; + } + + //---------------------------------------------------------------------------- +#ifdef _WININET_ + inline std::string get_string_from_systemtime(const SYSTEMTIME& sys_time) + { + std::string result_string; + + char buff[100] = {0}; + BOOL res = ::InternetTimeFromSystemTimeA(&sys_time, INTERNET_RFC1123_FORMAT, buff, 99); + if(!res) + { + LOG_ERROR("Failed to load SytemTime to string"); + } + + result_string = buff; + return result_string; + + } + //------------------------------------------------------------------------------------- + inline SYSTEMTIME get_systemtime_from_string(const std::string& buff) + { + SYSTEMTIME result_time = {0}; + + BOOL res = ::InternetTimeToSystemTimeA(buff.c_str(), &result_time, NULL); + if(!res) + { + LOG_ERROR("Failed to load SytemTime from string " << buff << "interval set to 15 minutes"); + } + + return result_time; + } +#endif + +#ifdef WINDOWS_PLATFORM + static const DWORD INFO_BUFFER_SIZE = 10000; + + static const wchar_t* get_pc_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetComputerNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } + + static const wchar_t* get_user_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = INFO_BUFFER_SIZE; + static bool init = false; + + if (!init) { + if (!GetUserNameW( info, &bufCharCount )) + info[0] = 0; + else + init = true; + } + + return info; + } +#endif + +#ifdef _LM_ + static const wchar_t* get_domain_name() + { + static wchar_t info[INFO_BUFFER_SIZE]; + static DWORD bufCharCount = 0; + static bool init = false; + + if (!init) { + LPWSTR domain( NULL ); + NETSETUP_JOIN_STATUS status; + info[0] = 0; + + if (NET_API_STATUS result = NetGetJoinInformation( NULL, &domain, &status )) + { + LOG_ERROR("get_domain_name error: " << log_space::get_win32_err_descr(result)); + } else + { + StringCchCopyW( info, sizeof(info)/sizeof( info[0] ), domain ); + NetApiBufferFree((void*)domain); + init = true; + } + } + + return info; + } +#endif +#ifdef WINDOWS_PLATFORM + inline + std::string load_resource_string_a(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + char buff[2000] = {0}; + + ::LoadStringA( h, id, buff, sizeof(buff)); + buff[sizeof(buff)-1] = 0; //be happy :) + return buff; + } + inline + std::wstring load_resource_string_w(int id, const char* pmodule_name = NULL) + { + //slow realization + HMODULE h = ::GetModuleHandleA( pmodule_name ); + + wchar_t buff[2000] = {0}; + + ::LoadStringW( h, id, buff, sizeof(buff) / sizeof( buff[0] ) ); + buff[(sizeof(buff)/sizeof(buff[0]))-1] = 0; //be happy :) + return buff; + } +#endif +} +} +#endif //_STRING_TOOLS_H_ diff --git a/contrib/epee/include/syncobj.h b/contrib/epee/include/syncobj.h new file mode 100644 index 000000000..ca7514ede --- /dev/null +++ b/contrib/epee/include/syncobj.h @@ -0,0 +1,241 @@ +// 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 __WINH_OBJ_H__ +#define __WINH_OBJ_H__ + +#include <condition_variable> +#include <mutex> +#include <boost/thread/locks.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/recursive_mutex.hpp> + +namespace epee +{ + + struct simple_event + { + simple_event() + { + rised = false; + } + std::mutex m_mx; + std::condition_variable m_cond_var; + bool rised; + + void rise() + { + std::unique_lock<std::mutex> lock(m_mx); + rised = true; + m_cond_var.notify_one(); + } + + void wait() + { + std::unique_lock<std::mutex> lock(m_mx); + while (!rised) + m_cond_var.wait(lock); + } + }; + + class critical_region; + + class critical_section + { + boost::recursive_mutex m_section; + + public: + //to make copy fake! + critical_section(const critical_section& section) + { + } + + critical_section() + { + } + + ~critical_section() + { + } + + void lock() + { + m_section.lock(); + //EnterCriticalSection( &m_section ); + } + + void unlock() + { + m_section.unlock(); + } + + bool tryLock() + { + return m_section.try_lock(); + } + + // to make copy fake + critical_section& operator=(const critical_section& section) + { + return *this; + } + }; + + + template<class t_lock> + class critical_region_t + { + t_lock& m_locker; + bool m_unlocked; + + critical_region_t(const critical_region_t&) {} + + public: + critical_region_t(t_lock& cs): m_locker(cs), m_unlocked(false) + { + m_locker.lock(); + } + + ~critical_region_t() + { + unlock(); + } + + void unlock() + { + if (!m_unlocked) + { + m_locker.unlock(); + m_unlocked = true; + } + } + }; + + +#if defined(WINDWOS_PLATFORM) + class shared_critical_section + { + public: + shared_critical_section() + { + ::InitializeSRWLock(&m_srw_lock); + } + ~shared_critical_section() + {} + + bool lock_shared() + { + AcquireSRWLockShared(&m_srw_lock); + return true; + } + bool unlock_shared() + { + ReleaseSRWLockShared(&m_srw_lock); + return true; + } + bool lock_exclusive() + { + ::AcquireSRWLockExclusive(&m_srw_lock); + return true; + } + bool unlock_exclusive() + { + ::ReleaseSRWLockExclusive(&m_srw_lock); + return true; + } + private: + SRWLOCK m_srw_lock; + }; + + + class shared_guard + { + public: + shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_shared(); + } + + ~shared_guard() + { + m_ref_sec.unlock_shared(); + } + + private: + shared_critical_section& m_ref_sec; + }; + + + class exclusive_guard + { + public: + exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_exclusive(); + } + + ~exclusive_guard() + { + m_ref_sec.unlock_exclusive(); + } + + private: + shared_critical_section& m_ref_sec; + }; +#endif + +#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) +#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) + +#define CRITICAL_REGION_LOCAL(x) epee::critical_region_t<decltype(x)> critical_region_var(x) +#define CRITICAL_REGION_BEGIN(x) { epee::critical_region_t<decltype(x)> critical_region_var(x) +#define CRITICAL_REGION_LOCAL1(x) epee::critical_region_t<decltype(x)> critical_region_var1(x) +#define CRITICAL_REGION_BEGIN1(x) { epee::critical_region_t<decltype(x)> critical_region_var1(x) + +#define CRITICAL_REGION_END() } + + +#if defined(WINDWOS_PLATFORM) + inline const char* get_wait_for_result_as_text(DWORD res) + { + switch(res) + { + case WAIT_ABANDONED: return "WAIT_ABANDONED"; + case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; + case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; + case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; + case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; + default: return "UNKNOWN CODE"; + } + } +#endif + +} + +#endif diff --git a/contrib/epee/include/time_helper.h b/contrib/epee/include/time_helper.h new file mode 100644 index 000000000..958176da6 --- /dev/null +++ b/contrib/epee/include/time_helper.h @@ -0,0 +1,159 @@ +// 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 <atltime.h> +//#include <sqlext.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/date_time/local_time/local_time.hpp> +#include "pragma_comp_defs.h" + +namespace epee +{ +namespace misc_utils +{ + +#ifdef __ATLTIME_H__ + + inline + bool get_time_t_from_ole_date(DATE src, time_t& res) + { + SYSTEMTIME st = {0}; + if(TRUE != ::VariantTimeToSystemTime(src, &st)) + return false; + ATL::CTime ss(st); + res = ss.GetTime(); + return true; + } +#endif + inline + std::string get_time_str(const time_t& time_) + { + + + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = localtime(&time_); +PRAGMA_WARNING_POP + + if(pt) + strftime( tmpbuf, 199, "%d.%m.%Y %H:%M:%S", pt ); + else + { + std::stringstream strs; + strs << "[wrong_time: " << std::hex << time_ << "]"; + return strs.str(); + } + return tmpbuf; + } + + inline + std::string get_time_str_v2(const time_t& time_) + { + + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = localtime(&time_); +PRAGMA_WARNING_POP + + if(pt) + strftime( tmpbuf, 199, "%Y_%m_%d %H_%M_%S", pt ); + else + { + std::stringstream strs; + strs << "[wrong_time: " << std::hex << time_ << "]"; + return strs.str(); + } + return tmpbuf; + } + + inline + std::string get_time_str_v3(const boost::posix_time::ptime& time_) + { + return boost::posix_time::to_simple_string(time_); + } + + + + inline std::string get_internet_time_str(const time_t& time_) + { + char tmpbuf[200] = {0}; + tm* pt = NULL; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4996) + pt = gmtime(&time_); +PRAGMA_WARNING_POP + strftime( tmpbuf, 199, "%a, %d %b %Y %H:%M:%S GMT", pt ); + return tmpbuf; + } + + inline std::string get_time_interval_string(const time_t& time_) + { + std::string res; + time_t tail = time_; +PRAGMA_WARNING_PUSH +PRAGMA_WARNING_DISABLE_VS(4244) + int days = tail/(60*60*24); + tail = tail%(60*60*24); + int hours = tail/(60*60); + tail = tail%(60*60); + int minutes = tail/(60); + tail = tail%(60); + int seconds = tail; +PRAGMA_WARNING_POP + res = std::string() + "d" + boost::lexical_cast<std::string>(days) + ".h" + boost::lexical_cast<std::string>(hours) + ".m" + boost::lexical_cast<std::string>(minutes) + ".s" + boost::lexical_cast<std::string>(seconds); + return res; + } + +#ifdef __SQLEXT + inline + bool odbc_time_to_oledb_taime(const SQL_TIMESTAMP_STRUCT& odbc_timestamp, DATE& oledb_date) + { + + SYSTEMTIME st = {0}; + st.wYear = odbc_timestamp.year; + st.wDay = odbc_timestamp.day; + st.wHour = odbc_timestamp.hour ; + st.wMilliseconds = (WORD)odbc_timestamp.fraction ; + st.wMinute = odbc_timestamp.minute ; + st.wMonth = odbc_timestamp.month ; + st.wSecond = odbc_timestamp.second ; + + if(TRUE != ::SystemTimeToVariantTime(&st, &oledb_date)) + return false; + return true; + } + +#endif +} +}
\ No newline at end of file diff --git a/contrib/epee/include/tiny_ini.h b/contrib/epee/include/tiny_ini.h new file mode 100644 index 000000000..2bc71fc1a --- /dev/null +++ b/contrib/epee/include/tiny_ini.h @@ -0,0 +1,75 @@ +// 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 _TINY_INI_H_ +#define _TINY_INI_H_ + +#include <boost/regex.hpp> +#include <boost/lexical_cast.hpp> +#include "string_tools.h" + +namespace epee +{ +namespace tiny_ini +{ + + inline + bool get_param_value(const std::string& param_name, const std::string& ini_entry, std::string& res) + { + std::string expr_str = std::string() + "^("+ param_name +") *=(.*?)$"; + const boost::regex match_ini_entry( expr_str, boost::regex::icase | boost::regex::normal); + boost::smatch result; + if(!boost::regex_search(ini_entry, result, match_ini_entry, boost::match_default)) + return false; + res = result[2]; + string_tools::trim(res); + return true; + } + inline + std::string get_param_value(const std::string& param_name, const std::string& ini_entry) + { + std::string buff; + get_param_value(param_name, ini_entry, buff); + return buff; + } + + template<class T> + bool get_param_value_as_t(const std::string& param_name, const std::string& ini_entry, T& res) + { + std::string str_res = get_param_value(param_name, ini_entry); + + string_tools::trim(str_res); + if(!str_res.size()) + return false; + + return string_tools::get_xtype_from_string(res, str_res); + } + +} +} + +#endif //_TINY_INI_H_ diff --git a/contrib/epee/include/to_nonconst_iterator.h b/contrib/epee/include/to_nonconst_iterator.h new file mode 100644 index 000000000..729b0e8b2 --- /dev/null +++ b/contrib/epee/include/to_nonconst_iterator.h @@ -0,0 +1,52 @@ +// 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 _TO_NONCONST_ITERATOR_H_ +#define _TO_NONCONST_ITERATOR_H_ + +namespace epee +{ + +template<class Type> +typename Type::iterator to_nonsonst_iterator(Type& obj, typename Type::const_iterator it) +{ + typename Type::difference_type dist = std::distance(static_cast<typename Type::const_iterator>(obj.begin()), it); + typename Type::iterator res_it = obj.begin()+dist; + return res_it; +} + + +template<class Type> +typename Type::iterator to_nonsonst_iterator(typename Type::iterator base_it, typename Type::const_iterator it) +{ + typename Type::difference_type dist = std::distance(static_cast<typename Type::const_iterator>(base_it), it); + typename Type::iterator res_it = base_it+dist; + return res_it; +} +}//namespace epee +#endif //_TO_NONCONST_ITERATOR_H_ diff --git a/contrib/epee/include/warnings.h b/contrib/epee/include/warnings.h new file mode 100644 index 000000000..37d7a2900 --- /dev/null +++ b/contrib/epee/include/warnings.h @@ -0,0 +1,30 @@ +#pragma once + +#if defined(_MSC_VER) + +#define PUSH_WARNINGS __pragma(warning(push)) +#define POP_WARNINGS __pragma(warning(pop)) +#define DISABLE_VS_WARNINGS(w) __pragma(warning(disable: w)) +#define DISABLE_GCC_WARNING(w) +#define DISABLE_CLANG_WARNING(w) +#define DISABLE_GCC_AND_CLANG_WARNING(w) + +#else + +#include <boost/preprocessor/stringize.hpp> + +#define PUSH_WARNINGS _Pragma("GCC diagnostic push") +#define POP_WARNINGS _Pragma("GCC diagnostic pop") +#define DISABLE_VS_WARNINGS(w) + +#if defined(__clang__) +#define DISABLE_GCC_WARNING(w) +#define DISABLE_CLANG_WARNING DISABLE_GCC_AND_CLANG_WARNING +#else +#define DISABLE_GCC_WARNING DISABLE_GCC_AND_CLANG_WARNING +#define DISABLE_CLANG_WARNING(w) +#endif + +#define DISABLE_GCC_AND_CLANG_WARNING(w) _Pragma(BOOST_PP_STRINGIZE(GCC diagnostic ignored BOOST_PP_STRINGIZE(-W##w))) + +#endif
\ No newline at end of file diff --git a/contrib/epee/include/winobj.h b/contrib/epee/include/winobj.h new file mode 100644 index 000000000..3279cdac6 --- /dev/null +++ b/contrib/epee/include/winobj.h @@ -0,0 +1,227 @@ +// 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 __WINH_OBJ_H__ +#define __WINH_OBJ_H__ + +#include <boost/thread/locks.hpp> + +namespace epee +{ +class critical_region; + +class critical_section { + + boost::mutex m_section; + +public: + + critical_section( const critical_section& section ) { + InitializeCriticalSection( &m_section ); + } + + critical_section() { + InitializeCriticalSection( &m_section ); + } + + ~critical_section() { + DeleteCriticalSection( &m_section ); + } + + void lock() { + EnterCriticalSection( &m_section ); + } + + void unlock() { + LeaveCriticalSection( &m_section ); + } + + bool tryLock() { + return TryEnterCriticalSection( &m_section )? true:false; + } + + critical_section& operator=( const critical_section& section ) + { + return *this; + } + + +}; + +class critical_region { + + ::critical_section *m_locker; + + critical_region( const critical_region& ){} + +public: + + critical_region(critical_section &cs ) { + m_locker = &cs; + cs.lock(); + } + + ~critical_region() + { + m_locker->unlock(); + } +}; + + +class shared_critical_section +{ +public: + shared_critical_section() + { + ::InitializeSRWLock(&m_srw_lock); + } + ~shared_critical_section() + {} + + bool lock_shared() + { + AcquireSRWLockShared(&m_srw_lock); + return true; + } + bool unlock_shared() + { + ReleaseSRWLockShared(&m_srw_lock); + return true; + } + bool lock_exclusive() + { + ::AcquireSRWLockExclusive(&m_srw_lock); + return true; + } + bool unlock_exclusive() + { + ::ReleaseSRWLockExclusive(&m_srw_lock); + return true; + } +private: + SRWLOCK m_srw_lock; + +}; + + +class shared_guard +{ +public: + shared_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_shared(); + } + + ~shared_guard() + { + m_ref_sec.unlock_shared(); + } + +private: + shared_critical_section& m_ref_sec; +}; + + +class exclusive_guard +{ +public: + exclusive_guard(shared_critical_section& ref_sec):m_ref_sec(ref_sec) + { + m_ref_sec.lock_exclusive(); + } + + ~exclusive_guard() + { + m_ref_sec.unlock_exclusive(); + } + +private: + shared_critical_section& m_ref_sec; +}; + + +class event +{ +public: + event() + { + m_hevent = ::CreateEvent(NULL, FALSE, FALSE, NULL); + } + ~event() + { + ::CloseHandle(m_hevent); + + } + + bool set() + { + return ::SetEvent(m_hevent) ? true:false; + } + + bool reset() + { + return ::ResetEvent(m_hevent) ? true:false; + } + + HANDLE get_handle() + { + return m_hevent; + } +private: + HANDLE m_hevent; + +}; + + +#define SHARED_CRITICAL_REGION_BEGIN(x) { shared_guard critical_region_var(x) +#define EXCLUSIVE_CRITICAL_REGION_BEGIN(x) { exclusive_guard critical_region_var(x) + + + +#define CRITICAL_REGION_LOCAL(x) critical_region critical_region_var(x) +#define CRITICAL_REGION_BEGIN(x) { critical_region critical_region_var(x) +#define CRITICAL_REGION_END() } + + + inline const char* get_wait_for_result_as_text(DWORD res) + { + switch(res) + { + case WAIT_ABANDONED: return "WAIT_ABANDONED"; + case WAIT_TIMEOUT: return "WAIT_TIMEOUT"; + case WAIT_OBJECT_0: return "WAIT_OBJECT_0"; + case WAIT_OBJECT_0+1: return "WAIT_OBJECT_1"; + case WAIT_OBJECT_0+2: return "WAIT_OBJECT_2"; + default: + return "UNKNOWN CODE"; + } + + } + +}// namespace epee + +#endif diff --git a/contrib/epee/include/zlib_helper.h b/contrib/epee/include/zlib_helper.h new file mode 100644 index 000000000..46c7f48e6 --- /dev/null +++ b/contrib/epee/include/zlib_helper.h @@ -0,0 +1,139 @@ +// 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 +extern "C" { +#include "zlib/zlib.h" +} +#pragma comment(lib, "zlibstat.lib") + +namespace epee +{ +namespace zlib_helper +{ + inline + bool pack(std::string& target){ + std::string result_packed_buff; + + z_stream zstream = {0}; + int ret = deflateInit(&zstream, Z_DEFAULT_COMPRESSION); + if(target.size()) + { + + + result_packed_buff.resize(target.size()*2, 'X'); + + zstream.next_in = (Bytef*)target.data(); + zstream.avail_in = (uInt)target.size(); + zstream.next_out = (Bytef*)result_packed_buff.data(); + zstream.avail_out = (uInt)result_packed_buff.size(); + + ret = deflate(&zstream, Z_FINISH); + CHECK_AND_ASSERT_MES(ret>=0, false, "Failed to deflate. err = " << ret); + + if(result_packed_buff.size() != zstream.avail_out) + result_packed_buff.resize(result_packed_buff.size()-zstream.avail_out); + + + result_packed_buff.erase(0, 2); + target.swap(result_packed_buff); + } + + deflateEnd(& zstream ); + return true; + } + + inline bool unpack(std::string& target) + { + z_stream zstream = {0}; + int ret = inflateInit(&zstream);// + + std::string decode_summary_buff; + size_t ungzip_buff_size = target.size() * 0x30; + std::string current_decode_buff(ungzip_buff_size, 'X'); + + while(target.size()) + { + + + zstream.next_out = (Bytef*)current_decode_buff.data(); + zstream.avail_out = (uInt)ungzip_buff_size; + + int flag = Z_SYNC_FLUSH; + + static char dummy_head[2] = + { + 0x8 + 0x7 * 0x10, + (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF, + }; + zstream.next_in = (Bytef*) dummy_head; + zstream.avail_in = sizeof(dummy_head); + ret = inflate(&zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + LOCAL_ASSERT(0); + return false; + } + + zstream.next_in = (Bytef*)target.data(); + zstream.avail_in = (uInt)target.size(); + + ret = inflate(&zstream, Z_SYNC_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + LOCAL_ASSERT(0); + return false; + } + + + target.erase(0, target.size()-zstream.avail_in); + + + if(ungzip_buff_size == zstream.avail_out) + { + LOG_ERROR("Can't unpack buffer"); + return false; + } + + + current_decode_buff.resize(ungzip_buff_size - zstream.avail_out); + if(decode_summary_buff.size()) + decode_summary_buff += current_decode_buff; + else + current_decode_buff.swap(decode_summary_buff); + + current_decode_buff.resize(ungzip_buff_size); + } + + inflateEnd(&zstream ); + + decode_summary_buff.swap(target); + return 1; + } + +}; +}//namespace epee |