Created
February 23, 2023 17:50
-
-
Save kala13x/6a891e015bdc1fb6d2849b7ec686a530 to your computer and use it in GitHub Desktop.
XDB File Reader for CLI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* @file xdb/xdb.c | |
* | |
* 2015-2022 Sun Dro ([email protected]) | |
* | |
* @brief Implementation of the XDB file parser | |
* Using: https://github.com/kala13x/libxutils | |
*/ | |
#include <xutils/xstd.h> | |
#include <xutils/crypt.h> | |
#include <xutils/xjson.h> | |
#include <xutils/xtype.h> | |
#include <xutils/xtime.h> | |
#include <xutils/xcli.h> | |
#include <xutils/xstr.h> | |
#include <xutils/xlog.h> | |
#include <xutils/xfs.h> | |
#define IS_MATCHING(a,b) (!xstrused(a) || (b && strstr(b, a))) | |
#define XKEY_SIZE XSHA256_LENGTH + 1 | |
#define XKEY_LEN 128 | |
#define XDB_VER_MAX 1 | |
#define XDB_VER_MIN 0 | |
#define XDB_BUILD 10 | |
extern char *optarg; | |
typedef struct { | |
char sInpit[XPATH_MAX]; | |
char sOutput[XPATH_MAX]; | |
char sKey[XKEY_SIZE]; | |
char sPersonalID[XSTR_TINY]; | |
char sBirthDate[XSTR_TINY]; | |
char sAddress[XSTR_TINY]; | |
char sSurname[XSTR_TINY]; | |
char sFather[XSTR_TINY]; | |
char sName[XSTR_TINY]; | |
size_t nAgeFrom; | |
size_t nAgeTo; | |
xtime_t timeNow; | |
xbool_t bForce; | |
} xdb_args_t; | |
void Greet(const char *pName) | |
{ | |
printf("================================================\n"); | |
printf("%s - Version: %d.%d build %d (%s)\n", pName, | |
XDB_VER_MAX, XDB_VER_MIN, XDB_BUILD, __DATE__); | |
printf("================================================\n"); | |
} | |
void Usage(const char *pName) | |
{ | |
printf("================================================\n"); | |
printf("XDB Reader - Version: %d.%d build %d (%s)\n", | |
XDB_VER_MAX, XDB_VER_MIN, XDB_BUILD, __DATE__); | |
printf("================================================\n"); | |
printf("Usage: %s [options] [filters]\n\n", pName); | |
printf("Options:\n"); | |
printf(" -i <input> %s# Input XDB file path%s*%s\n", XSTR_FMT_DIM, XSTR_CLR_RED, XSTR_FMT_RESET); | |
printf(" -o <output> %s# JSON output file path%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -w %s# Force overwrite output%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -v %s# Enable verbosity%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -h %s# Print version and usage%s\n\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf("Filters%s%s*%s:\n", XSTR_FMT_DIM, XSTR_CLR_RED, XSTR_FMT_RESET); | |
printf(" -n <name> %s# First name filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -s <surname> %s# Surname filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -f <father-name> %s# Father name filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -p <personal-id> %s# Personal ID filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -d <birth-date> %s# Birth date filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -a <address> %s# Address filter%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf(" -g <age> %s# Age filter%s\n\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf("Notes:\n%s1) Required an input file and at least one filter.%s\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf("%s2) Age filter can also be a range like: -a \"10-35\"%s\n\n", XSTR_FMT_DIM, XSTR_FMT_RESET); | |
printf("Example:\n%s%s -i data.xdb -n jhon -s doe -d 1993%s\n\n", XSTR_FMT_DIM, pName, XSTR_FMT_RESET); | |
} | |
static xbool_t ParseArgs(xdb_args_t *pArgs, int argc, char *argv[]) | |
{ | |
memset(pArgs, 0, sizeof(xdb_args_t)); | |
xbool_t bVerbose = XFALSE; | |
int nChar, nFilters = 0; | |
char sPass[XLINE_MAX]; | |
char sBuff[XLINE_MAX]; | |
char sAge[XSTR_TINY]; | |
while ((nChar = getopt(argc, argv, "i:o:a:f:g:n:s:p:d:v1:w1:h1")) != -1) | |
{ | |
switch (nChar) | |
{ | |
case 'i': | |
xstrncpy(pArgs->sInpit, sizeof(pArgs->sInpit), optarg); | |
break; | |
case 'o': | |
xstrncpy(pArgs->sOutput, sizeof(pArgs->sOutput), optarg); | |
break; | |
case 'a': | |
xstrncpy(pArgs->sAddress, sizeof(pArgs->sAddress), xstrtoen(sBuff, sizeof(sBuff), optarg)); | |
nFilters++; | |
break; | |
case 'f': | |
xstrncpy(pArgs->sFather, sizeof(pArgs->sFather), xstrtoen(sBuff, sizeof(sBuff), optarg)); | |
xloge("%s", pArgs->sFather); | |
nFilters++; | |
break; | |
case 'n': | |
xstrncpy(pArgs->sName, sizeof(pArgs->sName), xstrtoen(sBuff, sizeof(sBuff), optarg)); | |
nFilters++; | |
break; | |
case 's': | |
xstrncpy(pArgs->sSurname, sizeof(pArgs->sSurname), xstrtoen(sBuff, sizeof(sBuff), optarg)); | |
nFilters++; | |
break; | |
case 'p': | |
xstrncpy(pArgs->sPersonalID, sizeof(pArgs->sPersonalID), optarg); | |
nFilters++; | |
break; | |
case 'd': | |
xstrncpy(pArgs->sBirthDate, sizeof(pArgs->sBirthDate), optarg); | |
nFilters++; | |
break; | |
case 'g': | |
xstrncpy(sAge, sizeof(sAge), optarg); | |
nFilters++; | |
break; | |
case 'w': | |
pArgs->bForce = XTRUE; | |
break; | |
case 'v': | |
bVerbose = XTRUE; | |
break; | |
case 'h': | |
default: | |
Usage(argv[0]); | |
return XSTDERR; | |
} | |
} | |
if (xstrused(sAge)) | |
{ | |
if (!isdigit(sAge[0])) | |
{ | |
xloge("Age filter must be a number."); | |
Usage(argv[0]); | |
return XSTDERR; | |
} | |
xarray_t *pArr = xstrsplit(sAge, "-"); | |
if (pArr != NULL) | |
{ | |
const char *pFrom = XArray_GetData(pArr, 0); | |
const char *pTo = XArray_GetData(pArr, 1); | |
if (xstrused(pFrom)) pArgs->nAgeFrom = atoi(pFrom); | |
if (xstrused(pTo)) pArgs->nAgeTo = atoi(pTo); | |
XTime_Get(&pArgs->timeNow); | |
XArray_Destroy(pArr); | |
} | |
} | |
if (pArgs->bForce == XFALSE && | |
xstrused(pArgs->sOutput) && | |
XPath_Exists(pArgs->sOutput)) | |
{ | |
xlogw("File already exists: %s", pArgs->sOutput); | |
xlogn("Use option -w to force overwrite output"); | |
return XSTDERR; | |
} | |
if (!xstrused(pArgs->sInpit)) | |
{ | |
xloge("Please specify the input file."); | |
Usage(argv[0]); | |
return XSTDERR; | |
} | |
if (!nFilters) | |
{ | |
xloge("Please specify at least one filter."); | |
Usage(argv[0]); | |
return XSTDERR; | |
} | |
if (bVerbose) | |
{ | |
xlog_timing(XLOG_TIME); | |
xlog_enable(XLOG_INFO); | |
} | |
XASSERT((XCLI_GetPass("Enter password: ", sPass, sizeof(sPass)) > 0), xthrow(NULL)); | |
XCrypt_SHA256H(pArgs->sKey, sizeof(pArgs->sKey), (uint8_t*)sPass, strlen(sPass)); | |
return XSTDOK; | |
} | |
static const char *GetEntry(xarray_t *pArray, size_t nIndex, const char* pRet) | |
{ | |
const char *pEntry = (const char*)XArray_GetData(pArray, nIndex); | |
return (pEntry != NULL && pEntry[0] != XSTR_NUL) ? pEntry : pRet; | |
} | |
static xbool_t CheckAge(xdb_args_t *pArgs, const char *pBirthDate) | |
{ | |
if (!pArgs->nAgeFrom && !pArgs->nAgeTo) return XTRUE; | |
else if (pBirthDate == NULL) return XFALSE; | |
xtime_t birthDate; | |
XTime_FromRstr(&birthDate, pBirthDate); | |
size_t nAge = (size_t)XTime_Diff(&pArgs->timeNow, &birthDate, XTIME_DIFF_YEAR); | |
if (pArgs->nAgeFrom && !pArgs->nAgeTo && nAge != pArgs->nAgeFrom) return XFALSE; | |
if (pArgs->nAgeTo && !pArgs->nAgeFrom && nAge != pArgs->nAgeTo) return XFALSE; | |
if (pArgs->nAgeFrom && pArgs->nAgeFrom > nAge) return XFALSE; | |
if (pArgs->nAgeTo && pArgs->nAgeTo < nAge) return XFALSE; | |
return XTRUE; | |
} | |
static xbool_t CheckAddress(xdb_args_t *pArgs, const char *pAddrss, const char *pRegion) | |
{ | |
XASSERT_RET(xstrused(pArgs->sAddress), XTRUE); | |
XASSERT_RET(!IS_MATCHING(pArgs->sAddress, pAddrss), XTRUE); | |
XASSERT_RET(!IS_MATCHING(pArgs->sAddress, pRegion), XTRUE); | |
return XFALSE; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
xlog_defaults(); | |
size_t nLength; | |
xdb_args_t args; | |
if (ParseArgs(&args, argc, argv) != XSTDOK) return XSTDERR; | |
xlogi("Loading database..."); | |
uint8_t *pInput = XPath_Load(args.sInpit, &nLength); | |
XASSERT(pInput, xthrow("Failed to load input file: %s", strerror(errno))); | |
xlogi("Decrypting database..."); | |
char *pInputRaw = (char*)XDecrypt_AES(pInput, &nLength, (uint8_t*)args.sKey, XKEY_LEN, NULL); | |
XASSERT_FREE(pInputRaw, pInput, xthrow("Failed to decrypt input data: %s", strerror(errno))); | |
free(pInput); | |
xlogi("Searching entries..."); | |
xjson_obj_t *pJsonObj = XJSON_NewArray(NULL, XTRUE); | |
XASSERT_FREE(pJsonObj, pInputRaw, xthrow(NULL)); | |
char sBuff[XLINE_MAX]; | |
char *pSavePtr = NULL; | |
char *pLine = xstrtok(pInputRaw, "\n", &pSavePtr); | |
while (pLine != NULL) | |
{ | |
xarray_t *pTokens = xstrsplit(pLine, ","); | |
pLine = xstrtok(NULL, "\n", &pSavePtr); | |
if (pTokens == NULL) continue; | |
const char *pName = GetEntry(pTokens, 0, NULL); | |
const char *pSurname = GetEntry(pTokens, 1, NULL); | |
const char *pPersonalId = GetEntry(pTokens, 2, NULL); | |
const char *pFatherName = GetEntry(pTokens, 3, NULL); | |
const char *pBirthDate = GetEntry(pTokens, 4, NULL); | |
const char *pDocumentId = GetEntry(pTokens, 5, NULL); | |
const char *pAddress = GetEntry(pTokens, 6, NULL); | |
const char *pRegion = GetEntry(pTokens, 7, NULL); | |
if (!IS_MATCHING(args.sName, pName) || | |
!IS_MATCHING(args.sSurname, pSurname) || | |
!IS_MATCHING(args.sPersonalID, pPersonalId) || | |
!IS_MATCHING(args.sBirthDate, pBirthDate) || | |
!IS_MATCHING(args.sFather, pFatherName) || | |
!CheckAddress(&args, pAddress, pRegion) || | |
!CheckAge(&args, pBirthDate)) | |
{ | |
XArray_Destroy(pTokens); | |
continue; | |
} | |
xjson_obj_t *pObj = XJSON_NewObject(NULL, XFALSE); | |
XJSON_AddString(pObj, "name", xstrtoge(sBuff, sizeof(sBuff), pName)); | |
XJSON_AddString(pObj, "surname", xstrtoge(sBuff, sizeof(sBuff), pSurname)); | |
XJSON_AddString(pObj, "fatherName", xstrtoge(sBuff, sizeof(sBuff), pFatherName)); | |
XJSON_AddString(pObj, "address", xstrtoge(sBuff, sizeof(sBuff), pAddress)); | |
XJSON_AddString(pObj, "region", xstrtoge(sBuff, sizeof(sBuff), pRegion)); | |
XJSON_AddString(pObj, "personalId", pPersonalId); | |
XJSON_AddString(pObj, "documentId", pDocumentId); | |
XJSON_AddString(pObj, "birthDate", pBirthDate); | |
XJSON_AddObject(pJsonObj, pObj); | |
XArray_Destroy(pTokens); | |
} | |
free(pInputRaw); | |
size_t nCount = XJSON_GetArrayLength(pJsonObj); | |
if (nCount) | |
{ | |
xbool_t bWriteInFile = xstrused(args.sOutput); | |
xlogi("Dumping JSON object..."); | |
char *pOutput = bWriteInFile ? | |
XJSON_DumpObj(pJsonObj, 4, &nLength) : | |
XJSON_FormatObj(pJsonObj, 4, NULL, &nLength); | |
XASSERT_CALL(pOutput, XJSON_FreeObject, pJsonObj, | |
xthrow("Failed to serialize JSON object: %s", strerror(errno))); | |
if (bWriteInFile) | |
{ | |
xlogi("Writing results in the file: %s", args.sOutput); | |
if (XPath_Write(args.sOutput, "cwt", (uint8_t*)pOutput, nLength) <= 0) | |
xloge("Failed to write data in the file: %s", strerror(errno)); | |
} | |
else printf("\n%s\n\n", pOutput); | |
free(pOutput); | |
} | |
xlogi("Found %zu %s with specified filters.", | |
nCount, nCount > 1 ? "entries" : "entry"); | |
XJSON_FreeObject(pJsonObj); | |
return XSTDOK; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment