/*****************************************************************
Copyright (c) 2005 Michele Citterio
All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions 
are met:
* Redistributions of source code must retain the above copyright 
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright 
notice, this list of conditions and the following disclaimer in the 
documentation and/or other materials provided with the distribution.
* Neither the name of the <ORGANIZATION> nor the names of its 
contributors may be used to endorse or promote products derived from 
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
PARTICULAR PURPOSE AND NONINFRINGEMENT ARE DISCLAIMED. IN NO EVENT 
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.

owa is a win32 command line application to perform membership
accumulation and testing using the owaccu class, which contains an
implementation of cryptographic one-way accumulators as described 
in the paper: "Benaloh J. & de Mare M. (199?) - One-Way Accumulators: 
a Decentralized Alternative to Digital Signatures". Please see
the accompanying file owaccu.txt for further details.

Contacts: michele.citterio@unimi.it - www.citterio.net

TODO: implement exceptions handling, write actual file parser
*****************************************************************/

#include <fstream>
#include "owaccu.h"

using namespace std;

Miracl precision(500,10); 

int main(int argc, char *argv[])
{ 
	cout << "** OWACCU v0.1 - (c) 2005 Michele Citterio **\nPlease see owaccu.txt for further information!\n\n";

	miracl *mip = &precision;

//parsing the arguments on the command line... this one would be a nice and robust argument parser which could be
//elegantly turned into a class...

	const int recognized_switches_num = 6;
	int last_matched_req = 0;
	char strippedswitch[126];

	struct requests {
		int req;	//each element of the array is the id number of a recognized switch (and priority number: lowest numbered may be executed first)
		int param;	//each element of the array is the parameter passed for the corresponding recognized switch
		requests() {req = 0; param = 0;}
	};

	requests* request;
	request = new requests[recognized_switches_num];

	for(int arg_num = 1; arg_num < argc; arg_num++) //starting from 1 to skip the full pathname of the executable
	{ 
		if ((strlen(argv[arg_num]) > 1) && ((argv[arg_num][0] == '-')||(argv[arg_num][0] == '/')))// riconosce che c'č '-' e '/'
		{
			for(int it = 1; argv[arg_num][it]; it++) strippedswitch[it - 1] = argv[arg_num][it];
//sostituire gli == 0 con ! davanti
			if (((strncmp(strippedswitch, "D", 1) == 0) || (strncmp(strippedswitch, "d", 1) == 0) || (strncmp(strippedswitch, "DIGITS", 6) == 0) || (strncmp(strippedswitch, "digits", 6) == 0))){
				request[0].req = arg_num;
				last_matched_req = 0;
			}

			else if (((strncmp(strippedswitch, "R", 1) == 0) || (strncmp(strippedswitch, "r", 1) == 0) || (strncmp(strippedswitch, "RETAIN", 6) == 0) || (strncmp(strippedswitch, "retain", 6) == 0))){
				request[1].req = arg_num;
				last_matched_req = 1;
			}

			else if (((strncmp(strippedswitch, "P", 1) == 0) || (strncmp(strippedswitch, "p", 1) == 0) || (strncmp(strippedswitch, "PASSWD", 6) == 0) || (strncmp(strippedswitch, "passwd", 6) == 0))){
				request[2].req = arg_num;
				last_matched_req = 2;
			}

			else if (((strncmp(strippedswitch, "A", 1) == 0) || (strncmp(strippedswitch, "a", 1) == 0) || (strncmp(strippedswitch, "ACCUMULATE", 10) == 0) || (strncmp(strippedswitch, "accumulate", 10) == 0))){
				request[3].req = arg_num;
				last_matched_req = 3;
			}

			else if (((strncmp(strippedswitch, "C", 1) == 0) || (strncmp(strippedswitch, "c", 1) == 0) || (strncmp(strippedswitch, "CHECK", 5) == 0) || (strncmp(strippedswitch, "check", 5) == 0))){
				request[4].req = arg_num;
				last_matched_req = 4;
			}

				else if (((strncmp(strippedswitch, "H", 1) == 0) || (strncmp(strippedswitch, "h", 1) == 0) || (strncmp(strippedswitch, "HELP", 4) == 0) || (strncmp(strippedswitch, "help", 4) == 0))){
				request[5].req = arg_num;
				last_matched_req = 5;
			}

			else {
				cout << "'" << argv[arg_num] <<"' is not a recognized switch: skipped! (try owaccu -h for some help)\n\n";
				if ((arg_num < argc - 1) && ((argv[arg_num + 1][0] != '-') && (argv[arg_num + 1][0] != '/'))) arg_num++;//last_matched_req = 0;
				//return 1;// cosė esce se non trova uno switch valido o se ne trova uno invalido. Togliere per ignorarli
			}
		}
		else 
		{
			if (strlen(argv[arg_num]) > 2  && ((argv[arg_num][0] == '-')||(argv[arg_num][0] == '/'))){
				cout << "'" << argv[arg_num] <<"' is not a recognized command. Try owaccu -h for some help\n\n";
				//return 1;
			}
			else if (((request[last_matched_req].req) && !(request[last_matched_req].param))) request[last_matched_req].param = arg_num;
		}
	}
/*	cout << request[0].req << " - " << request[0].param << " " << argv[request[0].param] << "\n";
	cout << request[1].req << " - " << request[1].param << " " << argv[request[1].param] << "\n";
	cout << request[2].req << " - " << request[2].param << " " << argv[request[2].param] << "\n";
	cout << request[3].req << " - " << request[3].param << " " << argv[request[3].param] << "\n";
	cout << request[4].req << " - " << request[4].param << " " << argv[request[4].param] << "\n";
	cout << request[5].req << " - " << request[5].param << " " << argv[request[5].param] << "\n";	*/
							
//well, let's do the actual business now...
	int rigidprimedigits = 0;
	bool retain_passwds = false;
	int participants = 0;
	member_data user;
	char filename[120] = ""; //120 or whatever, is the filename length for output files
	bool done_something = false;



			
	for(int req_num = 0; req_num < recognized_switches_num; req_num++) { 
		if(request[req_num].req) {
			switch(req_num)	{
				case 0:
					if (!request[3].req) cout << "\nWarning: you specified a modulus size but you are not adding members: skipped!\n";
					else {
						if (!atoi(argv[request[0].param]))//controlla se l'argomento esiste ed č un numero (ma 12a3 viene letto come 12)
						{
							cout << "\nWarning: the number of digits for the modulus is\nmissing or mistyped! Will use the default (100)\n\n";
							rigidprimedigits = 100;
						}
						else
						{
						cout << "\nNumber of digits to be used for the modulus: " << argv[request[0].param] << "\n";
								rigidprimedigits = atoi(argv[request[0].param]);
						}
					}
					break;

				case 1:
					if (!request[3].req) cout << "\nWarning: you selected not to forget passwords but you\nare not adding members: skipped!\n";
					else {
						cout << "\nWarning - you selected not to forget passwords: they will be saved unencrypted\nin a separate file. STORE IT SECURELY AND DELETE IT WHEN YOU DON'T NEED IT\n ANYMORE!\n";
						retain_passwds = true;
					}
					break;

				case 2:
					if (!request[2].param) cout << "\nWarning: you used the -p switch but you didn't\nentered a password: assuming no password is needed!\n\n";
					break;

				case 3:
					if (!request[3].param) {
						cout << "Error: asked for accumulating a members list but no input file name was given:\nExiting!\n\n";
						return 1;
					}
					else
					{
						cout << "Asked to accumulate the members list from '" << argv[request[3].param] << "'\n";
						
						{
						ifstream in(argv[request[3].param]);
						if(!in) cout << "error opening the in file!";
						
						do //getting number of participants (lines) from the input file
						{
							in >> user.name >> user.passwd;//to improve, this will stop at spaces
							participants++;
						} while (!in.eof());
						in.close();
						cout << "\nThe " << argv[request[3].param] << " file cointains " << participants << " non-blank lines, interpreted\nas " << participants << " users to add\n\n";
						}
						
						owaccu accumulator(rigidprimedigits, 10, participants);

						ifstream in(argv[request[3].param]);
						if(!in) cout << "error opening the in file!";
						for(int it = 0; it< participants; it++)
						{
							in >> user.name >> user.passwd;
							cout << "iteration n. " << it + 1 << " - found user " << user.name << "... ";
							accumulator.add_member(&user);
							cout << "accumulated!\n";
#ifdef VERBOSE_OUTPUT
								cout << "\nAt this stage of members accumulation, the z_k credential for n. " << it << " " << accumulator.get_member_data(it).name << " is:\n" << accumulator.get_member_data(it).z_k <<"\n";
								cout << "Resulting accumulated hash is:\n" << accumulator.get_accumulated_hash()<< "\n\n";
#endif
						}
						in.close();

						strcpy(filename, "accumulated_") ;
						strcat(filename, argv[request[3].param]);
						ofstream out(filename);
						out << "[owaccu]\n";
						out << "Version=owaccu_1.00\n";
						out << "IONumberBase=60\n\n";

						mip->IOBASE = 60;
						out << "[one-way_accumulator]\n";
						out << "RigidPrimeDigits=" << rigidprimedigits << "\n";
						out << "Modulus=" << accumulator.get_rigid_prime() << "\n";
						out << "AgreedBase=" << accumulator.get_agreed_base() << "\n";
						out << "AccumulatedHash=" << accumulator.get_accumulated_hash() << "\n\n";
						mip->IOBASE = 10;
						out.close();


						for (it = 0; it < participants; it++)//writing each user's credantials file
						{
							user = accumulator.get_member_data(it);
							strcpy (filename, user.name);
							strcat (filename, "_");
							strcat (filename, argv[request[3].param]);
							ofstream out(filename);
							out << "[owaccu]\n";
							out << "Version=owaccu_1.00\n";
							out << "IONumberBase=60\n\n";

							mip->IOBASE = 60;
							out << "[accumulator's_data]\n";
							out << "RigidPrimeDigits=" << rigidprimedigits << "\n";
							out << "Modulus=" << accumulator.get_rigid_prime() << "\n";
							out << "AgreedBase=" << accumulator.get_agreed_base() << "\n";
							out << "AccumulatedHash=" << accumulator.get_accumulated_hash() << "\n\n";

							out << "[member's_data]\n";
							out << "Name=" << user.name << "\n";
							out << "Credential=" << user.z_k <<"\n";
							mip->IOBASE = 10;
							out.close();
						}

						if (retain_passwds) {
							strcpy(filename, "passwords_") ;
							strcat(filename, argv[request[3].param]);
							ofstream out(filename);
							out << "[owaccu]\n";
							out << "Version=owaccu_1.00\n\n";

							out << "[members'_secrets]\n";
							for (it = 0; it < participants; it++)//writing each user's secret passwds file
							{
								user = accumulator.get_member_data(it);
								out << "NameAndSecretPassword=" << user.name << "," << user.passwd <<"\n";
							}
							out.close();
						}

					}
					break;

				case 4:
					if (!request[4].param) {
						cout << "Error: asked for checking a user's membership but no input file name was given: Exiting!\n\n";
						return 1;
					}
					else
					{
//TODO: improve, all this is a quick hack using filenames, skipping fields, ...
						member_data member;
						char* filename;
						Big modulus=0;
						Big agreedbase=0;
						Big accumulatedhash=0;

						mip->IOBASE = 60;

						strcpy(member.name, argv[request[4].param]);
						strtok(member.name, "_");
						filename = strtok(NULL, ".");
						if (request[2].param) {
							strcpy(member.passwd, argv[request[2].param]);
							cout << "Asked to check " << member.name << ", whose password is " << member.passwd << ", for membership of \nthe group " << filename << "\n\n";
						}
						else {
							strcpy(member.passwd, "");
							cout << "Asked to check " << member.name << ", who has no password, for membership of \nthe group " << filename << "\n\n";
						}
					
					//	-a congress.txt -d 108 

						
						
							char linein[400];
							//char temp[200];
							ifstream in(argv[request[4].param]);
							if(!in) cout << "error opening the in file!";
						
							do //parsing the user's file. 
							{
								in >> linein;
								//cout << linein << "\n";
								if (!strncmp(linein, "Modulus", 7)) {
									strtok(linein, "=");
									modulus = strtok(NULL, "=");
								}
								if (!strncmp(linein, "AgreedBase", 10)) {
									strtok(linein, "=");
									agreedbase = strtok(NULL, "=");
								}
								if (!strncmp(linein, "AccumulatedHash", 15)) {
									strtok(linein, "=");
									accumulatedhash = strtok(NULL, "=");
								}
								if (!strncmp(linein, "Credential", 10)) {
									strtok(linein, "=");
									member.z_k = strtok(NULL, "=");
								}
								//cin >> linein;
									//in >> linein;
									//if (!strncmp(strtok(linein, "="), "Version", 7) ) 

								
							} while (!in.eof());
							in.close();
							if ((modulus != 0) && (agreedbase != 0) && (accumulatedhash != 0) && (member.z_k !=0 )) {
								owaccu accumulator (modulus, agreedbase, accumulatedhash);
								if (accumulator.check_membership(&member)) {cout << "YES, " << member.name << " is a member of " << filename << "!!!\n";}
								else cout << "NO, " << member.name << " is not a member of " << filename << " (or the password is wrong)!!!\n";
							}
						}
					break;

				case 5:
					cout << "\nRead the docs, and www.citterio.net/software/software.html\n";
					break;
			}
		}

	}
	return 0;
}

