/*
firepay.c - Generate a HashCash/MIME envelope around a message
Copyright (C) 2003 Ian Gulliver

This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.

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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "firemake.h"
#include "sha1.h"

#define MAX_TOKEN_LEN 256
#define MAXBITS (SHA1_DIGEST_BYTES * 8)
#define bit(c,n) (((c) >> (7 - (n))) & 1)
static char charset[] = "01235456789abcdef";
struct firestring_estr_t subheaders = {0};
int gargc;
char **gargv;

struct token_info {
	struct firestring_estr_t token;
	struct firestring_estr_t minthash;
};

int bitcmp(char *a, char *b) {
	int bits = 0;
	while (bits < MAXBITS) {
		if (a[0] != b[0])
			break;
		bits += 8;
		a++;
		b++;
	}
	while (bits < MAXBITS) {
		if (bit(a[0],bits % 8) != bit(b[0],bits % 8))
			break;
		bits++;
	}
	return bits;
}

int nextletter(char *moutput, SHA1_ctx *template, struct firestring_estr_t *token, int letter, int len) {
	SHA1_ctx chash;
	char coutput[SHA1_DIGEST_BYTES];
	int i;

	for (i = 0; i < 16; i++) {
		token->s[letter] = charset[i];
		if (token->l - 1 == letter) {
			memcpy(&chash,template,sizeof(chash));
			SHA1_Update(&chash,token->s,token->l);
			SHA1_Final(&chash,(byte *)coutput);
			if (bitcmp(moutput,coutput) >= len)
				return 0;
		} else {
			int j;
			j = nextletter(moutput,template,token,letter + 1,len);
			if (j == 0)
				return 0;
		}
	}
	return 1;
}

struct token_info *gen_token(struct firestring_estr_t *mint, int len) {
	SHA1_ctx mhash, ctemplate;
	static struct token_info info = {{0},{0}};
	int i;

	firestring_estr_expand(&info.minthash,SHA1_DIGEST_BYTES);
	SHA1_Init(&mhash);
	SHA1_Update(&mhash,mint->s,mint->l);
	memcpy(&ctemplate,&mhash,sizeof(ctemplate));
	SHA1_Final(&mhash,(byte *)info.minthash.s);
	info.minthash.l = SHA1_DIGEST_BYTES;

	firestring_estr_expand(&info.token,MAX_TOKEN_LEN);
	info.token.l = 1;

	while (info.token.l < MAX_TOKEN_LEN) {
		i = nextletter(info.minthash.s,&ctemplate,&info.token,0,len);
		if (i == 0)
			return &info;
		info.token.l++;
	}

	firestring_estr_free(&info.token);
	firestring_estr_free(&info.minthash);
	return NULL;
}

int foldback(struct firestring_estr_t *headers, struct firestring_estr_t *name) {
	struct firestring_estr_t *fullheader;

	fullheader = firemime_get_full_header(headers,name,&ESTR_S("\n"));
	if (fullheader == NULL)
		return -1;

	firestring_estr_aestrcat(&subheaders,fullheader,0);

	memmove(fullheader->s,fullheader->s + fullheader->l,headers->l - (fullheader->s - headers->s + fullheader->l));
	headers->l -= fullheader->l;

	return 0;
}

int handler(struct firemime_part *stack, int depth, void *opaque) {
	struct firestring_estr_t addheader = {0}, *tempheader, delim;
	struct firestring_estr_t mint = {0}, wirebody = {0};
	struct firestring_estr_t minthash_readable;
	struct token_info *info;
	time_t t;
	int l;

	if (foldback(&stack[depth].headers,&ESTR_S("Content-Type")) == -1)
		firestring_estr_aestrcat(&subheaders,&ESTR_S("Content-Type: text/plain\n"),0);
	foldback(&stack[depth].headers,&ESTR_S("Content-Transfer-Encoding"));
	foldback(&stack[depth].headers,&ESTR_S("Content-Dispostion"));
	foldback(&stack[depth].headers,&ESTR_S("Content-ID"));

	tempheader = firemime_get_header(&stack[depth].headers,&ESTR_S("MIME-Version"),&ESTR_S("\n"));
	if (tempheader == NULL)
		firestring_estr_aestrcat(&addheader,&ESTR_S("MIME-Version: 1.0\n"),0);

	putenv("TZ=UTC");
	time(&t);
	srand(t);

	firestring_estr_alloc(&mint,strlen(gargv[1]) + 21);
	firestring_estr_sprintf(&mint,"%s\r\n%t\r\n",gargv[1],t);
	firestring_estr_areplace(&wirebody,&stack[depth].body,&ESTR_S("\r\n"),&ESTR_S("\n"));
	firestring_estr_aestrcat(&mint,&wirebody,0);

	info = gen_token(&mint,atoi(gargv[2]));

	firestring_estr_free(&mint);

	firestring_estr_alloc(&delim,32);
	for (l = 0; l < 32; l++)
		delim.s[l] = charset[rand() % 16];
	delim.l = 32;

	firestring_estr_alloc(&minthash_readable,info->minthash.l * 4 / 3 + 4);
	firestring_estr_base64_encode(&minthash_readable,&info->minthash);

	firestring_fprintf(stdout,"%e%eContent-Type: multipart/postage; boundary=\"%e\"\n"
"\n"
"--%e\n"
"%e\n"
"%e\n"
"--%e\n"
"Content-Type: application/postage-hashcash\n"
"Version: 1.1\n"
"Mint-Hash: %e\n"
"To: %s\n"
"Date: %t\n"
"Amount: %d\n"
"\n"
"%e\n"
"--%e--\n",&stack[depth].headers,&addheader,&delim,&delim,&subheaders,&stack[depth].body,&delim,
	&minthash_readable,gargv[1],t,atoi(gargv[2]),&info->token,&delim);

	firestring_estr_free(&addheader);
	firestring_estr_free(&wirebody);
	firestring_estr_free(&info->token);
	firestring_estr_free(&info->minthash);
	firestring_estr_free(&delim);
	firestring_estr_free(&minthash_readable);

	return 2;
}

int main(int argc, char *argv[]) {
	struct firestring_estr_t message;
	int l;
	int r;

	if (argc != 3) {
		firestring_fprintf(stderr,"Usage: %s <recipient> <amount>\n",argv[0]);
		exit(1);
	}

	gargc = argc;
	gargv = argv;

	firestring_estr_alloc(&message,4096);
	while ((l = read(0,&message.s[message.l],message.a - message.l)) > 0) {
		message.l += l;
		firestring_estr_expand(&message,message.l + 4096);
	}

	r = firemime_parse(&message,handler,&ESTR_S("\n"),NULL);

	firestring_estr_free(&subheaders);
	firestring_estr_free(&message);

	/* we expect 2 back from the handler cancelling out */
	if (r != 2) {
		firestring_fprintf(stderr,"Error parsing MIME structure: %s\n",firemime_strerror(r));
		exit(1);
	}

	exit(0);
}


syntax highlighted by Code2HTML, v. 0.9.1