diff --git a/CMakeLists.txt b/CMakeLists.txt index 85f13b1..aebd708 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,9 @@ include_directories(${VCPKG_INSTALLED_DIR}/x86-windows/include) find_package(nlohmann_json CONFIG REQUIRED) find_package(unofficial-mongoose CONFIG REQUIRED) +# find_package(spdlog CONFIG REQUIRED) +# find_package(minhook CONFIG REQUIRED) + add_library(wxhelper SHARED ${CPP_FILES} ) @@ -26,6 +29,8 @@ add_library(wxhelper SHARED ${CPP_FILES} ) target_link_libraries(wxhelper PRIVATE nlohmann_json::nlohmann_json) target_link_libraries(wxhelper PRIVATE unofficial::mongoose::mongoose) +# target_link_libraries(wxhelper PRIVATE spdlog::spdlog spdlog::spdlog_header_only) +# target_link_libraries(wxhelper PRIVATE minhook::minhook) SET_TARGET_PROPERTIES(wxhelper PROPERTIES LINKER_LANGUAGE C ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib diff --git a/README.md b/README.md index bad1057..a813e39 100644 --- a/README.md +++ b/README.md @@ -13,30 +13,30 @@ 13.hook语音 14.取消hook语音 - +19.通过手机或qq查找微信 25.获取群成员 - +31.修改群昵称 32.获取数据库句柄 34.查询数据库 - +35.hook日志 +36.关闭hook日志 40.转发消息 44.退出登录 46.联系人列表 - +47.获取群详情 48.获取解密图片 50.拍一拍 - +51.群消息置顶消息 +52.群消息取消置顶 53.朋友圈首页 54.朋友圈下一页 - +55.获取联系人或者群名称 56.获取消息附件(图片,视频,文件) 57.获取消息语音文件 ### 接口文档: diff --git a/src/self_info.cc b/src/account_mgr.cc similarity index 81% rename from src/self_info.cc rename to src/account_mgr.cc index 9ab002a..e8579ee 100644 --- a/src/self_info.cc +++ b/src/account_mgr.cc @@ -1,22 +1,22 @@ -#include "pch.h" -#include "self_info.h" +#include "pch.h" +#include "account_mgr.h" +#include "easylogging++.h" -#include "common.h" -#include "wechat_data.h" +#include "wechat_function.h" + using namespace std; +namespace wxhelper { + AccountMgr::AccountMgr(DWORD base):BaseMgr(base){ + } + AccountMgr::~AccountMgr(){ - -#define WX_LOGOUT_OFFSET 0xe58870 -#define WX_ACCOUNT_SERVICE_OFFSET 0x768c80 -#define WX_GET_APP_DATA_SAVE_PATH_OFFSET 0xf3a610 -#define WX_GET_CURRENT_DATA_PATH_OFFSET 0xc872c0 -int GetSelfInfo(SelfInfoInner &out) { - DWORD base = GetWeChatWinBase(); - DWORD accout_service_addr = base + WX_ACCOUNT_SERVICE_OFFSET; - DWORD get_app_save_addr = base + WX_GET_APP_DATA_SAVE_PATH_OFFSET; - DWORD get_current_data_path_addr = base + WX_GET_CURRENT_DATA_PATH_OFFSET; + } +int AccountMgr::GetSelfInfo(SelfInfoInner &out) { + DWORD accout_service_addr = base_addr_ + WX_ACCOUNT_SERVICE_OFFSET; + DWORD get_app_save_addr = base_addr_ + WX_GET_APP_DATA_SAVE_PATH_OFFSET; + DWORD get_current_data_path_addr = base_addr_ + WX_GET_CURRENT_DATA_PATH_OFFSET; DWORD service_addr = NULL; __asm { PUSHAD @@ -143,12 +143,12 @@ int GetSelfInfo(SelfInfoInner &out) { } if (*(DWORD *)(service_addr + 0x4CC) == 0 || - *(DWORD *)(service_addr +0x4D0) == 0) { + *(DWORD *)(service_addr + 0x4D0) == 0) { out.db_key = string(); } else { - DWORD byte_addr = *(DWORD *)(service_addr + 0x4CC); - DWORD len = *(DWORD *)(service_addr +0x4D0); - out.db_key = Bytes2Hex((BYTE *)byte_addr,len); + DWORD byte_addr = *(DWORD *)(service_addr + 0x4CC); + DWORD len = *(DWORD *)(service_addr + 0x4D0); + out.db_key = Utils::Bytes2Hex((BYTE *)byte_addr, len); } } @@ -165,15 +165,15 @@ int GetSelfInfo(SelfInfoInner &out) { } if (data_save_path.ptr) { - out.data_save_path = - Wstring2String(wstring(data_save_path.ptr, data_save_path.length)); + out.data_save_path = Utils::WstringToUTF8( + wstring(data_save_path.ptr, data_save_path.length)); } else { out.data_save_path = string(); } if (current_data_path.ptr) { - out.current_data_path = Wstring2String( + out.current_data_path = Utils::WstringToUTF8( wstring(current_data_path.ptr, current_data_path.length)); } else { out.current_data_path = string(); @@ -181,9 +181,9 @@ int GetSelfInfo(SelfInfoInner &out) { return 1; } -int CheckLogin() { - DWORD base = GetWeChatWinBase(); - DWORD accout_service_addr = base + WX_ACCOUNT_SERVICE_OFFSET; +int AccountMgr::CheckLogin() { + int success = -1; + DWORD accout_service_addr = base_addr_ + WX_ACCOUNT_SERVICE_OFFSET; DWORD service_addr = NULL; __asm { PUSHAD @@ -192,21 +192,18 @@ int CheckLogin() { POPAD } if (service_addr) { - return *(DWORD *)(service_addr + 0x4C8); - } - else { - return 0; + success = *(DWORD *)(service_addr + 0x4C8); } + return success; } -int Logout() { - int success = 0; +int AccountMgr::Logout() { + int success = -1; if (!CheckLogin()) { return success; } - DWORD base = GetWeChatWinBase(); - DWORD account_service_addr = base + WX_ACCOUNT_SERVICE_OFFSET; - DWORD logout_addr = base + WX_LOGOUT_OFFSET; + DWORD account_service_addr = base_addr_ + WX_ACCOUNT_SERVICE_OFFSET; + DWORD logout_addr = base_addr_ + WX_LOGOUT_OFFSET; __asm { PUSHAD CALL account_service_addr @@ -217,4 +214,6 @@ int Logout() { POPAD } return success; -} \ No newline at end of file +} + +} // namespace wxhelper \ No newline at end of file diff --git a/src/account_mgr.h b/src/account_mgr.h new file mode 100644 index 0000000..67da58d --- /dev/null +++ b/src/account_mgr.h @@ -0,0 +1,19 @@ +#ifndef WXHELPER_ACCOUNT_MGR_H_ +#define WXHELPER_ACCOUNT_MGR_H_ +#include "wechat_function.h" +#include"base_mgr.h" +namespace wxhelper{ + class AccountMgr: public BaseMgr + { + public: + explicit AccountMgr(DWORD base); + ~AccountMgr(); + int GetSelfInfo(SelfInfoInner& out); + + int CheckLogin(); + + int Logout(); + }; + +} +#endif \ No newline at end of file diff --git a/src/api.cc b/src/api.cc deleted file mode 100644 index d935d56..0000000 --- a/src/api.cc +++ /dev/null @@ -1,782 +0,0 @@ -#include "pch.h" -#include "api.h" -#include - -#include - - -#include "send_text.h" -#include "common.h" -#include "send_image.h" -#include "send_file.h" -#include "hook_recv_msg.h" -#include "get_db_handle.h" -#include "wechat_data.h" -#include "forward.h" -#include "db_operation.h" -#include "contact.h" -#include "chat_room.h" -#include "self_info.h" -#include "hook_img.h" -#include "ocr.h" -#include "pat.h" -#include "confirm_receipt.h" -#include "sns.h" -#include "search_contact.h" -#include "download.h" -#include "hook_log.h" -#include "hook_voice.h" - -#pragma comment(lib, "ws2_32.lib") -using namespace std; -using namespace nlohmann; - -// #define STR2INT(str) (is_digit(str) ? stoi(str) : 0) -#define WS2LW(wstr) (LPWSTR) wstr.c_str() - -static bool kHttpRuning = false; -static HANDLE kHttpThread = NULL; - -bool is_digit(string str) { - if (str.length() == 0) { - return false; - } - - for (auto it : str) { - if (it < '0' || it > '9') { - return false; - } - } - return true; -} - -string get_var(mg_http_message *hm, string name) { - string ret; - char *buffer = new char[hm->query.len + 1]; - ZeroMemory(buffer, hm->query.len + 1); - int len = mg_http_get_var(&hm->query, name.c_str(), buffer, hm->query.len); - if (len > 0) ret = string(buffer, len); - delete[] buffer; - buffer = NULL; - return ret; -} -/// @brief 获取request中的请求参数int类型 -/// @param hm 消息 -/// @param data json数据 -/// @param key key -/// @param method 是否是post,暂时全部用post -/// @return int -static int get_http_req_param_int(mg_http_message *hm, json data, string key, int method){ - int result; - switch (method) { - case 0: { - result = STR2INT(get_var(hm,key).c_str()); - break; - } - case 1: { - try { - result = data[key].get(); - } catch (json::exception) { - result = STR2INT(data[key].get()); - } - break; - } - default: - break; - } - return result; -} - -/// @brief 获取request中的请求参数 -/// @param hm 消息 -/// @param data json数据 -/// @param key key -/// @param method 是否是post,暂时全部用post -/// @return -static wstring get_http_req_param(mg_http_message *hm, json data, string key, int method){ - wstring result; - switch (method) { - case 0: { - result = utf8_to_unicode(get_var(hm,key).c_str()); - break; - } - case 1: { - result = utf8_to_unicode(data[key].get().c_str()); - break; - } - default: - break; - } - return result; -} - -static unsigned long long get_http_param_ulong64(mg_http_message *hm, - json j_data, string key, - int method) { - unsigned long long result = 0; - switch (method) { - case 0: { - string value = get_var(hm, key); - istringstream is(value); - is >> result; - break; - } - case 1: { - try { - result = j_data[key].get(); - } catch (json::exception) { - string value = j_data[key].get(); - istringstream is(value); - is >> result; - } - break; - } - default: - break; - } - return result; -} - -static int get_http_param_int(mg_http_message *hm, json j_data, string key, - int method) { - int result = 0; - switch (method) { - case 0: { - result = STR2INT(get_var(hm, key)); - break; - } - case 1: { - try { - result = j_data[key].get(); - } catch (json::exception) { - result = STR2INT(j_data[key].get()); - } - break; - } - default: - break; - } - return result; -} - -static vector get_http_param_array(mg_http_message *hm, json j_data, - string key, int method) { - vector result; - switch (method) { - case 0: { - result = split(utf8_to_unicode(get_var(hm, key).c_str()), L','); - break; - } - case 1: { - result = split(utf8_to_unicode(j_data[key].get().c_str()), L','); - break; - } - default: - break; - } - return result; -} - -/// @brief api接口入口解析 -/// @param hm mg_http_message -/// @param c connection -/// @param ret json数据 -void api_handle(mg_http_message *hm, struct mg_connection *c, string &ret) { - int is_post = 0; - - if (mg_vcasecmp(&hm->method, "POST") == 0) { - is_post = 1; - } - #ifdef _DEBUG - printf("method:%s body: %s", hm->method.ptr,hm->body.ptr); - #endif - if (is_post == 0){ - json ret_data = {{"result", "ERROR"}, {"msg", "not support method"}}; - ret = ret_data.dump(); - return; - } - - json j_param = - json::parse(hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false); - if (hm->body.len != 0 && j_param.is_discarded() == true) { - json ret_data = {{"result", "ERROR"}, {"msg", "json string is invalid."}}; - ret = ret_data.dump(); - return; - } - int api_number = STR2INT(get_var(hm, "type")); - switch (api_number) { - case WECHAT_IS_LOGIN: { - int success = CheckLogin(); - json ret_data = {{"result", "OK"}, {"code", success}}; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_SELF_INFO: { - SelfInfoInner self_info; - int success = GetSelfInfo(self_info); - json ret_data = {{"result", "OK"}, {"code", success}}; - if (success) { - json j_info = { - {"name", self_info.name}, - {"city", self_info.city}, - {"province", self_info.province}, - {"country", self_info.country}, - {"account", self_info.account}, - {"wxid", self_info.wxid}, - {"mobile", self_info.mobile}, - {"headImage", self_info.head_img}, - {"signature",self_info.signature}, - {"dataSavePath",self_info.data_save_path}, - {"currentDataPath",self_info.current_data_path}, - {"dbKey",self_info.db_key}, - }; - ret_data["data"] = j_info; - } - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_SEND_TEXT: { - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - wstring msg = get_http_req_param(hm, j_param, "msg", is_post); - int success = SendText(WS2LW(wxid), WS2LW(msg)); - json ret_data = {{"result", "OK"}, {"code", success}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_SEND_AT: { - break; - wstring chat_room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - vector wxids = get_http_param_array(hm, j_param, "wxids", is_post); - wstring msg = get_http_req_param(hm, j_param, "msg", is_post); - vector wxid_list; - for (unsigned int i = 0; i < wxids.size(); i++){ - wxid_list.push_back(WS2LW(wxids[i])); - } - int success = SendAtText(WS2LW(chat_room_id), wxid_list.data(), wxid_list.size(),WS2LW(msg)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_SEND_CARD: { - break; - } - case WECHAT_MSG_SEND_IMAGE: { - wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post); - wstring img_path = get_http_req_param(hm, j_param, "imagePath", is_post); - int success = SendImage(WS2LW(receiver), WS2LW(img_path)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_SEND_FILE: { - wstring receiver = get_http_req_param(hm, j_param, "wxid", is_post); - wstring file_path = get_http_req_param(hm, j_param, "filePath", is_post); - int success = SendFile(WS2LW(receiver), WS2LW(file_path)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_SEND_ARTICLE: { - break; - } - case WECHAT_MSG_SEND_APP: { - break; - } - case WECHAT_MSG_START_HOOK: { - int port = get_http_req_param_int(hm, j_param, "port", is_post); - wstring ip = get_http_req_param(hm, j_param, "ip", is_post); - string client_ip = Wstring2String(ip); - char ip_cstr[16]; - strcpy_s(ip_cstr,client_ip.c_str()); - int success = HookRecvMsg(ip_cstr,port); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_STOP_HOOK: { - int success = UnHookRecvMsg(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_START_IMAGE_HOOK: { - break; - wstring img_dir = get_http_req_param(hm, j_param, "imgDir", is_post); - int success = HookImg(img_dir); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_STOP_IMAGE_HOOK: { - break; - int success = UnHookImg(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_START_VOICE_HOOK: { - wstring voice_dir = get_http_req_param(hm, j_param, "voiceDir", is_post); - int success = HookVoice(voice_dir); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_MSG_STOP_VOICE_HOOK: { - int success = UnHookVoice(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CONTACT_GET_LIST: { - - break; - } - case WECHAT_CONTACT_CHECK_STATUS: { - break; - } - case WECHAT_CONTACT_DEL: { - break; - wstring user_id = get_http_req_param(hm, j_param, "wxid", is_post); - int success = DelContact(WS2LW(user_id)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CONTACT_SEARCH_BY_CACHE: { - break; - } - case WECHAT_CONTACT_SEARCH_BY_NET: { - break; - wstring keyword = get_http_req_param(hm, j_param, "keyword", is_post); - UserInfo *user = nullptr; - int success = SearchContactNetScene(WS2LW(keyword), &user); - json ret_data = {{"code", success}, {"result", "OK"}}; - if (user) { - json info = { - {"bigImage", unicode_to_utf8(user->big_image)}, - {"smallImage", unicode_to_utf8(user->small_image)}, - {"city", unicode_to_utf8(user->city)}, - {"nation", unicode_to_utf8(user->nation)}, - {"nickname", unicode_to_utf8(user->nickname)}, - {"province", unicode_to_utf8(user->province)}, - {"sex", user->sex}, - {"signature", unicode_to_utf8(user->signature)}, - {"v2", unicode_to_utf8(user->v2)}, - {"v3", unicode_to_utf8(user->v3)}, - }; - ret_data["userInfo"] = info; - } - ret = ret_data.dump(); - break; - } - case WECHAT_CONTACT_ADD_BY_WXID: { - break; - wstring user_id = get_http_req_param(hm, j_param, "wxid", is_post); - int success = AddFriendByWxid(WS2LW(user_id)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CONTACT_ADD_BY_V3: { - break; - } - case WECHAT_CONTACT_ADD_BY_PUBLIC_ID: { - break; - } - case WECHAT_CONTACT_VERIFY_APPLY: { - break; - } - case WECHAT_CONTACT_EDIT_REMARK: { - break; - } - case WECHAT_CHATROOM_GET_MEMBER_LIST: { - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - ChatRoomInner out{0}; - int success = GetMemberFromChatRoom(WS2LW(room_id),out); - json ret_data = {{"code", success}, {"result", "OK"}}; - if (success){ - json member_info ={ - {"admin",unicode_to_utf8(out.admin)}, - {"chatRoomId",unicode_to_utf8(out.chat_room)}, - {"members",out.members}, - }; - ret_data["data"] = member_info; - } - ret = ret_data.dump(); - break; - } - case WECHAT_CHATROOM_GET_MEMBER_NICKNAME: { - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - wstring member_id = get_http_req_param(hm, j_param, "memberId", is_post); - - wstring nickname = GetChatRoomMemberNickname(WS2LW(room_id),WS2LW(member_id)); - json ret_data = {{"code", 1}, {"result", "OK"},{"nickname",unicode_to_utf8(WS2LW(nickname))}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CHATROOM_DEL_MEMBER: { - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - vector wxids = get_http_param_array(hm, j_param, "memberIds", is_post); - vector wxid_list; - for (unsigned int i = 0; i < wxids.size(); i++){ - wxid_list.push_back(WS2LW(wxids[i])); - } - int success = DelMemberFromChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size()); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CHATROOM_ADD_MEMBER: { - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - vector wxids = get_http_param_array(hm, j_param, "memberIds", is_post); - vector wxid_list; - for (unsigned int i = 0; i < wxids.size(); i++){ - wxid_list.push_back(WS2LW(wxids[i])); - } - int success = AddMemberToChatRoom(WS2LW(room_id), wxid_list.data(),wxid_list.size()); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CHATROOM_SET_ANNOUNCEMENT: { - break; - } - case WECHAT_CHATROOM_SET_CHATROOM_NAME: { - break; - } - case WECHAT_CHATROOM_SET_SELF_NICKNAME: { - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - wstring nick = get_http_req_param(hm, j_param, "nickName", is_post); - int success = ModChatRoomMemberNickName(WS2LW(room_id),WS2LW(wxid),WS2LW(nick)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_DATABASE_GET_HANDLES: { - vector v_ptr = GetDbHandles(); - json ret_data = {{"data", json::array()}, {"result", "OK"}}; - for (unsigned int i = 0; i < v_ptr.size(); i++) { - json db_info; - db_info["tables"] = json::array(); - DatabaseInfo *db = reinterpret_cast(v_ptr[i]); - db_info["handle"] = db->handle; - db_info["databaseName"] = unicode_to_utf8(db->db_name); - for (auto table : db->tables) { - json table_info = {{"name", table.name}, - {"tableName", table.table_name}, - {"sql", table.sql}, - {"rootpage", table.rootpage}}; - db_info["tables"].push_back(table_info); - } - ret_data["data"].push_back(db_info); - } - ret = ret_data.dump(); - break; - } - case WECHAT_DATABASE_BACKUP: { - break; - } - case WECHAT_DATABASE_QUERY: { - DWORD db_handle = get_http_param_int(hm, j_param, "dbHandle", is_post); - wstring sql = get_http_req_param(hm, j_param, "sql", is_post); - string sql_str = unicode_to_utf8(WS2LW(sql)); - vector> items; - int success = Select(db_handle, sql_str.c_str(),items); - json ret_data = {{"data", json::array()}, {"code",success},{"result", "OK"}}; - if(success == 0){ - ret = ret_data.dump(); - break; - } - for (auto it : items) { - json temp_arr = json::array(); - for (size_t i = 0; i < it.size(); i++) { - temp_arr.push_back(it[i]); - } - ret_data["data"].push_back(temp_arr); - } - ret = ret_data.dump(); - break; - } - case WECHAT_SET_VERSION: { - break; - } - case WECHAT_LOG_START_HOOK: { - break; - int success = HookLog(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_LOG_STOP_HOOK: { - break; - int success = UnHookLog(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_BROWSER_OPEN_WITH_URL: { - break; - } - case WECHAT_GET_PUBLIC_MSG: { - break; - } - case WECHAT_MSG_FORWARD_MESSAGE: { - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post); - int success = ForwardMsg(WS2LW(wxid), msgid); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_QRCODE_IMAGE: { - break; - } - case WECHAT_GET_A8KEY: { - break; - } - case WECHAT_MSG_SEND_XML: { - break; - } - case WECHAT_LOGOUT: { - int success = Logout(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_TRANSFER: { - break; - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - wstring transcationid = get_http_req_param(hm, j_param, "transcationId", is_post); - wstring transferid = get_http_req_param(hm, j_param, "transferId", is_post); - BOOL response = DoConfirmReceipt(WS2LW(wxid), WS2LW(transcationid), WS2LW(transferid)); - json ret_data = {{"msg", response}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_CONTACT_ALL: { - vector vec; - int success = GetAllContact(vec); - json ret_data = { - {"data", json::array()}, {"code", success}, {"result", "OK"}}; - - for (unsigned int i = 0; i < vec.size(); i++) { - #ifdef _DEBUG - cout << "vector :" < 0 - ? vec[i].custom_account.ptr != nullptr - ? unicode_to_utf8(vec[i].custom_account.ptr) - : string() - : string()}, - {"delFlag", vec[i].del_flag}, - {"userName", vec[i].encrypt_name.length > 0 - ? vec[i].encrypt_name.ptr != nullptr - ? unicode_to_utf8(vec[i].encrypt_name.ptr) - : string() - : string()}, - {"type", vec[i].type}, - {"verifyFlag", vec[i].verify_flag}, - {"verifyFlag", vec[i].verify_flag}, - {"wxid", vec[i].wxid.length > 0 ? unicode_to_utf8(vec[i].wxid.ptr) - : string()}, - }; - ret_data["data"].push_back(item); - } - ret = ret_data.dump(); - break; - } - case WECHAT_GET_CHATROOM_INFO: { - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - ChatRoomInfoInner chat_room_detail{0}; - int success = GetChatRoomDetailInfo(WS2LW(room_id), chat_room_detail); - json ret_data = {{"code", success}, {"result", "OK"}}; - if(!success){ - break; - } - json detail = { - {"chatRoomId", - chat_room_detail.chat_room_id.length > 0 - ? unicode_to_utf8(chat_room_detail.chat_room_id.ptr) - : string()}, - {"notice", chat_room_detail.notice.length > 0 - ? unicode_to_utf8(chat_room_detail.notice.ptr) - : string()}, - {"admin", chat_room_detail.admin.length > 0 - ? unicode_to_utf8(chat_room_detail.admin.ptr) - : string()}, - {"xml", chat_room_detail.xml.length > 0 - ? unicode_to_utf8(chat_room_detail.xml.ptr) - : string()}, - }; - ret_data["data"]=detail; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_IMG_BY_NAME: { - wstring image_path = get_http_req_param(hm, j_param, "imagePath", is_post); - wstring save_path = get_http_req_param(hm, j_param, "savePath", is_post); - int success = GetImgByName(WS2LW(image_path),WS2LW(save_path)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_DO_OCR:{ - break; - wstring image_path = get_http_req_param(hm, j_param, "imagePath", is_post); - string text(""); - int success = DoOCRTask(WS2LW(image_path),text); - json ret_data = {{"code", success}, {"result", "OK"},{"text",text}}; - ret = ret_data.dump(); - break; - } - case WECHAT_SEND_PAT_MSG:{ - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - int success = SendPatMsg(WS2LW(room_id),WS2LW(wxid)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_SET_TOP_MSG:{ - break; - wstring wxid = get_http_req_param(hm, j_param, "wxid", is_post); - ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post); - int success = SetTopMsg(WS2LW(wxid),msgid); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_REMOVE_TOP_MSG:{ - break; - wstring room_id = get_http_req_param(hm, j_param, "chatRoomId", is_post); - ULONG64 msgid = get_http_param_ulong64(hm, j_param, "msgid", is_post); - int success = RemoveTopMsg(WS2LW(room_id),msgid); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_SNS_GET_FIRST_PAGE:{ - int success = GetFirstPage(); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_SNS_GET_NEXT_PAGE: { - ULONG64 snsid = get_http_param_ulong64(hm, j_param, "snsId", is_post); - int success = GetNextPage(snsid); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_CONTACT_NAME:{ - break; - wstring pri_id = get_http_req_param(hm, j_param, "id", is_post); - wstring name =GetContactOrChatRoomNickname(WS2LW(pri_id)); - json ret_data = {{"code", 1}, {"result", "OK"},{"name",unicode_to_utf8(WS2LW(name))}}; - ret = ret_data.dump(); - break; - } - case WECHAT_ATTACH_DOWNLOAD:{ - ULONG64 msg_id = get_http_param_ulong64(hm, j_param, "msgId", is_post); - int success = DoDownloadTask(msg_id); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - case WECHAT_GET_VOICE:{ - ULONG64 msg_id = get_http_param_ulong64(hm, j_param, "msgId", is_post); - wstring voice_dir = get_http_req_param(hm, j_param, "voiceDir", is_post); - int success = GetVoice(msg_id,WS2LW(voice_dir)); - json ret_data = {{"code", success}, {"result", "OK"}}; - ret = ret_data.dump(); - break; - } - default: - break; - } -} - -/// @brief 事件回调函数 -/// @param c 链接 -/// @param ev 事件 -/// @param ev_data 事件数据 -/// @param fn_data 回调数据 -static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - struct mg_http_serve_opts opts = {0}; - if (ev == MG_EV_HTTP_MSG) { - struct mg_http_message *hm = (struct mg_http_message *)ev_data; - string ret = R"({"result":"OK"})"; - if (mg_http_match_uri(hm, "/api/")) { - try { - api_handle(hm, c, ret); - } catch (json::exception &e) { - json res = {{"result", "ERROR"}, {"msg", e.what()}}; - ret = res.dump(); - } - if (ret != "") { - mg_http_reply(c, 200, "", ret.c_str(), 0, 0); - } - } else { - mg_http_reply(c, 500, NULL, "%s", "Invalid URI"); - } - } - (void)fn_data; -} -/// @brief http server -/// @param port 端口 -void http_server(int port) { - string lsten_addr = "http://0.0.0.0:" + to_string(port); - struct mg_mgr mgr; - // Init manager - mg_mgr_init(&mgr); - // Setup listener - mg_http_listen(&mgr, lsten_addr.c_str(), fn, &mgr); - // Event loop - for (;;) mg_mgr_poll(&mgr, 1000); - // Cleanup - mg_mgr_free(&mgr); -} - -/// @brief 启动http服务 -/// @param port 端口 -/// @return 成功返回0 -int http_start(int port) { - if (kHttpRuning) { - return 1; - } - #ifdef _DEBUG - CreateConsole(); - #endif - kHttpRuning = true; - kHttpThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)http_server, - (LPVOID)port, NULL, 0); - return 0; -} - -int http_close() { - if (!kHttpRuning) { - return 1; - } - kHttpRuning = false; - if (kHttpThread) { - WaitForSingleObject(kHttpThread, -1); - CloseHandle(kHttpThread); - kHttpThread = NULL; - } - UnHookRecvMsg(); - UnHookImg(); - UnHookSearchContact(); - UnHookLog(); - return 0; -} \ No newline at end of file diff --git a/src/api.h b/src/api.h deleted file mode 100644 index 951bc67..0000000 --- a/src/api.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef API_H_ -#define API_H_ - - -typedef enum WECHAT_HTTP_APISTag -{ - // login check - WECHAT_IS_LOGIN = 0, - // self info - WECHAT_GET_SELF_INFO, - // send message - WECHAT_MSG_SEND_TEXT, - WECHAT_MSG_SEND_AT, - WECHAT_MSG_SEND_CARD, - WECHAT_MSG_SEND_IMAGE, - WECHAT_MSG_SEND_FILE, - WECHAT_MSG_SEND_ARTICLE, - WECHAT_MSG_SEND_APP, - // receive message - WECHAT_MSG_START_HOOK, - WECHAT_MSG_STOP_HOOK, - WECHAT_MSG_START_IMAGE_HOOK, - WECHAT_MSG_STOP_IMAGE_HOOK, - WECHAT_MSG_START_VOICE_HOOK, - WECHAT_MSG_STOP_VOICE_HOOK, - // contact - WECHAT_CONTACT_GET_LIST, - WECHAT_CONTACT_CHECK_STATUS, - WECHAT_CONTACT_DEL, - WECHAT_CONTACT_SEARCH_BY_CACHE, - WECHAT_CONTACT_SEARCH_BY_NET, - WECHAT_CONTACT_ADD_BY_WXID, - WECHAT_CONTACT_ADD_BY_V3, - WECHAT_CONTACT_ADD_BY_PUBLIC_ID, - WECHAT_CONTACT_VERIFY_APPLY, - WECHAT_CONTACT_EDIT_REMARK, - // chatroom - WECHAT_CHATROOM_GET_MEMBER_LIST, - WECHAT_CHATROOM_GET_MEMBER_NICKNAME, - WECHAT_CHATROOM_DEL_MEMBER, - WECHAT_CHATROOM_ADD_MEMBER, - WECHAT_CHATROOM_SET_ANNOUNCEMENT, - WECHAT_CHATROOM_SET_CHATROOM_NAME, - WECHAT_CHATROOM_SET_SELF_NICKNAME, - // database - WECHAT_DATABASE_GET_HANDLES, - WECHAT_DATABASE_BACKUP, - WECHAT_DATABASE_QUERY, - // version - WECHAT_SET_VERSION, - // log - WECHAT_LOG_START_HOOK, - WECHAT_LOG_STOP_HOOK, - // browser - WECHAT_BROWSER_OPEN_WITH_URL, - WECHAT_GET_PUBLIC_MSG, - WECHAT_MSG_FORWARD_MESSAGE, - WECHAT_GET_QRCODE_IMAGE, - WECHAT_GET_A8KEY, - WECHAT_MSG_SEND_XML, - WECHAT_LOGOUT, - WECHAT_GET_TRANSFER, - WECHAT_GET_CONTACT_ALL, - WECHAT_GET_CHATROOM_INFO, - WECHAT_GET_IMG_BY_NAME, - WECHAT_DO_OCR, - WECHAT_SEND_PAT_MSG, - WECHAT_SET_TOP_MSG, - WECHAT_REMOVE_TOP_MSG, - WECHAT_SNS_GET_FIRST_PAGE, - WECHAT_SNS_GET_NEXT_PAGE, - WECHAT_CONTACT_NAME, - WECHAT_ATTACH_DOWNLOAD, - WECHAT_GET_VOICE, -} WECHAT_HTTP_APIS, - *PWECHAT_HTTP_APIS; - - -extern "C" __declspec(dllexport) int http_start(int port); -extern "C" __declspec(dllexport) int http_close(); -#endif \ No newline at end of file diff --git a/src/api_route.h b/src/api_route.h new file mode 100644 index 0000000..224b7fe --- /dev/null +++ b/src/api_route.h @@ -0,0 +1,78 @@ +#ifndef WXHELPER_API_ROUTINES_H_ +#define WXHELPER_API_ROUTINES_H_ +namespace wxhelper { + +typedef enum HTTP_API_ROUTE { + // login check + WECHAT_IS_LOGIN = 0, + // self info + WECHAT_GET_SELF_INFO, + // send message + WECHAT_MSG_SEND_TEXT, + WECHAT_MSG_SEND_AT, + WECHAT_MSG_SEND_CARD, + WECHAT_MSG_SEND_IMAGE, + WECHAT_MSG_SEND_FILE, + WECHAT_MSG_SEND_ARTICLE, + WECHAT_MSG_SEND_APP, + // receive message + WECHAT_MSG_START_HOOK, + WECHAT_MSG_STOP_HOOK, + WECHAT_MSG_START_IMAGE_HOOK, + WECHAT_MSG_STOP_IMAGE_HOOK, + WECHAT_MSG_START_VOICE_HOOK, + WECHAT_MSG_STOP_VOICE_HOOK, + // contact + WECHAT_CONTACT_GET_LIST, + WECHAT_CONTACT_CHECK_STATUS, + WECHAT_CONTACT_DEL, + WECHAT_CONTACT_SEARCH_BY_CACHE, + WECHAT_CONTACT_SEARCH_BY_NET, + WECHAT_CONTACT_ADD_BY_WXID, + WECHAT_CONTACT_ADD_BY_V3, + WECHAT_CONTACT_ADD_BY_PUBLIC_ID, + WECHAT_CONTACT_VERIFY_APPLY, + WECHAT_CONTACT_EDIT_REMARK, + // chatroom + WECHAT_CHATROOM_GET_MEMBER_LIST, + WECHAT_CHATROOM_GET_MEMBER_NICKNAME, + WECHAT_CHATROOM_DEL_MEMBER, + WECHAT_CHATROOM_ADD_MEMBER, + WECHAT_CHATROOM_SET_ANNOUNCEMENT, + WECHAT_CHATROOM_SET_CHATROOM_NAME, + WECHAT_CHATROOM_SET_SELF_NICKNAME, + // database + WECHAT_DATABASE_GET_HANDLES, + WECHAT_DATABASE_BACKUP, + WECHAT_DATABASE_QUERY, + // version + WECHAT_SET_VERSION, + // log + WECHAT_LOG_START_HOOK, + WECHAT_LOG_STOP_HOOK, + // browser + WECHAT_BROWSER_OPEN_WITH_URL, + WECHAT_GET_PUBLIC_MSG, + WECHAT_MSG_FORWARD_MESSAGE, + WECHAT_GET_QRCODE_IMAGE, + WECHAT_GET_A8KEY, + WECHAT_MSG_SEND_XML, + WECHAT_LOGOUT, + WECHAT_GET_TRANSFER, + WECHAT_GET_CONTACT_ALL, + WECHAT_GET_CHATROOM_INFO, + WECHAT_GET_IMG_BY_NAME, + WECHAT_DO_OCR, + WECHAT_SEND_PAT_MSG, + WECHAT_SET_TOP_MSG, + WECHAT_REMOVE_TOP_MSG, + WECHAT_SNS_GET_FIRST_PAGE, + WECHAT_SNS_GET_NEXT_PAGE, + WECHAT_CONTACT_NAME, + WECHAT_ATTACH_DOWNLOAD, + WECHAT_GET_VOICE, +} WECHAT_HTTP_APIS, + *PWECHAT_HTTP_APIS; + +} +#endif \ No newline at end of file diff --git a/src/base_mgr.cc b/src/base_mgr.cc new file mode 100644 index 0000000..4131e0e --- /dev/null +++ b/src/base_mgr.cc @@ -0,0 +1,13 @@ +#include "base_mgr.h" + +namespace wxhelper{ + + BaseMgr::BaseMgr(DWORD base):base_addr_(base) + { + } + + BaseMgr::~BaseMgr() + { + } + +} \ No newline at end of file diff --git a/src/base_mgr.h b/src/base_mgr.h new file mode 100644 index 0000000..86f4608 --- /dev/null +++ b/src/base_mgr.h @@ -0,0 +1,13 @@ +#ifndef WXHELPER_BASE_MGR_H_ +#define WXHELPER_BASE_MGR_H_ +#include +namespace wxhelper{ + class BaseMgr{ + public: + explicit BaseMgr(DWORD base); + ~BaseMgr(); + protected: + DWORD base_addr_; + }; +} +#endif \ No newline at end of file diff --git a/src/chat_room.h b/src/chat_room.h deleted file mode 100644 index 336aa0b..0000000 --- a/src/chat_room.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef CHAT_ROOM_H_ -#define CHAT_ROOM_H_ -#include "wechat_data.h" - -int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info); -int DelMemberFromChatRoom(wchar_t* chat_room_id,wchar_t** wxids,int len); -int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len); - -int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out); -int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick); - -int SetTopMsg(wchar_t* wxid,ULONG64 msg_id); -int RemoveTopMsg(wchar_t* chat_room_id,ULONG64 msg_id); - -std::wstring GetChatRoomMemberNickname(wchar_t* chat_room_id,wchar_t* wxid); -#endif \ No newline at end of file diff --git a/src/chat_room.cc b/src/chat_room_mgr.cc similarity index 53% rename from src/chat_room.cc rename to src/chat_room_mgr.cc index 7a8ea57..a0c6a12 100644 --- a/src/chat_room.cc +++ b/src/chat_room_mgr.cc @@ -1,41 +1,25 @@ -#include "pch.h" -#include "chat_room.h" +#include "pch.h" +#include "chat_room_mgr.h" + +#include "db.h" -#include "common.h" -#include "get_db_handle.h" -#include "wechat_data.h" -#include "base64.h" using namespace std; -#define WX_CHAT_ROOM_MGR_OFFSET 0x78cf20 -#define WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET 0xb6f260 -#define WX_NEW_CHAT_ROOM_INFO_OFFSET 0xe15de0 -#define WX_FREE_CHAT_ROOM_INFO_OFFSET 0xe160b0 -#define WX_DEL_CHAT_ROOM_MEMBER_OFFSET 0xb64180 -#define WX_INIT_CHAT_MSG_OFFSET 0xed3be0 -#define WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET 0xb63c50 -#define WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET 0xbdf260 -#define WX_INIT_CHAT_ROOM_OFFSET 0xe97890 -#define WX_FREE_CHAT_ROOM_OFFSET 0xe97ab0 -#define WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET 0xb6adf0 -#define WX_NEW_CHAT_MSG_OFFSET 0x70e2a0 -#define WX_FREE_CHAT_MSG_OFFSET 0x6f4ea0 -#define WX_TOP_MSG_OFFSET 0xb727e0 -#define WX_REMOVE_TOP_MSG_OFFSET 0xb725a0 -#define WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET 0x6f5370 -#define WX_GET_MEMBER_NICKNAME_OFFSET 0xb703f0 -#define WX_CONTACT_MGR_INSTANCE_OFFSET 0x6f8990 -#define WX_GET_CONTACT_OFFSET 0xb93b20 -#define WX_FREE_CONTACT_OFFSET 0xe23690 +namespace wxhelper { -int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { - int success = 0; +ChatRoomMgr::ChatRoomMgr(DWORD base) : BaseMgr(base) {} + +ChatRoomMgr::~ChatRoomMgr() {} + +int ChatRoomMgr::GetChatRoomDetailInfo(wchar_t* chat_room_id, + ChatRoomInfoInner& room_info) { + int success = -1; WeChatString chat_room(chat_room_id); - DWORD base = GetWeChatWinBase(); - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD get_chat_room_detail_addr = base + WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET; - DWORD create_chat_room_info_addr = base + WX_NEW_CHAT_ROOM_INFO_OFFSET; - DWORD free_chat_room_info_addr = base + WX_FREE_CHAT_ROOM_INFO_OFFSET; + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD get_chat_room_detail_addr = + base_addr_ + WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET; + DWORD create_chat_room_info_addr = base_addr_ + WX_NEW_CHAT_ROOM_INFO_OFFSET; + DWORD free_chat_room_info_addr = base_addr_ + WX_FREE_CHAT_ROOM_INFO_OFFSET; char chat_room_info[0xDC] = {0}; __asm { PUSHAD @@ -54,21 +38,20 @@ int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { } DWORD room_id_len = *(DWORD*)(chat_room_info + 0x8); DWORD room_id_max_len = *(DWORD*)(chat_room_info + 0xC); - wchar_t * room_id = new wchar_t[room_id_len + 1]; - wmemcpy(room_id,*(wchar_t**)(chat_room_info + 0x4),room_id_len + 1); + wchar_t* room_id = new wchar_t[room_id_len + 1]; + wmemcpy(room_id, *(wchar_t**)(chat_room_info + 0x4), room_id_len + 1); room_info.chat_room_id.ptr = room_id; room_info.chat_room_id.length = room_id_len; room_info.chat_room_id.max_length = room_id_max_len; - DWORD notice_len = *(DWORD*)(chat_room_info + 0x1C); DWORD notice_max_len = *(DWORD*)(chat_room_info + 0x20); wchar_t* notice_ptr = *(wchar_t**)(chat_room_info + 0x18); - if(notice_len <= 0){ + if (notice_len <= 0) { room_info.notice.ptr = nullptr; - }else{ - wchar_t * notice = new wchar_t[notice_len + 1]; - wmemcpy(notice,notice_ptr,notice_len+1); + } else { + wchar_t* notice = new wchar_t[notice_len + 1]; + wmemcpy(notice, notice_ptr, notice_len + 1); room_info.notice.ptr = notice; } room_info.notice.length = notice_len; @@ -77,11 +60,11 @@ int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { DWORD admin_len = *(DWORD*)(chat_room_info + 0x30); DWORD admin_max_len = *(DWORD*)(chat_room_info + 0x34); wchar_t* admin_ptr = *(wchar_t**)(chat_room_info + 0x2C); - if(admin_len <= 0){ + if (admin_len <= 0) { room_info.admin.ptr = nullptr; - }else{ - wchar_t * admin = new wchar_t[admin_len + 1]; - wmemcpy(admin,admin_ptr,admin_len+1); + } else { + wchar_t* admin = new wchar_t[admin_len + 1]; + wmemcpy(admin, admin_ptr, admin_len + 1); room_info.admin.ptr = admin; } room_info.admin.length = admin_len; @@ -90,11 +73,11 @@ int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { DWORD xml_len = *(DWORD*)(chat_room_info + 0x54); DWORD xml_max_len = *(DWORD*)(chat_room_info + 0x58); wchar_t* xml_ptr = *(wchar_t**)(chat_room_info + 0x50); - if (xml_len <= 0){ + if (xml_len <= 0) { room_info.xml.ptr = nullptr; - }else{ - wchar_t * xml = new wchar_t[xml_len + 1]; - wmemcpy(xml,xml_ptr,xml_len+1); + } else { + wchar_t* xml = new wchar_t[xml_len + 1]; + wmemcpy(xml, xml_ptr, xml_len + 1); room_info.xml.ptr = xml; } room_info.xml.length = xml_len; @@ -109,20 +92,20 @@ int GetChatRoomDetailInfo(wchar_t* chat_room_id, ChatRoomInfoInner& room_info) { return success; } -int DelMemberFromChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len) { +int ChatRoomMgr::DelMemberFromChatRoom(wchar_t* chat_room_id, wchar_t** wxids, + int len) { int success = 0; WeChatString chat_room(chat_room_id); vector members; - VectorInner *list = (VectorInner *)&members; + VectorInner* list = (VectorInner*)&members; DWORD members_ptr = (DWORD)&list->start; for (int i = 0; i < len; i++) { WeChatString pwxid(wxids[i]); members.push_back(pwxid); } - DWORD base = GetWeChatWinBase(); - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD del_member_addr = base + WX_DEL_CHAT_ROOM_MEMBER_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD del_member_addr = base_addr_ + WX_DEL_CHAT_ROOM_MEMBER_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; __asm { PUSHAD CALL get_chat_room_mgr_addr @@ -142,22 +125,21 @@ int DelMemberFromChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len) { return success; } - -int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len){ - int success = 0; +int ChatRoomMgr::AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids, + int len) { + int success = -1; WeChatString chat_room(chat_room_id); vector members; - VectorInner *list = (VectorInner *)&members; + VectorInner* list = (VectorInner*)&members; DWORD members_ptr = (DWORD)&list->start; for (int i = 0; i < len; i++) { WeChatString pwxid(wxids[i]); members.push_back(pwxid); } - DWORD base = GetWeChatWinBase(); - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD add_member_addr = base + WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - DWORD temp=0; + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD add_member_addr = base_addr_ + WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; + DWORD temp = 0; __asm { PUSHAD PUSHFD @@ -184,17 +166,16 @@ int AddMemberToChatRoom(wchar_t* chat_room_id, wchar_t** wxids,int len){ return success; } - -int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out){ - int success = 0; +int ChatRoomMgr::GetMemberFromChatRoom(wchar_t* chat_room_id, + ChatRoomInner& out) { + int success = -1; WeChatString chat_room(chat_room_id); - DWORD chat_room_ptr = (DWORD) &chat_room; + DWORD chat_room_ptr = (DWORD)&chat_room; char buffer[0x1D4] = {0}; - DWORD base = GetWeChatWinBase(); - DWORD get_member_addr = base + WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET; - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD create_chat_room_addr = base + WX_INIT_CHAT_ROOM_OFFSET; - DWORD free_chat_room_addr = base + WX_FREE_CHAT_ROOM_OFFSET; + DWORD get_member_addr = base_addr_ + WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET; + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD create_chat_room_addr = base_addr_ + WX_INIT_CHAT_ROOM_OFFSET; + DWORD free_chat_room_addr = base_addr_ + WX_FREE_CHAT_ROOM_OFFSET; __asm { PUSHAD LEA ECX,buffer @@ -208,36 +189,37 @@ int GetMemberFromChatRoom(wchar_t* chat_room_id,ChatRoomInner & out){ MOV success,EAX POPAD } - char* members = *(char **)(buffer +0x1c); - wchar_t* room = *(wchar_t **)(buffer +0x8); - wchar_t* admin = *(wchar_t **)(buffer +0x4c); - + char* members = *(char**)(buffer + 0x1c); + wchar_t* room = *(wchar_t**)(buffer + 0x8); + wchar_t* admin = *(wchar_t**)(buffer + 0x4c); + out.members = new char[strlen(members) + 1]; memcpy(out.members, members, strlen(members) + 1); - out.chat_room = new wchar_t[wcslen(room)+1]; - wmemcpy(out.chat_room ,room,wcslen(room)+1); + out.chat_room = new wchar_t[wcslen(room) + 1]; + wmemcpy(out.chat_room, room, wcslen(room) + 1); - out.admin = new wchar_t[wcslen(admin)+1]; - wmemcpy(out.admin ,admin,wcslen(admin)+1); + out.admin = new wchar_t[wcslen(admin) + 1]; + wmemcpy(out.admin, admin, wcslen(admin) + 1); - __asm{ + __asm { LEA ECX,buffer CALL free_chat_room_addr - } + } return success; } -int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick){ - int success = 0; +int ChatRoomMgr::ModChatRoomMemberNickName(wchar_t* chat_room_id, wchar_t* wxid, + wchar_t* nick) { + int success = -1; WeChatString chat_room(chat_room_id); WeChatString self_wxid(wxid); WeChatString new_nick(nick); - DWORD base = GetWeChatWinBase(); - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD mod_member_nick_name_addr = base + WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - __asm{ + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD mod_member_nick_name_addr = + base_addr_ + WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET; + DWORD init_chat_msg_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; + __asm { PUSHAD CALL get_chat_room_mgr_addr SUB ESP,0x14 @@ -263,57 +245,34 @@ int ModChatRoomMemberNickName(wchar_t* chat_room_id,wchar_t* wxid,wchar_t * nick return success; } - -int SetTopMsg(wchar_t* wxid,ULONG64 msg_id){ +int ChatRoomMgr::SetTopMsg(wchar_t* wxid, ULONG64 msg_id) { int success = -1; - char chat_msg[0x2C4] ={0}; - DWORD base = GetWeChatWinBase(); - DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET; - DWORD get_chat_room_mgr_addr = base + WX_CHAT_ROOM_MGR_OFFSET; - DWORD handle_top_msg_addr = base + WX_TOP_MSG_OFFSET; - // DWORD free_addr = base + WX_FREE_CHAT_MSG_OFFSET; - DWORD free_addr = base + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET; - vector local_msg = GetChatMsgByMsgId(msg_id); - if(local_msg.empty()){ + char chat_msg[0x2D8] = {0}; + DWORD new_chat_msg_addr = base_addr_ + WX_NEW_CHAT_MSG_OFFSET; + DWORD get_chat_room_mgr_addr = base_addr_ + WX_CHAT_ROOM_MGR_OFFSET; + DWORD handle_top_msg_addr = base_addr_ + WX_TOP_MSG_OFFSET; + DWORD free_addr = base_addr_ + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET; + DWORD get_chat_mgr_addr = base_addr_ + WX_CHAT_MGR_OFFSET; + DWORD get_by_local_Id_addr = base_addr_ + WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET; + + + int db_index = 0; + int local_id = DB::GetInstance().GetLocalIdByMsgId(msg_id, db_index); + if (local_id < 1) { return -2; } - string type = local_msg[3]; - string status = local_msg[10]; - string talker = local_msg[13]; - string content = local_msg[14]; - wstring w_talker = String2Wstring(talker); - wstring w_content = String2Wstring(content); - int msg_type =stoi(type); - int msg_status =stoi(status); - - #ifdef _DEBUG - wcout << "w_talker:" < -#define READ_WSTRING(addr, offset) ((*(DWORD *)(addr + offset + 0x4) == 0) ? std::wstring(L"") : std::wstring((wchar_t *)(*(DWORD *)(addr + offset)), *(DWORD *)(addr + offset + 0x4))) -#define STR2INT(str) (IsDigit(str) ? stoi(str) : 0) -/// @brief utf8 转换成unicode -/// @param buffer utf8 -/// @return unicode -std::wstring utf8_to_unicode(const char *buffer); - -/// @brief unicode转换utf8 -/// @param wstr unicode -/// @return utf8 -std::string unicode_to_utf8(wchar_t *wstr); - -/// @brief 获取WeChatWin.dll基址 -/// @return 基址 -DWORD GetWeChatWinBase(); -/// @brief 创建窗口 -/// @param void -/// @return 创建结果 -BOOL CreateConsole(void); -/// @brief hook任意地址 -/// @param hook_addr 被hook的地址 -/// @param jmp_addr 被hook的函数的地址 -/// @param origin 原始code -void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin); - -/// @brief 取消hook -/// @param hook_addr 被hook的地址 -/// @param origin 原始code -void UnHookAnyAddress(DWORD hook_addr, char *origin); - -/// @brief get timeW -/// @param timestamp timestamp -/// @return str -std::wstring GetTimeW(long long timestamp); -/// @brief unicode trans utf8 -/// @param str unicode str -/// @return utf8 str -std::string UnicodeToUtf8(const wchar_t *str); -/// @brief string convert wstring -/// @param str -/// @return -std::wstring String2Wstring(std::string str); -/// @brief wstring convert string -/// @param str -/// @return -std::string Wstring2String(std::wstring wstr); - -/// @brief create dir -/// @param path -/// @return -BOOL FindOrCreateDirectoryW(const wchar_t *path); - -void CloseConsole(); - -std::string EncodeHexString(const std::string &str); - -std::string Hex2String(const std::string &hex_str); - -std::string Bytes2Hex(const BYTE *bytes, const int length); - -void Hex2Bytes(const std::string &hex, BYTE *bytes); - -bool IsDigit(std::string str); - -template -std::vector split(T1 str, T2 letter) { - vector arr; - size_t pos; - while ((pos = str.find_first_of(letter)) != T1::npos) { - T1 str1 = str.substr(0, pos); - arr.push_back(str1); - str = str.substr(pos + 1, str.length() - pos - 1); - } - arr.push_back(str); - return arr; -} - -template -T1 replace(T1 source, T2 replaced, T1 replaceto) { - vector v_arr = split(source, replaced); - if (v_arr.size() < 2) return source; - T1 temp; - for (unsigned int i = 0; i < v_arr.size() - 1; i++) { - temp += v_arr[i]; - temp += replaceto; - } - temp += v_arr[v_arr.size() - 1]; - return temp; -} -#endif \ No newline at end of file diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 0000000..2f86227 --- /dev/null +++ b/src/config.cc @@ -0,0 +1,6 @@ +#include "config.h" +namespace wxhelper { +Config::Config(/* args */) {} + +Config::~Config() {} +} // namespace wxhelper \ No newline at end of file diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..5df279e --- /dev/null +++ b/src/config.h @@ -0,0 +1,15 @@ +#ifndef WXHELPER_CONFIG_H_ +#define WXHELPER_CONFIG_H_ + +namespace wxhelper{ + + class Config + { + private: + /* data */ + public: + Config(/* args */); + ~Config(); + }; +} +#endif \ No newline at end of file diff --git a/src/confirm_receipt.cc b/src/confirm_receipt.cc deleted file mode 100644 index cb48b5b..0000000 --- a/src/confirm_receipt.cc +++ /dev/null @@ -1,49 +0,0 @@ -#include "pch.h" -#include "confirm_receipt.h" - -#include "common.h" -#include "wechat_data.h" - -#define WX_NEW_WCPAYINFO_OFFSET 0x756340 -#define WX_FREE_WCPAYINFO_OFFSET 0x73c170 -#define WX_CONFIRM_RECEIPT_OFFSET 0x15287a0 - -int DoConfirmReceipt(wchar_t *wxid, wchar_t *transcationid, - wchar_t *transferid) { - int success = -1; - WeChatString recv_id(wxid); - WeChatString transcation_id(transcationid); - WeChatString transfer_id(transferid); - char pay_info[0x134] = {0}; - DWORD base = GetWeChatWinBase(); - DWORD new_pay_info_addr = base + WX_NEW_WCPAYINFO_OFFSET; - DWORD free_pay_info_addr = base + WX_FREE_WCPAYINFO_OFFSET; - DWORD do_confirm_addr = base + WX_CONFIRM_RECEIPT_OFFSET; - __asm { - PUSHAD - LEA ECX,pay_info - CALL new_pay_info_addr - MOV dword ptr [pay_info + 0x4], 0x1 - MOV dword ptr [pay_info + 0x4C], 0x1 - POPAD - } - memcpy(&pay_info[0x1c], &transcation_id, sizeof(transcation_id)); - memcpy(&pay_info[0x38], &transfer_id, sizeof(transfer_id)); - - __asm { - PUSHAD - PUSH 0x1 - SUB ESP,0x8 - LEA EDX,recv_id - LEA ECX,pay_info - CALL do_confirm_addr - MOV success,EAX - ADD ESP,0xC - PUSH 0x0 - LEA ECX,pay_info - CALL free_pay_info_addr - POPAD - } - - return success; -} \ No newline at end of file diff --git a/src/confirm_receipt.h b/src/confirm_receipt.h deleted file mode 100644 index 0fdee8f..0000000 --- a/src/confirm_receipt.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CONFIRM_RECEIPT_H -#define CONFIRM_RECEIPT_H - -int DoConfirmReceipt(wchar_t* wxid,wchar_t *transcationid, wchar_t *transferid); - -#endif \ No newline at end of file diff --git a/src/contact.cc b/src/contact.cc deleted file mode 100644 index 346286a..0000000 --- a/src/contact.cc +++ /dev/null @@ -1,192 +0,0 @@ -#include "pch.h" -#include "contact.h" - -#include "common.h" -#include "wechat_data.h" -using namespace std; -#define WX_CONTACT_MGR_INSTANCE_OFFSET 0x75a4a0 -#define WX_CONTACT_GET_LIST_OFFSET 0xc089f0 -#define WX_CONTACT_DEL_OFFSET 0xb9b3b0 -#define WX_INIT_CHAT_MSG_OFFSET 0xed3be0 -#define WX_SYNC_MGR_OFFSET 0xa87fd0 -#define WX_SET_VALUE_OFFSET 0x1f80900 -#define WX_DO_DEL_CONTACT_OFFSET 0xca6480 -#define WX_FREE_CONTACT_OFFSET 0xe23690 -#define WX_GET_CONTACT_OFFSET 0xb93b20 -#define WX_DO_VERIFY_USER_OFFSET 0xB91160 - -int GetAllContact(vector &vec) { - DWORD base = GetWeChatWinBase(); - DWORD get_instance = base + WX_CONTACT_MGR_INSTANCE_OFFSET; - DWORD contact_get_list = base + WX_CONTACT_GET_LIST_OFFSET; - // char contact[0xc] = {0}; - DWORD* contact[3] = {0, 0, 0}; - int success = 0; - __asm { - PUSHAD - CALL get_instance - LEA ECX,contact - PUSH ECX - MOV ECX,EAX - CALL contact_get_list - MOVZX EAX,AL - MOV success,EAX - POPAD - } - DWORD start = (DWORD)contact[0]; - DWORD end = (DWORD)contact[2]; -#ifdef _DEBUG - cout << "address = " << &contact << endl; - cout << "refresh contact = " << success << endl; - cout << "start = " << start << endl; - cout << "end = " << end << endl; -#endif - while (start < end) { - Contact temp{0}; - - temp.wxid.ptr = *(wchar_t **)(start + 0x10); - temp.wxid.length = *(DWORD *)(start + 0x14); - temp.wxid.max_length = *(DWORD *)(start + 0x18); - - temp.custom_account.ptr = *(wchar_t **)(start + 0x24); - temp.custom_account.length = *(DWORD *)(start + 0x28); - temp.custom_account.max_length = *(DWORD *)(start + 0x2C); - - temp.encrypt_name.ptr = *(wchar_t **)(start + 0x6c); - temp.encrypt_name.length = *(DWORD *)(start + 0x70); - temp.encrypt_name.max_length = *(DWORD *)(start + 0x74); - - temp.pinyin.ptr = *(wchar_t **)(start + 0xAC); - temp.pinyin.length = *(DWORD *)(start + 0xB0); - temp.pinyin.max_length = *(DWORD *)(start + 0xB4); - - temp.pinyin_all.ptr = *(wchar_t **)(start + 0xC0); - temp.pinyin_all.length = *(DWORD *)(start + 0xC4); - temp.pinyin_all.max_length = *(DWORD *)(start + 0xC8); - - temp.del_flag = *(DWORD *)(start + 0x4c); - temp.type = *(DWORD *)(start + 0x50); - temp.verify_flag = *(DWORD *)(start + 0x54); - vec.push_back(temp); - start += 0x438; - } - return success; -} -// note maybe not delete -int DelContact(wchar_t *wxid) { - int success = -1; - WeChatString user_id(wxid); - DWORD id_ptr = (DWORD) &user_id; - DWORD base = GetWeChatWinBase(); - DWORD sync_mgr_addr = base + WX_SYNC_MGR_OFFSET; - DWORD set_id_addr = base + WX_SET_VALUE_OFFSET; - DWORD del_contact_addr = base + WX_DO_DEL_CONTACT_OFFSET; - int len = user_id.length; - - string id_cstr = unicode_to_utf8(wxid); - char id_[0x20]={0}; - memcpy(id_,id_cstr.c_str(),id_cstr.size()+1); - char buff[0x10]={0}; - __asm{ - PUSHAD - PUSHFD - CALL sync_mgr_addr - MOV ECX,EAX - LEA EAX,buff - MOV [ECX + 4],EAX - LEA EAX,id_ - Mov dword ptr[buff +0x4],EAX - CALL del_contact_addr - MOV success,EAX - POPFD - POPAD - } - return success; -} - -std::wstring GetContactOrChatRoomNickname(wchar_t *id) { - int success = -1; - char buff[0x440] = {0}; - WeChatString pri(id); - DWORD base = GetWeChatWinBase(); - DWORD contact_mgr_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET; - DWORD get_contact_addr = base + WX_GET_CONTACT_OFFSET; - DWORD free_contact_addr = base + WX_FREE_CONTACT_OFFSET; - wstring name = L""; - __asm { - PUSHAD - PUSHFD - CALL contact_mgr_addr - LEA ECX,buff - PUSH ECX - LEA ECX,pri - PUSH ECX - MOV ECX,EAX - CALL get_contact_addr - POPFD - POPAD - } - name += READ_WSTRING(buff, 0x6C); - __asm { - PUSHAD - PUSHFD - LEA ECX,buff - CALL free_contact_addr - POPFD - POPAD - } - return name; -} - - -int AddFriendByWxid(wchar_t *wxid){ - int success = -1; - DWORD base = GetWeChatWinBase(); - DWORD contact_mgr_addr = base + WX_CONTACT_MGR_INSTANCE_OFFSET; - DWORD set_group_addr = base + 0x746E20; - DWORD fn2_addr = base + 0x285D968; - DWORD fn3_addr = base + 0x6F6050; - DWORD fn4_addr = base + 0xED3BE0; - DWORD do_verify_user_addr = base + WX_DO_VERIFY_USER_OFFSET; - - DWORD instance =0; - WeChatString chat_room(NULL); - WeChatString user_id(wxid); - __asm{ - PUSHAD - PUSHFD - CALL contact_mgr_addr - SUB ESP,0x18 - MOV dword ptr [instance],EAX - MOV ECX,ESP - PUSH ECX - LEA ECX,chat_room - CALL set_group_addr - MOV EAX,fn2_addr - SUB ESP,0x18 - MOV ECX,ESP - PUSH EAX - CALL fn3_addr - PUSH 0x0 - PUSH 0XE - SUB ESP,0x14 - MOV ESI,ESP - MOV dword ptr [ESI],0x0 - MOV dword ptr [ESI+0x4],0x0 - MOV dword ptr [ESI+0x8],0x0 - MOV dword ptr [ESI+0xC],0x0 - MOV dword ptr [ESI+0x10],0x0 - PUSH 0x1 - SUB ESP,0x14 - MOV ECX,ESP - LEA EAX,user_id - PUSH EAX - CALL fn4_addr - MOV ECX,dword ptr [instance] - CALL do_verify_user_addr - MOV success,EAX - POPFD - POPAD - } - return success; -} \ No newline at end of file diff --git a/src/contact.h b/src/contact.h deleted file mode 100644 index 924ee70..0000000 --- a/src/contact.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CONTACT_H_ -#define CONTACT_H_ -#include -#include "wechat_data.h" - -int GetAllContact(std::vector &vec); - - - -int DelContact(wchar_t* wxid); - -std::wstring GetContactOrChatRoomNickname(wchar_t* id); - -int AddFriendByWxid(wchar_t *wxid); -#endif \ No newline at end of file diff --git a/src/contact_mgr.cc b/src/contact_mgr.cc new file mode 100644 index 0000000..4b2582a --- /dev/null +++ b/src/contact_mgr.cc @@ -0,0 +1,193 @@ +#include "pch.h" +#include "contact_mgr.h" + + +#include "wechat_function.h" + +using namespace std; +namespace wxhelper { +ContactMgr::ContactMgr(DWORD base) : BaseMgr(base) {} +ContactMgr::~ContactMgr() {} +int ContactMgr::GetAllContact(vector &vec) { + DWORD get_instance = base_addr_ + WX_CONTACT_MGR_OFFSET; + DWORD contact_get_list = base_addr_ + WX_CONTACT_GET_LIST_OFFSET; + DWORD *contact[3] = {0, 0, 0}; + int success = 0; + __asm { + PUSHAD + CALL get_instance + LEA ECX,contact + PUSH ECX + MOV ECX,EAX + CALL contact_get_list + MOVZX EAX,AL + MOV success,EAX + POPAD + } + DWORD start = (DWORD)contact[0]; + DWORD end = (DWORD)contact[2]; + while (start < end) { + Contact temp{0}; + + temp.wxid.ptr = *(wchar_t **)(start + 0x10); + temp.wxid.length = *(DWORD *)(start + 0x14); + temp.wxid.max_length = *(DWORD *)(start + 0x18); + + temp.custom_account.ptr = *(wchar_t **)(start + 0x24); + temp.custom_account.length = *(DWORD *)(start + 0x28); + temp.custom_account.max_length = *(DWORD *)(start + 0x2C); + + temp.encrypt_name.ptr = *(wchar_t **)(start + 0x6c); + temp.encrypt_name.length = *(DWORD *)(start + 0x70); + temp.encrypt_name.max_length = *(DWORD *)(start + 0x74); + + temp.pinyin.ptr = *(wchar_t **)(start + 0xAC); + temp.pinyin.length = *(DWORD *)(start + 0xB0); + temp.pinyin.max_length = *(DWORD *)(start + 0xB4); + + temp.pinyin_all.ptr = *(wchar_t **)(start + 0xC0); + temp.pinyin_all.length = *(DWORD *)(start + 0xC4); + temp.pinyin_all.max_length = *(DWORD *)(start + 0xC8); + + temp.del_flag = *(DWORD *)(start + 0x4c); + temp.type = *(DWORD *)(start + 0x50); + temp.verify_flag = *(DWORD *)(start + 0x54); + vec.push_back(temp); + start += 0x438; + } + return success; +} +int ContactMgr::DelContact(wchar_t *wxid) { + int success = -1; + WeChatString user_id(wxid); + DWORD id_ptr = (DWORD)&user_id; + DWORD sync_mgr_addr = base_addr_ + WX_SYNC_MGR_OFFSET; + DWORD set_id_addr = base_addr_ + WX_SET_VALUE_OFFSET; + DWORD del_contact_addr = base_addr_ + WX_DO_DEL_CONTACT_OFFSET; + int len = user_id.length; + wstring ws_wxid(wxid); + + string id_cstr = Utils::WstringToUTF8(ws_wxid); + char id_[0x20] = {0}; + memcpy(id_, id_cstr.c_str(), id_cstr.size() + 1); + char buff[0x10] = {0}; + __asm { + PUSHAD + PUSHFD + CALL sync_mgr_addr + MOV ECX,EAX + LEA EAX,buff + MOV [ECX + 4],EAX + LEA EAX,id_ + Mov dword ptr[buff +0x4],EAX + CALL del_contact_addr + MOV success,EAX + POPFD + POPAD + } + return success; +} +wstring ContactMgr::GetContactOrChatRoomNickname(wchar_t *id) { + int success = -1; + char buff[0x440] = {0}; + WeChatString pri(id); + DWORD contact_mgr_addr = base_addr_ + WX_CONTACT_MGR_OFFSET; + DWORD get_contact_addr = base_addr_ + WX_GET_CONTACT_OFFSET; + DWORD free_contact_addr = base_addr_ + WX_FREE_CONTACT_OFFSET; + wstring name = L""; + __asm { + PUSHAD + PUSHFD + CALL contact_mgr_addr + LEA ECX,buff + PUSH ECX + LEA ECX,pri + PUSH ECX + MOV ECX,EAX + CALL get_contact_addr + POPFD + POPAD + } + name += READ_WSTRING(buff, 0x6C); + __asm { + PUSHAD + PUSHFD + LEA ECX,buff + CALL free_contact_addr + POPFD + POPAD + } + return name; +} + +int ContactMgr::AddFriendByWxid(wchar_t *wxid,wchar_t* msg) { + int success = -1; + DWORD contact_mgr_addr = base_addr_ + WX_CONTACT_MGR_OFFSET; + DWORD verify_msg_addr = base_addr_ + WX_VERIFY_MSG_OFFSET; + DWORD set_value_addr = base_addr_ + WX_INIT_CHAT_MSG_OFFSET; + DWORD do_verify_user_addr = base_addr_ + WX_DO_VERIFY_USER_OFFSET; + DWORD fn1_addr = base_addr_ + 0x758720; + WeChatString user_id(wxid); + WeChatString w_msg(msg); + DWORD null_ptr = 0; + DWORD instance =0; + Unkown null_obj={}; + null_obj.field6 = 0xF; + __asm{ + PUSHAD + PUSHFD + CALL contact_mgr_addr + MOV dword ptr [instance],EAX + SUB ESP,0x18 + MOV EAX,ESP + SUB ESP,0x18 + LEA EAX,null_obj + MOV ECX,ESP + PUSH EAX + CALL fn1_addr + PUSH 0x0 + PUSH 0x6 + MOV EAX,w_msg + SUB ESP,0x14 + MOV ECX,ESP + PUSH -0x1 + PUSH EAX + CALL verify_msg_addr + PUSH 0x2 + LEA EAX,user_id + SUB ESP,0x14 + MOV ECX,ESP + PUSH EAX + CALL set_value_addr + MOV ECX,dword ptr [instance] + CALL do_verify_user_addr + MOV success,EAX + POPFD + POPAD + } + + // __asm { + // PUSHAD + // PUSHFD + // SUB ESP,0x14 + // MOV ECX,ESP + // PUSH -0x1 + // PUSH w_msg + // CALL verify_msg_addr + // PUSH 0x2 + // LEA EAX,user_id + // SUB ESP,0x14 + // MOV ECX,ESP + // PUSH EAX + // CALL set_value_addr + // CALL contact_mgr_addr + // MOV ECX,EAX + // CALL do_verify_user_addr + // MOV success,EAX + // POPFD + // POPAD + // } + return success; +} + +} // namespace wxhelper \ No newline at end of file diff --git a/src/contact_mgr.h b/src/contact_mgr.h new file mode 100644 index 0000000..b522427 --- /dev/null +++ b/src/contact_mgr.h @@ -0,0 +1,20 @@ +#ifndef WXHELPER_CONTACT_MGR_H_ +#define WXHELPER_CONTACT_MGR_H_ +#include +#include + +#include "base_mgr.h" +#include "wechat_function.h" +namespace wxhelper { +class ContactMgr : public BaseMgr { + public: + explicit ContactMgr(DWORD base); + ~ContactMgr(); + int GetAllContact(std::vector& vec); + int DelContact(wchar_t* wxid); + std::wstring GetContactOrChatRoomNickname(wchar_t* id); + int AddFriendByWxid(wchar_t* wxid,wchar_t* msg); +}; +} // namespace wxhelper + +#endif; \ No newline at end of file diff --git a/src/get_db_handle.cc b/src/db.cc similarity index 55% rename from src/get_db_handle.cc rename to src/db.cc index cd3779f..1737688 100644 --- a/src/get_db_handle.cc +++ b/src/db.cc @@ -1,57 +1,163 @@ -#include "get_db_handle.h" +#include "pch.h" +#include "db.h" -#include "common.h" -#include "db_operation.h" -#include "new_sqlite3.h" -#include "pch.h" -#include "wechat_data.h" -#define CONTACT_G_PINSTANCE_OFFSET 0x2ffddc8 -#define DB_MICRO_MSG_OFFSET 0x68 -#define DB_CHAT_MSG_OFFSET 0x1C0 -#define DB_MISC_OFFSET 0x3D8 -#define DB_EMOTION_OFFSET 0x558 -#define DB_MEDIA_OFFSET 0x9B8 -#define DB_BIZCHAT_MSG_OFFSET 0x1120 -#define DB_FUNCTION_MSG_OFFSET 0x11B0 -#define DB_NAME_OFFSET 0x14 - -#define STORAGE_START_OFFSET 0x13f8 -#define STORAGE_END_OFFSET 0x13fc - -#define PUBLIC_MSG_MGR_OFFSET 0x303df74 -#define MULTI_DB_MSG_MGR_OFFSET 0x30403b8 -#define FAVORITE_STORAGE_MGR_OFFSET 0x303fd40 -#define FTS_FAVORITE_MGR_OFFSET 0x2ffe908 - -#define OP_LOG_STORAGE_VFTABLE 0x2AD3A20 -#define CHAT_MSG_STORAGE_VFTABLE 0x2AC10F0 -#define CHAT_CR_MSG_STORAGE_VFTABLE 0x2ABEF14 -#define SESSION_STORAGE_VFTABLE 0x2AD3578 -#define APP_INFO_STORAGE_VFTABLE 0x2ABCC58 -#define HEAD_IMG_STORAGE_VFTABLE 0x2ACD9DC -#define HEAD_IMG_URL_STORAGE_VFTABLE 0x2ACDF70 - -#define BIZ_INFO_STORAGE_VFTABLE 0x2ABD718 -#define TICKET_INFO_STORAGE_VFTABLE 0x2AD5400 -#define CHAT_ROOM_STORAGE_VFTABLE 0x2AC299C -#define CHAT_ROOM_INFO_STORAGE_VFTABLE 0x2AC245C -#define MEDIA_STORAGE_VFTABLE 0x2ACE998 -#define NAME_2_ID_STORAGE_VFTABLE 0x2AD222C -#define EMOTION_PACKAGE_STORAGE_VFTABLE 0x2AC6400 - -#define EMOTION_STORAGE_VFTABLE 0x2AC7018 -#define BUFINFO_STORAGE_VFTABLE 0x2AC3178 - -#define CUSTOM_EMOTION_STORAGE_VFTABLE 0x2AC4E90 -#define DEL_SESSIONINFO_STORAGE_VFTABLE 0x2AC5F98 -#define FUNCTION_MSG_STORAGE_VFTABLE 0x2ACD10C - -#define FUNCTION_MSG_TASK_STORAGE_VFTABLE 0x2ACC5C8 -#define REVOKE_MSG_STORAGE_VFTABLE 0x2AD27BC +#include "base64.h" +#include "easylogging++.h" +#include "wechat_function.h" using namespace std; -map dbmap; -std::vector dbs; + +namespace wxhelper { + +void DB::init(DWORD base) { + base_addr_ = base; + dbmap_ = {}; + dbs_ = {}; +} + +void FreeResult(vector> &data) { + if (data.size() == 0) { + return; + } + for (unsigned int i = 0; i < data.size(); i++) { + for (unsigned j = 0; j < data[i].size(); j++) { + SqlResult *sr = (SqlResult *)&data[i][j]; + if (sr->column_name) { + delete[] sr->column_name; + sr->column_name = NULL; + } + if (sr->content) { + delete[] sr->content; + sr->content = NULL; + } + } + data[i].clear(); + } + data.clear(); +} + +int DB::SelectDataInner(DWORD db, const char *sql, + vector> &data) { + Sqlite3_prepare p_Sqlite3_prepare = + (Sqlite3_prepare)(base_addr_ + SQLITE3_PREPARE_OFFSET); + Sqlite3_step p_Sqlite3_step = + (Sqlite3_step)(base_addr_ + SQLITE3_STEP_OFFSET); + Sqlite3_column_count p_Sqlite3_column_count = + (Sqlite3_column_count)(base_addr_ + SQLITE3_COLUMN_COUNT_OFFSET); + Sqlite3_column_name p_Sqlite3_column_name = + (Sqlite3_column_name)(base_addr_ + SQLITE3_COLUMN_NAME_OFFSET); + Sqlite3_column_type p_Sqlite3_column_type = + (Sqlite3_column_type)(base_addr_ + SQLITE3_COLUMN_TYPE_OFFSET); + Sqlite3_column_blob p_Sqlite3_column_blob = + (Sqlite3_column_blob)(base_addr_ + SQLITE3_COLUMN_BLOB_OFFSET); + Sqlite3_column_bytes p_Sqlite3_column_bytes = + (Sqlite3_column_bytes)(base_addr_ + SQLITE3_COLUMN_BYTES_OFFSET); + Sqlite3_finalize p_Sqlite3_finalize = + (Sqlite3_finalize)(base_addr_ + SQLITE3_FINALIZE_OFFSET); + DWORD *stmt; + int rc = p_Sqlite3_prepare(db, sql, -1, &stmt, 0); + if (rc != SQLITE_OK) return NULL; + while (p_Sqlite3_step(stmt) == SQLITE_ROW) { + int col_count = p_Sqlite3_column_count(stmt); + vector tempStruct; + for (int i = 0; i < col_count; i++) { + SqlResult temp = {0}; + const char *ColName = p_Sqlite3_column_name(stmt, i); + int nType = p_Sqlite3_column_type(stmt, i); + const void *pReadBlobData = p_Sqlite3_column_blob(stmt, i); + int nLength = p_Sqlite3_column_bytes(stmt, i); + temp.column_name = new char[strlen(ColName) + 1]; + memcpy(temp.column_name, ColName, strlen(ColName) + 1); + temp.column_name_len = strlen(ColName); + temp.content_len = nLength; + switch (nType) { + case SQLITE_BLOB: { + temp.content = new char[nLength]; + memcpy(temp.content, pReadBlobData, nLength); + temp.is_blob = true; + break; + } + default: { + if (nLength != 0) { + temp.content = new char[nLength + 1]; + memcpy(temp.content, pReadBlobData, nLength + 1); + } else { + temp.content = new char[2]; + ZeroMemory(temp.content, 2); + } + temp.is_blob = false; + break; + } + } + tempStruct.push_back(temp); + } + data.push_back(tempStruct); + } + p_Sqlite3_finalize(stmt); + return 1; +} + +int DB::Select(DWORD db_hanle, const char *sql, + vector> &query_result) { + vector> data; + int status = SelectDataInner(db_hanle, sql, data); + if (status == 0) { + return 0; + } + if (data.size() == 0) { + return 1; + } + vector index; + for (size_t i = 0; i < data[0].size(); i++) { + index.push_back(data[0][i].column_name); + } + query_result.push_back(index); + + for (auto it : data) { + vector item; + for (size_t i = 0; i < it.size(); i++) { + if (!it[i].is_blob) { + string content(it[i].content); + item.push_back(content); + } else { + string b64_str = + base64_encode((BYTE *)it[i].content, it[i].content_len); + item.push_back(b64_str); + } + } + query_result.push_back(item); + } + FreeResult(data); + return 1; +} + +int SelectDbInfo(void *data, int argc, char **argv, char **name) { + vector result; + for (int i = 0; i < argc; i++) { + SqlResult temp = {0}; + temp.column_name = new char[strlen(name[i]) + 1]; + memcpy(temp.column_name, name[i], strlen(name[i]) + 1); + temp.column_name_len = strlen(name[i]); + if (argv[i]) { + temp.content = new char[strlen(argv[i]) + 1]; + memcpy(temp.content, argv[i], strlen(argv[i]) + 1); + temp.content_len = strlen(argv[i]); + } else { + temp.content = new char[2]; + ZeroMemory(temp.content, 2); + temp.content_len = 0; + } + result.push_back(temp); + } + return 1; +} + +int DB::ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data) { + DWORD sqlite3_exec_addr = base_addr_ + SQLITE3_EXEC_OFFSET; + Sqlite3_exec fn_sqlite3_exec = (Sqlite3_exec)sqlite3_exec_addr; + int status = fn_sqlite3_exec(db, sql, (Sqlite3_callback)callback, data, 0); + return status; +} int GetDbInfo(void *data, int argc, char **argv, char **name) { DatabaseInfo *pdata = (DatabaseInfo *)data; @@ -89,11 +195,10 @@ int GetDbInfo(void *data, int argc, char **argv, char **name) { return 0; } -std::vector GetDbHandles() { - dbs.clear(); - dbmap.clear(); - DWORD base = GetWeChatWinBase(); - DWORD p_contact_addr = *(DWORD *)(base + CONTACT_G_PINSTANCE_OFFSET); +std::vector DB::GetDbHandles() { + dbs_.clear(); + dbmap_.clear(); + DWORD p_contact_addr = *(DWORD *)(base_addr_ + CONTACT_G_PINSTANCE_OFFSET); DWORD micro_msg_db_addr = *(DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET); DWORD chat_msg_db_addr = *(DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET); DWORD misc_db_addr = *(DWORD *)(p_contact_addr + DB_MISC_OFFSET); @@ -114,10 +219,10 @@ std::vector GetDbHandles() { ExecuteSQL(micro_msg_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, µ_msg_db); - dbs.push_back(micro_msg_db); + dbs_.push_back(micro_msg_db); wstring micro_msg_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_MICRO_MSG_OFFSET + DB_NAME_OFFSET))); - dbmap[micro_msg_name] = micro_msg_db; + dbmap_[micro_msg_name] = micro_msg_db; // chatMsg.db DatabaseInfo chat_msg_db{0}; @@ -129,10 +234,10 @@ std::vector GetDbHandles() { ExecuteSQL(chat_msg_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &chat_msg_db); - dbs.push_back(chat_msg_db); + dbs_.push_back(chat_msg_db); wstring chat_msg_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_CHAT_MSG_OFFSET + DB_NAME_OFFSET))); - dbmap[chat_msg_name] = chat_msg_db; + dbmap_[chat_msg_name] = chat_msg_db; // misc.db DatabaseInfo misc_db{0}; @@ -143,10 +248,10 @@ std::vector GetDbHandles() { misc_db.handle = misc_db_addr; ExecuteSQL(misc_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &misc_db); - dbs.push_back(misc_db); + dbs_.push_back(misc_db); wstring misc_name = wstring(( wchar_t *)(*(DWORD *)(p_contact_addr + DB_MISC_OFFSET + DB_NAME_OFFSET))); - dbmap[misc_name] = misc_db; + dbmap_[misc_name] = misc_db; // emotion.db DatabaseInfo emotion_db{0}; @@ -158,10 +263,10 @@ std::vector GetDbHandles() { ExecuteSQL(emotion_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &emotion_db); - dbs.push_back(emotion_db); + dbs_.push_back(emotion_db); wstring emotion_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_EMOTION_OFFSET + DB_NAME_OFFSET))); - dbmap[emotion_name] = emotion_db; + dbmap_[emotion_name] = emotion_db; // media.db DatabaseInfo media_db{0}; @@ -172,10 +277,10 @@ std::vector GetDbHandles() { media_db.handle = media_db_addr; ExecuteSQL(media_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &media_db); - dbs.push_back(media_db); + dbs_.push_back(media_db); wstring media_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_MEDIA_OFFSET + DB_NAME_OFFSET))); - dbmap[media_name] = media_db; + dbmap_[media_name] = media_db; // functionMsg.db DatabaseInfo function_msg_db{0}; @@ -187,10 +292,10 @@ std::vector GetDbHandles() { ExecuteSQL(function_msg_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &function_msg_db); - dbs.push_back(function_msg_db); + dbs_.push_back(function_msg_db); wstring function_msg_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET))); - dbmap[function_msg_name] = function_msg_db; + dbmap_[function_msg_name] = function_msg_db; if (bizchat_msg_db_addr) { // functionMsg.db maybe null @@ -204,18 +309,18 @@ std::vector GetDbHandles() { ExecuteSQL(bizchat_msg_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &bizchat_msg_db); - dbs.push_back(bizchat_msg_db); + dbs_.push_back(bizchat_msg_db); wstring bizchat_msg_name = wstring((wchar_t *)(*( DWORD *)(p_contact_addr + DB_FUNCTION_MSG_OFFSET + DB_NAME_OFFSET))); - dbmap[bizchat_msg_name] = bizchat_msg_db; + dbmap_[bizchat_msg_name] = bizchat_msg_db; } - // Storage + // Storage DWORD storage_start = *(DWORD *)(p_contact_addr + STORAGE_START_OFFSET); DWORD storage_end = *(DWORD *)(p_contact_addr + STORAGE_END_OFFSET); // do { // DWORD vtable_ptr = *(DWORD *)(storage_start); - + // if(vtable_ptr == base + OP_LOG_STORAGE_VFTABLE){ // }else if(vtable_ptr == base + CHAT_MSG_STORAGE_VFTABLE){ @@ -259,37 +364,37 @@ std::vector GetDbHandles() { // }else if(vtable_ptr == base + REVOKE_MSG_STORAGE_VFTABLE){ // } - + // storage_start = storage_start + 0x4; // } while (storage_start != storage_end); - DWORD multi_db_mgr_addr = base + MULTI_DB_MSG_MGR_OFFSET; - DWORD public_msg_mgr_addr = base + PUBLIC_MSG_MGR_OFFSET; - DWORD favorite_storage_mgr_addr = base + FAVORITE_STORAGE_MGR_OFFSET; - DWORD fts_favorite_mgr_addr = base + FTS_FAVORITE_MGR_OFFSET; + DWORD multi_db_mgr_addr = base_addr_ + MULTI_DB_MSG_MGR_OFFSET; + DWORD public_msg_mgr_addr = base_addr_ + PUBLIC_MSG_MGR_OFFSET; + DWORD favorite_storage_mgr_addr = base_addr_ + FAVORITE_STORAGE_MGR_OFFSET; + DWORD fts_favorite_mgr_addr = base_addr_ + FTS_FAVORITE_MGR_OFFSET; // MsgX.db DWORD wrap_ptr = *(DWORD *)(multi_db_mgr_addr); - DWORD db_num = *(DWORD *)(wrap_ptr+0x30); - DWORD current_db_num = *(DWORD *)(wrap_ptr+0x38); + DWORD db_num = *(DWORD *)(wrap_ptr + 0x30); + DWORD current_db_num = *(DWORD *)(wrap_ptr + 0x38); DWORD begin_ptr = *(DWORD *)(wrap_ptr + 0x2c); for (unsigned int i = 0; i < current_db_num; i++) { DWORD next_addr = begin_ptr + i * 0x4; - DWORD db_addr = *(DWORD *) next_addr; + DWORD db_addr = *(DWORD *)next_addr; if (db_addr) { DWORD msg0_db_addr = *(DWORD *)(db_addr + 0x60); DatabaseInfo msg0_db{0}; msg0_db.db_name = (wchar_t *)(*(DWORD *)(db_addr)); msg0_db.db_name_len = *(DWORD *)(db_addr + 0x4); msg0_db.handle = msg0_db_addr; - msg0_db.extrainfo = *(DWORD *) (*(DWORD *)(db_addr + 0x18) +0x144); - ExecuteSQL( - msg0_db_addr, "select * from sqlite_master where type=\"table\";", - (DWORD)GetDbInfo, &msg0_db); - dbs.push_back(msg0_db); + msg0_db.extrainfo = *(DWORD *)(*(DWORD *)(db_addr + 0x18) + 0x144); + ExecuteSQL(msg0_db_addr, + "select * from sqlite_master where type=\"table\";", + (DWORD)GetDbInfo, &msg0_db); + dbs_.push_back(msg0_db); wstring msg_db_name = wstring((wchar_t *)(*(DWORD *)(db_addr))); - dbmap[msg_db_name] = msg0_db; + dbmap_[msg_db_name] = msg0_db; // BufInfoStorage DWORD buf_info_addr = *(DWORD *)(db_addr + 0x14); @@ -302,137 +407,134 @@ std::vector GetDbHandles() { ExecuteSQL(buf_info_handle, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &media_msg0_db); - dbs.push_back(media_msg0_db); - wstring media_msg_db_name = wstring((wchar_t *)(*(DWORD *)(buf_info_addr + 0x4C))); - dbmap[media_msg_db_name] = media_msg0_db; + dbs_.push_back(media_msg0_db); + wstring media_msg_db_name = + wstring((wchar_t *)(*(DWORD *)(buf_info_addr + 0x4C))); + dbmap_[media_msg_db_name] = media_msg0_db; } } // publicMsg.db - DWORD public_msg_ptr = *(DWORD *) (*(DWORD *)(public_msg_mgr_addr) + 0x8); + DWORD public_msg_ptr = *(DWORD *)(*(DWORD *)(public_msg_mgr_addr) + 0x8); if (public_msg_ptr) { DWORD public_msg_db_addr = *(DWORD *)(public_msg_ptr + 0x38); DatabaseInfo public_msg_db{0}; public_msg_db.db_name = (wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C)); - public_msg_db.db_name_len =*(DWORD *)(public_msg_ptr + 0x50); + public_msg_db.db_name_len = *(DWORD *)(public_msg_ptr + 0x50); public_msg_db.handle = public_msg_db_addr; ExecuteSQL(public_msg_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &public_msg_db); - dbs.push_back(public_msg_db); - wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C))); - dbmap[public_msg_db_name] = public_msg_db; + dbs_.push_back(public_msg_db); + wstring public_msg_db_name = + wstring((wchar_t *)(*(DWORD *)(public_msg_ptr + 0x4C))); + dbmap_[public_msg_db_name] = public_msg_db; } // Favorite.db - DWORD favItems_ptr = *(DWORD *)(*(DWORD *)(*(DWORD *)(favorite_storage_mgr_addr) + 0x8) + 0x4); + DWORD favItems_ptr = + *(DWORD *)(*(DWORD *)(*(DWORD *)(favorite_storage_mgr_addr) + 0x8) + 0x4); if (favItems_ptr) { DWORD favorite_db_addr = *(DWORD *)(favItems_ptr + 0x38); DatabaseInfo favorite_db{0}; - favorite_db.db_name =(wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C)); + favorite_db.db_name = (wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C)); favorite_db.db_name_len = *(DWORD *)(favItems_ptr + 0x50); favorite_db.handle = favorite_db_addr; ExecuteSQL(favorite_db_addr, "select * from sqlite_master where type=\"table\";", (DWORD)GetDbInfo, &favorite_db); - dbs.push_back(favorite_db); - wstring public_msg_db_name =wstring((wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C))); - dbmap[public_msg_db_name] = favorite_db; + dbs_.push_back(favorite_db); + wstring public_msg_db_name = + wstring((wchar_t *)(*(DWORD *)(favItems_ptr + 0x4C))); + dbmap_[public_msg_db_name] = favorite_db; } DatabaseInfo db_end = {0}; - dbs.push_back(db_end); + dbs_.push_back(db_end); #ifdef _DEBUG - for (unsigned int i = 0; i < dbs.size() - 1; i++) { - printf("dbname = %ws,handle = 0x%08X,table_count:%d\n", dbs[i].db_name, - dbs[i].handle, dbs[i].tables.size()); - for (unsigned int j = 0; j < dbs[i].tables.size(); j++) { - cout << "name = " << dbs[i].tables[j].name << endl; - cout << "tbl_name = " << dbs[i].tables[j].table_name << endl; - cout << "rootpage = " << dbs[i].tables[j].rootpage << endl; - cout << "sql = " << dbs[i].tables[j].sql << endl; - cout << endl; - } - cout << endl; + for (unsigned int i = 0; i < dbs_.size() - 1; i++) { + LOG(INFO) << "dbname =" << dbs_[i].db_name; + LOG(INFO) << "handle =" << dbs_[i].handle; + LOG(INFO) << "table_count =" << dbs_[i].tables.size(); } #endif vector ret_array; - for (unsigned int i = 0; i < dbs.size() - 1; i++) - ret_array.push_back(&dbs[i]); + for (unsigned int i = 0; i < dbs_.size() - 1; i++) + ret_array.push_back(&dbs_[i]); return ret_array; } -DWORD GetDbHandleByDbName(wchar_t *dbname) { - if (dbmap.size() == 0) { +DWORD DB::GetDbHandleByDbName(wchar_t *dbname) { + if (dbmap_.size() == 0) { GetDbHandles(); } - if (dbmap.find(dbname) != dbmap.end()) { - return dbmap[dbname].handle; + if (dbmap_.find(dbname) != dbmap_.end()) { + return dbmap_[dbname].handle; } return 0; } -unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex) { +unsigned int DB::GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex) { char sql[260] = {0}; sprintf_s(sql, "select localId from MSG where MsgSvrID=%llu;", msgid); wchar_t dbname[20] = {0}; for (int i = 0;; i++) { swprintf_s(dbname, L"MSG%d.db", i); DWORD handle = GetDbHandleByDbName(dbname); - #ifdef _DEBUG - cout <<" handle =" <> result; int ret = Select(handle, (const char *)sql, result); - #ifdef _DEBUG - cout <<" size =" < GetChatMsgByMsgId(ULONG64 msgid){ +vector DB::GetChatMsgByMsgId(ULONG64 msgid) { char sql[260] = {0}; - sprintf_s(sql, "select localId,TalkerId,MsgSvrID,Type,SubType,IsSender,CreateTime,Sequence,StatusEx,FlagEx,Status,MsgServerSeq,MsgSequence,StrTalker,StrContent,BytesExtra from MSG where MsgSvrID=%llu;", msgid); + sprintf_s(sql, + "select " + "localId,TalkerId,MsgSvrID,Type,SubType,IsSender,CreateTime," + "Sequence,StatusEx,FlagEx,Status,MsgServerSeq,MsgSequence," + "StrTalker,StrContent,BytesExtra from MSG where MsgSvrID=%llu;", + msgid); wchar_t dbname[20] = {0}; for (int i = 0;; i++) { swprintf_s(dbname, L"MSG%d.db", i); DWORD handle = GetDbHandleByDbName(dbname); - if (handle == 0) return {}; + if (handle == 0) { + LOG(INFO) << "MSG db handle is null"; + return {}; + } vector> result; int ret = Select(handle, (const char *)sql, result); - #ifdef _DEBUG - cout <<" size =" <> result; int ret = Select(handle, (const char *)sql, result); - #ifdef _DEBUG - cout <<" size =" < +#include + +#include "base_mgr.h" +#include "wechat_function.h" +#include "windows.h" +#include "singleton.h" +namespace wxhelper { +class DB :public Singleton{ + public: + void init(DWORD base); + int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data); + + int Select(DWORD db_hanle, const char *sql, + std::vector> &query_result); + + std::vector GetDbHandles(); + DWORD GetDbHandleByDbName(wchar_t *dbname); + unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex); + std::vector GetChatMsgByMsgId(ULONG64 msgid); + + std::string GetVoiceBuffByMsgId(ULONG64 msgid); + + private: + int SelectDataInner(DWORD db, const char *sql, + std::vector> &data); + + private: + std::map dbmap_; + std::vector dbs_; + DWORD base_addr_; +}; + +} // namespace wxhelper + +#endif \ No newline at end of file diff --git a/src/db_operation.cc b/src/db_operation.cc deleted file mode 100644 index 580d424..0000000 --- a/src/db_operation.cc +++ /dev/null @@ -1,160 +0,0 @@ -#include "pch.h" -#include "db_operation.h" - -#include "base64.h" -#include "common.h" -#include "new_sqlite3.h" -using namespace std; - -/// @brief free data -void FreeResult(vector> &data) { - if (data.size() == 0) { - return; - } - for (unsigned int i = 0; i < data.size(); i++) { - for (unsigned j = 0; j < data[i].size(); j++) { - SqlResult *sr = (SqlResult *)&data[i][j]; - if (sr->column_name) { - delete[] sr->column_name; - sr->column_name = NULL; - } - if (sr->content) { - delete[] sr->content; - sr->content = NULL; - } - } - data[i].clear(); - } - data.clear(); -} - -int SelectDataInner(DWORD db, const char *sql, - vector> &data) { - DWORD wxBaseAddress = GetWeChatWinBase(); - Sqlite3_prepare p_Sqlite3_prepare = - (Sqlite3_prepare)(wxBaseAddress + SQLITE3_PREPARE_OFFSET); - Sqlite3_step p_Sqlite3_step = - (Sqlite3_step)(wxBaseAddress + SQLITE3_STEP_OFFSET); - Sqlite3_column_count p_Sqlite3_column_count = - (Sqlite3_column_count)(wxBaseAddress + SQLITE3_COLUMN_COUNT_OFFSET); - Sqlite3_column_name p_Sqlite3_column_name = - (Sqlite3_column_name)(wxBaseAddress + SQLITE3_COLUMN_NAME_OFFSET); - Sqlite3_column_type p_Sqlite3_column_type = - (Sqlite3_column_type)(wxBaseAddress + SQLITE3_COLUMN_TYPE_OFFSET); - Sqlite3_column_blob p_Sqlite3_column_blob = - (Sqlite3_column_blob)(wxBaseAddress + SQLITE3_COLUMN_BLOB_OFFSET); - Sqlite3_column_bytes p_Sqlite3_column_bytes = - (Sqlite3_column_bytes)(wxBaseAddress + SQLITE3_COLUMN_BYTES_OFFSET); - Sqlite3_finalize p_Sqlite3_finalize = - (Sqlite3_finalize)(wxBaseAddress + SQLITE3_FINALIZE_OFFSET); - DWORD *stmt; - int rc = p_Sqlite3_prepare(db, sql, -1, &stmt, 0); - if (rc != SQLITE_OK) return NULL; - while (p_Sqlite3_step(stmt) == SQLITE_ROW) { - int col_count = p_Sqlite3_column_count(stmt); - vector tempStruct; - for (int i = 0; i < col_count; i++) { - SqlResult temp = {0}; - const char *ColName = p_Sqlite3_column_name(stmt, i); - int nType = p_Sqlite3_column_type(stmt, i); - const void *pReadBlobData = p_Sqlite3_column_blob(stmt, i); - int nLength = p_Sqlite3_column_bytes(stmt, i); - temp.column_name = new char[strlen(ColName) + 1]; - memcpy(temp.column_name, ColName, strlen(ColName) + 1); - temp.column_name_len = strlen(ColName); - temp.content_len = nLength; - switch (nType) { - case SQLITE_BLOB: { - temp.content = new char[nLength]; - memcpy(temp.content, pReadBlobData, nLength); - temp.is_blob = true; - break; - } - default: { - if (nLength != 0) { - temp.content = new char[nLength + 1]; - memcpy(temp.content, pReadBlobData, nLength + 1); - } else { - temp.content = new char[2]; - ZeroMemory(temp.content, 2); - } - temp.is_blob = false; - break; - } - } - tempStruct.push_back(temp); - } - data.push_back(tempStruct); - } - p_Sqlite3_finalize(stmt); - return 1; -} - -int Select(DWORD db_hanle, const char *sql, - vector> &query_result) { - vector> data; - int status = SelectDataInner(db_hanle, sql, data); - if (status == 0) { - return 0; - } - if(data.size() == 0){ - return 1; - } - vector index; - for (size_t i = 0; i < data[0].size(); i++){ - index.push_back(data[0][i].column_name); - } - query_result.push_back(index); - - for (auto it : data) { - vector item; - for (size_t i = 0; i < it.size(); i++) { - if (!it[i].is_blob) { - string content(it[i].content); - item.push_back(content); - } else { - string b64_str = - base64_encode((BYTE *)it[i].content, it[i].content_len); - item.push_back(b64_str); - } - } - query_result.push_back(item); - } - FreeResult(data); - return 1; -} - -int SelectDbInfo(void *data, int argc, char **argv, char **name) { - vector result; - for (int i = 0; i < argc; i++) { - SqlResult temp = {0}; - temp.column_name = new char[strlen(name[i]) + 1]; - memcpy(temp.column_name, name[i], strlen(name[i]) + 1); - temp.column_name_len = strlen(name[i]); - if (argv[i]) { - temp.content = new char[strlen(argv[i]) + 1]; - memcpy(temp.content, argv[i], strlen(argv[i]) + 1); - temp.content_len = strlen(argv[i]); - } else { - temp.content = new char[2]; - ZeroMemory(temp.content, 2); - temp.content_len = 0; - } - result.push_back(temp); - } - return 1; -} - -/// @brief sqlite3_exec -/// @param db -/// @param sql -/// @param callback -/// @param data -/// @return -int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data) { - DWORD base = GetWeChatWinBase(); - DWORD sqlite3_exec_addr = base + SQLITE3_EXEC_OFFSET; - Sqlite3_exec fn_sqlite3_exec = (Sqlite3_exec)sqlite3_exec_addr; - int status = fn_sqlite3_exec(db, sql, (Sqlite3_callback)callback, data, 0); - return status; -} \ No newline at end of file diff --git a/src/db_operation.h b/src/db_operation.h deleted file mode 100644 index 16ce011..0000000 --- a/src/db_operation.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DB_OPERATION_H_ -#define DB_OPERATION_H_ -#include -#include -struct SqlResult { - char *column_name; - DWORD column_name_len; - char *content; - DWORD content_len; - BOOL is_blob; -}; -/// @brief exec sql -/// @param db opened db -/// @param sql sql -/// @param callback callback func -/// @param data data -/// @return -int ExecuteSQL(DWORD db, const char *sql, DWORD callback, void *data); - -int Select(DWORD db_hanle, const char *sql,std::vector> &query_result); - -#endif \ No newline at end of file diff --git a/src/dllMain.cc b/src/dllMain.cc index 57880ea..6a54435 100644 --- a/src/dllMain.cc +++ b/src/dllMain.cc @@ -1,14 +1,20 @@ -#include "pch.h" -#include "api.h" -#include "common.h" -#include "wxhelper.h" +#include "pch.h" +#include "hide_module.h" +#include "global_context.h" + + +using namespace wxhelper; + + + + BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { - // http_start(19088); - wxhelper::WXHelper::GetInstance().http_start(19088); + DisableThreadLibraryCalls(hModule); + GlobalContext::GetInstance().initialize(hModule); break; } case DLL_THREAD_ATTACH: { @@ -18,9 +24,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, break; } case DLL_PROCESS_DETACH: { - CloseConsole(); - // http_close(); - wxhelper::WXHelper::GetInstance().http_close(); + GlobalContext::GetInstance().finally(); break; } } diff --git a/src/download.cc b/src/download.cc deleted file mode 100644 index 7544d8c..0000000 --- a/src/download.cc +++ /dev/null @@ -1,213 +0,0 @@ -#include "pch.h" -#include "download.h" - -#include "common.h" -#include "get_db_handle.h" - -#include "wechat_data.h" -#include "base64.h" - -#define WX_NEW_CHAT_MSG_OFFSET 0x76f010 -#define WX_GET_PRE_DOWNLOAD_MGR_OFFSET 0x80f110 -#define WX_PUSH_ATTACH_TASK_OFFSET 0x82bb40 -#define WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET 0x756e30 -#define WX_FREE_CHAT_MSG_OFFSET 0x756960 -#define WX_CHAT_MGR_OFFSET 0x792700 -#define WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET 0xbc0370 -#define WX_GET_CURRENT_DATA_PATH_OFFSET 0xc872c0 -#define WX_APP_MSG_INFO_OFFSET 0x7b3d20 -#define WX_GET_APP_MSG_XML_OFFSET 0xe628a0 -#define WX_FREE_APP_MSG_INFO_OFFSET 0x79d900 -#define WX_PUSH_THUMB_TASK_OFFSET 0x82ba40 -#define WX_VIDEO_MGR_OFFSET 0x829820 -#define WX_DOWNLOAD_VIDEO_IMG_OFFSET 0xd46c30 - -using namespace std; - -int DoDownloadTask(ULONG64 msg_id) { - int success = -1; - int db_index = 0; - int local_id = GetLocalIdByMsgId(msg_id, db_index); - if (local_id < 1) { - return -2; - } - - char chat_msg[0x2D8] = {0}; - DWORD base = GetWeChatWinBase(); - DWORD new_chat_msg_addr = base + WX_NEW_CHAT_MSG_OFFSET; - DWORD get_chat_mgr_addr = base + WX_CHAT_MGR_OFFSET; - DWORD pre_download_mgr_addr = base + WX_GET_PRE_DOWNLOAD_MGR_OFFSET; - DWORD push_attach_task_addr = base + WX_PUSH_ATTACH_TASK_OFFSET; - DWORD free_addr = base + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET; - DWORD get_by_local_Id_addr = base + WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET; - DWORD get_current_data_path_addr = base + WX_GET_CURRENT_DATA_PATH_OFFSET; - DWORD free_app_msg_info_addr = base + WX_FREE_APP_MSG_INFO_OFFSET; - DWORD push_thumb_task_addr = base + WX_PUSH_THUMB_TASK_OFFSET; - DWORD video_mgr_addr = base + WX_VIDEO_MGR_OFFSET; - DWORD download_video_image_addr = base + WX_VIDEO_MGR_OFFSET; - - WeChatString current_data_path; - - __asm { - PUSHAD - PUSHFD - LEA ECX,current_data_path - CALL get_current_data_path_addr - - LEA ECX,chat_msg - CALL new_chat_msg_addr - - CALL get_chat_mgr_addr - PUSH dword ptr [db_index] - LEA ECX,chat_msg - PUSH dword ptr [local_id] - CALL get_by_local_Id_addr - ADD ESP,0x8 - POPFD - POPAD - } - wstring save_path = L""; - wstring thumb_path = L""; - if (current_data_path.length > 0) { - save_path += current_data_path.ptr; - save_path += L"wxhelper"; - } else { - return -1; - } - - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return -3; - } - DWORD type = *(DWORD *)(chat_msg + 0x38); - wchar_t *content = *(wchar_t **)(chat_msg + 0x70); - - switch (type) { - case 0x3: { - save_path += L"\\image"; - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return -3; - } - save_path = save_path +L"\\"+ to_wstring(msg_id) + L".png"; - break; - } - case 0x3E: - case 0x2B: { - save_path += L"\\video"; - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return -3; - } - thumb_path = save_path + L"\\"+ to_wstring(msg_id) + L".jpg"; - save_path = save_path + L"\\"+ to_wstring(msg_id) + L".mp4"; - - break; - } - case 0x31: { - save_path += L"\\file"; - wcout << save_path << endl; - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return -3; - } - char xml_app_msg[0xC80] = {0}; - DWORD new_app_msg_addr = base + WX_APP_MSG_INFO_OFFSET; - DWORD get_xml_addr = base + WX_GET_APP_MSG_XML_OFFSET; - WeChatString w_content(content); - - __asm { - PUSHAD - PUSHFD - LEA ECX,xml_app_msg - CALL new_app_msg_addr - PUSH 0x1 - LEA EAX,w_content - PUSH EAX - LEA ECX,xml_app_msg - CALL get_xml_addr - MOV success,EAX - LEA ECX,xml_app_msg - CALL free_app_msg_info_addr - POPFD - POPAD - } - if (success != 1) { - return -4; - } - WeChatString *file_name = (WeChatString *)((DWORD)xml_app_msg + 0x44); - save_path = save_path +L"\\" + to_wstring(msg_id) + L"_" + - wstring(file_name->ptr, file_name->length); - break; - } - default: - break; - } - WeChatString w_save_path(save_path); - WeChatString w_thumb_path(thumb_path); - int temp =1; - memcpy(&chat_msg[0x19C], &w_thumb_path, sizeof(w_thumb_path)); - memcpy(&chat_msg[0x1B0], &w_save_path, sizeof(w_save_path)); - memcpy(&chat_msg[0x29C], &temp, sizeof(temp)); - // note: the image has been downloaded and will not be downloaded again - // use low-level method - // this function does not work, need to modify chatmsg. - // if (type == 0x3E || type == 0x2B){ - // __asm{ - // PUSHAD - // PUSHFD - // CALL video_mgr_addr - // LEA ECX,chat_msg - // PUSH ECX - // MOV ECX,EAX - // CALL download_video_image_addr - // POPFD - // POPAD - // } - // } - - __asm { - PUSHAD - PUSHFD - CALL pre_download_mgr_addr - PUSH 0x1 - PUSH 0x0 - LEA ECX,chat_msg - PUSH ECX - MOV ECX,EAX - CALL push_attach_task_addr - MOV success,EAX - LEA ECX,chat_msg - PUSH 0x0 - CALL free_addr - POPFD - POPAD - } - - return success; -} - -int GetVoice(ULONG64 msg_id, wchar_t *dir) { - int success = -1; - string buff = GetVoiceBuffByMsgId(msg_id); - if (buff.size() == 0) { - success = 0; - return success; - } - wstring save_path = wstring(dir); - if (!FindOrCreateDirectoryW(save_path.c_str())) { - success = -2; - return success; - } - save_path = save_path + L"\\" + to_wstring(msg_id) + L".amr"; - HANDLE file_handle = CreateFileW(save_path.c_str(), GENERIC_ALL, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file_handle == INVALID_HANDLE_VALUE) { - #ifdef _DEBUG - wcout <<" save_path =" <& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); +} + +// ConfigurationTypeHelper + +const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; +} + +struct ConfigurationStringToTypeItem { + const char* configString; + ConfigurationType configType; +}; + +static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { + { "enabled", ConfigurationType::Enabled }, + { "to_file", ConfigurationType::ToFile }, + { "to_standard_output", ConfigurationType::ToStandardOutput }, + { "format", ConfigurationType::Format }, + { "filename", ConfigurationType::Filename }, + { "subsecond_precision", ConfigurationType::SubsecondPrecision }, + { "milliseconds_width", ConfigurationType::MillisecondsWidth }, + { "performance_tracking", ConfigurationType::PerformanceTracking }, + { "max_log_file_size", ConfigurationType::MaxLogFileSize }, + { "log_flush_threshold", ConfigurationType::LogFlushThreshold }, +}; + +ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) { + for (auto& item : configStringToTypeMap) { + if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { + return item.configType; + } + } + return ConfigurationType::Unknown; +} + +void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); +} + +// Configuration + +Configuration::Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { +} + +Configuration& Configuration::operator=(const Configuration& c) { + if (&c != this) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + } + return *this; +} + +/// @brief Full constructor used to sets value of configuration +Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { +} + +void Configuration::log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); +} + +/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. +Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { +} + +bool Configuration::Predicate::operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); +} + +// Configurations + +Configurations::Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { +} + +Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, + Configurations* base) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } +} + +bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { + // We initial assertion with true because if we have assertion disabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; +} + +bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; +} + +void Configurations::setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } +} + +bool Configurations::hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; +} + +bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL +} + +void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } +} + +void Configurations::set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); +} + +void Configurations::setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#if defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToFile, std::string("false"), true); +#else + setGlobally(ConfigurationType::ToFile, std::string("true"), true); +#endif // defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +void Configurations::setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); +#if defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); +#else + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#endif // defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +void Configurations::Parser::ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } +} + +bool Configurations::Parser::isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); +} + +bool Configurations::Parser::isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); +} + +bool Configurations::Parser::isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && + (assignment != std::string::npos) && + (line.size() > assignment); +} + +bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, + Level* currLevel, + Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; +} + +void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } +} + +void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } +} + +void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +// LogBuilder + +void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!m_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + else if (level == Level::Debug) + *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; + else if (level == Level::Info) + *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; + else if (level == Level::Trace) + *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; +} + +// Logger + +Logger::Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); +} + +Logger::Logger(const std::string& id, const Configurations& configurations, + base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); +} + +Logger::Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; +} + +Logger& Logger::operator=(const Logger& logger) { + if (&logger != this) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + return *this; +} + +void Logger::configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; +} + +void Logger::reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); +} + +bool Logger::isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; +} + +void Logger::flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); +} + +void Logger::flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + std::unordered_map::iterator iter = m_unflushedCount.find(level); + if (iter != m_unflushedCount.end()) { + iter->second = 0; + } + Helpers::validateFileRolling(this, level); + } +} + +void Logger::initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); +} + +void Logger::resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); +} + +// el::base +namespace base { + +// el::base::utils +namespace utils { + +// File + +base::type::fstream_t* File::newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out +#if !defined(ELPP_FRESH_LOG_FILE) + | base::type::fstream_t::app +#endif + ); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +# if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +# endif // ELPP_OS_WINDOWS + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; +} + +std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + // Since the file stream is appended to or truncated, the current + // offset is the file size. + std::size_t size = static_cast(fs->tellg()); + return size; +} + +bool File::pathExists(const char* path, bool considerFile) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX +} + +bool File::createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeparator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; +} + +std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) { + if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(separator); + if (lastSlashAt == 0) { + return std::string(separator); + } + return fullPath.substr(0, lastSlashAt + 1); +} + +void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(separator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +// Str + +bool Str::wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; +} + +std::string& Str::ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { + return !std::isspace(c); + } )); + return str; +} + +std::string& Str::rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { + return !std::isspace(c); + }).base(), str.end()); + return str; +} + +std::string& Str::trim(std::string& str) { + return ltrim(rtrim(str)); +} + +bool Str::startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); +} + +bool Str::endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); +} + +std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; +} + +std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; +} + +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt - 1, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } +} +#if defined(ELPP_UNICODE) +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); +} +#endif // defined(ELPP_UNICODE) + +std::string& Str::toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), + [](char c) { + return static_cast(::toupper(c)); + }); + return str; +} + +bool Str::cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; +} + +bool Str::cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + + // With thanks to cygwin for this code + int d = 0; + + while (true) { + const int c1 = toupper(*s1++); + const int c2 = toupper(*s2++); + + if (((d = c1 - c2) != 0) || (c2 == '\0')) { + break; + } + } + + return d == 0; +} + +bool Str::contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; +} + +char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); +} + +char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; +} + +char* Str::clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; +} + +/// @brief Converts wchar* to char* +/// NOTE: Need to free return value after use! +char* Str::wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; +} + +// OS + +#if ELPP_OS_WINDOWS +/// @brief Gets environment variables for Windows based OS. +/// We are not using getenv(const char*) because of CRT deprecation +/// @param varname Variable name to get environment variable value for +/// @return If variable exist the value of it otherwise nullptr +const char* OS::getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; +} +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID +std::string OS::getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); +} + +std::string OS::getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); +} +#endif // ELPP_OS_ANDROID + +const std::string OS::getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + const std::size_t buffLen = strlen(hBuff); + if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { + hBuff[buffLen - 1] = '\0'; + } + return std::string(hBuff); + } else { + pclose(proc); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) +} + +std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); +} + +std::string OS::currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +std::string OS::currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +bool OS::termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" + || term == "screen" || term == "linux" || term == "cygwin" + || term == "screen-256color"; +} + +// DateTime + +void DateTime::gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS +} + +std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { + struct timeval currTime; + gettimeofday(&currTime); + return timevalToString(currTime, format, ssPrec); +} + +std::string DateTime::timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec) { + struct ::tm timeInfo; + buildTimeInfo(&tval, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(tval.tv_usec / ssPrec->m_offset), + ssPrec); + return std::string(buff_); +} + +base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (time <= base::consts::kTimeFormats[i].value) { + break; + } + if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { + break; + } + time /= static_cast(base::consts::kTimeFormats[i].value); + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << time << " " << unit; + return ss.str(); +} + +unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } + // milliseconds + auto conv = [](const struct timeval& tim) { + return static_cast((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); + }; + return static_cast(conv(endTime) - conv(startTime)); +} + +struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::elpptime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; +# if defined(_USE_32BIT_TIME_T) + _time32(&t); +# else + _time64(&t); +# endif + elpptime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = elpptime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX +} + +char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // subsecond part + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; +} + +// CommandLineArgs + +void CommandLineArgs::setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } +} + +bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); +} + +const char* CommandLineArgs::getParamValue(const char* paramKey) const { + std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); + return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; +} + +bool CommandLineArgs::hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); +} + +bool CommandLineArgs::empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); +} + +std::size_t CommandLineArgs::size(void) const { + return m_params.size() + m_paramsWithValue.size(); +} + +base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; +} + +} // namespace utils + +// el::base::threading +namespace threading { + +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# if ELPP_ASYNC_LOGGING +static void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +# if defined(ELPP_NO_SLEEP_FOR) + usleep(ms * 1000); +# else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +# endif // defined(ELPP_NO_SLEEP_FOR) +} +# endif // ELPP_ASYNC_LOGGING +# endif // !ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED + +} // namespace threading + +// el::base + +// SubsecondPrecision + +void SubsecondPrecision::init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultSubsecondPrecision; + } + m_width = width; + switch (m_width) { + case 3: + m_offset = 1000; + break; + case 4: + m_offset = 100; + break; + case 5: + m_offset = 10; + break; + case 6: + m_offset = 1; + break; + default: + m_offset = 1000; + break; + } +} + +// LogFormat + +LogFormat::LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0), + m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { +} + +LogFormat::LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { + parseFromFormat(m_userFormat); +} + +LogFormat::LogFormat(const LogFormat& logFormat): + m_level(logFormat.m_level), + m_userFormat(logFormat.m_userFormat), + m_format(logFormat.m_format), + m_dateTimeFormat(logFormat.m_dateTimeFormat), + m_flags(logFormat.m_flags), + m_currentUser(logFormat.m_currentUser), + m_currentHost(logFormat.m_currentHost) { +} + +LogFormat::LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + m_currentUser = std::move(logFormat.m_currentUser); + m_currentHost = std::move(logFormat.m_currentHost); +} + +LogFormat& LogFormat::operator=(const LogFormat& logFormat) { + if (&logFormat != this) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + m_currentUser = logFormat.m_currentUser; + m_currentHost = logFormat.m_currentHost; + } + return *this; +} + +bool LogFormat::operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; +} + +/// @brief Updates format to be used while logging. +/// @param userFormat User provided format +void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt - 1, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex != std::string::npos && dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); +} + +void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << static_cast(*ptr); + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } +} + +void LogFormat::updateFormatSpec(void) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + m_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + m_currentHost); + } + // Ignore Level::Global and Level::Unknown +} + +// TypedConfigurations + +TypedConfigurations::TypedConfigurations(Configurations* configurations, + LogStreamsReferenceMapPtr logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); +} + +TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); +} + +bool TypedConfigurations::enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); +} + +bool TypedConfigurations::toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); +} + +const std::string& TypedConfigurations::filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); +} + +bool TypedConfigurations::toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); +} + +const base::LogFormat& TypedConfigurations::logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); +} + +const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "subsecondPrecision"); +} + +const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "millisecondsWidth"); +} + +bool TypedConfigurations::performanceTracking(Level level) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); +} + +base::type::fstream_t* TypedConfigurations::fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); +} + +std::size_t TypedConfigurations::maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); +} + +std::size_t TypedConfigurations::logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); +} + +void TypedConfigurations::build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensitive operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { + setValue(Level::Global, + base::SubsecondPrecision(static_cast(getULong(conf->value()))), &m_subsecondPrecisionMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + auto v = getULong(conf->value()); + setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); + if (v != 0) { + withFileSizeLimit.push_back(conf); + } + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } +} + +unsigned long TypedConfigurations::getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { + return !base::utils::Str::isDigit(c); + }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); +} + +std::string TypedConfigurations::resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::SubsecondPrecision ssPrec(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; +} + +void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeparator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); +} + +bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + preRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; +} + +// RegisteredHitCounters + +bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; +} + +/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; +} + +/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; +} + +// RegisteredLoggers + +RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + m_logStreamsReference = std::make_shared(); +} + +Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + LoggerRegistrationCallback* callback = nullptr; + for (const std::pair& h + : m_loggerRegistrationCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(logger_); + } + } + } + return logger_; +} + +bool RegisteredLoggers::remove(const std::string& id) { + if (id == base::consts::kDefaultLoggerId) { + return false; + } + // get has internal lock + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + // unregister has internal lock + unregister(logger); + } + return true; +} + +void RegisteredLoggers::unsafeFlushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference->begin(); + it != m_logStreamsReference->end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } +} + +// VRegistry + +VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { +} + +/// @brief Sets verbose level. Accepted range is 0-9 +void VRegistry::setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; +} + +void VRegistry::setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + } +} + +bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; + base::utils::File::buildBaseFilename(file, baseFilename); + std::unordered_map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } +} + +void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--v")))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--V")))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } +} + +#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) +# define ELPP_DEFAULT_LOGGING_FLAGS 0x0 +#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) +// Storage +#if ELPP_ASYNC_LOGGING +Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else +Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(ELPP_DEFAULT_LOGGING_FLAGS), + m_vRegistry(new base::VRegistry(0, &m_flags)), + +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // We register default logger anyway (worse case it's not going to register) just in case + m_registeredLoggers->get("default"); + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + m_registeredLoggers->get("performance"); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + installPerformanceTrackingCallback + (std::string("DefaultPerformanceTrackingCallback")); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING +} + +Storage::~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); +} + +bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); +} + +void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); +} + +bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; +} + +void Storage::setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, + std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { + m_flags = userInput; + } else { + base::utils::addFlag(userInput, &m_flags); + } + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +} + +} // namespace base + +// LogDispatchCallback +#if defined(ELPP_THREAD_SAFE) +void LogDispatchCallback::handle(const LogDispatchData* data) { + base::threading::ScopedLock scopedLock(m_fileLocksMapLock); + std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); + auto lock = m_fileLocks.find(filename); + if (lock == m_fileLocks.end()) { + m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); + } +} +#else +void LogDispatchCallback::handle(const LogDispatchData* /*data*/) {} +#endif + +base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) { + auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); + return *(it->second.get()); +} + +namespace base { +// DefaultLogDispatchCallback + +void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + LogDispatchCallback::handle(data); + base::threading::ScopedLock scopedLock(fileHandle(data)); +#endif + m_data = data; + dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog)); +} + +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( + m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) + || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) +} + +#if ELPP_ASYNC_LOGGING + +// AsyncLogDispatchCallback + +void AsyncLogDispatchCallback::handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), + data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog + && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } +} + +// AsyncDispatchWorker +AsyncDispatchWorker::AsyncDispatchWorker() { + setContinueRunning(false); +} + +AsyncDispatchWorker::~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); +} + +bool AsyncDispatchWorker::clean(void) { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); +} + +void AsyncDispatchWorker::emptyQueue(void) { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } +} + +void AsyncDispatchWorker::start(void) { + base::threading::msleep(5000); // 5s (why?) + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::run, this); + t1.join(); +} + +void AsyncDispatchWorker::handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) +} + +void AsyncDispatchWorker::run(void) { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } +} +#endif // ELPP_ASYNC_LOGGING + +// DefaultLogBuilder + +base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + ELPP->getThreadName(base::threading::getCurrentThreadId())); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->subsecondPrecision(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, + false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); + ELPP_UNUSED(lock_); + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; +} + +// LogDispatcher + +void LogDispatcher::dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } +#ifndef ELPP_NO_GLOBAL_LOCK + // see https://github.com/muflihun/easyloggingpp/issues/580 + // global lock is turned on by default unless + // ELPP_NO_GLOBAL_LOCK is defined + base::threading::ScopedLock scopedLock(ELPP->lock()); +#endif + base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->handle(&data); + } + } +} + +// MessageBuilder + +void MessageBuilder::initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeparator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); +} + +MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; +} + +// Writer + +Writer& Writer::construct(Logger* logger, bool needLock) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; +} + +Writer& Writer::construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + m_loggerIds.reserve(count); + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; +} + +void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + { + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + } + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } +} + +void Writer::processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED +} + +void Writer::triggerDispatch(void) { + try { + if (m_proceed) { + if (m_msg == nullptr) { + LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger); + base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); + } else { + base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); + } + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } + catch(std::exception & ex){ + // Extremely low memory situation; don't let exception be unhandled. + } +} + +// PErrorWriter + +PErrorWriter::~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } +} + +// PerformanceTracker + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +PerformanceTracker::PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit, + const std::string& loggerId, + bool scopedLog, Level level) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuration at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED +} + +PerformanceTracker::~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) +} + +void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, + const char* func) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); +} + +const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast + (m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// StackTrace + +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, + const std::string& hex, + const std::string& addr) : + m_index(index), + m_location(loc), + m_demangled(demang), + m_hex(hex), + m_addr(addr) { +} + +std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr << + (si.m_demangled.empty() ? "" : ":") << si.m_demangled; + return ss; +} + +std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; +} + +void StackTrace::generateNew(void) { +#ifdef HAVE_EXECINFO + m_stack.clear(); + void* stack[kMaxStack]; + unsigned int size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + std::string mangName; + std::string location; + std::string hex; + std::string addr; + + // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 + const std::string line(strings[i]); + auto p = line.find("_"); + if (p != std::string::npos) { + mangName = line.substr(p); + mangName = mangName.substr(0, mangName.find(" +")); + } + p = line.find("0x"); + if (p != std::string::npos) { + addr = line.substr(p); + addr = addr.substr(0, addr.find("_")); + } + // Perform demangling if parsed properly + if (!mangName.empty()) { + int status = 0; + char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, location, demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, location, mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, line); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE +} + +// Static helper functions + +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { + return; + } + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} + +static inline void crashAbort(int sig) { + base::utils::abort(sig, std::string()); +} + +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} + +// CrashHandler + +CrashHandler::CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } +} + +void CrashHandler::setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base + +// el + +// Helpers + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); +} + +void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// Loggers + +Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); +} + +void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { + ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); +} + +bool Loggers::unregisterLogger(const std::string& identity) { + return ELPP->registeredLoggers()->remove(identity); +} + +bool Loggers::hasLogger(const std::string& identity) { + return ELPP->registeredLoggers()->has(identity); +} + +Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; +} + +void Loggers::reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } +} + +void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } +} + +void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } +} + +const Configurations* Loggers::defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); +} + +const base::LogStreamsReferenceMapPtr Loggers::logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); +} + +base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); +} + +std::vector* Loggers::populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; +} + +void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } +} + +bool Loggers::configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; +} + +void Loggers::flushAll(void) { + ELPP->registeredLoggers()->flushAll(); +} + +void Loggers::setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); +} + +base::type::VerboseLevel Loggers::verboseLevel(void) { + return ELPP->vRegistry()->level(); +} + +void Loggers::setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } +} + +void Loggers::clearVModules(void) { + ELPP->vRegistry()->clearModules(); +} + +// VersionInfo + +const std::string VersionInfo::version(void) { + return std::string("9.96.7"); +} +/// @brief Release date of current version +const std::string VersionInfo::releaseDate(void) { + return std::string("24-11-2018 0728hrs"); +} + +} // namespace el diff --git a/src/easylogging++.h b/src/easylogging++.h new file mode 100644 index 0000000..6e81edf --- /dev/null +++ b/src/easylogging++.h @@ -0,0 +1,4576 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.96.7 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2012-2018 Amrayn Web Services +// Copyright (c) 2012-2018 @abumusamq +// +// This library is released under the MIT Licence. +// https://github.com/amrayn/easyloggingpp/blob/master/LICENSE +// +// https://amrayn.com +// http://muflihun.com +// + +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L +#if (defined(__GNUC__)) +# define ELPP_COMPILER_GCC 1 +#else +# define ELPP_COMPILER_GCC 0 +#endif +#if ELPP_COMPILER_GCC +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ ++ __GNUC_MINOR__ * 100 \ ++ __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif +#endif +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +#else +# define ELPP_COMPILER_MSVC 0 +#endif +#define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC +#if ELPP_COMPILER_MSVC +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif +#endif +// Clang++ +#if (defined(__clang__) && (__clang__ == 1)) +# define ELPP_COMPILER_CLANG 1 +#else +# define ELPP_COMPILER_CLANG 0 +#endif +#if ELPP_COMPILER_CLANG +# if __has_include() +# include // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include() +#endif +#if (defined(__MINGW32__) || defined(__MINGW64__)) +# define ELPP_MINGW 1 +#else +# define ELPP_MINGW 0 +#endif +#if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) +# define ELPP_CYGWIN 1 +#else +# define ELPP_CYGWIN 0 +#endif +#if (defined(__INTEL_COMPILER)) +# define ELPP_COMPILER_INTEL 1 +#else +# define ELPP_COMPILER_INTEL 0 +#endif +// Operating System Evaluation +// Windows +#if (defined(_WIN32) || defined(_WIN64)) +# define ELPP_OS_WINDOWS 1 +#else +# define ELPP_OS_WINDOWS 0 +#endif +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#else +# define ELPP_OS_LINUX 0 +#endif +#if (defined(__APPLE__)) +# define ELPP_OS_MAC 1 +#else +# define ELPP_OS_MAC 0 +#endif +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) +# define ELPP_OS_FREEBSD 1 +#else +# define ELPP_OS_FREEBSD 0 +#endif +#if (defined(__sun)) +# define ELPP_OS_SOLARIS 1 +#else +# define ELPP_OS_SOLARIS 0 +#endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#else +# define ELPP_OS_EMSCRIPTEN 0 +#endif +#if (defined(__QNX__) || defined(__QNXNTO__)) +# define ELPP_OS_QNX 1 +#else +# define ELPP_OS_QNX 0 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN || ELPP_OS_QNX) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#else +# define ELPP_OS_UNIX 0 +#endif +#if (defined(__ANDROID__)) +# define ELPP_OS_ANDROID 1 +#else +# define ELPP_OS_ANDROID 0 +#endif +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ +"ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ +<< "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ +<< __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +{ char buff[256]; strerror_s(buff, 256, errno); \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ +if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_CYGWIN && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN && !ELPP_OS_QNX) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC +#else +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 +#else +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif +#endif +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#else +# define ELPP_THREADING_ENABLED 0 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \ +(ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) +// Logging Enable/Disable macros +#if defined(ELPP_DISABLE_LOGS) +#define ELPP_LOGGING_ENABLED 0 +#else +#define ELPP_LOGGING_ENABLED 1 +#endif +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#ifdef HAVE_EXECINFO +# include +# include +#endif // ENABLE_EXECINFO +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# if defined(ELPP_NO_SLEEP_FOR) +# include +# endif // defined(ELPP_NO_SLEEP_FOR) +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +#if defined(ELPP_UTC_DATETIME) +# define elpptime_r gmtime_r +# define elpptime_s gmtime_s +# define elpptime gmtime +#else +# define elpptime_r localtime_r +# define elpptime_s localtime_s +# define elpptime localtime +#endif // defined(ELPP_UTC_DATETIME) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr StoragePointer; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +typedef std::shared_ptr LoggerRegistrationCallbackPtr; +typedef std::unique_ptr PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situations + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for corresponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configuration type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensitive) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER +#endif + +#if defined(ELPP_SYSLOG) +static const char* kSysLogLoggerId = "syslog"; +#endif // defined(ELPP_SYSLOG) + +#if ELPP_OS_WINDOWS +static const char* kFilePathSeparator = "\\"; +#else +static const char* kFilePathSeparator = "/"; +#endif // ELPP_OS_WINDOWS + +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithmetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as
flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING +#else +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +#endif // ELPP_THREADING_ENABLED +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* separator = base::consts::kFilePathSeparator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* separator = base::consts::kFilePathSeparator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converts wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model separated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a separate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (separated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::unordered_map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { + public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + +/// @brief Registers new registry to repository. + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + +/// @brief Unregisters single entry mapped to specified unique key + void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + this->list().erase(uniqKey); + base::utils::safeDelete(existing); + } + } + +/// @brief Gets pointer from repository. If none found, nullptr is returned. + T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { + public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + +/// @brief Gets pointer from repository with specified arguments. Arguments are passed to predicate +/// in order to validate pointer. + template + T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; +class Utils { + public: + template + static bool installCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::unordered_map LogStreamsReferenceMap; +typedef std::shared_ptr LogStreamsReferenceMapPtr; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, LogStreamsReferenceMapPtr logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; + LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::unordered_map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { + public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + +}; +class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); + private: + friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; +}; +class PerformanceTrackingCallback : public Callback { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configurations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +template \ +inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ +template \ +inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::unordered_map m_unflushedCount; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_loggerRegistrationCallbacks); + } + + template + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_loggerRegistrationCallbacks); + } + + template + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline LogStreamsReferenceMapPtr logStreamsReference(void) { + return m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + std::unordered_map m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::unordered_map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::unordered_map m_modules; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder); +#endif // ELPP_ASYNC_LOGGING + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(m_threadNamesLock); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; + std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningLock); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningLock; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(logMessage), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, + public std::priority_queue { + public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { + public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { + public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeparator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ +m_logger->stream() << msg;\ +if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ +m_logger->stream() << " ";\ +}\ +return *this;\ +} + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Instance name is "elem". See WXELPP_ENABLED macro + /// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ +const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ +ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ +ContainerType::const_iterator elem = container.begin();\ +ContainerType::const_iterator endElem = container.end();\ +std::size_t size_ = container.SizeMethod; \ +ss << ELPP_LITERAL("[");\ +for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ +ss << ElementInstance;\ +ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ +}\ +if (elem != endElem) {\ +ss << ELPP_LITERAL("...");\ +}\ +ss << ELPP_LITERAL("]");\ +return ss;\ +} +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ +ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeparator; + + template + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + LogMessage* m_msg; + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +template +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + releaseLock(); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(level, 0, s, value, args...); +} +template +inline void Logger::log(Level level, const T& log) { + acquireLock(); // released in Writer! + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template +inline void Logger::verbose(int vlevel, const T& log) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, log); +} +# else +template +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ +log(LOG_LEVEL, s, value, args...);\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T& value) {\ +log(LOG_LEVEL, value);\ +} +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ +return;\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T&) {\ +return;\ +} + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : + m_index(index), + m_location(loc) { + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; +#else +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + (void)base::consts::kSysLogLoggerId; + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (ELPP == nullptr || logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback(id); + } + template + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMapPtr logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector* populateAllLoggerIds(std::vector* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Clears vmodules + static void clearVModules(void); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_SCOPE_IF +#undef TIMED_FUNC +#undef TIMED_FUNC_IF +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ +CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ +C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ +CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ +C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ +CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ +C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ +CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ +C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +#if !defined(ELPP_NO_DEBUG_MACROS) +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL((ptr), __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_CHECK_MACROS) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val) \ +namespace el { \ +namespace base { \ +el::base::type::StoragePointer elStorage(val); \ +} \ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ +} + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) +#else +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP \ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage;\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} +#define SHARE_EASYLOGGINGPP(initializedStorage)\ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage(initializedStorage);\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/src/forward.cc b/src/forward.cc deleted file mode 100644 index b014607..0000000 --- a/src/forward.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "pch.h" -#include "forward.h" - -#include "common.h" -#include "get_db_handle.h" -#include "wechat_data.h" -#define WX_FORWARD_MSG_OFFSET 0xce6730 -#define WX_INIT_CHAT_MSG_OFFSET 0xf59e40 - -int ForwardMsg(wchar_t *wxid, unsigned long long msgid) { - int success = 0; - - int db_index = 0; - int localid = GetLocalIdByMsgId(msgid, db_index); - - if (localid == 0) return 0; - WeChatString to_user(wxid); - DWORD base = GetWeChatWinBase(); - DWORD forward_msg_addr = base + WX_FORWARD_MSG_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - __asm { - PUSHAD - PUSHFD - MOV EDX, DWORD PTR [db_index] - PUSH EDX - MOV EAX, DWORD PTR [localid] - PUSH EAX - SUB ESP,0x14 - MOV ECX,ESP - LEA ESI,to_user - PUSH ESI - CALL init_chat_msg_addr - XOR ECX,ECX - CALL forward_msg_addr - MOVZX EAX,AL - MOV success,EAX - ADD ESP,0x1c - POPFD - POPAD - } - return success; -} \ No newline at end of file diff --git a/src/forward.h b/src/forward.h deleted file mode 100644 index cce23c2..0000000 --- a/src/forward.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef FORWARD_H_ -#define FORWARD_H_ - -int ForwardMsg(wchar_t *wxid, unsigned long long msgid); -#endif \ No newline at end of file diff --git a/src/get_db_handle.h b/src/get_db_handle.h deleted file mode 100644 index f48a539..0000000 --- a/src/get_db_handle.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef GET_DB_HANDLE_H_ -#define GET_DB_HANDLE_H_ -#include "windows.h" -#include -#include - -std::vector GetDbHandles(); -DWORD GetDbHandleByDbName(wchar_t *dbname); -unsigned int GetLocalIdByMsgId(ULONG64 msgid, int &dbIndex); -std::vector GetChatMsgByMsgId(ULONG64 msgid); - -std::string GetVoiceBuffByMsgId(ULONG64 msgid); -#endif \ No newline at end of file diff --git a/src/global_context.cc b/src/global_context.cc new file mode 100644 index 0000000..909973d --- /dev/null +++ b/src/global_context.cc @@ -0,0 +1,38 @@ +#include "pch.h" +#include "global_context.h" +#include "http_server.h" +#include "easylogging++.h" +#include "hooks.h" + + +namespace wxhelper { + +void GlobalContext::initialize(HMODULE module) { + module_ = module; + DWORD base = Utils::GetWeChatWinBase(); + config.emplace(); + log.emplace(); + log->initialize(); + hide_module.emplace(); + #ifndef _DEBUG + hide_module->Hide(module_); + #endif + + HttpServer::GetInstance().Init(19088); + HttpServer::GetInstance().HttpStart(); + DB::GetInstance().init(base); + contact_mgr.emplace(ContactMgr{base}); + misc_mgr.emplace(MiscMgr{base}); + send_mgr.emplace(SendMessageMgr{base}); + account_mgr.emplace(AccountMgr{base}); + chat_room_mgr.emplace(ChatRoomMgr{base}); + sns_mgr.emplace(SNSMgr{base}); +} + +void GlobalContext::finally() { + HttpServer::GetInstance().HttpClose(); + hooks::UnHookLog(); + hooks::UnHookRecvMsg(); + hooks::UnHookSearchContact(); +} +} // namespace wxhelper \ No newline at end of file diff --git a/src/global_context.h b/src/global_context.h new file mode 100644 index 0000000..0a6a7fa --- /dev/null +++ b/src/global_context.h @@ -0,0 +1,43 @@ +#ifndef GLOBAL_CONTEXT_H_ +#define GLOBAL_CONTEXT_H_ +#include "account_mgr.h" +#include "config.h" +#include "contact_mgr.h" +#include "db.h" +#include "hide_module.h" +#include "http_server.h" +#include "log.h" +#include "misc_mgr.h" +#include "send_message_mgr.h" +#include "chat_room_mgr.h" +#include "sns_mgr.h" +#include "singleton.h" + +namespace wxhelper { + +enum class GlobalContextState { NOT_INITIALIZED, INITIALIZING, INITIALIZED }; + +class GlobalContext :public Singleton{ + public: + void initialize(HMODULE module); + void finally(); + + public: + std::optional config; + std::optional hide_module; + std::optional log; + std::optional contact_mgr; + std::optional misc_mgr; + std::optional send_mgr; + std::optional account_mgr; + std::optional chat_room_mgr; + std::optional sns_mgr; + + GlobalContextState state = GlobalContextState::INITIALIZED; + + private: + HMODULE module_; +}; + +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/handler.h b/src/handler.h new file mode 100644 index 0000000..985843a --- /dev/null +++ b/src/handler.h @@ -0,0 +1,10 @@ +#ifndef WXHELPER_HANDLER_H_ +#define WXHELPER_HANDLER_H_ +#include +namespace wxhelper { +class Handler { + public: + virtual void HandlerRequest(struct mg_connection *c, void *ev_data) = 0; +}; +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/hide_module.cc b/src/hide_module.cc new file mode 100644 index 0000000..81e1b0d --- /dev/null +++ b/src/hide_module.cc @@ -0,0 +1,64 @@ +#include "pch.h" +#include "hide_module.h" + + +namespace wxhelper { + +void HideModule::Hide(const char* module_name) { + HMODULE hMod = ::GetModuleHandleA(module_name); + PLIST_ENTRY Head, Cur; + PPEB_LDR_DATA ldr; + PLDR_MODULE ldm; + + __asm { + mov eax, fs: [0x30] + mov ecx, [eax + 0x0c] + mov ldr, ecx + } + Head = &(ldr->InLoadOrderModuleList); + Cur = Head->Flink; + do { + ldm = CONTAINING_RECORD(Cur, LDR_MODULE, InLoadOrderModuleList); + if (hMod == ldm->BaseAddress) { + ldm->InLoadOrderModuleList.Blink->Flink = + ldm->InLoadOrderModuleList.Flink; + ldm->InLoadOrderModuleList.Flink->Blink = + ldm->InLoadOrderModuleList.Blink; + ldm->InInitializationOrderModuleList.Blink->Flink = + ldm->InInitializationOrderModuleList.Flink; + ldm->InInitializationOrderModuleList.Flink->Blink = + ldm->InInitializationOrderModuleList.Blink; + ldm->InMemoryOrderModuleList.Blink->Flink = + ldm->InMemoryOrderModuleList.Flink; + ldm->InMemoryOrderModuleList.Flink->Blink = + ldm->InMemoryOrderModuleList.Blink; + break; + } + Cur = Cur->Flink; + } while (Head != Cur); +} + +void HideModule::Hide(HMODULE module) { + void* peb_ptr = nullptr; + _asm { + PUSH EAX + MOV EAX, FS:[0x30] + MOV peb_ptr, EAX + POP EAX + } + void* ldr_ptr = *((void**)((unsigned char*)peb_ptr + 0xc)); + void* cur_ptr = *((void**)((unsigned char*)ldr_ptr + 0x0c)); + void* next_ptr = cur_ptr; + do { + void* next = *((void**)((unsigned char*)next_ptr)); + void* last = *((void**)((unsigned char*)next_ptr + 0x4)); + void* base_addr = *((void**)((unsigned char*)next_ptr + 0x18)); + if (base_addr == module) { + *((void**)((unsigned char*)last)) = next; + *((void**)((unsigned char*)next + 0x4)) = last; + cur_ptr = next; + } + next_ptr = *((void**)next_ptr); + } while (cur_ptr != next_ptr); +} +} // namespace wxhelper diff --git a/src/hide_module.h b/src/hide_module.h new file mode 100644 index 0000000..1cc4c0f --- /dev/null +++ b/src/hide_module.h @@ -0,0 +1,48 @@ +#ifndef WXHELEPER_HIDE_MODULE_H_ +#define WXHELEPER_HIDE_MODULE_H_ +#include +namespace wxhelper { +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _PEB_LDR_DATA { + ULONG Length; + BOOLEAN Initialized; + PVOID SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _LDR_DATA_TABLE_ENTRY { + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + void* BaseAddress; + void* EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + HANDLE SectionHandle; + ULONG CheckSum; + ULONG TimeDateStamp; +} LDR_MODULE, *PLDR_MODULE; + + +class HideModule { + private: + /* data */ + public: + static void Hide(const char* module_name); + static void Hide(HMODULE module); +}; + + +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/hook_img.cc b/src/hook_img.cc deleted file mode 100644 index 4a1a6aa..0000000 --- a/src/hook_img.cc +++ /dev/null @@ -1,231 +0,0 @@ -#include "pch.h" -#include "hook_img.h" - -#include "common.h" -using namespace std; - -// #define WX_HOOK_IMG_OFFSET 0xd7eaa5 -// #define WX_HOOK_IMG_NEXT_OFFSET 0xda56e0 -#define WX_HOOK_IMG_OFFSET 0xd723dc -#define WX_HOOK_IMG_NEXT_OFFSET 0xe91d90 -#define WX_SELF_ID_OFFSET 0x2E2CD3C -#define BUFSIZE 1024 - -#define JPEG0 0xFF -#define JPEG1 0xD8 -#define JPEG2 0xFF -#define PNG0 0x89 -#define PNG1 0x50 -#define PNG2 0x4E -#define BMP0 0x42 -#define BMP1 0x4D -#define GIF0 0x47 -#define GIF1 0x49 -#define GIF2 0x46 - - - -static wstring kImgStorePath = L""; -static int kImgHooked = FALSE; -static DWORD kWeChatWinBase = GetWeChatWinBase(); -static char kOriginImgAsmCode[5] = {0}; - -static DWORD kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET; -static DWORD kHookImgJmpBackAddress = kWeChatWinBase + WX_HOOK_IMG_OFFSET + 0x5; - -void OnHookImg(DWORD obj_addr) { - DWORD wxid_addr = GetWeChatWinBase() + WX_SELF_ID_OFFSET; - string wxid = string(*(char **)wxid_addr, *(DWORD *)(wxid_addr + 0x10)); - wstring self_id = String2Wstring(wxid); - wstring save_path = kImgStorePath + self_id; - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return; - } - wchar_t *origin_file_path = *(wchar_t **)obj_addr; - wstring img_path(origin_file_path); - if (img_path.find(L"_t.dat") != wstring::npos) { - return; - } - - int pos_begin = img_path.find_last_of(L"\\") + 1; - int pos_end = img_path.find_last_of(L"."); - wstring file_name = img_path.substr(pos_begin, pos_end - pos_begin); - char buffer[BUFSIZE] = {0}; - DWORD bytes_read = 0; - DWORD bytes_write = 0; - unsigned char magic_head[4] = {0}; - wchar_t suffix[5] = {0}; - - HANDLE h_origin_file = - CreateFileW(origin_file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (h_origin_file == INVALID_HANDLE_VALUE) { - return; - } - - if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { - memcpy(magic_head, buffer, 3); - } - - if (magic_head[0] == PNG0 && magic_head[1] == PNG1 && magic_head[2] == PNG2) { - lstrcpyW(suffix, L".png"); - } else if (magic_head[0] == GIF0 && magic_head[1] == GIF1 && - magic_head[2] == GIF2) { - lstrcpyW(suffix, L".gif"); - } else if (magic_head[0] == JPEG0 && magic_head[1] == JPEG1 && - magic_head[2] == JPEG2) { - lstrcpyW(suffix, L".jpg"); - } else { - lstrcpyW(suffix, L""); - } - - wstring save_img_path = save_path + L"\\" + file_name + suffix; - HANDLE save_file = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (save_file == INVALID_HANDLE_VALUE) { - return; - } - if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { - CloseHandle(h_origin_file); - CloseHandle(save_file); - return; - } - do { - if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { - if (!WriteFile(save_file, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { - CloseHandle(h_origin_file); - CloseHandle(save_file); - return; - } - } - } while (bytes_read == BUFSIZE); - CloseHandle(h_origin_file); - CloseHandle(save_file); -} - -/// @brief hook img implement -_declspec(naked) void handle_img() { - __asm { - PUSHAD - PUSHFD - PUSH ECX - CALL OnHookImg - ADD ESP, 0x4 - POPFD - POPAD - CALL kHookImgNextAddress - JMP kHookImgJmpBackAddress - } -} - -/// @brief hook image -/// @param save_path image save dir -/// @return -int HookImg(wstring save_path) { - kWeChatWinBase = GetWeChatWinBase(); - if (!kWeChatWinBase) { - return -1; - } - if (kImgHooked) { - return 2; - } - kImgStorePath = save_path; - if (kImgStorePath.back() != '\\') { - kImgStorePath += L"\\"; - } - wstring createpath = kImgStorePath.substr(0, kImgStorePath.length() - 1); - if (!FindOrCreateDirectoryW(createpath.c_str())) { - return -2; - } - DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET; - kHookImgNextAddress = kWeChatWinBase + WX_HOOK_IMG_NEXT_OFFSET; - static DWORD kHookImgJmpBackAddress = hook_img_addr + 0x5; - HookAnyAddress(hook_img_addr, (LPVOID)handle_img, kOriginImgAsmCode); - kImgHooked = TRUE; - return 1; -} - -int UnHookImg() { - if (!kImgHooked) return 1; - DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_IMG_OFFSET; - UnHookAnyAddress(hook_img_addr, kOriginImgAsmCode); - kImgHooked = FALSE; - return 1; -} - -int GetImgByName(wchar_t* file_path,wchar_t* save_dir) { - wstring save_path(save_dir); - wstring orign_file_path(file_path); - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return 0; - } - - int pos_begin = orign_file_path.find_last_of(L"\\") + 1; - int pos_end = orign_file_path.find_last_of(L"."); - wstring file_name = orign_file_path.substr(pos_begin, pos_end - pos_begin); - HANDLE h_origin_file = - CreateFileW(file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - char buffer[BUFSIZE] = {0}; - DWORD bytes_read = 0; - DWORD bytes_write = 0; - unsigned char magic_head[4] = {0}; - wstring suffix; - short key = 0; - if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { - memcpy(magic_head, buffer, 3); - } else { - CloseHandle(h_origin_file); - return 0; - } - if ((magic_head[0] ^ JPEG0) == (magic_head[1] ^ JPEG1)) { - key = magic_head[0] ^ JPEG0; - suffix = L".jpg"; - } else if ((magic_head[0] ^ PNG1) == (magic_head[1] ^ PNG2)) { - key = magic_head[0] ^ PNG1; - suffix = L".png"; - } else if ((magic_head[0] ^ GIF0) == (magic_head[1] ^ GIF1)) { - key = magic_head[0] ^ GIF0; - suffix = L".gif"; - } else if ((magic_head[0] ^ BMP0) == (magic_head[1] ^ BMP1)) { - key = magic_head[0] ^ BMP0; - suffix = L".bmp"; - } else { - key = -1; - suffix = L".dat"; - } - wstring save_img_path = save_path + L"\\" + file_name + suffix; - HANDLE save_img = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (save_img == INVALID_HANDLE_VALUE) { - return 0; - } - if (key > 0) { - for (unsigned int i = 0; i < bytes_read; i++) { - buffer[i]^=key; - } - } - if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { - CloseHandle(h_origin_file); - CloseHandle(save_img); - return 0; - } - - do { - if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { - if (key > 0) { - for (unsigned int i = 0; i < bytes_read; i++) { - buffer[i] ^= key; - } - } - if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { - CloseHandle(h_origin_file); - CloseHandle(save_img); - return 0; - } - } - } while (bytes_read == BUFSIZE); - CloseHandle(h_origin_file); - CloseHandle(save_img); - return 1; -} \ No newline at end of file diff --git a/src/hook_img.h b/src/hook_img.h deleted file mode 100644 index a0d6bb6..0000000 --- a/src/hook_img.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef HOOK_IMG_H_ -#define HOOK_IMG_H_ -#include "windows.h" - -int HookImg(std::wstring save_path); -int UnHookImg(); - -int GetImgByName(wchar_t* file_path,wchar_t* save_dir); -#endif \ No newline at end of file diff --git a/src/hook_log.cc b/src/hook_log.cc deleted file mode 100644 index bffe7b0..0000000 --- a/src/hook_log.cc +++ /dev/null @@ -1,78 +0,0 @@ -#include "pch.h" -#include "hook_log.h" - -#include "common.h" - -using namespace std; - -#define WX_HOOK_LOG_OFFSET 0xed1675 -#define WX_HOOK_LOG_NEXT_OFFSET 0x2344832 - -static int kLogHooked = FALSE; -static DWORD kWeChatWinBase = GetWeChatWinBase(); -static char kOriginLogAsmCode[5] = {0}; - -static DWORD kHookLogAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET; -static DWORD kHookLogNextAddress = kWeChatWinBase + WX_HOOK_LOG_NEXT_OFFSET; -static DWORD kHookLogJmpBackAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET + 0x5; - -void log_print(DWORD addr) { - if (!addr) { - return; - } - DWORD dwId = 0; - char *msg = (char *)addr; - int size = MultiByteToWideChar(CP_UTF8, 0, msg, -1, 0, 0); - wchar_t *w_msg = new wchar_t[size + 1]; - memset(w_msg, 0, (size + 1) * 2); - MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, size); - size = WideCharToMultiByte(CP_ACP, 0, w_msg, -1, 0, 0, 0, 0); - char *ansi_message = new char[size + 1]; - memset(ansi_message, 0, size + 1); - WideCharToMultiByte(CP_ACP, 0, w_msg, -1, ansi_message, size, 0, 0); - delete[] w_msg; - w_msg = NULL; - cout << ansi_message; - delete[] ansi_message; - ansi_message = NULL; -} - -_declspec(naked) void handle_log() { - __asm { - PUSHAD - PUSHFD - PUSH EAX - CALL log_print - ADD ESP, 0x4 - POPFD - POPAD - CALL kHookLogNextAddress - JMP kHookLogJmpBackAddress - } -} - -int HookLog() { - kWeChatWinBase = GetWeChatWinBase(); - if (!kWeChatWinBase) { - return -1; - } - if (kLogHooked) { - return 2; - } - kHookLogAddress = kWeChatWinBase + WX_HOOK_LOG_OFFSET; - kHookLogNextAddress = kWeChatWinBase + WX_HOOK_LOG_NEXT_OFFSET; - kHookLogJmpBackAddress = kHookLogAddress + 0x5; - HookAnyAddress(kHookLogAddress, (LPVOID)handle_log, kOriginLogAsmCode); - kLogHooked = TRUE; - return 1; -} - -int UnHookLog() { - if (!kLogHooked) { - return 1; - } - DWORD hook_img_addr = kWeChatWinBase + WX_HOOK_LOG_OFFSET; - UnHookAnyAddress(hook_img_addr, kOriginLogAsmCode); - kLogHooked = FALSE; - return 1; -} \ No newline at end of file diff --git a/src/hook_log.h b/src/hook_log.h deleted file mode 100644 index 5dcad93..0000000 --- a/src/hook_log.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef HOOK_LOG_H_ -#define HOOK_LOG_H_ -#include "windows.h" - -int HookLog(); -int UnHookLog(); - -#endif \ No newline at end of file diff --git a/src/hook_recv_msg.cc b/src/hook_recv_msg.cc deleted file mode 100644 index c57e58c..0000000 --- a/src/hook_recv_msg.cc +++ /dev/null @@ -1,301 +0,0 @@ -#include "hook_recv_msg.h" - -#include -#include - -#include - -#include "common.h" -#include "pch.h" -using namespace nlohmann; - -using namespace std; -#define WX_RECV_MSG_HOOK_OFFSET 0xd19a0b -#define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x756960 -#define WX_SNS_HOOK_OFFSET 0x14f9e15 -#define WX_SNS_HOOK_NEXT_OFFSET 0x14fa0a0 - -// SyncMgr::addMsgListToDB -// #define WX_RECV_MSG_HOOK_OFFSET 0xB9C919 -// #define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x722FF0 - -#define CLIENT_IP "127.0.0.1" -static int kServerPort = 0; -static int kMessageHooked = FALSE; -static char kServerIp[16]= "127.0.0.1"; -static DWORD kWeChatWinBase = GetWeChatWinBase(); - -static char kOriginReceMsgAsmCode[5] = {0}; -static DWORD kReceMsgJmpBackAddress = - kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET + 0x5; -static DWORD kReceMsgNextAddress = - kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET; - - -static char kOriginSnsMsgAsmCode[5] = {0}; -static DWORD kSnsMsgJmpBackAddress = - kWeChatWinBase + WX_SNS_HOOK_OFFSET + 0x5; -static DWORD kSnsMsgNextAddress = - kWeChatWinBase + WX_SNS_HOOK_NEXT_OFFSET; - -struct InnerMessageStruct { - char *buffer; - int length; - ~InnerMessageStruct() { - if (this->buffer != NULL) { - delete[] this->buffer; - this->buffer = NULL; - } - } -}; -/// @brief send message by socket -/// @param buffer content -/// @param len len -/// @return true/false -BOOL SendBySocket(const char *buffer, size_t len) { - if (kServerPort == 0) { - return false; - } - SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (client_socket < 0) { -#ifdef _DEBUG - cout << "create socket error," - << " errno:" << errno << endl; -#endif - return false; - } - BOOL status = false; - sockaddr_in client_addr; - memset(&client_addr, 0, sizeof(client_addr)); - client_addr.sin_family = AF_INET; - client_addr.sin_port = htons((u_short)kServerPort); - InetPtonA(AF_INET, kServerIp, &client_addr.sin_addr.s_addr); - if (connect(client_socket, reinterpret_cast(&client_addr), - sizeof(sockaddr)) < 0) { -#ifdef _DEBUG - cout << "kServerIp:" << kServerIp < sms(msg); - json j_msg = - json::parse(msg->buffer, msg->buffer + msg->length, nullptr, false); - if (j_msg.is_discarded() == true) { - return; - } - string jstr = j_msg.dump() + "\n"; -#ifdef _DEBUG - cout << "json:" + jstr << endl; -#endif - SendBySocket(jstr.c_str(), jstr.size()); -} -/// @brief msg handle -/// @param msg_addr msg address in memory -void __cdecl OnRecvMsg(DWORD msg_addr) { - json j_msg; - unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30); - j_msg["msgId"] = msgid; - j_msg["pid"] = GetCurrentProcessId(); - j_msg["type"] = *(DWORD *)(msg_addr + 0x38); - j_msg["isSendMsg"] = *(BOOL *)(msg_addr + 0x3C); - if (j_msg["isSendMsg"].get()) { - j_msg["isSendByPhone"] = (int)(*(BYTE *)(msg_addr + 0xD8)); - } - j_msg["time"] = - unicode_to_utf8((wchar_t *)GetTimeW(*(DWORD *)(msg_addr + 0x44)).c_str()); - j_msg["timestamp"] = *(DWORD *)(msg_addr + 0x44); - j_msg["fromGroup"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x48).c_str()); - int length = *(DWORD *)(msg_addr + 0x178); - if (length == 0) { - j_msg["fromUser"] = j_msg["fromGroup"].get(); - } else { - j_msg["fromUser"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x174).c_str()); - } - int content_len = *(DWORD *)(msg_addr + 0x74); - if (content_len > 0) { - j_msg["content"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x70).c_str()); - } - int sign_len = *(DWORD *)(msg_addr + 0x18C); - if (sign_len > 0) { - j_msg["sign"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x188).c_str()); - } - int thumb_len = *(DWORD *)(msg_addr + 0x1A0); - if (thumb_len > 0) { - j_msg["thumbPath"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x19C).c_str()); - } - int path_len = *(DWORD *)(msg_addr + 0x1B4); - if (path_len > 0) { - j_msg["path"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x1B0).c_str()); - } - - int signature_len = *(DWORD *)(msg_addr + 0x1F4); - if (signature_len > 0) { - j_msg["signature"] = - unicode_to_utf8((wchar_t *)READ_WSTRING(msg_addr, 0x1F0).c_str()); - } - - string jstr = j_msg.dump() + '\n'; - InnerMessageStruct *inner_msg = new InnerMessageStruct; - inner_msg->buffer = new char[jstr.size() + 1]; - memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1); - inner_msg->length = jstr.size(); - HANDLE thread = CreateThread( - NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, inner_msg, NULL, 0); - if (thread) { - CloseHandle(thread); - } -} - - - - void __cdecl OnSnsTimeLineMsg(DWORD msg_addr) { - json j_sns; - DWORD begin_addr = *(DWORD *)(msg_addr + 0x20); - DWORD end_addr = *(DWORD *)(msg_addr + 0x24); - #ifdef _DEBUG - cout << "begin" <buffer = new char[jstr.size() + 1]; - memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1); - inner_msg->length = jstr.size(); - HANDLE thread = CreateThread( - NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, inner_msg, NULL, 0); - if (thread) { - CloseHandle(thread); - } -} - -/// @brief hook implement -_declspec(naked) void handle_sync_msg() { - __asm { - PUSHAD - PUSHFD - PUSH ECX - CALL OnRecvMsg - ADD ESP, 0x4 - POPFD - POPAD - CALL kReceMsgNextAddress - JMP kReceMsgJmpBackAddress - } -} - - -/// @brief hook sns msg implement -_declspec(naked) void handle_sns_msg() { - __asm { - PUSHAD - PUSHFD - PUSH [ESP + 0x24] - CALL OnSnsTimeLineMsg - ADD ESP, 0x4 - POPFD - POPAD - CALL kSnsMsgNextAddress - JMP kSnsMsgJmpBackAddress - } -} - - -/// @brief hook any address address+0x5 -/// @param port 端口 -/// @return 成功返回1,已经hook返回2,失败返回-1 -int HookRecvMsg(char* client_ip,int port) { - kServerPort = port; - strcpy_s(kServerIp,client_ip); - kWeChatWinBase = GetWeChatWinBase(); - if (!kWeChatWinBase) { - return -1; - } - - if (kMessageHooked) { - return 2; - } - kWeChatWinBase = GetWeChatWinBase(); - DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET; - kReceMsgNextAddress = kWeChatWinBase + WX_RECV_MSG_HOOK_NEXT_OFFSET; - kReceMsgJmpBackAddress = hook_recv_msg_addr + 0x5; - HookAnyAddress(hook_recv_msg_addr, (LPVOID)handle_sync_msg, - kOriginReceMsgAsmCode); - - DWORD hook_sns_msg_addr = kWeChatWinBase + WX_SNS_HOOK_OFFSET; - kSnsMsgNextAddress = kWeChatWinBase + WX_SNS_HOOK_NEXT_OFFSET; - kSnsMsgJmpBackAddress = hook_sns_msg_addr + 0x5; - HookAnyAddress(hook_sns_msg_addr, (LPVOID)handle_sns_msg, - kOriginSnsMsgAsmCode); - - - kMessageHooked = TRUE; - return 1; -} - -int UnHookRecvMsg() { - kServerPort = 0; - if (!kMessageHooked) return 2; - DWORD hook_recv_msg_addr = kWeChatWinBase + WX_RECV_MSG_HOOK_OFFSET; - DWORD hook_sns_addr = kWeChatWinBase + WX_SNS_HOOK_OFFSET; - UnHookAnyAddress(hook_recv_msg_addr, kOriginReceMsgAsmCode); - UnHookAnyAddress(hook_sns_addr, kOriginSnsMsgAsmCode); - kMessageHooked = FALSE; - return 1; -} \ No newline at end of file diff --git a/src/hook_recv_msg.h b/src/hook_recv_msg.h deleted file mode 100644 index ac2cf8d..0000000 --- a/src/hook_recv_msg.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef HOOK_RECV_MSG_H_ -#define HOOK_RECV_MSG_H_ - -/// @brief hook any address address+0x5 -/// @param port 端口 -/// @return 成功返回1,已经hook返回2 -int HookRecvMsg(char* client_ip,int port); - -int UnHookRecvMsg(); -#endif \ No newline at end of file diff --git a/src/hook_voice.cc b/src/hook_voice.cc deleted file mode 100644 index 1fa3205..0000000 --- a/src/hook_voice.cc +++ /dev/null @@ -1,88 +0,0 @@ -#include "pch.h" -#include "hook_voice.h" - -#include "common.h" - -using namespace std; - -#define WX_HOOK_VOICE_OFFSET 0xd4d8d8 -#define WX_HOOK_VOICE_NEXT_OFFSET 0x203d130 -#define WX_SELF_ID_OFFSET 0x2FFD484 - -static wstring kVoiceStorePath = L""; -static int kVoiceHooked = FALSE; -static DWORD kWeChatWinBase = GetWeChatWinBase(); -static char kOriginVoiceAsmCode[5] = {0}; - -static DWORD kHookVoiceNextAddress = kWeChatWinBase + WX_HOOK_VOICE_NEXT_OFFSET; -static DWORD kHookVoiceJmpBackAddress = - kWeChatWinBase + WX_HOOK_VOICE_OFFSET + 0x5; - -void OnHookVoice(DWORD buff,int len , DWORD msg_addr) { - DWORD wxid_addr = GetWeChatWinBase() + WX_SELF_ID_OFFSET; - string wxid = string(*(char **)wxid_addr, *(DWORD *)(wxid_addr + 0x10)); - wstring self_id = utf8_to_unicode(wxid.c_str()); - wstring save_path = kVoiceStorePath + self_id; - if (!FindOrCreateDirectoryW(save_path.c_str())) { - return; - } - unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30); - save_path = save_path + L"\\" + to_wstring(msgid) + L".amr"; - HANDLE file_handle = CreateFileW(save_path.c_str(), GENERIC_ALL, 0, NULL, - CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file_handle == INVALID_HANDLE_VALUE) { - return; - } - DWORD bytes_write = 0; - WriteFile(file_handle, (LPCVOID)buff, len, &bytes_write, 0); - CloseHandle(file_handle); -} - -/// @brief hook voice implement -_declspec(naked) void handle_voice() { - __asm { - PUSHAD - PUSHFD - PUSH EDI - PUSH EDX - PUSH EAX - CALL OnHookVoice - ADD ESP, 0xC - POPFD - POPAD - CALL kHookVoiceNextAddress - JMP kHookVoiceJmpBackAddress - } -} - -int HookVoice(std::wstring save_path) { - kWeChatWinBase = GetWeChatWinBase(); - if (!kWeChatWinBase) { - return -1; - } - if (kVoiceHooked) { - return 2; - } - kVoiceStorePath = save_path; - if (kVoiceStorePath.back() != '\\') { - kVoiceStorePath += L"\\"; - } - wstring createpath = kVoiceStorePath.substr(0, kVoiceStorePath.length() - 1); - if (!FindOrCreateDirectoryW(createpath.c_str())) { - return -2; - } - DWORD hook_voice_addr = kWeChatWinBase + WX_HOOK_VOICE_OFFSET; - kHookVoiceNextAddress = kWeChatWinBase + WX_HOOK_VOICE_NEXT_OFFSET; - static DWORD kHookVoiceJmpBackAddress = hook_voice_addr + 0x5; - HookAnyAddress(hook_voice_addr, (LPVOID)handle_voice, kOriginVoiceAsmCode); - kVoiceHooked = TRUE; - return 1; -} - -int UnHookVoice() { - if (!kVoiceHooked) return 1; - DWORD hook_voice_addr = kWeChatWinBase + WX_HOOK_VOICE_OFFSET; - UnHookAnyAddress(hook_voice_addr, kOriginVoiceAsmCode); - kVoiceHooked = FALSE; - return 1; -} diff --git a/src/hook_voice.h b/src/hook_voice.h deleted file mode 100644 index 429221a..0000000 --- a/src/hook_voice.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef HOOK_VOICE_H_ -#define HOOK_VOICE_H_ -#include - -int HookVoice(std::wstring save_path); - -int UnHookVoice(); -#endif \ No newline at end of file diff --git a/src/hooks.cc b/src/hooks.cc new file mode 100644 index 0000000..c5147f9 --- /dev/null +++ b/src/hooks.cc @@ -0,0 +1,533 @@ +#include +#include + +#include + +#include "easylogging++.h" +#include "pch.h" +#include "wechat_function.h" +using namespace nlohmann; +using namespace std; +namespace wxhelper { +namespace hooks { +static int server_port_ = 0; +static bool msg_hook_flag_ = false; +static char server_ip_[16] = "127.0.0.1"; + +static char msg_asm_code_[5] = {0}; +static DWORD msg_back_addr_ = 0; +static DWORD msg_next_addr_ = 0; + +static char sns_asm_code_[5] = {0}; +static DWORD sns_back_addr_ = 0; +static DWORD sns_next_addr_ = 0; + +static bool log_hook_flag_ = false; +static char log_asm_code_[5] = {0}; +static DWORD log_back_addr_ = 0; +static DWORD log_next_addr_ = 0; + +static bool search_contact_flag_ = false; +static char search_contact_asm_code_[5] = {0}; +static DWORD search_contact_back_addr_ = 0; +static DWORD search_contact_next_addr_ = 0; + +static bool error_code_flag_ = false; +static char error_code_asm_code_[5] = {0}; +static DWORD error_code_back_addr_ = 0; +static DWORD error_code_next_addr_ = 0; + +bool user_info_flag_ = false; +static char user_info_asm_code_[5] = {0}; +static DWORD user_info_back_addr_ = 0; +static DWORD user_info_next_addr_ = 0; + +UserInfo userinfo = {}; + +void SendSocketMessage(InnerMessageStruct *msg) { + if (msg == NULL) { + return; + } + unique_ptr sms(msg); + json j_msg = + json::parse(msg->buffer, msg->buffer + msg->length, nullptr, false); + if (j_msg.is_discarded() == true) { + return; + } + string jstr = j_msg.dump() + "\n"; + + if (server_port_ == 0) { + LOG(INFO) << "http server port error :" << server_port_; + return; + } + SOCKET client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (client_socket < 0) { + LOG(INFO) << "socket init fail"; + return; + } + BOOL status = false; + sockaddr_in client_addr; + memset(&client_addr, 0, sizeof(client_addr)); + client_addr.sin_family = AF_INET; + client_addr.sin_port = htons((u_short)server_port_); + InetPtonA(AF_INET, server_ip_, &client_addr.sin_addr.s_addr); + if (connect(client_socket, reinterpret_cast(&client_addr), + sizeof(sockaddr)) < 0) { + LOG(INFO) << "socket connect fail"; + return; + } + char recv_buf[1024] = {0}; + int ret = send(client_socket, jstr.c_str(), jstr.size(), 0); + if (ret == -1 || ret == 0) { + LOG(INFO) << "socket send fail ,ret:" << ret; + closesocket(client_socket); + return; + } + memset(recv_buf, 0, sizeof(recv_buf)); + ret = recv(client_socket, recv_buf, sizeof(recv_buf), 0); + closesocket(client_socket); + if (ret == -1 || ret == 0) { + LOG(INFO) << "socket recv fail ,ret:" << ret; + } +} + +void __cdecl OnRecvMsg(DWORD msg_addr) { + json j_msg; + unsigned long long msgid = *(unsigned long long *)(msg_addr + 0x30); + j_msg["msgId"] = msgid; + j_msg["pid"] = GetCurrentProcessId(); + j_msg["type"] = *(DWORD *)(msg_addr + 0x38); + j_msg["isSendMsg"] = *(BOOL *)(msg_addr + 0x3C); + if (j_msg["isSendMsg"].get()) { + j_msg["isSendByPhone"] = (int)(*(BYTE *)(msg_addr + 0xD8)); + } + j_msg["time"] = + Utils::WstringToUTF8(Utils::GetTimeW(*(DWORD *)(msg_addr + 0x44))); + j_msg["timestamp"] = *(DWORD *)(msg_addr + 0x44); + j_msg["fromGroup"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x48)); + int length = *(DWORD *)(msg_addr + 0x178); + if (length == 0) { + j_msg["fromUser"] = j_msg["fromGroup"].get(); + } else { + j_msg["fromUser"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x174)); + } + int content_len = *(DWORD *)(msg_addr + 0x74); + if (content_len > 0) { + j_msg["content"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x70)); + } + int sign_len = *(DWORD *)(msg_addr + 0x18C); + if (sign_len > 0) { + j_msg["sign"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x188)); + } + int thumb_len = *(DWORD *)(msg_addr + 0x1A0); + if (thumb_len > 0) { + j_msg["thumbPath"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x19C)); + } + int path_len = *(DWORD *)(msg_addr + 0x1B4); + if (path_len > 0) { + j_msg["path"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x1B0)); + } + + int signature_len = *(DWORD *)(msg_addr + 0x1F4); + if (signature_len > 0) { + j_msg["signature"] = Utils::WstringToUTF8(READ_WSTRING(msg_addr, 0x1F0)); + } + + string jstr = j_msg.dump() + '\n'; + InnerMessageStruct *inner_msg = new InnerMessageStruct; + inner_msg->buffer = new char[jstr.size() + 1]; + memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1); + inner_msg->length = jstr.size(); + HANDLE thread = CreateThread( + NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, inner_msg, NULL, 0); + if (thread) { + CloseHandle(thread); + } +} + +/// @brief hook msg implement +_declspec(naked) void HandleSyncMsg() { + __asm { + PUSHAD + PUSHFD + PUSH ECX + CALL OnRecvMsg + ADD ESP, 0x4 + POPFD + POPAD + CALL msg_next_addr_ + JMP msg_back_addr_ + } +} + +void __cdecl OnSnsTimeLineMsg(DWORD msg_addr) { + json j_sns; + DWORD begin_addr = *(DWORD *)(msg_addr + 0x20); + DWORD end_addr = *(DWORD *)(msg_addr + 0x24); + if (begin_addr == 0) { + j_sns = {{"data", json::array()}}; + } else { + while (begin_addr < end_addr) { + json j_item; + j_item["snsId"] = *(unsigned long long *)(begin_addr); + j_item["createTime"] = *(DWORD *)(begin_addr + 0x2C); + + j_item["senderId"] = Utils::WstringToUTF8(READ_WSTRING(begin_addr, 0x18)); + + j_item["content"] = Utils::WstringToUTF8(READ_WSTRING(begin_addr, 0x3c)); + + j_item["xml"] = Utils::WstringToUTF8(READ_WSTRING(begin_addr, 0x384)); + + j_sns["data"].push_back(j_item); + begin_addr += 0xB48; + } + } + string jstr = j_sns.dump() + '\n'; + InnerMessageStruct *inner_msg = new InnerMessageStruct; + inner_msg->buffer = new char[jstr.size() + 1]; + memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1); + inner_msg->length = jstr.size(); + HANDLE thread = CreateThread( + NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, inner_msg, NULL, 0); + if (thread) { + CloseHandle(thread); + } +} + +/// @brief hook sns msg implement +_declspec(naked) void HandleSNSMsg() { + __asm { + PUSHAD + PUSHFD + PUSH [ESP + 0x24] + CALL OnSnsTimeLineMsg + ADD ESP, 0x4 + POPFD + POPAD + CALL sns_next_addr_ + JMP sns_back_addr_ + } +} + +int HookRecvMsg(char *client_ip, int port) { + server_port_ = port; + strcpy_s(server_ip_, client_ip); + DWORD base = Utils::GetWeChatWinBase(); + if (!base) { + return -1; + } + + if (msg_hook_flag_) { + return 2; + } + + DWORD hook_recv_msg_addr = base + WX_RECV_MSG_HOOK_OFFSET; + msg_next_addr_ = base + WX_RECV_MSG_HOOK_NEXT_OFFSET; + msg_back_addr_ = hook_recv_msg_addr + 0x5; + LOG(INFO) << "base" << base; + LOG(INFO) << "msg_next_addr_" << msg_next_addr_; + LOG(INFO) << "msg_back_addr_" << msg_back_addr_; + Utils::HookAnyAddress(hook_recv_msg_addr, (LPVOID)HandleSyncMsg, + msg_asm_code_); + + DWORD hook_sns_msg_addr = base + WX_SNS_HOOK_OFFSET; + sns_next_addr_ = base + WX_SNS_HOOK_NEXT_OFFSET; + sns_back_addr_ = hook_sns_msg_addr + 0x5; + LOG(INFO) << "base" << base; + LOG(INFO) << "sns_next_addr_" << sns_next_addr_; + LOG(INFO) << "sns_back_addr_" << sns_back_addr_; + Utils::HookAnyAddress(hook_sns_msg_addr, (LPVOID)HandleSNSMsg, sns_asm_code_); + + msg_hook_flag_ = true; + return 1; +} + +int UnHookRecvMsg() { + server_port_ = 0; + if (!msg_hook_flag_) { + LOG(INFO) << "this port already hooked"; + return 2; + } + DWORD base = Utils::GetWeChatWinBase(); + DWORD hook_recv_msg_addr = base + WX_RECV_MSG_HOOK_OFFSET; + DWORD hook_sns_addr = base + WX_SNS_HOOK_OFFSET; + Utils::UnHookAnyAddress(hook_recv_msg_addr, msg_asm_code_); + Utils::UnHookAnyAddress(hook_sns_addr, sns_asm_code_); + msg_hook_flag_ = false; + return 1; +} + +void PrintLog(DWORD addr) { + if (!addr) { + return; + } + DWORD dwId = 0; + char *msg = (char *)addr; + int size = MultiByteToWideChar(CP_UTF8, 0, msg, -1, 0, 0); + wchar_t *w_msg = new wchar_t[size + 1]; + memset(w_msg, 0, (size + 1) * 2); + MultiByteToWideChar(CP_UTF8, 0, msg, -1, w_msg, size); + size = WideCharToMultiByte(CP_ACP, 0, w_msg, -1, 0, 0, 0, 0); + char *ansi_message = new char[size + 1]; + memset(ansi_message, 0, size + 1); + WideCharToMultiByte(CP_ACP, 0, w_msg, -1, ansi_message, size, 0, 0); + delete[] w_msg; + w_msg = NULL; + LOG(INFO) << ansi_message; + delete[] ansi_message; + ansi_message = NULL; +} + +_declspec(naked) void HandleLog() { + __asm { + PUSHAD + PUSHFD + PUSH EAX + CALL PrintLog + ADD ESP, 0x4 + POPFD + POPAD + CALL log_next_addr_ + JMP log_back_addr_ + } +} + +int HookLog() { + DWORD base = Utils::GetWeChatWinBase(); + if (!base) { + return -1; + } + if (log_hook_flag_) { + return 2; + } + DWORD hook_log_addr = base + WX_HOOK_LOG_OFFSET; + log_next_addr_ = base + WX_HOOK_LOG_NEXT_OFFSET; + log_back_addr_ = hook_log_addr + 0x5; + Utils::HookAnyAddress(hook_log_addr, (LPVOID)HandleLog, log_asm_code_); + log_hook_flag_ = true; + return 1; +} +int UnHookLog() { + if (!log_hook_flag_) { + return 1; + } + DWORD base = Utils::GetWeChatWinBase(); + DWORD hook_log_addr = base + WX_HOOK_LOG_OFFSET; + Utils::UnHookAnyAddress(hook_log_addr, log_asm_code_); + log_hook_flag_ = FALSE; + return 1; +} + +void SetErrorCode(int code) { userinfo.error_code = code; } + +void SetUserInfoDetail(DWORD address) { + LOG(INFO) << "hook userinfo addr" <<&userinfo; + DWORD length = *(DWORD *)(address + 0x8); + userinfo.keyword = new wchar_t[length + 1]; + userinfo.keyword_len = length; + if (length) { + memcpy(userinfo.keyword, (wchar_t *)(*(DWORD *)(address + 0x4)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.keyword, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x1C); + userinfo.v3 = new wchar_t[length + 1]; + userinfo.v3_len = length; + if (length) { + memcpy(userinfo.v3, (wchar_t *)(*(DWORD *)(address + 0x18)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.v3, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x30); + userinfo.big_image = new wchar_t[length + 1]; + userinfo.big_image_len = length; + if (length) { + memcpy(userinfo.big_image, (wchar_t *)(*(DWORD *)(address + 0x2C)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.big_image, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0xC8); + userinfo.nickname = new wchar_t[length + 1]; + userinfo.nickname_len = length; + if (length) { + memcpy(userinfo.nickname, (wchar_t *)(*(DWORD *)(address + 0xC4)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.nickname, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x108); + userinfo.v2 = new wchar_t[length + 1]; + userinfo.v2_len = length; + if (length) { + memcpy(userinfo.v2, (wchar_t *)(*(DWORD *)(address + 0x104)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.v2, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x16C); + userinfo.small_image = new wchar_t[length + 1]; + userinfo.small_image_len = length; + if (length) { + memcpy(userinfo.small_image, (wchar_t *)(*(DWORD *)(address + 0x168)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.small_image, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x1F8); + userinfo.signature = new wchar_t[length + 1]; + userinfo.signature_len = length; + if (length) { + memcpy(userinfo.signature, (wchar_t *)(*(DWORD *)(address + 0x1F4)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.signature, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x20C); + userinfo.nation = new wchar_t[length + 1]; + userinfo.nation_len = length; + if (length) { + memcpy(userinfo.nation, (wchar_t *)(*(DWORD *)(address + 0x208)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.nation, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x220); + userinfo.province = new wchar_t[length + 1]; + userinfo.province_len = length; + if (length) { + memcpy(userinfo.province, (wchar_t *)(*(DWORD *)(address + 0x21C)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.province, (length + 1) * sizeof(wchar_t)); + } + + length = *(DWORD *)(address + 0x234); + userinfo.city = new wchar_t[length + 1]; + userinfo.city_len = length; + if (length) { + memcpy(userinfo.city, (wchar_t *)(*(DWORD *)(address + 0x230)), + (length + 1) * sizeof(wchar_t)); + } else { + ZeroMemory(userinfo.city, (length + 1) * sizeof(wchar_t)); + } + + userinfo.sex = *(DWORD *)(address + 0x1BC); + userinfo.over = true; +} + +void DeleteUserInfoCache() { + if (userinfo.keyword) { + delete userinfo.keyword; + } + if (userinfo.v2) { + delete userinfo.v2; + } + if (userinfo.v3) { + delete userinfo.v3; + } + if (userinfo.nickname) { + delete userinfo.nickname; + } + if (userinfo.nation) { + delete userinfo.nation; + } + if (userinfo.province) { + delete userinfo.province; + } + if (userinfo.city) { + delete userinfo.city; + } + if (userinfo.signature) { + delete userinfo.signature; + } + if (userinfo.small_image) { + delete userinfo.small_image; + } + if (userinfo.big_image) { + delete userinfo.big_image; + } + ZeroMemory(&userinfo, sizeof(UserInfo)); + userinfo.error_code = 1; +} + +__declspec(naked) void HandleErrorCode() { + __asm { + PUSHAD; + PUSHFD; + PUSH ESI; + CALL SetErrorCode; + ADD ESP, 0x4; + POPFD; + POPAD; + CALL error_code_next_addr_; + JMP error_code_back_addr_; + } +} + +__declspec(naked) void HandleUserInfoDetail() { + __asm { + PUSHAD; + PUSHFD; + PUSH dword ptr [EBP + 0x14]; + CALL SetUserInfoDetail; + ADD ESP, 0x4; + POPFD; + POPAD; + CALL user_info_next_addr_; + JMP user_info_back_addr_; + } +} + +int HookSearchContact() { + DWORD base = Utils::GetWeChatWinBase(); + if (!base) { + return -1; + } + if (search_contact_flag_) { + return 2; + } + DWORD hook_error_code_addr = base + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSET; + error_code_next_addr_ = base + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_NEXT_OFFSET; + error_code_back_addr_ = hook_error_code_addr + 0x5; + Utils::HookAnyAddress(hook_error_code_addr, (LPVOID)HandleErrorCode, + error_code_asm_code_); + + DWORD hook_user_info_addr = base + WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSET; + user_info_next_addr_ = base + WX_SEARCH_CONTACT_DETAIL_HOOK_NEXT_OFFSET; + user_info_back_addr_ = hook_user_info_addr + 0x5; + + Utils::HookAnyAddress(hook_user_info_addr, (LPVOID)HandleUserInfoDetail, + user_info_asm_code_); + error_code_flag_ = true; + user_info_flag_ = true; + return 1; +} + +int UnHookSearchContact() { + DWORD base = Utils::GetWeChatWinBase(); + DWORD hook_user_info_addr = base + WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSET; + DWORD hook_error_code_addr = base + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSET; + + if (!user_info_flag_) return 2; + Utils::UnHookAnyAddress(hook_user_info_addr, user_info_asm_code_); + user_info_flag_ = false; + + if (!error_code_flag_) return 2; + Utils::UnHookAnyAddress(hook_error_code_addr, error_code_asm_code_); + error_code_flag_ = false; + + return 1; +} +} // namespace hooks +} // namespace wxhelper \ No newline at end of file diff --git a/src/hooks.h b/src/hooks.h new file mode 100644 index 0000000..960712e --- /dev/null +++ b/src/hooks.h @@ -0,0 +1,23 @@ +#ifndef WXHELPER_HOOKS_H_ +#define WXHELPER_HOOKS_H_ +#include "Windows.h" +#include "wechat_function.h" +namespace wxhelper { +namespace hooks { +extern UserInfo userinfo; +extern bool user_info_flag_ ; + +int HookRecvMsg(char* client_ip, int port); + +int UnHookRecvMsg(); + +void SendSocketMessage(InnerMessageStruct* msg); + +int HookLog(); +int UnHookLog(); +int HookSearchContact(); +int UnHookSearchContact(); +void DeleteUserInfoCache(); +} // namespace hooks +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/http_handler.cc b/src/http_handler.cc new file mode 100644 index 0000000..84458b7 --- /dev/null +++ b/src/http_handler.cc @@ -0,0 +1,594 @@ +#include "pch.h" +#include "http_handler.h" + +#include + +#include "utils.h" +#include "account_mgr.h" +#include "api_route.h" +#include "chat_room_mgr.h" +#include "contact_mgr.h" +#include "db.h" +#include "easylogging++.h" +#include "hooks.h" +#include "misc_mgr.h" +#include "send_message_mgr.h" +#include "sns_mgr.h" +#include "global_context.h" +#include "hooks.h" + +using namespace std; +using namespace nlohmann; + +namespace wxhelper { +string GetParamOfGetReq(mg_http_message *hm, string name) { + string ret; + char *buffer = new char[hm->query.len + 1]; + ZeroMemory(buffer, hm->query.len + 1); + int len = mg_http_get_var(&hm->query, name.c_str(), buffer, hm->query.len); + if (len > 0) ret = string(buffer, len); + delete[] buffer; + buffer = NULL; + return ret; +} + +int GetIntParam(json data, string key) { + int result; + try { + result = data[key].get(); + } catch (json::exception) { + result = STRING2INT(data[key].get()); + } + return result; +} + +wstring GetWStringParam(json data, string key) { + return Utils::UTF8ToWstring(data[key].get()); +} + +unsigned long long GetULong64Param(json j_data, string key) { + unsigned long long result = 0; + try { + result = j_data[key].get(); + } catch (json::exception) { + string value = j_data[key].get(); + istringstream is(value); + is >> result; + } + return result; +} + +static vector getArrayParam(json j_data, string key) { + vector result; + wstring param = GetWStringParam(j_data, key); + result = Utils::split(param, L','); + return result; +} + +string Dispatch(struct mg_connection *c, struct mg_http_message *hm) { + int is_post = 0; + string ret; + if (mg_vcasecmp(&hm->method, "POST") == 0) { + is_post = 1; + } + el::Logger *defaultLogger = el::Loggers::getLogger("default"); + defaultLogger->info("method: %v body: %v", hm->method.ptr, hm->body.ptr); + LOG_IF(is_post != 1, INFO) << "request method is not post"; + + if (is_post == 0) { + json ret_data = {{"result", "ERROR"}, {"msg", "not support method"}}; + ret = ret_data.dump(); + return ret; + } + + json j_param = + json::parse(hm->body.ptr, hm->body.ptr + hm->body.len, nullptr, false); + if (hm->body.len != 0 && j_param.is_discarded() == true) { + json ret_data = {{"result", "ERROR"}, {"msg", "json string is invalid."}}; + ret = ret_data.dump(); + return ret; + } + int api_number = STRING2INT(GetParamOfGetReq(hm, "type")); + GlobalContext& g_context = GlobalContext::GetInstance(); + switch (api_number) { + case WECHAT_IS_LOGIN: { + int success = -1; + success = g_context.account_mgr->CheckLogin(); + json ret_data = {{"result", "OK"}, {"code", success}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_SELF_INFO: { + SelfInfoInner self_info; + int success = g_context.account_mgr->GetSelfInfo(self_info); + json ret_data = {{"result", "OK"}, {"code", success}}; + if (success) { + json j_info = { + {"name", self_info.name}, + {"city", self_info.city}, + {"province", self_info.province}, + {"country", self_info.country}, + {"account", self_info.account}, + {"wxid", self_info.wxid}, + {"mobile", self_info.mobile}, + {"headImage", self_info.head_img}, + {"signature", self_info.signature}, + {"dataSavePath", self_info.data_save_path}, + {"currentDataPath", self_info.current_data_path}, + {"dbKey", self_info.db_key}, + }; + ret_data["data"] = j_info; + } + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_TEXT: { + wstring wxid = GetWStringParam(j_param, "wxid"); + wstring msg = GetWStringParam(j_param, "msg"); + int success = g_context.send_mgr->SendText(WS2LPWS(wxid), WS2LPWS(msg)); + json ret_data = {{"result", "OK"}, {"code", success}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_AT: { + break; + wstring chat_room_id = GetWStringParam(j_param, "chatRoomId"); + vector wxids = getArrayParam(j_param, "wxids"); + wstring msg = GetWStringParam(j_param, "msg"); + vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++) { + wxid_list.push_back(WS2LPWS(wxids[i])); + } + int success = g_context.send_mgr->SendAtText(WS2LPWS(chat_room_id), wxid_list.data(), + wxid_list.size(), WS2LPWS(msg)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_CARD: { + break; + } + case WECHAT_MSG_SEND_IMAGE: { + wstring receiver = GetWStringParam(j_param, "wxid"); + wstring img_path = GetWStringParam(j_param, "imagePath"); + int success = + g_context.send_mgr->SendImage(WS2LPWS(receiver), WS2LPWS(img_path)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_FILE: { + wstring receiver = GetWStringParam(j_param, "wxid"); + wstring file_path = GetWStringParam(j_param, "filePath"); + int success = + g_context.send_mgr->SendFile(WS2LPWS(receiver), WS2LPWS(file_path)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_SEND_ARTICLE: { + break; + } + case WECHAT_MSG_SEND_APP: { + break; + } + case WECHAT_MSG_START_HOOK: { + int port = GetIntParam(j_param, "port"); + wstring ip = GetWStringParam(j_param, "ip"); + string client_ip = Utils::WstringToUTF8(ip); + char ip_cstr[16]; + strcpy_s(ip_cstr, client_ip.c_str()); + int success = hooks::HookRecvMsg(ip_cstr, port); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_STOP_HOOK: { + int success = hooks::UnHookRecvMsg(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_MSG_START_IMAGE_HOOK: { + break; + } + case WECHAT_MSG_STOP_IMAGE_HOOK: { + break; + } + case WECHAT_MSG_START_VOICE_HOOK: { + break; + } + case WECHAT_MSG_STOP_VOICE_HOOK: { + break; + } + case WECHAT_CONTACT_GET_LIST: { + break; + } + case WECHAT_CONTACT_CHECK_STATUS: { + break; + } + case WECHAT_CONTACT_DEL: { + break; + } + case WECHAT_CONTACT_SEARCH_BY_CACHE: { + break; + } + case WECHAT_CONTACT_SEARCH_BY_NET: { + wstring keyword = GetWStringParam(j_param, "keyword"); + UserInfo *user = nullptr; + int success = g_context.misc_mgr->SearchContactNetScene(WS2LPWS(keyword), &user); + json ret_data = {{"code", success}, {"result", "OK"}}; + if (user) { + json info = { + {"bigImage", Utils::WCharToUTF8(user->big_image)}, + {"smallImage", Utils::WCharToUTF8(user->small_image)}, + {"city", Utils::WCharToUTF8(user->city)}, + {"nation", Utils::WCharToUTF8(user->nation)}, + {"nickname", Utils::WCharToUTF8(user->nickname)}, + {"province", Utils::WCharToUTF8(user->province)}, + {"sex", user->sex}, + {"signature", Utils::WCharToUTF8(user->signature)}, + {"v2", Utils::WCharToUTF8(user->v2)}, + {"v3", Utils::WCharToUTF8(user->v3)}, + }; + ret_data["userInfo"] = info; + } + ret = ret_data.dump(); + break; + } + case WECHAT_CONTACT_ADD_BY_WXID: { + // wstring user_id = GetWStringParam(j_param, "wxid"); + // wstring msg = GetWStringParam(j_param, "msg"); + // int success = g_context.contact_mgr->AddFriendByWxid(WS2LPWS(user_id),WS2LPWS(msg)); + // json ret_data = {{"code", success}, {"result", "OK"}}; + // ret = ret_data.dump(); + break; + } + case WECHAT_CONTACT_ADD_BY_V3: { + break; + } + case WECHAT_CONTACT_ADD_BY_PUBLIC_ID: { + break; + } + case WECHAT_CONTACT_VERIFY_APPLY: { + break; + } + case WECHAT_CONTACT_EDIT_REMARK: { + break; + } + case WECHAT_CHATROOM_GET_MEMBER_LIST: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + ChatRoomInner out{0}; + int success = g_context.chat_room_mgr->GetMemberFromChatRoom(WS2LPWS(room_id), out); + json ret_data = {{"code", success}, {"result", "OK"}}; + if (success) { + json member_info = { + {"admin", Utils::WstringToUTF8(out.admin)}, + {"chatRoomId", Utils::WstringToUTF8(out.chat_room)}, + {"members", out.members}, + }; + ret_data["data"] = member_info; + } + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_GET_MEMBER_NICKNAME: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + wstring member_id = GetWStringParam(j_param, "memberId"); + + wstring nickname =g_context.chat_room_mgr->GetChatRoomMemberNickname( + WS2LPWS(room_id), WS2LPWS(member_id)); + json ret_data = {{"code", 1}, + {"result", "OK"}, + {"nickname", Utils::WstringToUTF8(nickname)}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_DEL_MEMBER: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + vector wxids = getArrayParam(j_param, "memberIds"); + vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++) { + wxid_list.push_back(WS2LPWS(wxids[i])); + } + int success = g_context.chat_room_mgr->DelMemberFromChatRoom( + WS2LPWS(room_id), wxid_list.data(), wxid_list.size()); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_ADD_MEMBER: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + vector wxids = getArrayParam(j_param, "memberIds"); + vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++) { + wxid_list.push_back(WS2LPWS(wxids[i])); + } + int success = g_context.chat_room_mgr->AddMemberToChatRoom( + WS2LPWS(room_id), wxid_list.data(), wxid_list.size()); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CHATROOM_SET_ANNOUNCEMENT: { + break; + } + case WECHAT_CHATROOM_SET_CHATROOM_NAME: { + break; + } + case WECHAT_CHATROOM_SET_SELF_NICKNAME: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + wstring wxid = GetWStringParam(j_param, "wxid"); + wstring nick = GetWStringParam(j_param, "nickName"); + int success = g_context.chat_room_mgr->ModChatRoomMemberNickName( + WS2LPWS(room_id), WS2LPWS(wxid), WS2LPWS(nick)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_DATABASE_GET_HANDLES: { + vector v_ptr = DB::GetInstance().GetDbHandles(); + json ret_data = {{"data", json::array()}, {"result", "OK"}}; + for (unsigned int i = 0; i < v_ptr.size(); i++) { + json db_info; + db_info["tables"] = json::array(); + DatabaseInfo *db = reinterpret_cast(v_ptr[i]); + db_info["handle"] = db->handle; + wstring dbname(db->db_name); + db_info["databaseName"] = Utils::WstringToUTF8(dbname); + for (auto table : db->tables) { + json table_info = {{"name", table.name}, + {"tableName", table.table_name}, + {"sql", table.sql}, + {"rootpage", table.rootpage}}; + db_info["tables"].push_back(table_info); + } + ret_data["data"].push_back(db_info); + } + ret = ret_data.dump(); + break; + } + case WECHAT_DATABASE_BACKUP: { + break; + } + case WECHAT_DATABASE_QUERY: { + DWORD db_handle = GetIntParam(j_param, "dbHandle"); + wstring sql = GetWStringParam(j_param, "sql"); + string sql_str = Utils::WstringToUTF8(sql); + vector> items; + int success = DB::GetInstance().Select(db_handle, sql_str.c_str(), items); + json ret_data = { + {"data", json::array()}, {"code", success}, {"result", "OK"}}; + if (success == 0) { + ret = ret_data.dump(); + break; + } + for (auto it : items) { + json temp_arr = json::array(); + for (size_t i = 0; i < it.size(); i++) { + temp_arr.push_back(it[i]); + } + ret_data["data"].push_back(temp_arr); + } + ret = ret_data.dump(); + break; + } + case WECHAT_SET_VERSION: { + break; + } + case WECHAT_LOG_START_HOOK: { + int success = hooks::HookLog(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_LOG_STOP_HOOK: { + int success = hooks::UnHookLog(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_BROWSER_OPEN_WITH_URL: { + break; + } + case WECHAT_GET_PUBLIC_MSG: { + break; + } + case WECHAT_MSG_FORWARD_MESSAGE: { + wstring wxid = GetWStringParam(j_param, "wxid"); + ULONG64 msgid = GetULong64Param(j_param, "msgid"); + int success =g_context.send_mgr->ForwardMsg(WS2LPWS(wxid), msgid); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_QRCODE_IMAGE: { + break; + } + case WECHAT_GET_A8KEY: { + break; + } + case WECHAT_MSG_SEND_XML: { + break; + } + case WECHAT_LOGOUT: { + int success = g_context.account_mgr->Logout(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_TRANSFER: { + wstring wxid = GetWStringParam(j_param, "wxid"); + wstring transcationid = GetWStringParam(j_param, "transcationId"); + wstring transferid = GetWStringParam(j_param, "transferId"); + BOOL response =g_context.misc_mgr->DoConfirmReceipt( + WS2LPWS(wxid), WS2LPWS(transcationid), WS2LPWS(transferid)); + json ret_data = {{"msg", response}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_CONTACT_ALL: { + vector vec; + int success = g_context.contact_mgr->GetAllContact(vec); + json ret_data = { + {"data", json::array()}, {"code", success}, {"result", "OK"}}; + + for (unsigned int i = 0; i < vec.size(); i++) { + json item = { + {"customAccount", + vec[i].custom_account.length > 0 + ? vec[i].custom_account.ptr != nullptr + ? Utils::WCharToUTF8(vec[i].custom_account.ptr) + : string() + : string()}, + {"delFlag", vec[i].del_flag}, + {"userName", vec[i].encrypt_name.length > 0 + ? vec[i].encrypt_name.ptr != nullptr + ? Utils::WCharToUTF8(vec[i].encrypt_name.ptr) + : string() + : string()}, + {"type", vec[i].type}, + {"verifyFlag", vec[i].verify_flag}, + {"verifyFlag", vec[i].verify_flag}, + {"wxid", vec[i].wxid.length > 0 + ? Utils::WCharToUTF8(vec[i].wxid.ptr) + : string()}, + }; + ret_data["data"].push_back(item); + } + ret = ret_data.dump(); + break; + } + case WECHAT_GET_CHATROOM_INFO: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + ChatRoomInfoInner chat_room_detail{0}; + int success = g_context.chat_room_mgr->GetChatRoomDetailInfo(WS2LPWS(room_id), + chat_room_detail); + json ret_data = {{"code", success}, {"result", "OK"}}; + if (!success) { + break; + } + json detail = { + {"chatRoomId", + chat_room_detail.chat_room_id.length > 0 + ? Utils::WCharToUTF8(chat_room_detail.chat_room_id.ptr) + : string()}, + {"notice", chat_room_detail.notice.length > 0 + ? Utils::WCharToUTF8(chat_room_detail.notice.ptr) + : string()}, + {"admin", chat_room_detail.admin.length > 0 + ? Utils::WCharToUTF8(chat_room_detail.admin.ptr) + : string()}, + {"xml", chat_room_detail.xml.length > 0 + ? Utils::WCharToUTF8(chat_room_detail.xml.ptr) + : string()}, + }; + ret_data["data"] = detail; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_IMG_BY_NAME: { + wstring image_path = GetWStringParam( j_param, "imagePath"); + wstring save_path = GetWStringParam( j_param, "savePath"); + int success = + g_context.misc_mgr->GetImgByName(WS2LPWS(image_path),WS2LPWS(save_path)); json + ret_data = {{"code", success}, {"result", "OK"}}; ret = + ret_data.dump(); + break; + } + case WECHAT_DO_OCR: { + // wstring image_path = GetWStringParam(j_param, "imagePath"); + // string text(""); + // int success = g_context.misc_mgr->DoOCRTask(WS2LPWS(image_path), text); + // json ret_data = {{"code", success}, {"result", "OK"}, {"text", text}}; + // ret = ret_data.dump(); + break; + } + case WECHAT_SEND_PAT_MSG: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + wstring wxid = GetWStringParam(j_param, "wxid"); + int success = g_context.misc_mgr->SendPatMsg(WS2LPWS(room_id), WS2LPWS(wxid)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_SET_TOP_MSG: { + wstring wxid = GetWStringParam(j_param, "wxid"); + ULONG64 msgid = GetULong64Param(j_param, "msgid"); + int success = g_context.chat_room_mgr->SetTopMsg(WS2LPWS(wxid), msgid); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_REMOVE_TOP_MSG: { + wstring room_id = GetWStringParam(j_param, "chatRoomId"); + ULONG64 msgid = GetULong64Param(j_param, "msgid"); + int success = g_context.chat_room_mgr->RemoveTopMsg(WS2LPWS(room_id), msgid); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_SNS_GET_FIRST_PAGE: { + int success = g_context.sns_mgr->GetFirstPage(); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_SNS_GET_NEXT_PAGE: { + ULONG64 snsid = GetULong64Param(j_param, "snsId"); + int success = g_context.sns_mgr->GetNextPage(snsid); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_CONTACT_NAME: { + wstring pri_id = GetWStringParam(j_param, "id"); + wstring name = g_context.contact_mgr->GetContactOrChatRoomNickname(WS2LPWS(pri_id)); + json ret_data = { + {"code", 1}, {"result", "OK"}, {"name", Utils::WstringToUTF8(name)}}; + ret = ret_data.dump(); + break; + } + case WECHAT_ATTACH_DOWNLOAD: { + ULONG64 msg_id = GetULong64Param(j_param, "msgId"); + int success = g_context.misc_mgr->DoDownloadTask(msg_id); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + case WECHAT_GET_VOICE: { + ULONG64 msg_id = GetULong64Param(j_param, "msgId"); + wstring voice_dir = GetWStringParam(j_param, "voiceDir"); + int success = g_context.misc_mgr->GetVoice(msg_id, WS2LPWS(voice_dir)); + json ret_data = {{"code", success}, {"result", "OK"}}; + ret = ret_data.dump(); + break; + } + default: + break; + } + + return ret; +} + +HttpHandler::HttpHandler() {} +HttpHandler::~HttpHandler() {} +void HttpHandler::HandlerRequest(struct mg_connection *c, void *ev_data) { + struct mg_http_message *hm = (struct mg_http_message *)ev_data; + string ret = R"({"result":"OK"})"; + if (mg_http_match_uri(hm, "/api/")) { + try { + ret = Dispatch(c, hm); + } catch (json::exception &e) { + json res = {{"result", "ERROR"}, {"msg", e.what()}}; + ret = res.dump(); + } + if (ret != "") { + mg_http_reply(c, 200, "", ret.c_str(), 0, 0); + } + } else { + mg_http_reply(c, 500, NULL, "%s", "Invalid URI"); + } +} + +} // namespace wxhelper \ No newline at end of file diff --git a/src/http_handler.h b/src/http_handler.h new file mode 100644 index 0000000..828d678 --- /dev/null +++ b/src/http_handler.h @@ -0,0 +1,17 @@ +#ifndef WXHELPER_HTTP_HANDLER_H_ +#define WXHELPER_HTTP_HANDLER_H_ +#include "handler.h" +#include "mongoose.h" +#include +#include +#include +namespace wxhelper { +class HttpHandler : public Handler { + public: + HttpHandler(); + ~HttpHandler(); + void HandlerRequest(struct mg_connection *c, void *ev_data); +}; + +} // namespace wxhelper +#endif diff --git a/src/http_server.cc b/src/http_server.cc index 9e57bc1..0a839fd 100644 --- a/src/http_server.cc +++ b/src/http_server.cc @@ -1,10 +1,10 @@ +#include "pch.h" #include "http_server.h" #include #include "api_route.h" -#include "common.h" -#include "pch.h" + #pragma comment(lib, "ws2_32.lib") using namespace std; using namespace nlohmann; @@ -33,12 +33,12 @@ bool HttpServer::HttpStart() { return true; } #ifdef _DEBUG - CreateConsole(); + Utils::CreateConsole(); #endif running_ = true; - CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartHttpServer, NULL, + thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)StartHttpServer, NULL, NULL, 0); - return false; + return true; } void HttpServer::StartHttpServer() { @@ -81,6 +81,20 @@ void HttpServer::HandleWebsocketRequest(struct mg_connection *c, mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT); } -bool HttpServer::HttpClose() { return false; } +bool HttpServer::HttpClose() { + if (!running_) { + return true; + } + #ifdef _DEBUG + Utils::CloseConsole(); + #endif + running_ = false; + if (thread_) { + WaitForSingleObject(thread_, -1); + CloseHandle(thread_); + thread_ = NULL; + } + return true; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/http_server.h b/src/http_server.h index 89df650..b4b170c 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -2,34 +2,33 @@ #define WXHELPER_HTTP_SERVER_H_ #include + #include "http_handler.h" namespace wxhelper { class HttpServer { - - public: - static HttpServer &GetInstance(); bool HttpStart(); bool HttpClose(); void Init(int port); - -private: - HttpServer(){} - HttpServer(const HttpServer &) = delete; - HttpServer &operator=(const HttpServer &) = delete; - ~HttpServer(); - static void StartHttpServer(); - static void EventHandler(struct mg_connection *c, int ev, void *ev_data, void *fn_data); - void HandleHttpRequest(struct mg_connection *c, void *ev_data); - void HandleWebsocketRequest(struct mg_connection *c, void *ev_data); + private: + HttpServer(){}; + HttpServer(const HttpServer &) = delete; + HttpServer &operator=(const HttpServer &) = delete; + ~HttpServer(); + static void StartHttpServer(); + static void EventHandler(struct mg_connection *c, int ev, void *ev_data, + void *fn_data); + void HandleHttpRequest(struct mg_connection *c, void *ev_data); + void HandleWebsocketRequest(struct mg_connection *c, void *ev_data); private: int port_; bool running_; struct mg_mgr mgr_; - HttpHandler* http_handler_; + HttpHandler *http_handler_; + HANDLE thread_; }; } // namespace wxhelper diff --git a/src/log.cc b/src/log.cc new file mode 100644 index 0000000..77841a8 --- /dev/null +++ b/src/log.cc @@ -0,0 +1,37 @@ +#include "log.h" + +#include "easylogging++.h" +INITIALIZE_EASYLOGGINGPP +namespace wxhelper { +Log::Log(/* args */) {} + +Log::~Log() {} + +void Log::initialize() { + + el::Configurations conf; + // 启用日志 + conf.setGlobally(el::ConfigurationType::Enabled, "true"); + // 设置日志文件目录以及文件名 + conf.setGlobally(el::ConfigurationType::Filename, + "log\\log_%datetime{%Y%M%d %H%m%s}.log"); + // 设置日志文件最大文件大小 + conf.setGlobally(el::ConfigurationType::MaxLogFileSize, "20971520"); + // 是否写入文件 + conf.setGlobally(el::ConfigurationType::ToFile, "true"); + // 是否输出控制台 + conf.setGlobally(el::ConfigurationType::ToStandardOutput, "true"); + // 设置日志输出格式 + conf.setGlobally(el::ConfigurationType::Format, + "[%datetime] [%thread] [%loc] [%level] : %msg"); + // 设置日志文件写入周期,如下每100条刷新到输出流中 + #ifdef _DEBUG + conf.setGlobally(el::ConfigurationType::LogFlushThreshold, "1"); + #else + conf.setGlobally(el::ConfigurationType::LogFlushThreshold, "100"); + #endif + // 设置配置文件 + el::Loggers::reconfigureAllLoggers(conf); +} + +} // namespace wxhelper \ No newline at end of file diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..ebca51a --- /dev/null +++ b/src/log.h @@ -0,0 +1,18 @@ +#ifndef WXHELPER_LOG_H_ +#define WXHELPER_LOG_H_ +namespace wxhelper{ + class Log + { + private: + /* data */ + public: + Log(/* args */); + ~Log(); + void initialize(); + }; + + + +} + +#endif \ No newline at end of file diff --git a/src/misc_mgr.cc b/src/misc_mgr.cc new file mode 100644 index 0000000..3aa02b9 --- /dev/null +++ b/src/misc_mgr.cc @@ -0,0 +1,436 @@ +#include "misc_mgr.h" + +#include "pch.h" +#include "wechat_function.h" +#include "base64.h" +#include "db.h" +#include "hooks.h" +#include "easylogging++.h" +#define BUFSIZE 1024 + +#define JPEG0 0xFF +#define JPEG1 0xD8 +#define JPEG2 0xFF +#define PNG0 0x89 +#define PNG1 0x50 +#define PNG2 0x4E +#define BMP0 0x42 +#define BMP1 0x4D +#define GIF0 0x47 +#define GIF1 0x49 +#define GIF2 0x46 +using namespace std; +namespace wxhelper { +MiscMgr::MiscMgr(DWORD base) : BaseMgr::BaseMgr(base) {} +MiscMgr::~MiscMgr() {} +int MiscMgr::SendPatMsg(wchar_t *chat_room_id, wchar_t *wxid) { + int success = -1; + WeChatString chat_room(chat_room_id); + WeChatString self_wxid(wxid); + DWORD get_pat_mgr_addr = base_addr_ + WX_PAT_MGR_OFFSET; + DWORD send_pat_msg_addr = base_addr_ + WX_SEND_PAT_MSG_OFFSET; + DWORD ret_addr = base_addr_ + WX_RET_OFFSET; + __asm { + PUSHAD + CALL get_pat_mgr_addr + PUSH ret_addr + PUSH 0x0 + PUSH EAX + LEA ECX,chat_room + LEA EDX,self_wxid + CALL send_pat_msg_addr + ADD ESP,0xc + MOVZX EAX,AL + MOV success,EAX + POPAD + } + return success; +} + +int MiscMgr::DoOCRTask(wchar_t *img_path, std::string &result) { + int success = -1; + WeChatString path(img_path); + WeChatString null_obj = {0}; + WeChatString ocr_result = {0}; + DWORD ocr_manager_addr = base_addr_ + WX_OCR_MANAGER_OFFSET; + DWORD do_ocr_task_addr = base_addr_ + WX_DO_OCR_TASK_OFFSET; + DWORD init_addr = base_addr_ + WX_INIT_OBJ_OFFSET; + DWORD flag = 0; + __asm { + PUSHAD + PUSHFD + LEA ECX,ocr_result + CALL init_addr + CALL ocr_manager_addr + LEA ECX,null_obj + PUSH ECX + LEA ECX,flag + PUSH ECX + LEA ECX,ocr_result + PUSH ECX + PUSH 0x0 + LEA ECX,path + PUSH ECX + MOV ECX,EAX + CALL do_ocr_task_addr + MOV success,EAX + POPFD + POPAD + } + + if (success == 0) { + DWORD addr = (DWORD)&ocr_result; + DWORD ptr = *(DWORD *)addr; + DWORD num = *(DWORD *)(addr + 0x4); + if (num <= 0) { + return success; + } + + DWORD header = *(DWORD *)ptr; + for (unsigned int i = 0; i < num - 1; i++) { + DWORD content = *(DWORD *)header; + result += Utils::WstringToUTF8(READ_WSTRING(content, 0x14)); + + header = content; + } + } + return success; +} + +int MiscMgr::DoConfirmReceipt(wchar_t *wxid, wchar_t *transcationid, + wchar_t *transferid) { + int success = -1; + WeChatString recv_id(wxid); + WeChatString transcation_id(transcationid); + WeChatString transfer_id(transferid); + char pay_info[0x134] = {0}; + DWORD new_pay_info_addr = base_addr_ + WX_NEW_WCPAYINFO_OFFSET; + DWORD free_pay_info_addr = base_addr_ + WX_FREE_WCPAYINFO_OFFSET; + DWORD do_confirm_addr = base_addr_ + WX_CONFIRM_RECEIPT_OFFSET; + __asm { + PUSHAD + LEA ECX,pay_info + CALL new_pay_info_addr + MOV dword ptr [pay_info + 0x4], 0x1 + MOV dword ptr [pay_info + 0x4C], 0x1 + POPAD + } + memcpy(&pay_info[0x1c], &transcation_id, sizeof(transcation_id)); + memcpy(&pay_info[0x38], &transfer_id, sizeof(transfer_id)); + + __asm { + PUSHAD + PUSH 0x1 + SUB ESP,0x8 + LEA EDX,recv_id + LEA ECX,pay_info + CALL do_confirm_addr + MOV success,EAX + ADD ESP,0xC + PUSH 0x0 + LEA ECX,pay_info + CALL free_pay_info_addr + POPAD + } + + return success; +} + +int MiscMgr::DoDownloadTask(ULONG64 msg_id) { + int success = -1; + int db_index = 0; + int local_id = DB::GetInstance().GetLocalIdByMsgId(msg_id, db_index); + if (local_id < 1) { + return -2; + } + + char chat_msg[0x2D8] = {0}; + DWORD new_chat_msg_addr = base_addr_ + WX_NEW_CHAT_MSG_OFFSET; + DWORD get_chat_mgr_addr = base_addr_ + WX_CHAT_MGR_OFFSET; + DWORD pre_download_mgr_addr = base_addr_ + WX_GET_PRE_DOWNLOAD_MGR_OFFSET; + DWORD push_attach_task_addr = base_addr_ + WX_PUSH_ATTACH_TASK_OFFSET; + DWORD free_addr = base_addr_ + WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET; + DWORD get_by_local_Id_addr = base_addr_ + WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET; + DWORD get_current_data_path_addr = base_addr_ + WX_GET_CURRENT_DATA_PATH_OFFSET; + DWORD free_app_msg_info_addr = base_addr_ + WX_FREE_APP_MSG_INFO_OFFSET; + DWORD push_thumb_task_addr = base_addr_ + WX_PUSH_THUMB_TASK_OFFSET; + DWORD video_mgr_addr = base_addr_ + WX_VIDEO_MGR_OFFSET; + DWORD download_video_image_addr = base_addr_ + WX_VIDEO_MGR_OFFSET; + + WeChatString current_data_path; + + __asm { + PUSHAD + PUSHFD + LEA ECX,current_data_path + CALL get_current_data_path_addr + + LEA ECX,chat_msg + CALL new_chat_msg_addr + + CALL get_chat_mgr_addr + PUSH dword ptr [db_index] + LEA ECX,chat_msg + PUSH dword ptr [local_id] + CALL get_by_local_Id_addr + ADD ESP,0x8 + POPFD + POPAD + } + wstring save_path = L""; + wstring thumb_path = L""; + if (current_data_path.length > 0) { + save_path += current_data_path.ptr; + save_path += L"wxhelper"; + } else { + return -1; + } + + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + DWORD type = *(DWORD *)(chat_msg + 0x38); + wchar_t *content = *(wchar_t **)(chat_msg + 0x70); + + switch (type) { + case 0x3: { + save_path += L"\\image"; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + save_path = save_path +L"\\"+ to_wstring(msg_id) + L".png"; + break; + } + case 0x3E: + case 0x2B: { + save_path += L"\\video"; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + thumb_path = save_path + L"\\"+ to_wstring(msg_id) + L".jpg"; + save_path = save_path + L"\\"+ to_wstring(msg_id) + L".mp4"; + + break; + } + case 0x31: { + save_path += L"\\file"; + wcout << save_path << endl; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + char xml_app_msg[0xC80] = {0}; + DWORD new_app_msg_addr = base_addr_ + WX_APP_MSG_INFO_OFFSET; + DWORD get_xml_addr = base_addr_ + WX_GET_APP_MSG_XML_OFFSET; + WeChatString w_content(content); + + __asm { + PUSHAD + PUSHFD + LEA ECX,xml_app_msg + CALL new_app_msg_addr + PUSH 0x1 + LEA EAX,w_content + PUSH EAX + LEA ECX,xml_app_msg + CALL get_xml_addr + MOV success,EAX + LEA ECX,xml_app_msg + CALL free_app_msg_info_addr + POPFD + POPAD + } + if (success != 1) { + return -4; + } + WeChatString *file_name = (WeChatString *)((DWORD)xml_app_msg + 0x44); + save_path = save_path +L"\\" + to_wstring(msg_id) + L"_" + + wstring(file_name->ptr, file_name->length); + break; + } + default: + break; + } + WeChatString w_save_path(save_path); + WeChatString w_thumb_path(thumb_path); + int temp =1; + memcpy(&chat_msg[0x19C], &w_thumb_path, sizeof(w_thumb_path)); + memcpy(&chat_msg[0x1B0], &w_save_path, sizeof(w_save_path)); + memcpy(&chat_msg[0x29C], &temp, sizeof(temp)); + // note: the image has been downloaded and will not be downloaded again + // use low-level method + // this function does not work, need to modify chatmsg. + // if (type == 0x3E || type == 0x2B){ + // __asm{ + // PUSHAD + // PUSHFD + // CALL video_mgr_addr + // LEA ECX,chat_msg + // PUSH ECX + // MOV ECX,EAX + // CALL download_video_image_addr + // POPFD + // POPAD + // } + // } + + __asm { + PUSHAD + PUSHFD + CALL pre_download_mgr_addr + PUSH 0x1 + PUSH 0x0 + LEA ECX,chat_msg + PUSH ECX + MOV ECX,EAX + CALL push_attach_task_addr + MOV success,EAX + LEA ECX,chat_msg + PUSH 0x0 + CALL free_addr + POPFD + POPAD + } + + return success; +} + +int MiscMgr::GetVoice(ULONG64 msg_id, wchar_t *dir) { + int success = -1; + string buff = DB::GetInstance().GetVoiceBuffByMsgId(msg_id); + if (buff.size() == 0) { + success = 0; + return success; + } + wstring save_path = wstring(dir); + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + success = -2; + return success; + } + save_path = save_path + L"\\" + to_wstring(msg_id) + L".amr"; + HANDLE file_handle = CreateFileW(save_path.c_str(), GENERIC_ALL, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + #ifdef _DEBUG + wcout <<" save_path =" < 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i]^=key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + + do { + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + if (key > 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i] ^= key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + } + } while (bytes_read == BUFSIZE); + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 1; +} + +int MiscMgr::SearchContactNetScene(wchar_t *keyword,UserInfo ** user) { + int success = -1; + hooks::HookSearchContact(); + hooks::DeleteUserInfoCache(); + DWORD search_contact_mgr_addr = base_addr_ + WX_SEARCH_CONTACT_MGR_OFFSET; + DWORD search_contact_addr = base_addr_ + WX_SEARCH_CONTACT_OFFSET; + WeChatString key(keyword); + + __asm { + pushad; + pushfd; + call search_contact_mgr_addr; + lea ebx, key; + push ebx; + mov ecx, eax; + call search_contact_addr; + popfd; + popad; + } + success = 1; + while (hooks::userinfo.error_code == 1 && hooks::user_info_flag_) { + Sleep(20); + } + if (hooks::userinfo.error_code == 0) { + while (hooks::userinfo.over == false && hooks::user_info_flag_) { + Sleep(20); + } + } + *user= &hooks::userinfo; + LOG(INFO)<<"user:" < +#include "Windows.h" +#include "base_mgr.h" +#include "wechat_function.h" +namespace wxhelper { +class MiscMgr :public BaseMgr{ + public: + MiscMgr(DWORD base); + ~MiscMgr(); + int SendPatMsg(wchar_t* chat_room_id, wchar_t* wxid); + int DoOCRTask(wchar_t* img_path, std::string& result); + int DoConfirmReceipt(wchar_t* wxid, wchar_t* transcationid, + wchar_t* transferid); + int DoDownloadTask(ULONG64 msg_id); + + int GetVoice(ULONG64 msg_id, wchar_t* dir); + int GetImgByName(wchar_t* file_path,wchar_t* save_dir); + int SearchContactNetScene(wchar_t *keyword,UserInfo ** user); +}; +} // namespace wxhelper + +#endif \ No newline at end of file diff --git a/src/ocr.cc b/src/ocr.cc deleted file mode 100644 index 4ac88d4..0000000 --- a/src/ocr.cc +++ /dev/null @@ -1,64 +0,0 @@ -#include "pch.h" -#include "ocr.h" - -#include "common.h" -#include "wechat_data.h" - -#define WX_INIT_OBJ_OFFSET 0x7a98f0 -#define WX_OCR_MANAGER_OFFSET 0x7ae470 -#define WX_DO_OCR_TASK_OFFSET 0x13230c0 -using namespace std; -int DoOCRTask(wchar_t *img_path, std::string &result) { - int success = -1; - WeChatString path(img_path); - WeChatString null_obj = {0}; - WeChatString ocr_result = {0}; - DWORD base = GetWeChatWinBase(); - DWORD ocr_manager_addr = base + WX_OCR_MANAGER_OFFSET; - DWORD do_ocr_task_addr = base + WX_DO_OCR_TASK_OFFSET; - DWORD init_addr = base + WX_INIT_OBJ_OFFSET; - DWORD flag = 0; - __asm { - PUSHAD - PUSHFD - LEA ECX,ocr_result - CALL init_addr - CALL ocr_manager_addr - LEA ECX,null_obj - PUSH ECX - LEA ECX,flag - PUSH ECX - LEA ECX,ocr_result - PUSH ECX - PUSH 0x0 - LEA ECX,path - PUSH ECX - MOV ECX,EAX - CALL do_ocr_task_addr - MOV success,EAX - POPFD - POPAD - } - - if (success == 0) { - DWORD addr = (DWORD)&ocr_result; - DWORD ptr = *(DWORD *)addr; - DWORD num = *(DWORD *)(addr + 0x4); - if (num <= 0) { - return success; - } - - DWORD header = *(DWORD *)ptr; - for (unsigned int i = 0; i < num -1; i++) { - DWORD content = *(DWORD *)header; - result += unicode_to_utf8((wchar_t *)READ_WSTRING(content, 0x14).c_str()); - - header = content; - } -#ifdef _DEBUG - cout << "num:" << num << endl; - cout << "all:" << result << endl; -#endif - } - return success; -} \ No newline at end of file diff --git a/src/ocr.h b/src/ocr.h deleted file mode 100644 index b6e73aa..0000000 --- a/src/ocr.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef OCR_H_ -#define OCR_H_ -#include -int DoOCRTask(wchar_t* img_path,std::string &result); -#endif \ No newline at end of file diff --git a/src/pat.cc b/src/pat.cc deleted file mode 100644 index bd609a2..0000000 --- a/src/pat.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "pch.h" -#include "pat.h" - -#include "common.h" -#include "wechat_data.h" - -#define WX_PAT_MGR_OFFSET 0x931730 -#define WX_SEND_PAT_MSG_OFFSET 0x1421940 -#define WX_RET_OFFSET 0x1D58751 - -int SendPatMsg(wchar_t* chat_room_id, wchar_t* wxid) { - int success = -1; - WeChatString chat_room(chat_room_id); - WeChatString self_wxid(wxid); - DWORD base = GetWeChatWinBase(); - DWORD get_pat_mgr_addr = base + WX_PAT_MGR_OFFSET; - DWORD send_pat_msg_addr = base + WX_SEND_PAT_MSG_OFFSET; - DWORD ret_addr = base + WX_RET_OFFSET; - __asm { - PUSHAD - CALL get_pat_mgr_addr - PUSH ret_addr - PUSH 0x0 - PUSH EAX - LEA ECX,chat_room - LEA EDX,self_wxid - CALL send_pat_msg_addr - ADD ESP,0xc - MOVZX EAX,AL - MOV success,EAX - POPAD - } - return success; -} \ No newline at end of file diff --git a/src/pat.h b/src/pat.h deleted file mode 100644 index 7893f9e..0000000 --- a/src/pat.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef PAT_H_ -#define PAT_H_ -int SendPatMsg(wchar_t* chat_room_id,wchar_t* wxid); -#endif \ No newline at end of file diff --git a/src/pch.h b/src/pch.h index 6e2a672..7dc3789 100644 --- a/src/pch.h +++ b/src/pch.h @@ -9,10 +9,14 @@ #include #include #include +#include #include #include #include #include +#include "Windows.h" +#include "utils.h" + #endif // PCH_H diff --git a/src/search_contact.cc b/src/search_contact.cc deleted file mode 100644 index 8eec925..0000000 --- a/src/search_contact.cc +++ /dev/null @@ -1,275 +0,0 @@ -#include "pch.h" -#include "search_contact.h" - -#include "common.h" - -#include "wechat_data.h" - -#define WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSeT 0xd94d1e -#define WX_SEARCH_CONTACT_ERROR_CODE_HOOK_NEXT_OFFSeT 0xed13a0 -#define WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSeT 0xa2a260 -#define WX_SEARCH_CONTACT_DETAIL_HOOK_NEXT_OFFSeT 0xa2a4c0 - - -#define WX_SEARCH_CONTACT_OFFSeT 0xc5bb00 -#define WX_SEARCH_CONTACT_MGR_OFFSeT 0xa0d550 - - - -static BOOL kSearchContactHooked = false; -static char kHookSearchContactErrcodeOldAsm[5] = {0}; -static char kHookUserInfoOldAsm[5] = {0}; -static DWORD kWeChatWinBase = GetWeChatWinBase(); - -static DWORD kHookSearchContactErrcodeNextAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_NEXT_OFFSeT; -static DWORD kHookSearchContactErrcodeAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSeT; -static DWORD kHookSearchContactErrcodeJmpBackAddr = - kHookSearchContactErrcodeAddr + 0x5; - -static DWORD kHookUserInfoNextAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_DETAIL_HOOK_NEXT_OFFSeT; -static DWORD kHookUserInfoAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSeT; -static DWORD kHookUserInfoJmpBackAddr = kHookUserInfoAddr + 0x5; - -static UserInfo userinfo; - -void SetErrorCode(int code) { userinfo.error_code = code; } - -void SetUserInfoDetail(DWORD address) { - DWORD length = *(DWORD *)(address + 0x8); - userinfo.keyword = new wchar_t[length + 1]; - userinfo.keyword_len = length; - if (length) { - memcpy(userinfo.keyword, (wchar_t *)(*(DWORD *)(address + 0x4)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.keyword, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x1C); - userinfo.v3 = new wchar_t[length + 1]; - userinfo.v3_len = length; - if (length) { - memcpy(userinfo.v3, (wchar_t *)(*(DWORD *)(address + 0x18)), - (length + 1) * sizeof(wchar_t)); - }else { - ZeroMemory(userinfo.v3, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x30); - userinfo.big_image = new wchar_t[length + 1]; - userinfo.big_image_len = length; - if (length) { - memcpy(userinfo.big_image, (wchar_t *)(*(DWORD *)(address + 0x2C)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.big_image, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0xC8); - userinfo.nickname = new wchar_t[length + 1]; - userinfo.nickname_len = length; - if (length) { - memcpy(userinfo.nickname, (wchar_t *)(*(DWORD *)(address + 0xC4)), - (length + 1) * sizeof(wchar_t)); - }else { - ZeroMemory(userinfo.nickname, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x108); - userinfo.v2 = new wchar_t[length + 1]; - userinfo.v2_len = length; - if (length) { - memcpy(userinfo.v2, (wchar_t *)(*(DWORD *)(address + 0x104)), - (length + 1) * sizeof(wchar_t)); - }else { - ZeroMemory(userinfo.v2, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x16C); - userinfo.small_image = new wchar_t[length + 1]; - userinfo.small_image_len = length; - if (length) { - memcpy(userinfo.small_image, (wchar_t *)(*(DWORD *)(address + 0x168)), - (length + 1) * sizeof(wchar_t)); - }else { - ZeroMemory(userinfo.small_image, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x1F8); - userinfo.signature = new wchar_t[length + 1]; - userinfo.signature_len = length; - if (length) { - memcpy(userinfo.signature, (wchar_t *)(*(DWORD *)(address + 0x1F4)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.signature, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x20C); - userinfo.nation = new wchar_t[length + 1]; - userinfo.nation_len = length; - if (length) { - memcpy(userinfo.nation, (wchar_t *)(*(DWORD *)(address + 0x208)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.nation, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x220); - userinfo.province = new wchar_t[length + 1]; - userinfo.province_len = length; - if (length) { - memcpy(userinfo.province, (wchar_t *)(*(DWORD *)(address + 0x21C)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.province, (length + 1) * sizeof(wchar_t)); - } - - length = *(DWORD *)(address + 0x234); - userinfo.city = new wchar_t[length + 1]; - userinfo.city_len = length; - if (length) { - memcpy(userinfo.city, (wchar_t *)(*(DWORD *)(address + 0x230)), - (length + 1) * sizeof(wchar_t)); - } else { - ZeroMemory(userinfo.city, (length + 1) * sizeof(wchar_t)); - } - - userinfo.sex = *(DWORD *)(address + 0x1BC); - userinfo.over = true; -} - -static void DeleteUserInfoCache() { - if (userinfo.keyword) { - delete userinfo.keyword; - } - if (userinfo.v2) { - delete userinfo.v2; - } - if (userinfo.v3) { - delete userinfo.v3; - } - if (userinfo.nickname) { - delete userinfo.nickname; - } - if (userinfo.nation) { - delete userinfo.nation; - } - if (userinfo.province) { - delete userinfo.province; - } - if (userinfo.city) { - delete userinfo.city; - } - if (userinfo.signature) { - delete userinfo.signature; - } - if (userinfo.small_image) { - delete userinfo.small_image; - } - if (userinfo.big_image) { - delete userinfo.big_image; - } - ZeroMemory(&userinfo, sizeof(UserInfo)); - userinfo.error_code = 1; -} - -__declspec(naked) void HandleErrorCode() { - __asm { - pushad; - pushfd; - push edi; - call SetErrorCode; - add esp, 0x4; - popfd; - popad; - call kHookSearchContactErrcodeNextAddr; - jmp kHookSearchContactErrcodeJmpBackAddr; - } -} - -__declspec(naked) void HandleUserInfoDetail() { - __asm { - pushad; - pushfd; - push dword ptr [ebp + 0x14]; - call SetUserInfoDetail; - add esp, 0x4; - popfd; - popad; - call kHookUserInfoNextAddr; - jmp kHookUserInfoJmpBackAddr; - } -} - -int SearchContactNetScene(wchar_t *keyword,UserInfo ** user) { - int success = -1; - HookSearchContact(); - DeleteUserInfoCache(); - DWORD base = GetWeChatWinBase(); - DWORD search_contact_mgr_addr = base + WX_SEARCH_CONTACT_MGR_OFFSeT; - DWORD search_contact_addr = base + WX_SEARCH_CONTACT_OFFSeT; - WeChatString key(keyword); - - __asm { - pushad; - pushfd; - call search_contact_mgr_addr; - lea ebx, key; - push ebx; - mov ecx, eax; - call search_contact_addr; - popfd; - popad; - } - success = 1; - while (userinfo.error_code == 1 && kSearchContactHooked) { - Sleep(20); - } - if (userinfo.error_code == 0) { - while (userinfo.over == false && kSearchContactHooked) { - Sleep(20); - } - } - *user= &userinfo; - return success; -} - -int HookSearchContact() { - kWeChatWinBase = GetWeChatWinBase(); - if (!kWeChatWinBase) { - return -1; - } - if (kSearchContactHooked) { - return 2; - } - kHookSearchContactErrcodeNextAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_NEXT_OFFSeT; - kHookSearchContactErrcodeAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSeT; - kHookSearchContactErrcodeJmpBackAddr = kHookSearchContactErrcodeAddr + 0x5; - - kHookUserInfoNextAddr = - kWeChatWinBase + WX_SEARCH_CONTACT_DETAIL_HOOK_NEXT_OFFSeT; - kHookUserInfoAddr = kWeChatWinBase + WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSeT; - kHookUserInfoJmpBackAddr = kHookUserInfoAddr + 0x5; - HookAnyAddress(kHookSearchContactErrcodeAddr, - (LPVOID)HandleErrorCode, - kHookSearchContactErrcodeOldAsm); - HookAnyAddress(kHookUserInfoAddr, (LPVOID)HandleUserInfoDetail, kHookUserInfoOldAsm); - kSearchContactHooked = true; - return 1; -} - -int UnHookSearchContact() { - if (!kSearchContactHooked) return 2; - UnHookAnyAddress(kHookSearchContactErrcodeAddr, - kHookSearchContactErrcodeOldAsm); - UnHookAnyAddress(kHookUserInfoAddr, kHookUserInfoOldAsm); - kSearchContactHooked = false; - return 1; -} \ No newline at end of file diff --git a/src/search_contact.h b/src/search_contact.h deleted file mode 100644 index 5af1548..0000000 --- a/src/search_contact.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef SEARCH_CONTACT_H_ -#define SEARCH_CONTACT_H_ -#include "wechat_data.h" -int SearchContactNetScene(wchar_t *keyword,UserInfo ** user); - -int HookSearchContact(); -int UnHookSearchContact(); -#endif \ No newline at end of file diff --git a/src/self_info.h b/src/self_info.h deleted file mode 100644 index ebec883..0000000 --- a/src/self_info.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SELF_INFO_H_ -#define SELF_INFO_H_ -#include "wechat_data.h" -int GetSelfInfo(SelfInfoInner& out); - -int CheckLogin(); - -int Logout(); -#endif \ No newline at end of file diff --git a/src/send_file.cc b/src/send_file.cc deleted file mode 100644 index 258a650..0000000 --- a/src/send_file.cc +++ /dev/null @@ -1,67 +0,0 @@ -#include "pch.h" -#include "send_file.h" -#include "common.h" -#include "wechat_data.h" - -#define WX_APP_MSG_MGR_OFFSET 0x76ae20 -#define WX_SEND_FILE_OFFSET 0xb6d1f0 -#define WX_INIT_CHAT_MSG_OFFSET 0xf59e40 -#define WX_FREE_CHAT_MSG_OFFSET 0x756960 - -int SendFile(wchar_t *wxid, wchar_t *file_path){ - int success = 0; - WeChatString to_user(wxid); - WeChatString path(file_path); - char chat_msg[0x2D8] = {0}; - DWORD base = GetWeChatWinBase(); - DWORD app_msg_mgr_addr = base + WX_APP_MSG_MGR_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - DWORD send_file_addr = base + WX_SEND_FILE_OFFSET; - DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; - DWORD temp = 0; - WeChatString null_obj = {0}; - __asm{ - PUSHAD - PUSHFD - CALL app_msg_mgr_addr - SUB ESP,0x14 - MOV temp,EAX - LEA EAX,null_obj - MOV ECX,ESP - PUSH EAX - CALL init_chat_msg_addr - PUSH 0x0 - SUB ESP,0x14 - MOV EDI,ESP - MOV dword ptr [EDI],0 - MOV dword ptr [EDI + 0x4],0 - MOV dword ptr [EDI + 0x8],0 - MOV dword ptr [EDI + 0xc],0 - MOV dword ptr [EDI + 0x10],0 - SUB ESP,0x14 - LEA EAX,path - MOV ECX,ESP - PUSH EAX - CALL init_chat_msg_addr - SUB ESP,0x14 - LEA EAX,to_user - MOV ECX,ESP - PUSH EAX - CALL init_chat_msg_addr - MOV ECX,dword ptr [temp] - LEA EAX,chat_msg - PUSH EAX - CALL send_file_addr - MOV AL,byte ptr [eax + 0x38] - MOVZX EAX,AL - MOV success,EAX - LEA ECX,chat_msg - CALL free_msg_addr - POPFD - POPAD - } - if (success == 0x31){ - return 1; - } - return 0; -} \ No newline at end of file diff --git a/src/send_file.h b/src/send_file.h deleted file mode 100644 index 7436b4d..0000000 --- a/src/send_file.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef SEND_FILE_H_ -#define SEND_FILE_H_ - -int SendFile(wchar_t *wxid, wchar_t *file_path); -#endif \ No newline at end of file diff --git a/src/send_image.cc b/src/send_image.cc deleted file mode 100644 index f30ef47..0000000 --- a/src/send_image.cc +++ /dev/null @@ -1,46 +0,0 @@ -#include "pch.h" -#include "send_image.h" -#include "common.h" -#include "wechat_data.h" - -#define WX_SEND_IMAGE_OFFSET 0xce6640 -#define WX_SEND_MESSAGE_MGR_OFFSET 0x768140 -#define WX_INIT_CHAT_MSG_OFFSET 0xf59e40 -#define WX_FREE_CHAT_MSG_OFFSET 0x756960 -int SendImage(wchar_t *wxid, wchar_t *image_path){ - - int success = 0; - WeChatString to_user(wxid); - WeChatString path(image_path); - char chat_msg[0x2D8] ={0}; - DWORD base = GetWeChatWinBase(); - DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; - DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; - DWORD send_image_msg_addr = base + WX_SEND_IMAGE_OFFSET; - DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; - DWORD temp = 0; - WeChatString null_obj = {0}; - __asm{ - PUSHAD - CALL send_message_mgr_addr - SUB ESP,0x14 - MOV temp,EAX - LEA EAX,null_obj - MOV ECX,ESP - LEA EDI,path - PUSH EAX - CALL init_chat_msg_addr - MOV ECX,dword ptr [temp] - LEA EAX,to_user - PUSH EDI - PUSH EAX - LEA EAX,chat_msg - PUSH EAX - CALL send_image_msg_addr - MOV success,EAX - LEA ECX,chat_msg - CALL free_msg_addr - POPAD - } - return success; -} \ No newline at end of file diff --git a/src/send_image.h b/src/send_image.h deleted file mode 100644 index 0be9085..0000000 --- a/src/send_image.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef SEND_IMAGE_H_ -#define SEND_IMAGE_H_ - -int SendImage(wchar_t *wxid, wchar_t *image_path); -#endif \ No newline at end of file diff --git a/src/send_message_mgr.cc b/src/send_message_mgr.cc new file mode 100644 index 0000000..7a9b38e --- /dev/null +++ b/src/send_message_mgr.cc @@ -0,0 +1,178 @@ +#include "pch.h" +#include "send_message_mgr.h" + +#include "easylogging++.h" + +#include "wechat_function.h" +#include "db.h" + +namespace wxhelper { +SendMessageMgr::SendMessageMgr(DWORD base):BaseMgr(base) {} +SendMessageMgr::~SendMessageMgr() {} +int SendMessageMgr::SendText(wchar_t* wxid, wchar_t* msg) { + int success = -1; + WeChatString to_user(wxid); + WeChatString text_msg(msg); + wchar_t** msg_pptr = &text_msg.ptr; + DWORD base = Utils::GetWeChatWinBase(); + DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD send_text_msg_addr = base + WX_SEND_TEXT_OFFSET; + DWORD free_chat_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + char chat_msg[0x2D8] = {0}; + __asm { + PUSHAD + CALL send_message_mgr_addr + PUSH 0x0 + PUSH 0x0 + PUSH 0x0 + PUSH 0x1 + PUSH 0x0 + MOV EAX,msg_pptr + PUSH EAX + LEA EDX,to_user + LEA ECX,chat_msg + CALL send_text_msg_addr + MOV success,EAX + ADD ESP,0x18 + LEA ECX,chat_msg + CALL free_chat_msg_addr + POPAD + } + LOG_IF((success == -1), ERROR) << "SendText fail"; + return success; +} +int SendMessageMgr::SendAtText(wchar_t* chat_room_id, wchar_t** wxids, int len, + wchar_t* msg) { + int success = -1; + return success; +} +int SendMessageMgr::SendImage(wchar_t* wxid, wchar_t* image_path) { + int success = -1; + WeChatString to_user(wxid); + WeChatString path(image_path); + char chat_msg[0x2D8] = {0}; + DWORD base = Utils::GetWeChatWinBase(); + DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_image_msg_addr = base + WX_SEND_IMAGE_OFFSET; + DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD temp = 0; + WeChatString null_obj = {0}; + __asm { + PUSHAD + CALL send_message_mgr_addr + SUB ESP,0x14 + MOV temp,EAX + LEA EAX,null_obj + MOV ECX,ESP + LEA EDI,path + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,dword ptr [temp] + LEA EAX,to_user + PUSH EDI + PUSH EAX + LEA EAX,chat_msg + PUSH EAX + CALL send_image_msg_addr + MOV success,EAX + LEA ECX,chat_msg + CALL free_msg_addr + POPAD + } + LOG_IF((success == -1), ERROR) << "SendImage fail"; + return success; +} +int SendMessageMgr::SendFile(wchar_t* wxid, wchar_t* file_path) { + int success = -1; + WeChatString to_user(wxid); + WeChatString path(file_path); + char chat_msg[0x2D8] = {0}; + DWORD base = Utils::GetWeChatWinBase(); + DWORD app_msg_mgr_addr = base + WX_APP_MSG_MGR_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + DWORD send_file_addr = base + WX_SEND_FILE_OFFSET; + DWORD free_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; + DWORD temp = 0; + WeChatString null_obj = {0}; + __asm { + PUSHAD + PUSHFD + CALL app_msg_mgr_addr + SUB ESP,0x14 + MOV temp,EAX + LEA EAX,null_obj + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + PUSH 0x0 + SUB ESP,0x14 + MOV EDI,ESP + MOV dword ptr [EDI],0 + MOV dword ptr [EDI + 0x4],0 + MOV dword ptr [EDI + 0x8],0 + MOV dword ptr [EDI + 0xc],0 + MOV dword ptr [EDI + 0x10],0 + SUB ESP,0x14 + LEA EAX,path + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + SUB ESP,0x14 + LEA EAX,to_user + MOV ECX,ESP + PUSH EAX + CALL init_chat_msg_addr + MOV ECX,dword ptr [temp] + LEA EAX,chat_msg + PUSH EAX + CALL send_file_addr + MOV AL,byte ptr [eax + 0x38] + MOVZX EAX,AL + MOV success,EAX + LEA ECX,chat_msg + CALL free_msg_addr + POPFD + POPAD + } + if (success == 0x31) { + return 1; + } + LOG_IF((success == -1), ERROR) << "SendFile fail"; + return success; +} + +int SendMessageMgr::ForwardMsg(wchar_t* wxid, unsigned long long msgid) { + int success = 0; + + int db_index = 0; + int localid = DB::GetInstance().GetLocalIdByMsgId(msgid, db_index); + + if (localid == 0) return 0; + WeChatString to_user(wxid); + DWORD base = Utils::GetWeChatWinBase(); + DWORD forward_msg_addr = base + WX_FORWARD_MSG_OFFSET; + DWORD init_chat_msg_addr = base + WX_INIT_CHAT_MSG_OFFSET; + __asm { + PUSHAD + PUSHFD + MOV EDX, DWORD PTR [db_index] + PUSH EDX + MOV EAX, DWORD PTR [localid] + PUSH EAX + SUB ESP,0x14 + MOV ECX,ESP + LEA ESI,to_user + PUSH ESI + CALL init_chat_msg_addr + XOR ECX,ECX + CALL forward_msg_addr + MOVZX EAX,AL + MOV success,EAX + ADD ESP,0x1c + POPFD + POPAD + } + return success; +} +} // namespace wxhelper \ No newline at end of file diff --git a/src/send_message_mgr.h b/src/send_message_mgr.h new file mode 100644 index 0000000..7e34db5 --- /dev/null +++ b/src/send_message_mgr.h @@ -0,0 +1,18 @@ +#ifndef WXHELPER_SEND_MESSAGE_MGR_H_ +#define WXHELPER_SEND_MESSAGE_MGR_H_ +#include "base_mgr.h" +namespace wxhelper { +class SendMessageMgr:public BaseMgr { + public: + explicit SendMessageMgr(DWORD base); + ~SendMessageMgr(); + int SendText(wchar_t* wxid, wchar_t* msg); + int SendAtText(wchar_t* chat_room_id, wchar_t** wxids, int len, wchar_t* msg); + int SendImage(wchar_t *wxid, wchar_t *image_path); + int SendFile(wchar_t *wxid, wchar_t *file_path); + int ForwardMsg(wchar_t *wxid, unsigned long long msgid); + + private: +}; +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/send_text.cc b/src/send_text.cc deleted file mode 100644 index 96c349e..0000000 --- a/src/send_text.cc +++ /dev/null @@ -1,118 +0,0 @@ -#include "pch.h" -#include "send_text.h" - - -#include "common.h" -#include "wechat_data.h" -#include "contact.h" - -#define WX_SEND_TEXT_OFFSET 0xce6c80 - -#define WX_SEND_MESSAGE_MGR_OFFSET 0x768140 - -#define WX_FREE_CHAT_MSG_OFFSET 0x756960 -using namespace std; -/// @brief 发生文本消息 -/// @param wxid wxid -/// @param msg 文本消息 -/// @return 成功返回1 -int SendText(wchar_t* wxid, wchar_t* msg) { - int success = 0; - WeChatString to_user(wxid); - WeChatString text_msg(msg); - wchar_t **msg_pptr = &text_msg.ptr; - - DWORD base = GetWeChatWinBase(); - DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; - DWORD send_text_msg_addr = base + WX_SEND_TEXT_OFFSET; - DWORD free_chat_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; - char chat_msg[0x2D8] ={0}; - __asm{ - PUSHAD - CALL send_message_mgr_addr - PUSH 0x0 - PUSH 0x0 - PUSH 0x0 - PUSH 0x1 - PUSH 0x0 - MOV EAX,msg_pptr - PUSH EAX - LEA EDX,to_user - LEA ECX,chat_msg - CALL send_text_msg_addr - MOV success,EAX - ADD ESP,0x18 - LEA ECX,chat_msg - CALL free_chat_msg_addr - POPAD - } - return success; -} - - - -int SendAtText(wchar_t* chat_room_id,wchar_t** wxids,int len,wchar_t* msg){ - int success = -1; - WeChatString * at_users = new WeChatString[len+1]; - wstring at_msg = L""; - int number =0; - for (int i = 0; i < len; i++) { - wstring nickname; - if (!lstrcmpiW((wchar_t *)wxids[i], (wchar_t *)L"notify@all")) { - nickname = L"������"; - } else { - nickname = GetContactOrChatRoomNickname(wxids[i]); - } - if (nickname.length() == 0) { - continue; - } - - WeChatString temp = {0}; - temp.ptr = (wchar_t *)wxids[i]; - temp.length = wcslen((wchar_t *)wxids[i]); - temp.max_length = wcslen((wchar_t *)wxids[i]) * 2; - memcpy(&at_users[number], &temp, sizeof(WeChatString)); - at_msg = at_msg + L"@" + nickname + L" "; - number++; - } - if (number < 1){ - return success; - } - wstring origin(msg); - at_msg += origin; - AtInner at_list = {0}; - at_list.start = (DWORD)at_users; - at_list.finsh = (DWORD)&at_users[number]; - at_list.end = (DWORD)&at_users[number]; - WeChatString to_user(chat_room_id); - WeChatString text_msg((wchar_t *)at_msg.c_str()); - wchar_t **msg_pptr = &text_msg.ptr; - - DWORD base = GetWeChatWinBase(); - DWORD send_message_mgr_addr = base + WX_SEND_MESSAGE_MGR_OFFSET; - DWORD send_text_msg_addr = base + WX_SEND_TEXT_OFFSET; - DWORD free_chat_msg_addr = base + WX_FREE_CHAT_MSG_OFFSET; - char chat_msg[0x2C4] ={0}; - - __asm{ - PUSHAD - CALL send_message_mgr_addr - PUSH 0x0 - PUSH 0x0 - PUSH 0x0 - PUSH 0x1 - LEA EAX,at_list - PUSH EAX - MOV EAX,msg_pptr - PUSH EAX - LEA EDX,to_user - LEA ECX,chat_msg - CALL send_text_msg_addr - MOV success,EAX - ADD ESP,0x18 - LEA ECX,chat_msg - CALL free_chat_msg_addr - POPAD - } - return success; -} \ No newline at end of file diff --git a/src/send_text.h b/src/send_text.h deleted file mode 100644 index c7b1a76..0000000 --- a/src/send_text.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef SEND_TEXT_H_ -#define SEND_TEXT_H_ - -int SendText(wchar_t* wxid, wchar_t* msg); -int SendAtText(wchar_t* chat_room_id,wchar_t** wxids,int len,wchar_t* msg); -#endif \ No newline at end of file diff --git a/src/singleton.h b/src/singleton.h new file mode 100644 index 0000000..826a09c --- /dev/null +++ b/src/singleton.h @@ -0,0 +1,21 @@ +#ifndef WXHELPER_SINGLETON_H_ +#define WXHELPER_SINGLETON_H_ +template +class Singleton { + protected: + Singleton() {} + ~Singleton() {} + + Singleton(const Singleton&) = delete; + Singleton& operator=(const Singleton&) = delete; + + Singleton(Singleton&&) = delete; + Singleton& operator=(Singleton&&) = delete; + + public: + static T& GetInstance() { + static T instance{}; + return instance; + } +}; +#endif diff --git a/src/sns.h b/src/sns.h deleted file mode 100644 index db782c0..0000000 --- a/src/sns.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef SNS_H_ -#define SNS_H_ - -int GetFirstPage(); -int GetNextPage(ULONG64 sns_id); -#endif \ No newline at end of file diff --git a/src/sns.cc b/src/sns_mgr.cc similarity index 54% rename from src/sns.cc rename to src/sns_mgr.cc index d4a6f91..274e91d 100644 --- a/src/sns.cc +++ b/src/sns_mgr.cc @@ -1,18 +1,17 @@ -#include "pch.h" -#include "sns.h" +#include "pch.h" +#include "sns_mgr.h" -#include "common.h" -#include "wechat_data.h" -using namespace std; -#define WX_SNS_DATA_MGR_OFFSET 0xc39680 -#define WX_SNS_GET_FIRST_PAGE_OFFSET 0x14e2140 -#define WX_SNS_GET_NEXT_PAGE_OFFSET 0x14e21e0 -int GetFirstPage() { +#include "wechat_function.h" + + +namespace wxhelper { +SNSMgr::SNSMgr(DWORD base):BaseMgr(base) {} +SNSMgr::~SNSMgr() {} +int SNSMgr::GetFirstPage() { int success = -1; - DWORD base = GetWeChatWinBase(); - DWORD sns_data_mgr_addr = base + WX_SNS_DATA_MGR_OFFSET; - DWORD get_first_page_addr = base + WX_SNS_GET_FIRST_PAGE_OFFSET; + DWORD sns_data_mgr_addr = base_addr_ + WX_SNS_DATA_MGR_OFFSET; + DWORD get_first_page_addr = base_addr_ + WX_SNS_GET_FIRST_PAGE_OFFSET; char buff[0xB44] = {}; __asm { @@ -29,15 +28,12 @@ int GetFirstPage() { return success; } - - -int GetNextPage(ULONG64 sns_id) { +int SNSMgr::GetNextPage(ULONG64 sns_id) { int success = -1; - DWORD base = GetWeChatWinBase(); - DWORD sns_data_mgr_addr = base + WX_SNS_DATA_MGR_OFFSET; - DWORD get_next_page_addr = base + WX_SNS_GET_NEXT_PAGE_OFFSET; + DWORD sns_data_mgr_addr = base_addr_ + WX_SNS_DATA_MGR_OFFSET; + DWORD get_next_page_addr = base_addr_ + WX_SNS_GET_NEXT_PAGE_OFFSET; VectorInner temp = {}; - __asm{ + __asm { PUSHAD CALL sns_data_mgr_addr LEA ECX,temp @@ -53,3 +49,4 @@ int GetNextPage(ULONG64 sns_id) { } return success; } +} // namespace wxhelper \ No newline at end of file diff --git a/src/sns_mgr.h b/src/sns_mgr.h new file mode 100644 index 0000000..cb90059 --- /dev/null +++ b/src/sns_mgr.h @@ -0,0 +1,14 @@ +#ifndef WXHELPER_SNS_MGR_H_ +#define WXHELPER_SNS_MGR_H_ +#include "Windows.h" +#include "base_mgr.h" +namespace wxhelper{ + class SNSMgr:public BaseMgr{ + public: + explicit SNSMgr(DWORD base); + ~SNSMgr(); + int GetFirstPage(); + int GetNextPage(ULONG64 sns_id); + }; +} +#endif \ No newline at end of file diff --git a/src/common.cc b/src/utils.cc similarity index 54% rename from src/common.cc rename to src/utils.cc index 5a1e218..e2dc855 100644 --- a/src/common.cc +++ b/src/utils.cc @@ -1,51 +1,43 @@ -#include "pch.h" -#include "common.h" +#include "pch.h" +#include "utils.h" -using namespace std; -/// @brief utf8 转换成unicode -/// @param buffer utf8 -/// @return unicode -wstring utf8_to_unicode(const char *buffer) { - int c_size = MultiByteToWideChar(CP_UTF8, 0, buffer, -1, NULL, 0); - if (c_size > 0) { - wchar_t *temp = new wchar_t[c_size + 1]; - MultiByteToWideChar(CP_UTF8, 0, buffer, -1, temp, c_size); - temp[c_size] = L'\0'; - wstring ret(temp); - delete[] temp; - temp = NULL; - return ret; - } - return wstring(); +namespace wxhelper { +std::wstring Utils::UTF8ToWstring(const std::string &str) { + return Utils::AnsiToWstring(str, CP_UTF8); } -/// @brief unicode转换utf8 -/// @param wstr unicode -/// @return string utf8 -string unicode_to_utf8(wchar_t *wstr) { - int c_size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, FALSE); - if (c_size > 0) { - char *buffer = new char[c_size + 1]; - WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, c_size, NULL, FALSE); - buffer[c_size] = '\0'; - string str(buffer); - delete[] buffer; - buffer = NULL; - return str; - } - return string(); +std::string Utils::WstringToUTF8(const std::wstring &str) { + return Utils::WstringToAnsi(str, CP_UTF8); } -/// @brief 获取WeChatWin.dll基址 -/// @return 基址 -DWORD GetWeChatWinBase() { return (DWORD)GetModuleHandleA("WeChatWin.dll"); } +std::wstring Utils::AnsiToWstring(const std::string &input, DWORD locale) { + int wchar_len = MultiByteToWideChar(locale, 0, input.c_str(), -1, NULL, 0); + if (wchar_len > 0) { + std::vector temp(wchar_len); + MultiByteToWideChar(CP_UTF8, 0, input.c_str(), -1, &temp[0], wchar_len); + return std::wstring(&temp[0]); + } + + return std::wstring(); +} -/// @brief 创建窗口 -/// @param void -/// @return 创建结果 -BOOL CreateConsole(void) { +std::string Utils::WstringToAnsi(const std::wstring &input, DWORD locale) { + int char_len = WideCharToMultiByte(locale, 0, input.c_str(), -1, 0, 0, 0, 0); + if (char_len > 0) { + std::vector temp(char_len); + WideCharToMultiByte(locale, 0, input.c_str(), -1, &temp[0], char_len, 0, 0); + return std::string(&temp[0]); + } + return std::string(); +} + +DWORD Utils::GetWeChatWinBase() { + return (DWORD)GetModuleHandleA("WeChatWin.dll"); +} + +bool Utils::CreateConsole() { if (AllocConsole()) { AttachConsole(GetCurrentProcessId()); FILE *retStream; @@ -53,100 +45,21 @@ BOOL CreateConsole(void) { if (!retStream) throw std::runtime_error("Stdout redirection failed."); freopen_s(&retStream, "CONOUT$", "w", stderr); if (!retStream) throw std::runtime_error("Stderr redirection failed."); - return 0; - } - return 1; -} - -/// @brief hook any addr -/// @param hook_addr need hook of addr -/// @param jmp_addr hook function addr -/// @param origin origin code -void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin) { - BYTE jmp_code[5] = {0}; - jmp_code[0] = 0xE9; - *(DWORD *)&jmp_code[1] = (DWORD)jmp_addr - hook_addr - 5; - DWORD old_protext = 0; - VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); - ReadProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); - memcpy((void *)hook_addr, jmp_code, 5); - VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); -} - -/// @brief unhook -/// @param hook_addr hook addr -/// @param origin origin addr code -void UnHookAnyAddress(DWORD hook_addr, char *origin) { - DWORD old_protext = 0; - VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); - WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); - VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); -} - -/// @brief get timeW -/// @param timestamp timestamp -/// @return str -wstring GetTimeW(long long timestamp) { - wchar_t *wstr = new wchar_t[20]; - memset(wstr, 0, 20 * 2); - tm tm_out; - localtime_s(&tm_out, ×tamp); - swprintf_s(wstr, 20, L"%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm_out.tm_year, - tm_out.tm_mon + 1, tm_out.tm_mday, tm_out.tm_hour, tm_out.tm_min, - tm_out.tm_sec); - wstring strTimeW(wstr); - delete[] wstr; - return strTimeW; -} - -wstring String2Wstring(string str) { - wstring result; - int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0); - wchar_t *buffer = new wchar_t[len + 1]; - MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len); - buffer[len] = '\0'; - result.append(buffer); - delete[] buffer; - return result; -} - -string Wstring2String(wstring wstr) { - string result; - int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, - NULL, NULL); - char *buffer = new char[len + 1]; - WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, - NULL); - buffer[len] = '\0'; - result.append(buffer); - delete[] buffer; - return result; -} - -BOOL FindOrCreateDirectoryW(const wchar_t *path) { - WIN32_FIND_DATAW fd; - HANDLE hFind = ::FindFirstFileW(path, &fd); - if (hFind != INVALID_HANDLE_VALUE && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - FindClose(hFind); - return true; - } - - if (!::CreateDirectoryW(path, NULL)) { return false; } return true; } -void CloseConsole(){ +void Utils::CloseConsole() { fclose(stdin); fclose(stdout); fclose(stderr); FreeConsole(); } -std::string EncodeHexString(const std::string &str) { +std::string Utils::EncodeHexString(const std::string &str) { const std::string hex_table = "0123456789abcdef"; - string sb; + std::string sb; for (int i = 0; i < str.length(); i++) { sb += hex_table.at((str[i] & 0xf0) >> 4); sb += hex_table.at((str[i] & 0x0f) >> 0); @@ -154,7 +67,7 @@ std::string EncodeHexString(const std::string &str) { return sb; } -std::string Hex2String(const std::string &hex_str) { +std::string Utils::Hex2String(const std::string &hex_str) { std::string ret; const std::string hex_table = "0123456789abcdef"; for (int i = 0; i < hex_str.length(); i += 2) { @@ -164,7 +77,7 @@ std::string Hex2String(const std::string &hex_str) { return ret; } -std::string Bytes2Hex(const BYTE *bytes, const int length) { +std::string Utils::Bytes2Hex(const BYTE *bytes, const int length) { if (bytes == NULL) { return ""; } @@ -178,7 +91,7 @@ std::string Bytes2Hex(const BYTE *bytes, const int length) { return buff; } -void Hex2Bytes(const std::string &hex, BYTE *bytes) { +void Utils::Hex2Bytes(const std::string &hex, BYTE *bytes) { int byte_len = hex.length() / 2; std::string str; unsigned int n; @@ -190,7 +103,8 @@ void Hex2Bytes(const std::string &hex, BYTE *bytes) { } -bool IsDigit(string str) { + +bool Utils::IsDigit(std::string str) { if (str.length() == 0) { return false; } @@ -200,4 +114,64 @@ bool IsDigit(string str) { } } return true; -} \ No newline at end of file +} + +bool Utils::FindOrCreateDirectoryW(const wchar_t *path) { + WIN32_FIND_DATAW fd; + HANDLE hFind = ::FindFirstFileW(path, &fd); + if (hFind != INVALID_HANDLE_VALUE && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + FindClose(hFind); + return true; + } + + if (!::CreateDirectoryW(path, NULL)) { + return false; + } + return true; +} + +void Utils::HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin) { + BYTE jmp_code[5] = {0}; + jmp_code[0] = 0xE9; + *(DWORD *)&jmp_code[1] = (DWORD)jmp_addr - hook_addr - 5; + DWORD old_protext = 0; + VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); + ReadProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); + memcpy((void *)hook_addr, jmp_code, 5); + VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); +} + +void Utils::UnHookAnyAddress(DWORD hook_addr, char *origin) { + DWORD old_protext = 0; + VirtualProtect((LPVOID)hook_addr, 5, PAGE_EXECUTE_READWRITE, &old_protext); + WriteProcessMemory(GetCurrentProcess(), (LPVOID)hook_addr, origin, 5, 0); + VirtualProtect((LPVOID)hook_addr, 5, old_protext, &old_protext); +} + +std::wstring Utils::GetTimeW(long long timestamp) { + wchar_t *wstr = new wchar_t[20]; + memset(wstr, 0, 20 * 2); + tm tm_out; + localtime_s(&tm_out, ×tamp); + swprintf_s(wstr, 20, L"%04d-%02d-%02d %02d:%02d:%02d", 1900 + tm_out.tm_year, + tm_out.tm_mon + 1, tm_out.tm_mday, tm_out.tm_hour, tm_out.tm_min, + tm_out.tm_sec); + std::wstring str_time(wstr); + delete[] wstr; + return str_time; +} + +std::string Utils::WCharToUTF8(wchar_t *wstr) { + int c_size = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, FALSE); + if (c_size > 0) { + char *buffer = new char[c_size]; + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buffer, c_size, NULL, FALSE); + std::string str(buffer); + delete[] buffer; + buffer = NULL; + return str; + } + return std::string(); +} + +} // namespace wxhelper \ No newline at end of file diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..b5e50b1 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,77 @@ +#ifndef WXHELPER_UTILS_H_ +#define WXHELPER_UTILS_H_ +#include + +#include +#include +#define STRING2INT(str) (Utils::IsDigit(str) ? stoi(str) : 0) +#define WS2LPWS(wstr) (LPWSTR) wstr.c_str() +#define READ_WSTRING(addr, offset) ((*(DWORD *)(addr + offset + 0x4) == 0) ? std::wstring(L"") : std::wstring((wchar_t *)(*(DWORD *)(addr + offset)), *(DWORD *)(addr + offset + 0x4))) + + +namespace wxhelper { + +class Utils { + public: + static std::wstring UTF8ToWstring(const std::string &str); + + static std::string WstringToUTF8(const std::wstring &str); + + static std::wstring AnsiToWstring(const std::string &input, + DWORD locale = CP_ACP); + + static std::string WstringToAnsi(const std::wstring &input, + DWORD locale = CP_ACP); + + static DWORD GetWeChatWinBase(); + + static bool CreateConsole(); + + static void CloseConsole(); + + static std::string EncodeHexString(const std::string &str); + + static std::string Hex2String(const std::string &hex_str); + + static std::string Bytes2Hex(const BYTE *bytes, const int length); + + static void Hex2Bytes(const std::string &hex, BYTE *bytes); + + static bool IsDigit(std::string str); + + static bool FindOrCreateDirectoryW(const wchar_t *path); + + static void HookAnyAddress(DWORD hook_addr, LPVOID jmp_addr, char *origin); + static void UnHookAnyAddress(DWORD hook_addr, char *origin); + static std::wstring GetTimeW(long long timestamp); + + static std::string WCharToUTF8(wchar_t *wstr); + + template + static std::vector split(T1 str, T2 letter) { + std::vector arr; + size_t pos; + while ((pos = str.find_first_of(letter)) != T1::npos) { + T1 str1 = str.substr(0, pos); + arr.push_back(str1); + str = str.substr(pos + 1, str.length() - pos - 1); + } + arr.push_back(str); + return arr; + } + + template + static T1 replace(T1 source, T2 replaced, T1 replaceto) { + std::vector v_arr = split(source, replaced); + if (v_arr.size() < 2) return source; + T1 temp; + for (unsigned int i = 0; i < v_arr.size() - 1; i++) { + temp += v_arr[i]; + temp += replaceto; + } + temp += v_arr[v_arr.size() - 1]; + return temp; + } +}; +} // namespace wxhelper +#endif \ No newline at end of file diff --git a/src/wechat_data.h b/src/wechat_data.h deleted file mode 100644 index 404bfb8..0000000 --- a/src/wechat_data.h +++ /dev/null @@ -1,337 +0,0 @@ -#ifndef WECHAT_DATA_H_ -#define WECHAT_DATA_H_ - -// #include - -#include -struct WeChatString { - wchar_t *ptr; - DWORD length; - DWORD max_length; - DWORD c_ptr = 0; - DWORD c_len = 0; - WeChatString() { WeChatString(NULL); } - - WeChatString(std::wstring &s) { - ptr = (wchar_t *)(s.c_str()); - length = s.length(); - max_length = s.length() * 2; - } - WeChatString(const wchar_t *pStr) { WeChatString((wchar_t *)pStr); } - WeChatString(int tmp) { - ptr = NULL; - length = 0x0; - max_length = 0x0; - } - WeChatString(wchar_t *pStr) { - ptr = pStr; - length = wcslen(pStr); - max_length = wcslen(pStr) * 2; - } - void set_value(const wchar_t *pStr) { - ptr = (wchar_t *)pStr; - length = wcslen(pStr); - max_length = wcslen(pStr) * 2; - } -}; - -struct TableInfo { - char *name; - DWORD name_len; - char *table_name; - DWORD table_name_len; - char *sql; - DWORD sql_len; - char *rootpage; - DWORD rootpage_len; -}; - -struct DatabaseInfo { - DWORD handle = 0; - wchar_t *db_name = NULL; - DWORD db_name_len = 0; - std::vector tables; - DWORD count = 0; - DWORD extrainfo = 0; -}; - -struct Contact { - WeChatString wxid; - WeChatString custom_account; - WeChatString encrypt_name; - WeChatString nick_name; - WeChatString pinyin; - WeChatString pinyin_all; - int del_flag; - int type; - int verify_flag; -}; - -struct ChatRoomInfo { - DWORD vftable; - WeChatString chat_room_id; - WeChatString notice; - WeChatString admin; - DWORD filed_40; - DWORD filed_44; - DWORD filed_48; - DWORD filed_4C; - WeChatString xml; - DWORD filed_64; - DWORD filed_68; - DWORD filed_6C; - DWORD filed_70; - DWORD filed_74; - DWORD filed_78; - DWORD filed_7C; - DWORD filed_80; - DWORD filed_84; - DWORD filed_88; - DWORD filed_8c; - DWORD filed_90; - DWORD filed_94; - DWORD filed_98; - DWORD filed_9C; - DWORD filed_A0; -}; - -struct ChatRoomInfoInner { - WeChatString chat_room_id; - WeChatString notice; - WeChatString admin; - WeChatString xml; - - ~ChatRoomInfoInner(){ - if(chat_room_id.ptr){ - delete []chat_room_id.ptr; - chat_room_id.ptr = nullptr; - } - if(notice.ptr){ - delete []notice.ptr; - notice.ptr = nullptr; - } - if(admin.ptr){ - delete []admin.ptr; - admin.ptr = nullptr; - } - if(xml.ptr){ - delete []xml.ptr; - xml.ptr = nullptr; - } - } -}; - -struct VectorInner { -#ifdef _DEBUG - DWORD head; -#endif - DWORD start; - DWORD finsh; - DWORD end; -}; - -struct ChatRoomInner{ - char* members; - wchar_t* chat_room; - wchar_t* admin; - ~ChatRoomInner(){ - delete []members; - delete []chat_room; - delete []admin; - } -}; - -struct SelfInfoInner{ - std::string name; - std::string city; - std::string province; - std::string country; - std::string account; - std::string wxid; - std::string mobile; - std::string head_img; - std::string data_save_path; - std::string signature; - std::string current_data_path; - std::string db_key; -}; - -struct UserInfo { - int error_code; - wchar_t *keyword; - int keyword_len; - wchar_t *v3; - int v3_len; - wchar_t *nickname; - int nickname_len; - wchar_t *signature; - int signature_len; - wchar_t *v2; - int v2_len; - wchar_t *nation; - int nation_len; - wchar_t *province; - int province_len; - wchar_t *city; - int city_len; - wchar_t *big_image; - int big_image_len; - wchar_t *small_image; - int small_image_len; - DWORD sex; - BOOL over; -}; - -struct AtInner{ - DWORD start; - DWORD finsh; - DWORD end; -}; - -struct ChatMsg { - DWORD **field0_0x0; - DWORD field1_0x4; - ULONG64 sequence; - DWORD field3_0x10; - DWORD field4_0x14; - ULONG64 msgSequence; - DWORD localId; - DWORD field7_0x24; - DWORD field8_0x28; - DWORD field9_0x2c; - ULONG64 msgId; - DWORD type; - DWORD isSendMsg; - DWORD msgStatus; - DWORD timestamp; - WeChatString talker; - DWORD field16_0x5c; - DWORD field17_0x60; - DWORD field18_0x64; - DWORD field19_0x68; - DWORD field20_0x6c; - WeChatString content; - DWORD field22_0x84; - DWORD field23_0x88; - DWORD field24_0x8c; - DWORD field25_0x90; - DWORD field26_0x94; - DWORD field27_0x98; - DWORD field28_0x9c; - DWORD field29_0xa0; - DWORD field30_0xa4; - DWORD field31_0xa8; - DWORD field32_0xac; - DWORD field33_0xb0; - DWORD field34_0xb4; - DWORD field35_0xb8; - DWORD field36_0xbc; - DWORD field37_0xc0; - DWORD field38_0xc4; - DWORD field39_0xc8; - DWORD field40_0xcc; - DWORD field41_0xd0; - DWORD field42_0xd4; - DWORD field43_0xd8; - DWORD field44_0xdc; - DWORD field45_0xe0; - DWORD field46_0xe4; - DWORD field47_0xe8; - DWORD field48_0xec; - DWORD field49_0xf0; - DWORD field50_0xf4; - DWORD field51_0xf8; - DWORD field52_0xfc; - DWORD field53_0x100; - DWORD field54_0x104; - DWORD field55_0x108; - DWORD field56_0x10c; - DWORD field57_0x110; - DWORD field58_0x114; - DWORD field59_0x118; - DWORD field60_0x11c; - DWORD field61_0x120; - DWORD field62_0x124; - DWORD field63_0x128; - DWORD field64_0x12c; - DWORD field65_0x130; - DWORD field66_0x134; - DWORD field67_0x138; - DWORD field68_0x13c; - DWORD field69_0x140; - DWORD field70_0x144; - DWORD field71_0x148; - DWORD field72_0x14c; - DWORD field73_0x150; - DWORD field74_0x154; - DWORD field75_0x158; - DWORD field76_0x15c; - DWORD field77_0x160; - DWORD field78_0x164; - DWORD field79_0x168; - DWORD field80_0x16c; - DWORD field81_0x170; - WeChatString fromGroup; - WeChatString sign; - WeChatString thumbPath; - WeChatString path; - DWORD field86_0x1c4; - DWORD field87_0x1c8; - DWORD field88_0x1cc; - DWORD field89_0x1d0; - DWORD field90_0x1d4; - DWORD field91_0x1d8; - DWORD field92_0x1dc; - DWORD field93_0x1e0; - DWORD field94_0x1e4; - DWORD field95_0x1e8; - DWORD field96_0x1ec; - WeChatString signature; - DWORD field98_0x204; - DWORD field99_0x208; - DWORD field100_0x20c; - DWORD field101_0x210; - DWORD field102_0x214; - DWORD field103_0x218; - DWORD field104_0x21c; - DWORD field105_0x220; - DWORD field106_0x224; - DWORD field107_0x228; - DWORD field108_0x22c; - DWORD field109_0x230; - DWORD field110_0x234; - DWORD field111_0x238; - DWORD field112_0x23c; - DWORD field113_0x240; - DWORD field114_0x244; - DWORD field115_0x248; - DWORD field116_0x24c; - DWORD field117_0x250; - DWORD field118_0x254; - DWORD field119_0x258; - DWORD field120_0x25c; - DWORD field121_0x260; - DWORD field122_0x264; - DWORD field123_0x268; - DWORD field124_0x26c; - DWORD field125_0x270; - DWORD field126_0x274; - DWORD field127_0x278; - DWORD field128_0x27c; - DWORD field129_0x280; - DWORD field130_0x284; - DWORD field131_0x288; - DWORD field132_0x28c; - DWORD field133_0x290; - DWORD field134_0x294; - DWORD field135_0x298; - DWORD field136_0x29c; - DWORD field137_0x2a0; - DWORD field138_0x2a4; - DWORD field139_0x2a8; - DWORD field140_0x2ac; - DWORD field141_0x2b0; - int field142_0x2b4; -}; - -#endif diff --git a/src/wechat_function.h b/src/wechat_function.h new file mode 100644 index 0000000..9db923f --- /dev/null +++ b/src/wechat_function.h @@ -0,0 +1,770 @@ +#ifndef WXHELPER_WECHAT_FUNCTION_H_ +#define WXHELPER_WECHAT_FUNCTION_H_ +#include +#include + +// snsDataMgr +#define WX_SNS_DATA_MGR_OFFSET 0xc39680 +// chatRoomMgr +#define WX_CHAT_ROOM_MGR_OFFSET 0x78cf20 +// contactMgr +#define WX_CONTACT_MGR_OFFSET 0x75a4a0 +// syncMgr +#define WX_SYNC_MGR_OFFSET 0xa87fd0 +// preDownloadMgr +#define WX_GET_PRE_DOWNLOAD_MGR_OFFSET 0x80f110 +// chatMgr +#define WX_CHAT_MGR_OFFSET 0x792700 +// videoMgr +#define WX_VIDEO_MGR_OFFSET 0x829820 +// patMgr +#define WX_PAT_MGR_OFFSET 0x931730 +// searchContactMgr +#define WX_SEARCH_CONTACT_MGR_OFFSET 0xa6cb00 +// appMsgMgr +#define WX_APP_MSG_MGR_OFFSET 0x76ae20 +// sendMessageMgr +#define WX_SEND_MESSAGE_MGR_OFFSET 0x768140 + + +// setChatMsgValue +#define WX_INIT_CHAT_MSG_OFFSET 0xf59e40 + +// chatMsg +#define WX_NEW_CHAT_MSG_OFFSET 0x76f010 +#define WX_FREE_CHAT_MSG_OFFSET 0x756960 +#define WX_FREE_CHAT_MSG_2_OFFSET 0x6f4ea0 +#define WX_FREE_CHAT_MSG_INSTANCE_COUNTER_OFFSET 0x756e30 + + +//sns +#define WX_SNS_GET_FIRST_PAGE_OFFSET 0x14e2140 +#define WX_SNS_GET_NEXT_PAGE_OFFSET 0x14e21e0 + +//chat room +#define WX_GET_CHAT_ROOM_DETAIL_INFO_OFFSET 0xbde090 +// chatRoomInfo +#define WX_NEW_CHAT_ROOM_INFO_OFFSET 0xe99c40 +#define WX_FREE_CHAT_ROOM_INFO_OFFSET 0xe99f40 +#define WX_DEL_CHAT_ROOM_MEMBER_OFFSET 0xbd22a0 +#define WX_ADD_MEMBER_TO_CHAT_ROOM_OFFSET 0xbd1dc0 + + +// chatRoom +#define WX_INIT_CHAT_ROOM_OFFSET 0xe97890 +#define WX_FREE_CHAT_ROOM_OFFSET 0xe97ab0 + +#define WX_GET_MEMBER_FROM_CHAT_ROOM_OFFSET 0xbdf260 +#define WX_MOD_CHAT_ROOM_MEMBER_NICK_NAME_OFFSET 0xbd9680 + +#define WX_TOP_MSG_OFFSET 0xbe1840 +#define WX_REMOVE_TOP_MSG_OFFSET 0xbe1620 + +#define WX_GET_MEMBER_NICKNAME_OFFSET 0xbdf3f0 + +#define WX_FREE_CONTACT_OFFSET 0xea7880 + +// wcpayinfo +#define WX_NEW_WCPAYINFO_OFFSET 0x7b2e60 +#define WX_FREE_WCPAYINFO_OFFSET 0x79c250 +#define WX_CONFIRM_RECEIPT_OFFSET 0x15e2c20 + + +//contact +#define WX_CONTACT_GET_LIST_OFFSET 0xc089f0 +#define WX_CONTACT_DEL_OFFSET 0xb9b3b0 + +#define WX_SET_VALUE_OFFSET 0x1f80900 +#define WX_DO_DEL_CONTACT_OFFSET 0xca6480 +#define WX_GET_CONTACT_OFFSET 0xc04e00 +#define WX_DO_VERIFY_USER_OFFSET 0xc02100 +#define WX_VERIFY_MSG_OFFSET 0xf59d40 + + +// pushAttachTask + + +#define WX_PUSH_ATTACH_TASK_OFFSET 0x82bb40 + +#define WX_FREE_CHAT_MSG_OFFSET 0x756960 +#define WX_GET_MGR_BY_PREFIX_LOCAL_ID_OFFSET 0xbc0370 +#define WX_GET_CURRENT_DATA_PATH_OFFSET 0xc872c0 +#define WX_APP_MSG_INFO_OFFSET 0x7b3d20 +#define WX_GET_APP_MSG_XML_OFFSET 0xe628a0 +#define WX_FREE_APP_MSG_INFO_OFFSET 0x79d900 +#define WX_PUSH_THUMB_TASK_OFFSET 0x82ba40 +#define WX_DOWNLOAD_VIDEO_IMG_OFFSET 0xd46c30 + + + + + +// pat +#define WX_SEND_PAT_MSG_OFFSET 0x1421940 +#define WX_RET_OFFSET 0x1D58751 + + +//search hook +#define WX_SEARCH_CONTACT_ERROR_CODE_HOOK_OFFSET 0xe17054 +#define WX_SEARCH_CONTACT_ERROR_CODE_HOOK_NEXT_OFFSET 0xf57a20 +#define WX_SEARCH_CONTACT_DETAIL_HOOK_OFFSET 0xa8ceb0 +#define WX_SEARCH_CONTACT_DETAIL_HOOK_NEXT_OFFSET 0xa8d100 +#define WX_SEARCH_CONTACT_OFFSET 0xcd1510 + + + +//login +#define WX_LOGOUT_OFFSET 0xe58870 +#define WX_ACCOUNT_SERVICE_OFFSET 0x768c80 +#define WX_GET_APP_DATA_SAVE_PATH_OFFSET 0xf3a610 +#define WX_GET_CURRENT_DATA_PATH_OFFSET 0xc872c0 + + +//forward +#define WX_FORWARD_MSG_OFFSET 0xce6730 +// send file +#define WX_SEND_FILE_OFFSET 0xb6d1f0 +// send image +#define WX_SEND_IMAGE_OFFSET 0xce6640 +// send text +#define WX_SEND_TEXT_OFFSET 0xce6c80 + + +//ocr +#define WX_INIT_OBJ_OFFSET 0x7a98f0 +#define WX_OCR_MANAGER_OFFSET 0x7ae470 +#define WX_DO_OCR_TASK_OFFSET 0x13230c0 + + +//storage + +#define CONTACT_G_PINSTANCE_OFFSET 0x2ffddc8 +#define DB_MICRO_MSG_OFFSET 0x68 +#define DB_CHAT_MSG_OFFSET 0x1C0 +#define DB_MISC_OFFSET 0x3D8 +#define DB_EMOTION_OFFSET 0x558 +#define DB_MEDIA_OFFSET 0x9B8 +#define DB_BIZCHAT_MSG_OFFSET 0x1120 +#define DB_FUNCTION_MSG_OFFSET 0x11B0 +#define DB_NAME_OFFSET 0x14 + +#define STORAGE_START_OFFSET 0x13f8 +#define STORAGE_END_OFFSET 0x13fc + +#define PUBLIC_MSG_MGR_OFFSET 0x303df74 +#define MULTI_DB_MSG_MGR_OFFSET 0x30403b8 +#define FAVORITE_STORAGE_MGR_OFFSET 0x303fd40 +#define FTS_FAVORITE_MGR_OFFSET 0x2ffe908 + +#define OP_LOG_STORAGE_VFTABLE 0x2AD3A20 +#define CHAT_MSG_STORAGE_VFTABLE 0x2AC10F0 +#define CHAT_CR_MSG_STORAGE_VFTABLE 0x2ABEF14 +#define SESSION_STORAGE_VFTABLE 0x2AD3578 +#define APP_INFO_STORAGE_VFTABLE 0x2ABCC58 +#define HEAD_IMG_STORAGE_VFTABLE 0x2ACD9DC +#define HEAD_IMG_URL_STORAGE_VFTABLE 0x2ACDF70 + +#define BIZ_INFO_STORAGE_VFTABLE 0x2ABD718 +#define TICKET_INFO_STORAGE_VFTABLE 0x2AD5400 +#define CHAT_ROOM_STORAGE_VFTABLE 0x2AC299C +#define CHAT_ROOM_INFO_STORAGE_VFTABLE 0x2AC245C +#define MEDIA_STORAGE_VFTABLE 0x2ACE998 +#define NAME_2_ID_STORAGE_VFTABLE 0x2AD222C +#define EMOTION_PACKAGE_STORAGE_VFTABLE 0x2AC6400 + +#define EMOTION_STORAGE_VFTABLE 0x2AC7018 +#define BUFINFO_STORAGE_VFTABLE 0x2AC3178 + +#define CUSTOM_EMOTION_STORAGE_VFTABLE 0x2AC4E90 +#define DEL_SESSIONINFO_STORAGE_VFTABLE 0x2AC5F98 +#define FUNCTION_MSG_STORAGE_VFTABLE 0x2ACD10C + +#define FUNCTION_MSG_TASK_STORAGE_VFTABLE 0x2ACC5C8 +#define REVOKE_MSG_STORAGE_VFTABLE 0x2AD27BC + + + +/*******************hook*********************************************/ + + +// hook image +#define WX_HOOK_IMG_OFFSET 0xd723dc +#define WX_HOOK_IMG_NEXT_OFFSET 0xe91d90 + + + +// hook log +#define WX_HOOK_LOG_OFFSET 0xf57d67 +#define WX_HOOK_LOG_NEXT_OFFSET 0x240ea71 + +// hook msg + +#define WX_RECV_MSG_HOOK_OFFSET 0xd19a0b +#define WX_RECV_MSG_HOOK_NEXT_OFFSET 0x756960 +#define WX_SNS_HOOK_OFFSET 0x14f9e15 +#define WX_SNS_HOOK_NEXT_OFFSET 0x14fa0a0 + + +// hook voice +#define WX_HOOK_VOICE_OFFSET 0xd4d8d8 +#define WX_HOOK_VOICE_NEXT_OFFSET 0x203d130 +#define WX_SELF_ID_OFFSET 0x2FFD484 + +/*******************hook end*********************************************/ + + +/***************************sqlite3***************************************/ +#define SQLITE_OK 0 /* Successful result */ +/* beginning-of-error-codes */ +#define SQLITE_ERROR 1 /* Generic error */ +#define SQLITE_INTERNAL 2 /* Internal logic error in SQLite */ +#define SQLITE_PERM 3 /* Access permission denied */ +#define SQLITE_ABORT 4 /* Callback routine requested an abort */ +#define SQLITE_BUSY 5 /* The database file is locked */ +#define SQLITE_LOCKED 6 /* A table in the database is locked */ +#define SQLITE_NOMEM 7 /* A malloc() failed */ +#define SQLITE_READONLY 8 /* Attempt to write a readonly database */ +#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/ +#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */ +#define SQLITE_CORRUPT 11 /* The database disk image is malformed */ +#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */ +#define SQLITE_FULL 13 /* Insertion failed because database is full */ +#define SQLITE_CANTOPEN 14 /* Unable to open the database file */ +#define SQLITE_PROTOCOL 15 /* Database lock protocol error */ +#define SQLITE_EMPTY 16 /* Internal use only */ +#define SQLITE_SCHEMA 17 /* The database schema changed */ +#define SQLITE_TOOBIG 18 /* String or BLOB exceeds size limit */ +#define SQLITE_CONSTRAINT 19 /* Abort due to constraint violation */ +#define SQLITE_MISMATCH 20 /* Data type mismatch */ +#define SQLITE_MISUSE 21 /* Library used incorrectly */ +#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */ +#define SQLITE_AUTH 23 /* Authorization denied */ +#define SQLITE_FORMAT 24 /* Not used */ +#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */ +#define SQLITE_NOTADB 26 /* File opened that is not a database file */ +#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */ +#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */ +#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */ +#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */ +/* end-of-error-codes */ + +/* +** CAPI3REF: Extended Result Codes +** KEYWORDS: {extended result code definitions} +** +** In its default configuration, SQLite API routines return one of 30 integer +** [result codes]. However, experience has shown that many of +** these result codes are too coarse-grained. They do not provide as +** much information about problems as programmers might like. In an effort to +** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8] +** and later) include +** support for additional result codes that provide more detailed information +** about errors. These [extended result codes] are enabled or disabled +** on a per database connection basis using the +** [sqlite3_extended_result_codes()] API. Or, the extended code for +** the most recent error can be obtained using +** [sqlite3_extended_errcode()]. +*/ +#define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1 << 8)) +#define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2 << 8)) +#define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3 << 8)) +#define SQLITE_IOERR_READ (SQLITE_IOERR | (1 << 8)) +#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2 << 8)) +#define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3 << 8)) +#define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4 << 8)) +#define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5 << 8)) +#define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6 << 8)) +#define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7 << 8)) +#define SQLITE_IOERR_UNLOCK (SQLITE_IOERR | (8 << 8)) +#define SQLITE_IOERR_RDLOCK (SQLITE_IOERR | (9 << 8)) +#define SQLITE_IOERR_DELETE (SQLITE_IOERR | (10 << 8)) +#define SQLITE_IOERR_BLOCKED (SQLITE_IOERR | (11 << 8)) +#define SQLITE_IOERR_NOMEM (SQLITE_IOERR | (12 << 8)) +#define SQLITE_IOERR_ACCESS (SQLITE_IOERR | (13 << 8)) +#define SQLITE_IOERR_CHECKRESERVEDLOCK (SQLITE_IOERR | (14 << 8)) +#define SQLITE_IOERR_LOCK (SQLITE_IOERR | (15 << 8)) +#define SQLITE_IOERR_CLOSE (SQLITE_IOERR | (16 << 8)) +#define SQLITE_IOERR_DIR_CLOSE (SQLITE_IOERR | (17 << 8)) +#define SQLITE_IOERR_SHMOPEN (SQLITE_IOERR | (18 << 8)) +#define SQLITE_IOERR_SHMSIZE (SQLITE_IOERR | (19 << 8)) +#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20 << 8)) +#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21 << 8)) +#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22 << 8)) +#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23 << 8)) +#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24 << 8)) +#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25 << 8)) +#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26 << 8)) +#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27 << 8)) +#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28 << 8)) +#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29 << 8)) +#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30 << 8)) +#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31 << 8)) +#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32 << 8)) +#define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33 << 8)) +#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1 << 8)) +#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2 << 8)) +#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1 << 8)) +#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2 << 8)) +#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3 << 8)) +#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1 << 8)) +#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2 << 8)) +#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3 << 8)) +#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4 << 8)) +#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5 << 8)) /* Not Used */ +#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6 << 8)) +#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1 << 8)) +#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2 << 8)) +#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3 << 8)) +#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1 << 8)) +#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2 << 8)) +#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3 << 8)) +#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4 << 8)) +#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5 << 8)) +#define SQLITE_READONLY_DIRECTORY (SQLITE_READONLY | (6 << 8)) +#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2 << 8)) +#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1 << 8)) +#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2 << 8)) +#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3 << 8)) +#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4 << 8)) +#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5 << 8)) +#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6 << 8)) +#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7 << 8)) +#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8 << 8)) +#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9 << 8)) +#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT | (10 << 8)) +#define SQLITE_CONSTRAINT_PINNED (SQLITE_CONSTRAINT | (11 << 8)) +#define SQLITE_CONSTRAINT_DATATYPE (SQLITE_CONSTRAINT | (12 << 8)) +#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1 << 8)) +#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2 << 8)) +#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1 << 8)) +#define SQLITE_AUTH_USER (SQLITE_AUTH | (1 << 8)) +#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1 << 8)) +#define SQLITE_OK_SYMLINK (SQLITE_OK | (2 << 8)) /* internal use only */ + + +#define SQLITE_INTEGER 1 +#define SQLITE_FLOAT 2 +#define SQLITE_BLOB 4 +#define SQLITE_NULL 5 +#define SQLITE_TEXT 3 + +#define SQLITE3_EXEC_OFFSET 0x1e24f70 +#define SQLITE3_BACKUP_INIT_OFFSET 0x1dea900 +#define SQLITE3_PREPARE_OFFSET 0x1e2b8c0 +#define SQLITE3_OPEN_OFFSET 0x1e598b0 +#define SQLITE3_BACKUP_STEP_OFFSET 0x1dead00 +#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1deb440 +#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1deb450 +#define SQLITE3_BACKUP_FINISH_OFFSET 0x1deb340 +#define SQLITE3_SLEEP_OFFSET 0x1e5a0f0 +#define SQLITE3_ERRCODE_OFFSET 0x1e58550 +#define SQLITE3_CLOSE_OFFSET 0x1e56cd0 +#define SQLITE3_STEP_OFFSET 0x1df3770 +#define SQLITE3_COLUMN_COUNT_OFFSET 0x1df3c80 +#define SQLITE3_COLUMN_NAME_OFFSET 0x1df4570 +#define SQLITE3_COLUMN_TYPE_OFFSET 0x1df4410 +#define SQLITE3_COLUMN_BLOB_OFFSET 0x1df3cc0 +#define SQLITE3_COLUMN_BYTES_OFFSET 0x1df3da0 +#define SQLITE3_FINALIZE_OFFSET 0x1df2740 + +typedef int (*Sqlite3_callback)(void*, int, char**, char**); + +typedef int(__cdecl* Sqlite3_exec)(DWORD, /* An open database */ + const char* sql, /* SQL to be evaluated */ + Sqlite3_callback, /* Callback function */ + void*, /* 1st argument to callback */ + char** errmsg /* Error msg written here */ +); +typedef DWORD(__cdecl* Sqlite3_backup_init)( + DWORD* pDest, /* Destination database handle */ + const char* zDestName, /* Destination database name */ + DWORD* pSource, /* Source database handle */ + const char* zSourceName /* Source database name */ +); +typedef int(__cdecl* Sqlite3_prepare)( + DWORD db, /* Database handle */ + const char* zSql, /* SQL statement, UTF-8 encoded */ + int nByte, /* Maximum length of zSql in bytes. */ + DWORD** ppStmt, /* OUT: Statement handle */ + const char** pzTail /* OUT: Pointer to unused portion of zSql */ +); +typedef int(__cdecl* Sqlite3_open)(const char* filename, DWORD** ppDb); +typedef int(__cdecl* Sqlite3_backup_step)(DWORD* p, int nPage); +typedef int(__cdecl* Sqlite3_backup_remaining)(DWORD* p); +typedef int(__cdecl* Sqlite3_backup_pagecount)(DWORD* p); +typedef int(__cdecl* Sqlite3_backup_finish)(DWORD* p); +typedef int(__cdecl* Sqlite3_sleep)(int); +typedef int(__cdecl* Sqlite3_errcode)(DWORD* db); +typedef int(__cdecl* Sqlite3_close)(DWORD*); + +typedef int(__cdecl* Sqlite3_step)(DWORD*); +typedef int(__cdecl* Sqlite3_column_count)(DWORD* pStmt); +typedef const char*(__cdecl* Sqlite3_column_name)(DWORD*, int N); +typedef int(__cdecl* Sqlite3_column_type)(DWORD*, int iCol); +typedef const void*(__cdecl* Sqlite3_column_blob)(DWORD*, int iCol); +typedef int(__cdecl* Sqlite3_column_bytes)(DWORD*, int iCol); +typedef int(__cdecl* Sqlite3_finalize)(DWORD* pStmt); + + +/***************************sqlite3 end*************************************/ + +struct SqlResult { + char *column_name; + DWORD column_name_len; + char *content; + DWORD content_len; + BOOL is_blob; +}; + +struct WeChatString { + wchar_t *ptr; + DWORD length; + DWORD max_length; + DWORD c_ptr = 0; + DWORD c_len = 0; + WeChatString() { WeChatString(NULL); } + + WeChatString(std::wstring &s) { + ptr = (wchar_t *)(s.c_str()); + length = s.length(); + max_length = s.length() * 2; + } + WeChatString(const wchar_t *pStr) { WeChatString((wchar_t *)pStr); } + WeChatString(int tmp) { + ptr = NULL; + length = 0x0; + max_length = 0x0; + } + WeChatString(wchar_t *pStr) { + ptr = pStr; + length = wcslen(pStr); + max_length = wcslen(pStr) * 2; + } + void set_value(const wchar_t *pStr) { + ptr = (wchar_t *)pStr; + length = wcslen(pStr); + max_length = wcslen(pStr) * 2; + } +}; + + +struct SelfInfoInner{ + std::string name; + std::string city; + std::string province; + std::string country; + std::string account; + std::string wxid; + std::string mobile; + std::string head_img; + std::string data_save_path; + std::string signature; + std::string current_data_path; + std::string db_key; +}; + +struct VectorInner { +#ifdef _DEBUG + DWORD head; +#endif + DWORD start; + DWORD finsh; + DWORD end; +}; + +struct TableInfo { + char *name; + DWORD name_len; + char *table_name; + DWORD table_name_len; + char *sql; + DWORD sql_len; + char *rootpage; + DWORD rootpage_len; +}; + +struct DatabaseInfo { + DWORD handle = 0; + wchar_t *db_name = NULL; + DWORD db_name_len = 0; + std::vector tables; + DWORD count = 0; + DWORD extrainfo = 0; +}; + + +struct Contact { + WeChatString wxid; + WeChatString custom_account; + WeChatString encrypt_name; + WeChatString nick_name; + WeChatString pinyin; + WeChatString pinyin_all; + int del_flag; + int type; + int verify_flag; +}; + +struct ChatRoomInfo { + DWORD vftable; + WeChatString chat_room_id; + WeChatString notice; + WeChatString admin; + DWORD filed_40; + DWORD filed_44; + DWORD filed_48; + DWORD filed_4C; + WeChatString xml; + DWORD filed_64; + DWORD filed_68; + DWORD filed_6C; + DWORD filed_70; + DWORD filed_74; + DWORD filed_78; + DWORD filed_7C; + DWORD filed_80; + DWORD filed_84; + DWORD filed_88; + DWORD filed_8c; + DWORD filed_90; + DWORD filed_94; + DWORD filed_98; + DWORD filed_9C; + DWORD filed_A0; +}; + +struct ChatRoomInfoInner { + WeChatString chat_room_id; + WeChatString notice; + WeChatString admin; + WeChatString xml; + + ~ChatRoomInfoInner(){ + if(chat_room_id.ptr){ + delete []chat_room_id.ptr; + chat_room_id.ptr = nullptr; + } + if(notice.ptr){ + delete []notice.ptr; + notice.ptr = nullptr; + } + if(admin.ptr){ + delete []admin.ptr; + admin.ptr = nullptr; + } + if(xml.ptr){ + delete []xml.ptr; + xml.ptr = nullptr; + } + } +}; + +struct ChatRoomInner{ + char* members; + wchar_t* chat_room; + wchar_t* admin; + ~ChatRoomInner(){ + delete []members; + delete []chat_room; + delete []admin; + } +}; + +struct UserInfo { + int error_code; + wchar_t *keyword; + int keyword_len; + wchar_t *v3; + int v3_len; + wchar_t *nickname; + int nickname_len; + wchar_t *signature; + int signature_len; + wchar_t *v2; + int v2_len; + wchar_t *nation; + int nation_len; + wchar_t *province; + int province_len; + wchar_t *city; + int city_len; + wchar_t *big_image; + int big_image_len; + wchar_t *small_image; + int small_image_len; + DWORD sex; + BOOL over; +}; + +struct AtInner{ + DWORD start; + DWORD finsh; + DWORD end; +}; + +struct ChatMsg { + DWORD **field0_0x0; + DWORD field1_0x4; + ULONG64 sequence; + DWORD field3_0x10; + DWORD field4_0x14; + ULONG64 msgSequence; + DWORD localId; + DWORD field7_0x24; + DWORD field8_0x28; + DWORD field9_0x2c; + ULONG64 msgId; + DWORD type; + DWORD isSendMsg; + DWORD msgStatus; + DWORD timestamp; + WeChatString talker; + DWORD field16_0x5c; + DWORD field17_0x60; + DWORD field18_0x64; + DWORD field19_0x68; + DWORD field20_0x6c; + WeChatString content; + DWORD field22_0x84; + DWORD field23_0x88; + DWORD field24_0x8c; + DWORD field25_0x90; + DWORD field26_0x94; + DWORD field27_0x98; + DWORD field28_0x9c; + DWORD field29_0xa0; + DWORD field30_0xa4; + DWORD field31_0xa8; + DWORD field32_0xac; + DWORD field33_0xb0; + DWORD field34_0xb4; + DWORD field35_0xb8; + DWORD field36_0xbc; + DWORD field37_0xc0; + DWORD field38_0xc4; + DWORD field39_0xc8; + DWORD field40_0xcc; + DWORD field41_0xd0; + DWORD field42_0xd4; + DWORD field43_0xd8; + DWORD field44_0xdc; + DWORD field45_0xe0; + DWORD field46_0xe4; + DWORD field47_0xe8; + DWORD field48_0xec; + DWORD field49_0xf0; + DWORD field50_0xf4; + DWORD field51_0xf8; + DWORD field52_0xfc; + DWORD field53_0x100; + DWORD field54_0x104; + DWORD field55_0x108; + DWORD field56_0x10c; + DWORD field57_0x110; + DWORD field58_0x114; + DWORD field59_0x118; + DWORD field60_0x11c; + DWORD field61_0x120; + DWORD field62_0x124; + DWORD field63_0x128; + DWORD field64_0x12c; + DWORD field65_0x130; + DWORD field66_0x134; + DWORD field67_0x138; + DWORD field68_0x13c; + DWORD field69_0x140; + DWORD field70_0x144; + DWORD field71_0x148; + DWORD field72_0x14c; + DWORD field73_0x150; + DWORD field74_0x154; + DWORD field75_0x158; + DWORD field76_0x15c; + DWORD field77_0x160; + DWORD field78_0x164; + DWORD field79_0x168; + DWORD field80_0x16c; + DWORD field81_0x170; + WeChatString fromGroup; + WeChatString sign; + WeChatString thumbPath; + WeChatString path; + DWORD field86_0x1c4; + DWORD field87_0x1c8; + DWORD field88_0x1cc; + DWORD field89_0x1d0; + DWORD field90_0x1d4; + DWORD field91_0x1d8; + DWORD field92_0x1dc; + DWORD field93_0x1e0; + DWORD field94_0x1e4; + DWORD field95_0x1e8; + DWORD field96_0x1ec; + WeChatString signature; + DWORD field98_0x204; + DWORD field99_0x208; + DWORD field100_0x20c; + DWORD field101_0x210; + DWORD field102_0x214; + DWORD field103_0x218; + DWORD field104_0x21c; + DWORD field105_0x220; + DWORD field106_0x224; + DWORD field107_0x228; + DWORD field108_0x22c; + DWORD field109_0x230; + DWORD field110_0x234; + DWORD field111_0x238; + DWORD field112_0x23c; + DWORD field113_0x240; + DWORD field114_0x244; + DWORD field115_0x248; + DWORD field116_0x24c; + DWORD field117_0x250; + DWORD field118_0x254; + DWORD field119_0x258; + DWORD field120_0x25c; + DWORD field121_0x260; + DWORD field122_0x264; + DWORD field123_0x268; + DWORD field124_0x26c; + DWORD field125_0x270; + DWORD field126_0x274; + DWORD field127_0x278; + DWORD field128_0x27c; + DWORD field129_0x280; + DWORD field130_0x284; + DWORD field131_0x288; + DWORD field132_0x28c; + DWORD field133_0x290; + DWORD field134_0x294; + DWORD field135_0x298; + DWORD field136_0x29c; + DWORD field137_0x2a0; + DWORD field138_0x2a4; + DWORD field139_0x2a8; + DWORD field140_0x2ac; + DWORD field141_0x2b0; + int field142_0x2b4; +}; + +struct InnerMessageStruct { + char *buffer; + int length; + ~InnerMessageStruct() { + if (this->buffer != NULL) { + delete[] this->buffer; + this->buffer = NULL; + } + } +}; + +struct Unkown{ + DWORD field1 = 0; + DWORD field2= 0; + DWORD field3= 0; + DWORD field4= 0; + DWORD field5= 0; + DWORD field6= 0; +}; +#endif \ No newline at end of file diff --git a/src/wxhelper.cc b/src/wxhelper.cc deleted file mode 100644 index 73e9397..0000000 --- a/src/wxhelper.cc +++ /dev/null @@ -1,24 +0,0 @@ -#include "wxhelper.h" - -#include - -#include "pch.h" - -namespace wxhelper { -WXHelper& WXHelper::GetInstance() { - static WXHelper p; - return p; -} - -int WXHelper::http_start(int port) { - HttpServer::GetInstance().Init(port); - bool ret = HttpServer::GetInstance().HttpStart(); - return ret == true ? 1 : 0; -} - -int WXHelper::http_close() { - bool ret = HttpServer::GetInstance().HttpClose(); - return ret == true ? 1 : 0; -} - -} // namespace wxhelper \ No newline at end of file diff --git a/src/wxhelper.h b/src/wxhelper.h deleted file mode 100644 index d0f60be..0000000 --- a/src/wxhelper.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef WXHELPER_WXHELPER_H_ -#define WXHELPER_WXHELPER_H_ -#include "http_server.h" -namespace wxhelper { - -class WXHelper { - public: - static WXHelper &GetInstance(); - - int http_start(int port); - int http_close(); - - private: - WXHelper(){}; - WXHelper(const WXHelper &) = delete; - WXHelper &operator=(const WXHelper &) = delete; - ~WXHelper(){}; -}; -} // namespace wxhelper - -#endif \ No newline at end of file