/*
 * Copyright (C) 1998 Jonas Borgstrm <jonas_b@bitsmart.com>
 *
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "../getopt/getopt.h"
#endif

#include "zipcracker.h"

#if USE_PVM
#include <pvm3.h>
#include "pvm.h"
#else

#endif

#ifdef __riscos__
#include <unixlib/local.h>
#endif /* __riscos__ */

extern char *optarg;
extern int optind, opterr, optopt;

char passwd[PW_LEN];           /* The Latest passwd block end */
/* sizeof(string)^BLOCK_SIZE */
struct crack_todo *todo = NULL, *todo_first = NULL;
struct crack_slave slaves[MAX_SLAVES];
int num_slaves = 0;            /* Number of slaves running */
int block_size;                /* Combinations per block,  */

char string[257];
char format[257];

char zipname[1024];
char targetname[1024];
/*****************************************************************************/
/*    Reads saved variables from ~/.zipcracker */
/*****************************************************************************/
int open_saved() {
	FILE *in;
	char *home, filename[512], buffer[512];
	struct crack_todo *todo_tmp;

#ifdef WIN32
	home=getenv("windir");
	sprintf(filename,"%s\\zipcracker.ini",home);
#else
	home=getenv("HOME");
	sprintf(filename,"%s/.zipcracker",home);
#endif
	if((in=fopen(filename,"r"))==NULL) {
		printf("Can't continue. Can't open file '%s'.\n", filename);

		exit(1);
	}

	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	if(strcmp(buffer, "[ZIPCRACKER]")!=0) {
		printf("Not an valid file '%s'\n", buffer);
		exit(RETURN_ERROR_PARSING_ZIPCRACKER);
	}
	/* Zipname */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	strtok(buffer, "'");
	strcpy(zipname, strtok(NULL, "'"));
	/* Targetname */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	strtok(buffer, "'");
	strcpy(targetname, strtok(NULL, "'"));
	/* Passwd */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	strtok(buffer, "'");
	strcpy(passwd, strtok(NULL, "'"));
	/* String */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	strtok(buffer, "'");
	strcpy(string, strtok(NULL, "'"));
	/* Format */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	strtok(buffer, "'");
	strcpy(format, strtok(NULL, "'"));
	/* Todo */
	fgets(buffer, 512, in);
	buffer[strlen(buffer) - 1] = 0;
	if(strcmp(buffer, "[TODO]") == 0) {
		while(fgets(buffer, 512, in) != NULL) {
			buffer[strlen(buffer) - 1] = 0;
			todo_tmp = calloc(sizeof(struct crack_todo), 1);
			strcpy(todo_tmp->start, strtok(buffer, "'"));
			strtok(NULL, "'");
			strcpy(todo_tmp->end, strtok(NULL, "'"));

			if(todo_first == NULL) {

				todo = todo_first = todo_tmp;
			} else if(todo_first->next == NULL) {

				todo = todo_first->next = todo_tmp;
			} else {

				todo->next = todo_tmp;
				todo = todo->next;
			}
		}
		todo = todo_first;
		fclose(in);
	}
	return(1);
}
/*****************************************************************************/
/*    Saves variables if program gets interrupted */
/*****************************************************************************/
void ctrl_c(int sig) {
	FILE *out;
	char *home, filename[512];
	int i;

	if(sig == SIGINT)
		printf("\n\n-- Interrupted --\n");

#ifdef WIN32
	home=getenv("windir");
	sprintf(filename,"%s\\zipcracker.ini",home);
#else
	home = getenv("HOME");
	sprintf(filename,"%s/.zipcracker",home);
#endif
	if((out=fopen(filename,"w"))!=0) {
		fprintf(out, "[ZIPCRACKER]\n");
		fprintf(out, "zipname = '%s'\n", zipname);
		fprintf(out, "targetname = '%s'\n", targetname);
		fprintf(out, "passwd = '%s'\n", passwd);
		fprintf(out, "string = '%s'\n", string);
		fprintf(out, "format = '%s'\n", format);
		fprintf(out, "[TODO]\n");
		/* Saving unfinished blocks */
		for(i=0;i<MAX_SLAVES;i++) {
			if(slaves[i].needsave)
				fprintf(out, "'%s' to '%s'\n",
					slaves[i].start,
					slaves[i].end);
		}
		/* Saving unfinished block-list, if any */
		while(todo) {
			fprintf(out, "'%s' to '%s'\n",
				todo->start,
				todo->end);
			todo = todo->next;
		}
		fclose(out);
	}
#if USE_PVM
	pvm_exit();
#endif
	exit((sig == SIGINT)?RETURN_INTERRUPTED: RETURN_OK);
}
/*****************************************************************************/
/*   Updates passwd i.e: a -> b,  bb -> cb */
/*****************************************************************************/
void inc_passwd(char *guess) {
	int i;

	i = 0;
	guess[i] = *(strchr(string, guess[i]) + 1);

	while(guess[i] == 0) {
		if(guess[i+1] == 0) {
			guess[i] = string[0];
			guess[i + 1] = string[0];
			guess[++i+1] = 0;
			break;
		}
		else {
			guess[i] = string[0];
			i++;
			guess[i] = *(strchr(string, guess[i])+ 1);
		}
	}
}
/*****************************************************************************/
/*   Updates passwd i.e a -> aaaaaa */
/*****************************************************************************/
void inc_much_passwd(char *guess) {
	int i, len;

	/* If password is to short, make it longer */
	if(strlen(guess) < block_size) {
		len = strlen(guess);
		for(i=len;i < block_size;i++) {
			guess[i] = string[0];
		}
		guess[block_size] = 0;
		return;
	}

	i = block_size - 1;
	guess[i] = *(strchr(string, guess[i]) + 1);
	while(guess[i] == 0) {
		if(guess[i+1] == 0) {
			guess[i] = string[0];
			guess[i + 1] = string[0];
			guess[++i+1] = 0;
			break;
		}
		else {
			guess[i] = string[0];
			i++;
			guess[i] = *(strchr(string, guess[i])+ 1);
		}
	}
}
/******************************************************************b***********/
/*   Initializes the password */
/*****************************************************************************/
void init_passwd() {
	int minlen = 0, len, i;
	char *ptr = format;

	for(i=0;i<strlen(passwd);i++) {
		if(strchr(string, passwd[i]) == NULL) {
			printf("Invalid password character('%c')\n",
			       passwd[i]);
			exit(1);
		}
	}
	/* Calculating min length of password */
	while( (ptr = strchr(ptr, '?') + 1) != (char *)1)
		minlen++;

	/* Make it longer if it's to short */
	if(strlen(passwd) < minlen) {
		len = strlen(passwd);
		for(i=len;i<minlen;i++) {
			passwd[i] = string[0];
		}
		passwd[minlen] = 0;
	}
	if(strchr(format, '*') == NULL) {

		printf("Adding * to format\n");
		strcat(format, "*");
	}
}
/****************************************************************************/
/*    Formats the real password */
/****************************************************************************/
char *format_passwd(char *guess, char *realpw) {
	int i, j = 0, k = 0;

	for(i=0;i<strlen(format);i++) {
		switch(format[i]) {
		case '?':
			if(guess[k])
				realpw[j++] = guess[k++];
			break;
		case '*':
			while(guess[k] != 0)
				realpw[j++] = guess[k++];
			break;
		default:
			realpw[j++] = format[i];
		};
	}
	realpw[j] = 0;

	return realpw;
}
/*****************************************************************************/
/*    Displaying help page */
/*****************************************************************************/
void display_help() {
	printf("Usage: zipcracker [OPTION]... [ZIP] [TARGET]\n");
	printf("Tries to find the [TARGET] file's password in the [ZIP] archive.\n");
	printf("\n");
	printf("-b, --blocksize=BLOCKSIZE  Using blocksize BLOCKSIZE. (0 < BLOCKSIZE < %d).\n", PW_LEN);
	printf("                           Default: %d.\n", DEFAULT_BLOCK_SIZE);
	printf("-c, --continue             Continues with a previous task from ~/.zipcracker.\n");
	printf("-f, --format=FORMAT        Uses FORMAT to format the password.\n");
	printf("                           Default: \"%s\".\n", DEFAULT_FORMAT);
	printf("-h, --help                 Displays this help page.\n");
#if USE_PVM
	printf("-n, --number=NUMBER        Uses NUMBER tasks in virtual machine.\n");
	printf("                           Default: number of computer in virtual machine.\n");
#endif
	printf("-s, --string=STRING        Uses the characters in STRING to create the passwords\n");
	printf("                           Default: \"%s\"\n", DEFAULT_STRING);
	printf("-v, --version              Displays program version.\n");
	printf("\n");
	printf("Example: zipcracker secret.zip secret.txt\n");
	printf("If you know that the first character in password is 's':\n");
	printf("         zipcracker secret.zip secret.txt --format=\"s*\"\n");
	printf("If you know the password is digits only:\n");
	printf("         zipcracker secret.zip secret.txt --string=\"0123456789\"\n");
	printf("If you know the password is four character long:\n");
	printf("         zipcracker secret.zip secret.txt --format=\"????*\"\n");
}
#ifndef USE_PVM
/*****************************************************************************/
/*    Main loop for the "NON PVM" Version */
/*****************************************************************************/
void main_loop() {
	char start[PW_LEN];
	char end[PW_LEN];
	unsigned long rate = 0;
	struct crack_todo *todo_tmp;
	char buf[256]; /* temporary buffer*/

	parse_zipfile();
	num_slaves++;

	printf("-----------\n");
	printf("Zip Cracker\n");
	printf("-----------\n");

	signal(SIGTERM, ctrl_c);
	signal(SIGINT, ctrl_c);

	if(strlen(passwd) == 0)
		strncpy(passwd, string, 1);

	init_passwd();
	while(1) {
		printf("New block: ");
		if(todo) {
			/* Using saved blocks */
			strcpy(start, todo->start);
			strcpy(slaves[0].start, start);
			strcpy(end, todo->end);
			strcpy(slaves[0].end, end);
			todo_tmp = todo;
			todo = todo->next;
			free(todo_tmp);
		} else {
			/* Using new block */
			strcpy(start, passwd);
			strcpy(slaves[0].start, passwd);
			inc_much_passwd(passwd);
			strcpy(end, passwd);
			strcpy(slaves[0].end, passwd);
			inc_passwd(passwd);
		}
		slaves[0].needsave = 1;
		slaves[0].start_t = time(NULL);
		/* Display info on screen */
		printf("'%s'->",
		       format_passwd(start, buf));
		printf("'%s'. ",
		       format_passwd(end, buf));
		if(rate) {
			printf("Rate = %lu combs/s\n",rate);
		} else
			puts(""); /* New line */

		crack(start, end, &rate);
	};
}
#endif
/*****************************************************************************/
/*    Program entry point */
/*****************************************************************************/
void main(int argc, char *argv[]) {
	int c;
	int continue_flag = 0;

#ifdef __riscos__
        __riscosify_control = __RISCOSIFY_NO_PROCESS;
#endif /* __riscos__ */

#if USE_PVM
	/* Are we a slave? */
	if((argc > 1) && (strcmp(argv[1], "--slave") == 0)) {

		slave_mainloop();
		exit(0);
	}
#endif
	/* Default values */
	strcpy(string, DEFAULT_STRING);
	strcpy(format, DEFAULT_FORMAT);
	block_size = DEFAULT_BLOCK_SIZE;/* Combinations per block,  */

	// Initializes structure
	memset(slaves, 0, sizeof(struct crack_slave) * MAX_SLAVES);

	while (1) {
		int option_index = 0;
		static struct option long_options[] = {
			{"blocksize", 1, 0, 'b'},
			{"continue", 0, 0, 'c'},
			{"format", 1, 0, 'f'},
			{"help", 0, 0, 'h'},
#if USE_PVM
			{"number", 1, 0, 'n'},
#endif
			{"string", 1, 0, 's'},
			{"version", 0, 0, 'v'},
			{0, 0, 0, 0}
		};
#if USE_PVM
		c = getopt_long (argc, argv, "b:cf:hn:s:v",
				 long_options, &option_index);
#else
		c = getopt_long (argc, argv, "b:cf:hs:v",
				 long_options, &option_index);

#endif
		if (c == -1)
			break;

		switch (c) {
		case 'b':
			if(atoi(optarg) > 0 && atoi(optarg) < PW_LEN)
				block_size = atoi(optarg);
			break;
		case 'c':
			open_saved();
			continue_flag = 1;
			break;
		case 'f':
			strcpy(format, optarg);
			break;
		case 'h':
			display_help();
			exit(RETURN_OK);
#if USE_PVM
		case 'n':
			nhost = atoi(optarg);
			break;
#endif
		case 's':
			strcpy(string, optarg);
			break;
		case 'v':
			printf("zipcracker: Version %s\n", VERSION);
			exit(RETURN_OK);

		default:
			printf ("?? getopt returned character code 0%o ??\n", c);
			exit(RETURN_PARSE_ERROR);
		}
	}
	if(continue_flag) {
#if USE_PVM
		pvm_start();
#else
		main_loop();
#endif
	} else {
		if ((argc - optind) != 2) {
			/* Display help and exit */
			display_help();
			exit(RETURN_OK);
		} else {
			/* Add CWD if zipname is without path */
#ifdef WIN32
			if((strchr(argv[optind], '\\') == NULL) &&
			   (strchr(argv[optind], ':') == NULL)) {

				getcwd(zipname, 1024);
				strcat(zipname, "\\");
				strcat(zipname, argv[optind++]);
			} else
				strcpy(zipname, argv[optind++]);
#else
#ifndef RISCOS
			if(strchr(argv[optind], '/') == NULL) {

				getcwd(zipname, 1024);
				strcat(zipname, "/");
				strcat(zipname, argv[optind++]);
			} else
#endif
				strcpy(zipname, argv[optind++]);
#endif
			strcpy(targetname, argv[optind++]);
		}
#if USE_PVM
		pvm_start();
#else
		main_loop();
#endif
	}
}







