<commit_before>/*
 *
 *		iddb.c - Identity database / VCF parser
 *
 */

#include "iddb.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <sqlite3.h>

int usage(const char *err, const char *cmd) {

	if(err[0]) printf("Error: %s\n", err);
	if(errno) printf("Error: %s\n", strerror(errno));

	printf("%s %s\n", cmd, VER);
	printf("Usage: %s [options] [operation] [args]\n\n", cmd);
	printf("Operations:\n"
			"h[elp]    Display this text\n"
			"c[reate]  Create or reset database\n"
			"a[ll]     Search and display all contact information\n"
			"i[mport]  Import VCF from file(s) or dir(s)\n"
			"e[xport]  Search and export output to VCF\n"
			"p[hone]   Search and display phone numbers\n"
			"m[ail]    Search and display email addresses\n"
			"n[ew]     Interactively add new contact\n"
			"d[elete]  Delete user with specified ID\n\n"

			"Options:\n"
			"-d dir    Specify output directory for export\n"
			"-f file   Specify database file\n"
			"-h        Display this text\n"
			"-n num    Display max num results\n"
			"-v        Increase verbosity level:\n");

	return errno;
}

// Check for valid operation
static int chops(const char *cop, size_t mxl) {

	char vops[9][7] = {"create", "delete", "import", "export", "help",
		"phone", "mail", "new", "all"};

	int clen = strlen(cop);
	if(clen > mxl) return -1;

	char lccop[(clen +1)];
	int opnum = sizeof(vops) / sizeof(vops[0]);

	unsigned int a = 0;

	for(a = 0; a < clen; a++) lccop[a] = tolower(cop[a]);

	for(a = 0; a < opnum; a++) {
		if(clen == 1) { if(lccop[0] == vops[a][0]) return a; }
		else { if(!strcmp(lccop, vops[a])) return a; }
	}

	errno = EINVAL;
	return -1;
}

// Fetch index size
static int getindex(sqlite3 *db, const int verb) {

	int ret = 0;
	unsigned int a = 0;

	char *sql = calloc(BBCH, sizeof(char));
	sqlite3_stmt *stmt;

	snprintf(sql, BBCH, "SELECT num FROM ctr;");

	int dbop = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
	if (verb > 1) printf("Query: %s\n", sql);

	// TODO: Duplicates at low count. Why?
	while((dbop = sqlite3_step(stmt)) != SQLITE_DONE) {
		if(dbop == SQLITE_ROW) ret = matoi((char*)sqlite3_column_text(stmt, a));
	}

	free(sql);
	return ret;
}

// Export card to file
static char *ecard(card *c, char *fpath, int psz, const char *edir,
	const int verb) {

	char *rstr = calloc(EXPLEN + 1, sizeof(char));
	unsigned int a = 0;

	randstr(rstr, EXPLEN);

	if(!edir[0]) strncpy(fpath, getenv("PWD"), MBCH);
	else strncpy(fpath, edir, MBCH);

	int dlen = strlen(fpath);
	if(fpath[(dlen)] != DDIV) fpath[(dlen)] = DDIV;
	fpath[(dlen + 1)] = '\0';

	strncat(fpath, rstr, MBCH);
	strncat(fpath, EXPSUF, MBCH);

	FILE *f = fopen(fpath, "w");

	fprintf(f, "BEGIN:VCARD\nVERSION:3.0\n");
	fprintf(f, "FN:%s\n", c->fn);
	if(c->uid[0]) fprintf(f, "UID:%s\n", c->uid);
	if(c->org[0]) fprintf(f, "ORG:%s\n", c->org);
	for(a = 0; a < c->emnum; a++) fprintf(f, "EMAIL;TYPE=INTERNET:%s\n", c->em[a]);
	for(a = 0; a < c->phnum; a++) fprintf(f, "TEL;TYPE=VOICE:%s\n", c->ph[a]);
	fprintf(f, "END:VCARD\n");

	fclose(f);
	free(rstr);

	return fpath;
}

