///////////////////////////////////////////////////////////////////////////////
//                                                                             
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>     
//                                                                             
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#include <cppcms/json.h>
#include <cppcms/urandom.h>
#include <cppcms/crypto.h>
#include <iostream>
#include <fstream>

#include "tohex.h"

void help()
{
	std::cerr << 
	"Usage: cppcms_make_key --hmac HMAC [--hmac-file hmac_key_file] [--cbc CBC] \n"
	"                    [--cbc-file cbc_key_file.txt] [-h] [--output output_file.js]\n"
	"    -h:     display help\n"
	"    HMAC:   is one of md5, sha1, sha224, sah256, sha384 or sha512\n"
	"    CBC:    is one of aes, aes128, aes192, aes256\n"
	"\n"
	"    If the options --hmac-file or --cbc-file are not given \n"
	"    the keys are created inline withing the json configuration\n"
	"    --output output configuration file, default stdout\n"
	"\n"
	"For example:\n"
	"  cppcms_make_key --hmac sha1 --hmac-file hmac.txt --output config.js\n"
	"  cppcms_make_key --hmac sha1 --cbc aes --hmac-file hmac.txt --cbc-file cbc.txt\n";

}

std::string make_key(int size)
{
	std::vector<char> key(size,0);
	std::vector<char> res(size*2+1,0);
	cppcms::urandom_device r;
	r.generate(&key[0],size);
	cppcms::impl::tohex(&key[0],size,&res[0]);
	std::string s = &res[0];
	return s;
}

bool make_entry(cppcms::json::value &val,std::string type,std::string algo,std::string file,int bytes)
{
	val["session"]["client"][type]=algo;
	std::string key=make_key(bytes);
	if(!file.empty()) {
		std::ofstream f(file.c_str());
		if(!f) {
			std::cerr << "Failed to open file " << file << std::endl;
			return false;
		}
		f << key << std::endl;
		f.close();
		val["session"]["client"][type +"_key_file"]=file;
	}
	else {
		val["session"]["client"][type+"_key"] = key;
	}
	return true;
}

int main(int argc,char **argv)
{
	std::string hmac,hmac_file;
	std::string cbc,cbc_file;
	std::string output;
	for(int i=1;i<argc;i++) {
		std::string arg=argv[i];
		if(arg=="-h") {
			help();
			return 0;
		}
		else if(i+1==argc) {
			help();
			return 1;
		}
		i++;
		std::string next = argv[i];
		if(arg=="--hmac") 
			hmac = next;
		else if(arg=="--cbc") 
			cbc = next;
		else if(arg=="--hmac-file")
			hmac_file = next;
		else if(arg=="--cbc-file")
			cbc_file = next;
		else if(arg=="--output")
			output = next;
		else {
			help();
			return 1;
		}
	}
	if(hmac.empty()) {
		help();
		return 1;
	}

	cppcms::json::value val;

	std::auto_ptr<cppcms::crypto::message_digest> digest = cppcms::crypto::message_digest::create_by_name(hmac);
	if(!digest.get()) {
		std::cerr << "Unsupported HMAC " << hmac << std::endl;
		return 1;
	}
	if(!make_entry(val,"hmac",hmac,hmac_file,digest->digest_size()))
		return 1;
	if(!cbc.empty()) {
		std::auto_ptr<cppcms::crypto::cbc> p = cppcms::crypto::cbc::create(cbc);
		if(!p.get()) {
			std::cerr << "Unsupported CBC " << cbc << std::endl;
			return 1;
		}
		if(!make_entry(val,"cbc",cbc,cbc_file,p->key_size()))
			return 1;
	}

	if(output.empty()) {
		val.save(std::cout,cppcms::json::readable);
	}
	else {
		std::ofstream out(output.c_str());
		if(!out) {
			std::cerr << "Failed to create a file " << output << std::endl;
			return 1;
		}
		val.save(out,cppcms::json::readable);
	}
	return 0;

}
