Skip to content

Instantly share code, notes, and snippets.

@kala13x
Created February 23, 2023 17:50
Show Gist options
  • Save kala13x/6a891e015bdc1fb6d2849b7ec686a530 to your computer and use it in GitHub Desktop.
Save kala13x/6a891e015bdc1fb6d2849b7ec686a530 to your computer and use it in GitHub Desktop.
XDB File Reader for CLI
/*!
* @file xdb/xdb.c
*
* 2015-2022 Sun Dro (f4tb0y@protonmail.com)
*
* @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