// Import card to struct
static card *icard(card *c, FILE *f, sqlite3 *db, const int verb) {

	char *buf = malloc(MBCH);
	char *fphone = malloc(PHLEN);
	int verc = 0;

	c->phnum = 0;
	c->emnum = 0;

	while(fgets(buf, MBCH, f)){

		if(!strst(buf, STARTKEY) || !strst(buf, STOPKEY)) {
			verc++;

		} else if(!strst(buf, UIDKEY)) {
			esccpy(c->uid, robj(buf), ESCCHAR, ESCCHAR, ULEN);

		} else if(!strst(buf, FNKEY)) {
			esccpy(c->fn, robj(buf), ESCCHAR, ESCCHAR, NALEN);

		} else if(!strst(buf, ORGKEY)) {
			esccpy(c->org, robj(buf), ESCCHAR, ESCCHAR, ORLEN);

		} else if(!strst(buf, EMKEY)) {
			if(isemail(buf))
				esccpy(c->em[c->emnum++], robj(buf), ESCCHAR, ESCCHAR, EMLEN);

		} else if(!strst(buf, PHKEY)) {
			if(isphone(buf, PHLEN))
				esccpy(c->ph[c->phnum++], formphone(fphone, robj(buf)),
					ESCCHAR, ESCCHAR, PHLEN);
		}
	}

	if(verc == 2) {
		c->lid = getindex(db, verb);
		c->lid++;
	}

	free(buf);
	free(fphone);
	return c;
}

