const args = process.argv,
	fs = require('fs');
const {
	Curl,
	CurlHttpVersion
} = require('node-libcurl');
const cur1 = new Curl();
cur1.setOpt('SSL_VERIFYPEER', false);
cur1.setOpt('HTTP_VERSION', CurlHttpVersion.V2PriorKnowledge);
cur1.setOpt('FOLLOWLOCATION', true);
cur1.setOpt('CONNECTTIMEOUT', 8);
cur1.setOpt('TIMEOUT', 600);
const Fconfig = require('os').homedir() + '/.cfdomains.cfg',
	Fproxy = require('os').homedir() + '/.cfdomains_sox.cfg';
let Dlist = './cfdomains_Data/',
	myproxy = '';
function forceExit(m) {
	console.log('\x1b[31m%s\x1b[0m', m);
	process.exit();
}
function is_domain(x) {
	return /^([a-z0-9]{1})([a-z0-9.-]{0,254})\.([a-z]{2,50})$/.test(x);
}
if (fs.existsSync(Fconfig)) {
	Dlist = fs.readFileSync(Fconfig);
	if (!fs.existsSync(Dlist)) {
		try {
			fs.unlinkSync(Fconfig);
		} catch (e) {}
		forceExit('List directory is not okay! Fix this by running --dir');
	}
}
if (fs.existsSync(Fproxy)) {
	myproxy = fs.readFileSync(Fproxy);
	if (myproxy.indexOf(':') < 6) {
		myproxy = '';
	}
}
function showUsage() {
	console.log("\n	\x1b[33mCfDomains\x1b[0m  v1.0.2.3\n");
	console.log('\x1b[36mUsage:\x1b[0m');
	console.log('	cfdomains \x1b[43mexample.com\x1b[0m');
	console.log('	cfdomains --dir');
	console.log('	cfdomains --proxy');
	console.log('	cfdomains [--dl|--dl2][ |0,1,...,a,b,...z]');
	console.log("	cfdomains [--report|--delist] \x1b[43mexample.com\x1b[0m\n");
	console.log('\x1b[36mDetails:\x1b[0m');
	console.log('	cfdomains \x1b[43mexample.com\x1b[0m');
	console.log('		return "\x1b[32my\x1b[0m", "\x1b[32mn\x1b[0m", or "\x1b[32me\x1b[0m"');
	console.log('			\x1b[32my\x1b[0m: Domain is on the list');
	console.log('			\x1b[32mn\x1b[0m: Domain is not listed');
	console.log("			\x1b[32me\x1b[0m: List data not found (fix it by running --dl)\n");
	console.log('	cfdomains --dir');
	console.log('		Change list directory');
	console.log('		\x1b[4mCurrent directory\x1b[0m: ' + Dlist + "\n");
	console.log('	cfdomains --proxy');
	console.log('		Set or Unset SOCKS proxy');
	console.log('		\x1b[4mCurrent SOCKS proxy\x1b[0m: ' + myproxy + "\n");
	console.log('	cfdomains --dl|dl2');
	console.log('	cfdomains --dl|dl2 \x1b[43ma,b,c\x1b[0m');
	console.log('		\x1b[32mdl\x1b[0m: Download list files from Archive.org');
	console.log('		\x1b[32mdl2\x1b[0m: Download list files from deCloudflare git');
	console.log("		\x1b[32ma,b,c\x1b[0m: Download only these files (comma-separated)\n");
	console.log('	cfdomains --report|delist \x1b[43mexample.com\x1b[0m');
	console.log('		Submit domain to #Karma for automated analysis');
	console.log('		\x1b[4mOnly the domain will be submitted\x1b[0m. We NEVER record anything else.');
	console.log('		\x1b[32mreport\x1b[0m: Report not-yet-listed domain (New Cloudflare)');
	console.log("		\x1b[32mdelist\x1b[0m: Report currently-listed domain (Left Cloudflare)\n");
	console.log();
	process.exit();
}
function chgLDR() {
	const rle = require('readline');
	let rl = rle.createInterface(process.stdin, process.stdout);
	console.log('Current save directory: ' + Dlist);
	console.log('Set "empty" to use default location.');
	rl.question('List save directory: ', (l) => {
		rl.close();
		if (l.length > 1 && l !== 'empty') {
			if (l.indexOf('/', l.length - 1) !== -1) {
				if (fs.existsSync(l) && fs.lstatSync(l).isDirectory()) {
					fs.writeFileSync(Fconfig, l);
				} else {
					forceExit('Directory not found');
				}
			} else {
				forceExit('Directory is not ended with /');
			}
		} else {
			try {
				fs.unlinkSync(Fconfig);
			} catch (e) {}
		}
		process.exit();
	});
}
function chgPROXY() {
	const rle = require('readline');
	let rl = rle.createInterface(process.stdin, process.stdout);
	console.log('Current SOCKS Proxy: ' + myproxy);
	console.log('Set none to use direct connection.');
	rl.question('Input SOCKS Proxy [IP:Port]: ', (l) => {
		rl.close();
		if (/^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\:([0-9]{1,5})$/.test(l)) {
			fs.writeFileSync(Fproxy, l);
		} else {
			try {
				fs.unlinkSync(Fproxy);
			} catch (e) {}
		}
		process.exit();
	});
}
function dlFile(bu, i) {
	return new Promise((okay, nope) => {
		const curl = cur1.dupHandle(false);
		curl.setOpt('URL', bu + 'cloudflare_' + i + '.json');
		curl.setOpt('USERAGENT', 'CfDomains');
		if (myproxy != '') {
			curl.setOpt('HTTPPROXYTUNNEL', 1);
			curl.setOpt('PROXY', 'socks5h://' + myproxy + '/');
		}
		curl.on('error', (er, ec) => {
			curl.close();
			console.log('\x1b[31m%s\x1b[0m', er);
			forceExit('Connection Error while downloading list ' + i);
		});
		curl.on('end', (sc, b) => {
			curl.close();
			if (sc != 200) {
				forceExit('Server error ' + r.statusCode);
			}
			try {
				JSON.parse(b)
			} catch (e1) {
				forceExit('Bad Data Error while downloading list ' + i);
			}
			fs.writeFileSync(Dlist + i, b);
			okay();
		});
		curl.perform();
	});
}
function shuffle(array) {
	let currentIndex = array.length,
		randomIndex;
	while (currentIndex != 0) {
		randomIndex = Math.floor(Math.random() * currentIndex);
		currentIndex--;
		[array[currentIndex], array[randomIndex]] = [
			array[randomIndex], array[currentIndex]
		];
	}
	return array;
}
async function dlFiles(bu) {
	if (!fs.existsSync(Dlist)) {
		fs.mkdirSync(Dlist);
	}
	if (!fs.existsSync(Dlist)) {
		forceExit('Directory not found!');
	}
	if (myproxy == '') {
		console.log('\x1b[36mNOTICE\x1b[0m: You are not using Tor.');
	}
	console.log('Downloading lists: ' + bu);
	let w, names = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
	let aonly = args[3];
	if (aonly && aonly.length > 0) {
		let names1 = [];
		for (x of aonly.split(',')) {
			if (names.indexOf(x) > -1) {
				names1.push(x);
			}
		}
		if (names1.length > 0) {
			names = names1;
		} else {
			forceExit('invalid input');
		}
	}
	shuffle(names);
	for (x of names) {
		console.log('Downloading ' + x);
		w = await dlFile(bu, x);
	}
	for (x of names) {
		console.log('Testing ' + x);
		if (!fs.existsSync(Dlist + x)) {
			forceExit('File not found');
		}
		let data = fs.readFileSync(Dlist + x, {
			encoding: 'utf8'
		});
		try {
			JSON.parse(data)
		} catch (e) {
			forceExit('Bad data found');
		}
	}
	process.exit();
}
function queryDom() {
	let dom = args[2];
	if (!is_domain(dom)) {
		showUsage();
	}
	if (!fs.existsSync(Dlist + dom[0])) {
		process.stdout.write('e');
		process.exit();
	}
	let data = fs.readFileSync(Dlist + dom[0], {
		encoding: 'utf8'
	});
	data = JSON.parse(data);
	if (data[dom]) {
		process.stdout.write('y');
	} else {
		process.stdout.write('n');
	}
	process.exit();
}
function reporter(dom, t) {
	return new Promise((okay, nope) => {
		const curl = cur1.dupHandle(false);
		curl.setOpt('URL', 'https://karma.crimeflare.eu.org/api/is_cf.php');
		curl.setOpt('USERAGENT', 'CfDomains');
		if (myproxy != '') {
			curl.setOpt('HTTPPROXYTUNNEL', 1);
			curl.setOpt('PROXY', 'socks5h://' + myproxy + '/');
		}
		curl.setOpt(Curl.option.POST, true);
		curl.setOpt(Curl.option.POSTFIELDS, 'ana=' + t + '&f=' + dom);
		curl.on('error', (er, ec) => {
			curl.close();
			console.log('\x1b[31m%s\x1b[0m', er);
			forceExit('Connection Error');
		});
		curl.on('end', (sc, b) => {
			curl.close();
			if (sc != 404) {
				forceExit('Server error');
			}
			try {
				JSON.parse(b)
			} catch (e1) {
				forceExit('Bad Response Error');
			}
			console.log('Done!');
			okay(b);
		});
		curl.perform();
	});
}
async function reporters(yn) {
	let dom = args[3];
	if (!is_domain(dom)) {
		showUsage();
	}
	if (!fs.existsSync(Dlist + dom[0])) {
		forceExit('List not found');
	}
	let data = fs.readFileSync(Dlist + dom[0], {
		encoding: 'utf8'
	});
	data = JSON.parse(data);
	if (data[dom]) {
		if (yn == 'y') {
			forceExit('Already exist, no need to report.');
		}
	} else {
		if (yn == 'n') {
			forceExit('Already not listed, no need to report.');
		}
	}
	if (myproxy == '') {
		console.log('\x1b[36mNOTICE\x1b[0m: You are not using Tor.');
	}
	console.log('Reporting ' + dom);
	let w = await reporter(dom, yn);
	if (yn == 'y') {
		if (w == '[true,true]') {
			console.log('\x1b[32mResponse\x1b[0m: \x1b[33mNo\x1b[0m, It is already known.');
		} else if (w == '[true,false]') {
			console.log('\x1b[32mResponse\x1b[0m: \x1b[35mRequest received\x1b[0m. It is not listed in \x1b[4mdelayed\x1b[0m channel at this moment.');
		} else {
			forceExit('Bad Response Error!');
		}
	} else {
		if (w == '[true,false]') {
			console.log('\x1b[32mResponse\x1b[0m: \x1b[33mNo\x1b[0m, It is not listed.');
		} else if (w == '[true,true]') {
			console.log('\x1b[32mResponse\x1b[0m: \x1b[35mRequest received\x1b[0m. It is listed in \x1b[4mdelayed\x1b[0m channel at this moment.');
		} else {
			forceExit('Bad Response Error!');
		}
	}
	process.exit();
}
if (args.length <= 2) {
	showUsage();
} else if (args[2] === '--dir') {
	chgLDR();
} else if (args[2] === '--proxy') {
	chgPROXY();
} else if (args[2] === '--dl') {
	dlFiles('https://archive.org/download/crimeflare/');
} else if (args[2] === '--dl2') {
	dlFiles('https://framagit.org/dCF/deCloudflare/-/raw/master/cloudflare_users/domains/JSON/');
} else if (args[2] === '--report') {
	reporters('y');
} else if (args[2] === '--delist') {
	reporters('n');
} else {
	queryDom();
}