diff --git a/Include/Reai/Api/Reai.h b/Include/Reai/Api/Reai.h index da1570c..bf17519 100644 --- a/Include/Reai/Api/Reai.h +++ b/Include/Reai/Api/Reai.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,10 @@ extern "C" { CStrVec* reai_get_available_models (Reai* reai, ReaiResponse* response); + Reai* reai_begin_ai_decompilation (Reai* reai, ReaiResponse* response, ReaiFunctionId fn_id); + ReaiAiDecompilationStatus + reai_poll_ai_decompilation (Reai* reai, ReaiResponse* response, ReaiFunctionId fn_id); + #ifdef __cplusplus } #endif diff --git a/Include/Reai/Api/Response.h b/Include/Reai/Api/Response.h index c3761e3..1430f4c 100644 --- a/Include/Reai/Api/Response.h +++ b/Include/Reai/Api/Response.h @@ -62,6 +62,40 @@ extern "C" { REAI_RESPONSE_TYPE_MAX, /* enum value less than this is valid */ } ReaiResponseType; + typedef enum ReaiAiDecompilationStatus { + REAI_AI_DECOMPILATION_STATUS_ERROR, + REAI_AI_DECOMPILATION_STATUS_UNINITIALIZED, + REAI_AI_DECOMPILATION_STATUS_PENDING, + REAI_AI_DECOMPILATION_STATUS_SUCCESS, + } ReaiAiDecompilationStatus; + + static inline ReaiAiDecompilationStatus reai_ai_decompilation_status_from_cstr (CString status + ) { + if (!status) { + return REAI_AI_DECOMPILATION_STATUS_ERROR; + } else if (!strcmp (status, "uninitialized")) { + return REAI_AI_DECOMPILATION_STATUS_UNINITIALIZED; + } else if (!strcmp (status, "pending")) { + return REAI_AI_DECOMPILATION_STATUS_PENDING; + } else if (!strcmp (status, "success")) { + return REAI_AI_DECOMPILATION_STATUS_SUCCESS; + } else { + return REAI_AI_DECOMPILATION_STATUS_ERROR; + } + } + + static inline CString reai_ai_decompilation_status_to_cstr (ReaiAiDecompilationStatus status) { + switch (status) { + case REAI_AI_DECOMPILATION_STATUS_UNINITIALIZED : + return "uninitialized"; + case REAI_AI_DECOMPILATION_STATUS_PENDING : + return "pending"; + case REAI_AI_DECOMPILATION_STATUS_SUCCESS : + return "success"; + default : + return "error"; + } + } /** * @b Structure returned and taken by reai_request calls that get @@ -181,8 +215,8 @@ extern "C" { struct { Bool status; struct { - CString status; - CString decompilation; + ReaiAiDecompilationStatus status; + CString decompilation; // TODO: function mapping? } data; CString message; diff --git a/Include/Reai/Common.h b/Include/Reai/Common.h index 8bf4fc5..e9f24b2 100644 --- a/Include/Reai/Common.h +++ b/Include/Reai/Common.h @@ -29,8 +29,12 @@ # define ALLOCATE(type, n) (type *)calloc (n, sizeof (type)) # define REALLOCATE(ptr, type, n) (type *)realloc (ptr, n * sizeof (type)); # define FREE(x) \ - free ((void *)(x)); \ - x = NULL + do { \ + if (x) { \ + free ((void *)(x)); \ + x = NULL; \ + } \ + } while (0) #endif #define PACKED __attribute__ ((packed)) diff --git a/Include/Reai/Util/CStrVec.h b/Include/Reai/Util/CStrVec.h index ebbe8cd..9fc65e5 100644 --- a/Include/Reai/Util/CStrVec.h +++ b/Include/Reai/Util/CStrVec.h @@ -47,9 +47,11 @@ extern "C" { * @return @c NULL otherwise. * */ PRIVATE CString *cstr_clone_deinit (CString *clone) { - RETURN_VALUE_IF (!clone || !*clone, (CString *)NULL, ERR_INVALID_ARGUMENTS); + RETURN_VALUE_IF (!clone, (CString *)NULL, ERR_INVALID_ARGUMENTS); - FREE (*clone); + if (*clone) { + FREE (*clone); + } return clone; } diff --git a/README.md b/README.md index 6f730c0..7e384e7 100644 --- a/README.md +++ b/README.md @@ -56,15 +56,12 @@ There are three main objects you need to interact with. ### Configurations To connect with RevEng.AI servers, you first need to load a config file. The config file must -usually be present in your home directory and must have name `~/.reai-rz.toml`. When using a +usually be present in your home directory and must have name `~/.creait.toml`. When using a plugin, this file can be auto-generated using one of the commands. A very basic config is ```toml apikey = "libr3" # Replace this with your own API key host = "https://api.reveng.ai/v1" # API version and base endpoint -model = "binnet-0.3-x86" # Set the latest AI model here. -db_dir_path = "/home//.reai" # This path may change depending on your OS -log_dir_path = "/tmp" # This path may change depending on your OS ``` To load the config, you must create a `ReaiConfig` object that parses this toml file and stores diff --git a/Source/Reai/Api/Helpers/JsonGetters.h b/Source/Reai/Api/Helpers/JsonGetters.h index df4bb11..0be6c79 100644 --- a/Source/Reai/Api/Helpers/JsonGetters.h +++ b/Source/Reai/Api/Helpers/JsonGetters.h @@ -8,79 +8,80 @@ #ifndef CREAIT_API_HELPERS_JSON_GETTERS_H #define CREAIT_API_HELPERS_JSON_GETTERS_H -#define IF_JSON_OBJECT_EXISTS(json_obj, name) if (cJSON_GetObjectItemCaseSensitive (json_obj, name)) +#define IF_JSON_OBJECT_EXISTS(json_obj, name) \ + if (cJSON_GetObjectItemCaseSensitive ((json_obj), (name))) #define GET_OPTIONAL_JSON_BOOL(json_bool, name, var) \ IF_JSON_OBJECT_EXISTS (json_bool, name) { \ - GET_JSON_BOOL (json_bool, name, var); \ + (var) = json_response_get_bool ((json_bool), (name)); \ } \ else { \ - var = 0; \ + (var) = 0; \ } #define GET_OPTIONAL_JSON_U64(json_u64, name, var) \ IF_JSON_OBJECT_EXISTS (json_u64, name) { \ - GET_JSON_U64 (json_u64, name, var); \ + json_response_get_u64 ((json_u64), (name), &(var)); \ } \ else { \ - var = 0; \ + (var) = 0; \ } #define GET_OPTIONAL_JSON_F64(json_f64, name, var) \ IF_JSON_OBJECT_EXISTS (json_f64, name) { \ - GET_JSON_F64 (json_f64, name, var); \ + json_response_get_f64 ((json_f64), (name), &(var)); \ } \ else { \ - var = 0; \ + (var) = 0; \ } #define GET_OPTIONAL_JSON_STRING(json_str, name, var) \ IF_JSON_OBJECT_EXISTS (json_str, name) { \ - GET_JSON_STRING (json_str, name, var); \ + (var) = json_response_get_string ((json_str), (name)); \ } \ else { \ - var = 0; \ + (var) = 0; \ } #define GET_OPTIONAL_JSON_STRING_ARR(json_str_arr, name, var) \ IF_JSON_OBJECT_EXISTS (json_str_arr, name) { \ - GET_JSON_STRING_ARR (json_str_arr, name, var); \ + (var) = json_response_get_string_arr ((json_str_arr), (name)); \ } \ else { \ - var = 0; \ + (var) = 0; \ } -#define GET_JSON_BOOL(json_bool, name, var) var = json_response_get_bool (json_bool, name); +#define GET_JSON_BOOL(json_bool, name, var) (var) = json_response_get_bool ((json_bool), name); #define GET_JSON_U64(json_u64, name, var) \ { \ Uint64 num = 0; \ GOTO_HANDLER_IF ( \ - !(json_response_get_u64 (json_u64, name, &num)), \ + !(json_response_get_u64 ((json_u64), (name), &num)), \ INIT_FAILED, \ "Failed to get number '%s' from response.", \ - name \ + (name) \ ); \ \ - var = num; \ + (var) = num; \ } #define GET_JSON_F64(json_f64, name, var) \ { \ Float64 num = 0; \ GOTO_HANDLER_IF ( \ - !(json_response_get_f64 (json_f64, name, &num)), \ + !(json_response_get_f64 ((json_f64), (name), &num)), \ INIT_FAILED, \ "Failed to get number '%s' from response.", \ - name \ + (name) \ ); \ \ - var = num; \ + (var) = num; \ } /* retrieved string must be freed after use */ @@ -88,23 +89,23 @@ { \ CString str = NULL; \ GOTO_HANDLER_IF ( \ - !(str = json_response_get_string (json_str, name)), \ + !(str = json_response_get_string ((json_str), (name))), \ INIT_FAILED, \ "Failed to get string '%s' from response.", \ - name \ + (name) \ ); \ \ - var = str; \ + (var) = str; \ } -/* retrieved string array must be freed after use */ +/* retrieved string array must be freed after use */ #define GET_JSON_STRING_ARR(json_str_arr, name, arr) \ { \ GOTO_HANDLER_IF ( \ - !(arr = json_response_get_string_arr (json_str_arr, name)), \ + !(arr = json_response_get_string_arr ((json_str_arr), (name))), \ INIT_FAILED, \ "Failed to get '%s' string array in response.", \ - name \ + (name) \ ); \ } @@ -113,39 +114,39 @@ if (success) { \ GET_JSON_STRING (json_str, name, var); \ } else { \ - var = NULL; \ + (var) = NULL; \ } #define GET_JSON_U64_ON_SUCCESS(json_u64, name, var, success) \ if (success) { \ GET_JSON_U64 (json_u64, name, var); \ } else { \ - var = 0; \ + (var) = 0; \ } #define GET_JSON_F64_ON_SUCCESS(json_f64, name, var, success) \ if (success) { \ GET_JSON_F64 (json_f64, name, var); \ } else { \ - var = 0; \ + (var) = 0; \ } #define GET_JSON_FN_INFO(json_fn_info, fn) \ do { \ - GET_JSON_U64 (json_fn_info, "function_id", fn.id); \ - GET_JSON_STRING (json_fn_info, "function_name", fn.name); \ - GET_JSON_U64 (json_fn_info, "function_size", fn.size); \ - GET_JSON_U64 (json_fn_info, "function_vaddr", fn.vaddr); \ + GET_JSON_U64 (json_fn_info, "function_id", (fn).id); \ + GET_JSON_STRING (json_fn_info, "function_name", (fn).name); \ + GET_JSON_U64 (json_fn_info, "function_size", (fn).size); \ + GET_JSON_U64 (json_fn_info, "function_vaddr", (fn).vaddr); \ } while (0) #define GET_JSON_ANALYSIS_INFO(json_analysis_info, ainfo) \ do { \ - GET_JSON_U64 (json_analysis_info, "binary_id", ainfo.binary_id); \ - GET_JSON_STRING (json_analysis_info, "binary_name", ainfo.binary_name); \ - GET_JSON_STRING (json_analysis_info, "creation", ainfo.creation); \ - GET_JSON_U64 (json_analysis_info, "model_id", ainfo.model_id); \ - GET_JSON_STRING (json_analysis_info, "model_name", ainfo.model_name); \ - GET_JSON_STRING (json_analysis_info, "sha_256_hash", ainfo.sha_256_hash); \ + GET_JSON_U64 (json_analysis_info, "binary_id", (ainfo).binary_id); \ + GET_JSON_STRING (json_analysis_info, "binary_name", (ainfo).binary_name); \ + GET_JSON_STRING (json_analysis_info, "creation", (ainfo).creation); \ + GET_JSON_U64 (json_analysis_info, "model_id", (ainfo).model_id); \ + GET_JSON_STRING (json_analysis_info, "model_name", (ainfo).model_name); \ + GET_JSON_STRING (json_analysis_info, "sha_256_hash", (ainfo).sha_256_hash); \ \ CString status = NULL; \ ReaiAnalysisStatus estatus = 0; \ @@ -154,56 +155,60 @@ PRINT_ERR ("Failed to convert analysis status to enum"); \ goto INIT_FAILED; \ } \ - ainfo.status = estatus; \ + (ainfo).status = estatus; \ } while (0) #define GET_JSON_QUERY_RESULT(json_query_res, qres) \ do { \ - GET_JSON_U64 (json_query_res, "binary_id", qres.binary_id); \ - GET_JSON_STRING (json_query_res, "binary_name", qres.binary_name); \ - GET_JSON_STRING_ARR (json_query_res, "collections", qres.collections); \ - GET_JSON_STRING (json_query_res, "creation", qres.creation); \ - GET_JSON_U64 (json_query_res, "model_id", qres.model_id); \ - GET_JSON_STRING (json_query_res, "model_name", qres.model_name); \ - GET_JSON_STRING (json_query_res, "sha_256_hash", qres.sha_256_hash); \ - GET_JSON_STRING_ARR (json_query_res, "tags", qres.tags); \ + GET_JSON_U64 (json_query_res, "binary_id", (qres).binary_id); \ + GET_JSON_STRING (json_query_res, "binary_name", (qres).binary_name); \ + GET_JSON_STRING_ARR (json_query_res, "collections", (qres).collections); \ + GET_JSON_STRING (json_query_res, "creation", (qres).creation); \ + GET_JSON_U64 (json_query_res, "model_id", (qres).model_id); \ + GET_JSON_STRING (json_query_res, "model_name", (qres).model_name); \ + GET_JSON_STRING (json_query_res, "sha_256_hash", (qres).sha_256_hash); \ + GET_JSON_STRING_ARR (json_query_res, "tags", (qres).tags); \ \ CString status = NULL; \ ReaiAnalysisStatus estatus = 0; \ GET_JSON_STRING (json_query_res, "status", status); \ - if (!(qres.status = reai_analysis_status_from_cstr (status))) { \ + if (!((qres).status = reai_analysis_status_from_cstr (status))) { \ PRINT_ERR ("Failed to convert analysis status to enum"); \ goto INIT_FAILED; \ } \ - qres.status = estatus; \ + (qres).status = estatus; \ } while (0) #define GET_JSON_ANN_FN_MATCH(json_fn_match, fn_match) \ do { \ - GET_OPTIONAL_JSON_F64 (json_fn_match, "confidence", fn_match.confidence); \ + GET_OPTIONAL_JSON_F64 (json_fn_match, "confidence", (fn_match).confidence); \ GET_OPTIONAL_JSON_U64 ( \ json_fn_match, \ "nearest_neighbor_binary_id", \ - fn_match.nn_binary_id \ + (fn_match).nn_binary_id \ ); \ GET_OPTIONAL_JSON_STRING ( \ json_fn_match, \ "nearest_neighbor_binary_name", \ - fn_match.nn_binary_name \ + (fn_match).nn_binary_name \ ); \ - GET_OPTIONAL_JSON_BOOL (json_fn_match, "nearest_neighbor_debug", fn_match.nn_debug); \ + GET_OPTIONAL_JSON_BOOL (json_fn_match, "nearest_neighbor_debug", (fn_match).nn_debug); \ GET_OPTIONAL_JSON_STRING ( \ json_fn_match, \ "nearest_neighbor_function_name", \ - fn_match.nn_function_name \ + (fn_match).nn_function_name \ ); \ - GET_OPTIONAL_JSON_U64 (json_fn_match, "nearest_neighbor_id", fn_match.nn_function_id); \ + GET_OPTIONAL_JSON_U64 (json_fn_match, "nearest_neighbor_id", (fn_match).nn_function_id); \ GET_OPTIONAL_JSON_STRING ( \ json_fn_match, \ "nearest_neighbor_sha_256_hash", \ - fn_match.nn_sha_256_hash \ + (fn_match).nn_sha_256_hash \ + ); \ + GET_OPTIONAL_JSON_U64 ( \ + json_fn_match, \ + "origin_function_id", \ + (fn_match).origin_function_id \ ); \ - GET_OPTIONAL_JSON_U64 (json_fn_match, "origin_function_id", fn_match.origin_function_id); \ } while (0) #define GET_JSON_AI_MODEL(json_ai_model, model_name) \ @@ -213,20 +218,20 @@ #define GET_JSON_API_ERROR(json_api_error, api_error) \ do { \ - GET_JSON_STRING (json_api_error, "code", api_error.code); \ - GET_JSON_STRING (json_api_error, "message", api_error.message); \ + GET_OPTIONAL_JSON_STRING (json_api_error, "code", (api_error).code); \ + GET_OPTIONAL_JSON_STRING (json_api_error, "message", (api_error).message); \ } while (0) #define GET_JSON_CUSTOM_ARR(json_arr, type_name, type_infix, reader, vec) \ do { \ - vec = reai_##type_infix##_vec_create(); \ - GOTO_HANDLER_IF (!vec, INIT_FAILED, "Failed to create " #type_name " vector."); \ + (vec) = reai_##type_infix##_vec_create(); \ + GOTO_HANDLER_IF (!(vec), INIT_FAILED, "Failed to create " #type_name " vector."); \ \ if (cJSON_IsObject (json_arr)) { \ type_name item = {0}; \ reader (json_arr, item); \ \ - if (!reai_##type_infix##_vec_append (vec, &item)) { \ + if (!reai_##type_infix##_vec_append ((vec), &item)) { \ PRINT_ERR ("Failed to insert " #type_name " object into vector."); \ reai_##type_infix##_vec_destroy (vec); \ goto INIT_FAILED; \ @@ -237,7 +242,7 @@ type_name item = {0}; \ reader (arr_item, item); \ \ - if (!reai_##type_infix##_vec_append (vec, &item)) { \ + if (!reai_##type_infix##_vec_append ((vec), &item)) { \ PRINT_ERR ("Failed to insert " #type_name " object into vector."); \ reai_##type_infix##_vec_destroy (vec); \ goto INIT_FAILED; \ diff --git a/Source/Reai/Api/Reai.c b/Source/Reai/Api/Reai.c index 1691703..bb6422f 100644 --- a/Source/Reai/Api/Reai.c +++ b/Source/Reai/Api/Reai.c @@ -285,7 +285,9 @@ ReaiResponse* reai_request (Reai* reai, ReaiRequest* request, ReaiResponse* resp REAI_LOG_TRACE ("REQUEST.JSON : '%s'", json); \ \ /* set json data */ \ - curl_easy_setopt (reai->curl, CURLOPT_POSTFIELDS, json); \ + /* HACK: it fails for some reason if we don't do this, seems to be a bug in curl */ \ + CString tmp_json_for_curl_setopt = json; \ + curl_easy_setopt (reai->curl, CURLOPT_POSTFIELDS, tmp_json_for_curl_setopt); \ \ /* WARN: this method will fail if MAKE request makes any jump */ \ MAKE_REQUEST (expected_retcode, expected_response); \ @@ -460,7 +462,7 @@ ReaiResponse* reai_request (Reai* reai, ReaiRequest* request, ReaiResponse* resp request->poll_ai_decompilation.function_id ); SET_METHOD ("GET"); - MAKE_REQUEST (200, REAI_RESPONSE_TYPE_BEGIN_AI_DECOMPILATION); + MAKE_REQUEST (200, REAI_RESPONSE_TYPE_POLL_AI_DECOMPILATION); break; } @@ -1014,3 +1016,63 @@ CStrVec* reai_get_available_models (Reai* reai, ReaiResponse* response) { return NULL; } } + +Reai* reai_begin_ai_decompilation (Reai* reai, ReaiResponse* response, ReaiFunctionId fn_id) { + RETURN_VALUE_IF (!reai || !response || !fn_id, NULL, ERR_INVALID_ARGUMENTS); + + ReaiRequest request = {0}; + request.type = REAI_REQUEST_TYPE_BEGIN_AI_DECOMPILATION; + request.begin_ai_decompilation.function_id = fn_id; + + if ((response = reai_request (reai, &request, response))) { + switch (response->type) { + case REAI_RESPONSE_TYPE_BEGIN_AI_DECOMPILATION : { + return response->begin_ai_decompilation.status ? reai : NULL; + } + case REAI_RESPONSE_TYPE_VALIDATION_ERR : { + REAI_LOG_ERROR ("reveng.ai request returned validation error."); + return NULL; + } + default : { + RETURN_VALUE_IF_REACHED (NULL, "Unexpected response type."); + } + } + } else { + REAI_LOG_ERROR ("Failed to make reveng.ai request"); + return NULL; + } +} + +ReaiAiDecompilationStatus + reai_poll_ai_decompilation (Reai* reai, ReaiResponse* response, ReaiFunctionId fn_id) { + RETURN_VALUE_IF ( + !reai || !response || !fn_id, + REAI_AI_DECOMPILATION_STATUS_ERROR, + ERR_INVALID_ARGUMENTS + ); + + ReaiRequest request = {0}; + request.type = REAI_REQUEST_TYPE_POLL_AI_DECOMPILATION; + request.poll_ai_decompilation.function_id = fn_id; + + if ((response = reai_request (reai, &request, response))) { + switch (response->type) { + case REAI_RESPONSE_TYPE_POLL_AI_DECOMPILATION : { + return response->poll_ai_decompilation.data.status; + } + case REAI_RESPONSE_TYPE_VALIDATION_ERR : { + REAI_LOG_ERROR ("reveng.ai request returned validation error."); + return REAI_AI_DECOMPILATION_STATUS_ERROR; + } + default : { + RETURN_VALUE_IF_REACHED ( + REAI_AI_DECOMPILATION_STATUS_ERROR, + "Unexpected response type." + ); + } + } + } else { + REAI_LOG_ERROR ("Failed to make reveng.ai request"); + return REAI_AI_DECOMPILATION_STATUS_ERROR; + } +} diff --git a/Source/Reai/Api/Response.c b/Source/Reai/Api/Response.c index 9c66d1f..9d6b471 100644 --- a/Source/Reai/Api/Response.c +++ b/Source/Reai/Api/Response.c @@ -428,17 +428,27 @@ HIDDEN ReaiResponse* reai_response_init_for_type (ReaiResponse* response, ReaiRe case REAI_RESPONSE_TYPE_BEGIN_AI_DECOMPILATION : { response->type = REAI_RESPONSE_TYPE_BEGIN_AI_DECOMPILATION; GET_JSON_BOOL (json, "status", response->begin_ai_decompilation.status); - GET_JSON_STRING (json, "message", response->begin_ai_decompilation.message); + GET_OPTIONAL_JSON_STRING (json, "message", response->begin_ai_decompilation.message); cJSON* errors = cJSON_GetObjectItem (json, "errors"); if (errors) { - GET_JSON_CUSTOM_ARR ( - json, - ReaiApiError, - api_error, - GET_JSON_API_ERROR, - response->begin_ai_decompilation.errors - ); + Size numerr = 0; + + if (cJSON_IsObject (errors)) { + numerr = 1; + } else if (cJSON_IsArray (errors)) { + numerr = cJSON_GetArraySize (errors); + } + + if (numerr) { + GET_JSON_CUSTOM_ARR ( + json, + ReaiApiError, + api_error, + GET_JSON_API_ERROR, + response->begin_ai_decompilation.errors + ); + } } break; } @@ -447,27 +457,48 @@ HIDDEN ReaiResponse* reai_response_init_for_type (ReaiResponse* response, ReaiRe response->type = REAI_RESPONSE_TYPE_POLL_AI_DECOMPILATION; GET_JSON_BOOL (json, "status", response->poll_ai_decompilation.status); - if (response->poll_ai_decompilation.status) { - cJSON* data = cJSON_GetObjectItem (json, "data"); - GET_JSON_STRING (data, "status", response->poll_ai_decompilation.data.status); - GET_JSON_STRING ( - data, - "decompilation", - response->poll_ai_decompilation.data.decompilation - ); + cJSON* data = cJSON_GetObjectItem (json, "data"); + if (data) { + /* get decompilation status */ + CString status = NULL; + GET_JSON_STRING (data, "status", status); + response->poll_ai_decompilation.data.status = + reai_ai_decompilation_status_from_cstr (status); + FREE (status); + + if (response->poll_ai_decompilation.data.status == + REAI_AI_DECOMPILATION_STATUS_SUCCESS) { + GET_OPTIONAL_JSON_STRING ( + data, + "decompilation", + response->poll_ai_decompilation.data.decompilation + ); + } else { + response->poll_ai_decompilation.data.decompilation = NULL; + } } - GET_JSON_STRING (json, "message", response->poll_ai_decompilation.message); + GET_OPTIONAL_JSON_STRING (json, "message", response->poll_ai_decompilation.message); cJSON* errors = cJSON_GetObjectItem (json, "errors"); if (errors) { - GET_JSON_CUSTOM_ARR ( - json, - ReaiApiError, - api_error, - GET_JSON_API_ERROR, - response->poll_ai_decompilation.errors - ); + Size numerr = 0; + + if (cJSON_IsObject (errors)) { + numerr = 1; + } else if (cJSON_IsArray (errors)) { + numerr = cJSON_GetArraySize (errors); + } + + if (numerr) { + GET_JSON_CUSTOM_ARR ( + json, + ReaiApiError, + api_error, + GET_JSON_API_ERROR, + response->poll_ai_decompilation.errors + ); + } } break; } @@ -648,7 +679,7 @@ PRIVATE Float64* json_response_get_f64 (cJSON* json, CString name, Float64* num) /** * @b Helper method to extract a String field form given JSON. * - * The returned string is not strdupped and hence user must NOT free it + * The returned string is strdup and hence user must be free it * after use. * * @param json[in] JSON Object containing the string field. @@ -670,19 +701,19 @@ PRIVATE CString json_response_get_string (cJSON* json, CString name) { ); /* copy value */ - CString str = NULL; - RETURN_VALUE_IF (!(str = cJSON_GetStringValue (str_value)), NULL, ERR_OUT_OF_MEMORY); - - return str; + CString val = cJSON_GetStringValue (str_value); + if (val) { + return strdup (val); + } + return NULL; } /** + * Returned CStrVec containes strdupped values of given string array object. + * The returned string must be freed after use. + * * @param json[in] JSON Object containing the string field. * @param name[in] Name of string field. - * @param buf[out] Buffer where string list will be written to. - * @param buf_cap[in,out] Pointer to variable that must contain the current - * capacity of given buffer. If buffer is resized, new capacity will be - * stored in this variable through this pointer. * * @return @c buf if field is present. * @return @c NULL otherwise. @@ -720,11 +751,8 @@ PRIVATE CStrVec* json_response_get_string_arr (cJSON* json, CString name) { ); CString loc = cJSON_GetStringValue (location); - if (!reai_cstr_vec_append (vec, &loc)) { - PRINT_ERR ("Failed to append value to list."); - reai_cstr_vec_destroy (vec); - return NULL; - } + loc = loc ? strdup (loc) : NULL; + reai_cstr_vec_append (vec, &loc); } return vec; @@ -750,10 +778,30 @@ HIDDEN ReaiResponse* reai_response_reset (ReaiResponse* response) { if (response->validation_error.locations) { reai_cstr_vec_destroy (response->validation_error.locations); - response->validation_error.locations = NULL; + FREE (response->validation_error.message); + FREE (response->validation_error.type); + memset (&response->validation_error, 0, sizeof (response->validation_error)); } switch (response->type) { + case REAI_RESPONSE_TYPE_DELETE_ANALYSIS : + case REAI_RESPONSE_TYPE_HEALTH_CHECK : { + FREE (response->health_check.message); + break; + } + + case REAI_RESPONSE_TYPE_AUTH_CHECK : { + FREE (response->auth_check.message); + break; + } + + + case REAI_RESPONSE_TYPE_UPLOAD_FILE : { + FREE (response->upload_file.message); + FREE (response->upload_file.sha_256_hash); + break; + } + case REAI_RESPONSE_TYPE_BASIC_FUNCTION_INFO : { if (response->basic_function_info.fn_infos) { reai_fn_info_vec_destroy (response->basic_function_info.fn_infos); @@ -770,6 +818,11 @@ HIDDEN ReaiResponse* reai_response_reset (ReaiResponse* response) { break; } + case REAI_RESPONSE_TYPE_RENAME_FUNCTION : { + FREE (response->rename_function.msg); + break; + } + case REAI_RESPONSE_TYPE_SEARCH : { if (response->search.query_results) { reai_query_result_vec_destroy (response->search.query_results); @@ -780,6 +833,11 @@ HIDDEN ReaiResponse* reai_response_reset (ReaiResponse* response) { case REAI_RESPONSE_TYPE_BATCH_BINARY_SYMBOL_ANN : case REAI_RESPONSE_TYPE_BATCH_FUNCTION_SYMBOL_ANN : { + if (response->batch_binary_symbol_ann.settings.collections) { + reai_cstr_vec_destroy (response->batch_binary_symbol_ann.settings.collections); + response->batch_binary_symbol_ann.settings.collections = NULL; + } + if (response->batch_binary_symbol_ann.function_matches) { reai_ann_fn_match_vec_destroy (response->batch_binary_symbol_ann.function_matches); response->batch_binary_symbol_ann.function_matches = NULL; @@ -789,12 +847,7 @@ HIDDEN ReaiResponse* reai_response_reset (ReaiResponse* response) { reai_cstr_vec_destroy (response->batch_binary_symbol_ann.settings.collections); response->batch_binary_symbol_ann.settings.collections = NULL; } - } - - case REAI_RESPONSE_TYPE_RENAME_FUNCTION : { - if (response->rename_function.msg) { - FREE (response->rename_function.msg); - } + break; } case REAI_RESPONSE_TYPE_GET_MODELS : { @@ -802,6 +855,25 @@ HIDDEN ReaiResponse* reai_response_reset (ReaiResponse* response) { reai_cstr_vec_destroy (response->get_models.models); response->get_models.models = NULL; } + break; + } + + case REAI_RESPONSE_TYPE_BEGIN_AI_DECOMPILATION : { + if (response->begin_ai_decompilation.errors) { + reai_api_error_vec_destroy (response->begin_ai_decompilation.errors); + response->begin_ai_decompilation.errors = NULL; + } + break; + } + + case REAI_RESPONSE_TYPE_POLL_AI_DECOMPILATION : { + if (response->poll_ai_decompilation.errors) { + reai_api_error_vec_destroy (response->poll_ai_decompilation.errors); + response->poll_ai_decompilation.errors = NULL; + } + FREE (response->poll_ai_decompilation.message); + FREE (response->poll_ai_decompilation.data.decompilation); + break; } default : diff --git a/Source/Reai/Config.c b/Source/Reai/Config.c index bb33ad3..8113038 100644 --- a/Source/Reai/Config.c +++ b/Source/Reai/Config.c @@ -10,6 +10,9 @@ #include #include +// TODO: remove toml dependency, by converting the toml file to a simple kv config file +// simply store k=v in a single line where anything before = is a key and after = is the corresponding value + /** * @b Get default file path where .reait.toml is supposed to be present. *