// Create (or reset) database table
static int ctable(sqlite3 *db) {

	char *sql = calloc(DBCH, sizeof(char));
	char *err = 0;
	int dbrc = 0;

	strncpy(sql, "DROP TABLE IF EXISTS id;"
		"CREATE TABLE id(lid INT, uid TEXT, fn TEXT,"
		" org TEXT, ph TEXT, em TEXT);", DBCH);<commit_msg>Bugfix: Incorrect data passed to isphone()<commit_after>/*
 *
 *		iddb.c - Identity database / VCF parser
 *
 */

#include "iddb.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <sqlite3.h>

int usage(const char *err, const char *cmd) {

	if(err[0]) printf("Error: %s\n", err);
	if(errno) printf("Error: %s\n", strerror(errno));

	printf("%s %s\n", cmd, VER);
	printf("Usage: %s [options] [operation] [args]\n\n", cmd);
	printf("Operations:\n"
			"h[elp]    Display this text\n"
			"c[reate]  Create or reset database\n"
			"a[ll]     Search and display all contact information\n"
			"i[mport]  Import VCF from file(s) or dir(s)\n"
			"e[xport]  Search and export output to VCF\n"
			"p[hone]   Search and display phone numbers\n"
			"m[ail]    Search and display email addresses\n"
			"n[ew]     Interactively add new contact\n"
			"d[elete]  Delete user with specified ID\n\n"

			"Options:\n"
			"-d dir    Specify output directory for export\n"
			"-f file   Specify database file\n"
			"-h        Display this text\n"
			"-n num    Display max num results\n"
			"-v        Increase verbosity level:\n");

	return errno;
}

// Check for valid operation
static int chops(const char *cop, size_t mxl) {

	char vops[9][7] = {"create", "delete", "import", "export", "help",
		"phone", "mail", "new", "all"};

	int clen = strlen(cop);
	if(clen > mxl) return -1;

	char lccop[(clen +1)];
	int opnum = sizeof(vops) / sizeof(vops[0]);

	unsigned int a = 0;

	for(a = 0; a < clen; a++) lccop[a] = tolower(cop[a]);

	for(a = 0; a < opnum; a++) {
		if(clen == 1) { if(lccop[0] == vops[a][0]) return a; }
		else { if(!strcmp(lccop, vops[a])) return a; }
	}

	errno = EINVAL;
	return -1;
}

// Fetch index size
static int getindex(sqlite3 *db, const int verb) {

	int ret = 0;
	unsigned int a = 0;

	char *sql = calloc(BBCH, sizeof(char));
	sqlite3_stmt *stmt;

	snprintf(sql, BBCH, "SELECT num FROM ctr;");

	int dbop = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
	if (verb > 1) printf("Query: %s\n", sql);

	// TODO: Duplicates at low count. Why?
	while((dbop = sqlite3_step(stmt)) != SQLITE_DONE) {
		if(dbop == SQLITE_ROW) ret = matoi((char*)sqlite3_column_text(stmt, a));
	}

	free(sql);
	return ret;
}

// Export card to file
static char *ecard(card *c, char *fpath, int psz, const char *edir,
	const int verb) {

	char *rstr = calloc(EXPLEN + 1, sizeof(char));
	unsigned int a = 0;

	randstr(rstr, EXPLEN);

	if(!edir[0]) strncpy(fpath, getenv("PWD"), MBCH);
	else strncpy(fpath, edir, MBCH);

	int dlen = strlen(fpath);
	if(fpath[(dlen)] != DDIV) fpath[(dlen)] = DDIV;
	fpath[(dlen + 1)] = '\0';

	strncat(fpath, rstr, MBCH);
	strncat(fpath, EXPSUF, MBCH);

	FILE *f = fopen(fpath, "w");

	fprintf(f, "BEGIN:VCARD\nVERSION:3.0\n");
	fprintf(f, "FN:%s\n", c->fn);
	if(c->uid[0]) fprintf(f, "UID:%s\n", c->uid);
	if(c->org[0]) fprintf(f, "ORG:%s\n", c->org);
	for(a = 0; a < c->emnum; a++) fprintf(f, "EMAIL;TYPE=INTERNET:%s\n", c->em[a]);
	for(a = 0; a < c->phnum; a++) fprintf(f, "TEL;TYPE=VOICE:%s\n", c->ph[a]);
	fprintf(f, "END:VCARD\n");

	fclose(f);
	free(rstr);

	return fpath;
}

// Import card to struct
static card *icard(card *c, FILE *f, sqlite3 *db, const int verb) {

	char *buf = malloc(MBCH);
	char *sbuf = malloc(SBCH);
	char *fphone = malloc(PHLEN);
	int verc = 0;

	c->phnum = 0;
	c->emnum = 0;

	while(fgets(buf, MBCH, f)){

		if(!strst(buf, STARTKEY) || !strst(buf, STOPKEY)) {
			verc++;

		} else if(!strst(buf, UIDKEY)) {
			esccpy(c->uid, robj(buf), ESCCHAR, ESCCHAR, ULEN);

		} else if(!strst(buf, FNKEY)) {
			esccpy(c->fn, robj(buf), ESCCHAR, ESCCHAR, NALEN);

		} else if(!strst(buf, ORGKEY)) {
			esccpy(c->org, robj(buf), ESCCHAR, ESCCHAR, ORLEN);

		} else if(!strst(buf, EMKEY)) {
			if(isemail(buf))
				esccpy(c->em[c->emnum++], robj(buf), ESCCHAR, ESCCHAR, EMLEN);

		} else if(!strst(buf, PHKEY)) {
			strncpy(sbuf, robj(buf), SBCH);
			if(isphone(sbuf, PHLEN)) {
				esccpy(c->ph[c->phnum++], formphone(fphone, sbuf),
					ESCCHAR, ESCCHAR, PHLEN);
			}
		}
	}

	if(verc == 2) {
		c->lid = getindex(db, verb);
		c->lid++;
	}

	free(buf);
	free(sbuf);
	free(fphone);
	return c;
}

// Create (or reset) database table
static int ctable(sqlite3 *db) {

	char *sql = calloc(DBCH, sizeof(char));
	char *err = 0;
	int dbrc = 0;

	strncpy(sql, "DROP TABLE IF EXISTS id;"
		"CREATE TABLE id(lid INT, uid TEXT, fn TEXT,"
		" org TEXT, ph TEXT, em TEXT);", DBCH);
{'fraction_non_alphanumeric': 0.19726211849192102, 'fraction_numerical': 0.009874326750448833, 'mean_word_length': 3.7208686440677967, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2151074', 'n_tokens_mistral': 4098, 'n_tokens_neox': 3820, 'n_words': 975}